╭──────────╮ │← previous│ ╰──────────╯ ╭ changed ──╮ │src/web.zig│ ╰───────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │159 switch (host) { │ │160 .server => |server| { │ │161 // "repo/new" and "user/new" come before "new", which would claim them │ │162 - const PostRoute = enum { login, logout, @"repo/new", @"user/new", user, settings, grant, revoke, promote, demote, new, edit, remove, open, close, resolve, publish, merge, squash, attach, assign, unassign, subscribe, mute, undo, clear }; │ │162 + const PostRoute = enum { login, logout, @"repo/new", @"user/new", user, addkey, removekey, settings, grant, revoke, promote, demote, new, edit, remove, open, close, resolve, publish, merge, squash, attach, assign, unassign, subscribe, mute, undo, clear };│ │163 inline for (@typeInfo(PostRoute).@"enum".field_names) |name| { │ │164 const suffix = "/" ++ name; │ │165 if (std.mem.endsWith(u8, path, suffix)) { │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │170 .@"repo/new" => handleRepoNew(io, request, allocator, path, server.admin_repo_path, server.users_dir, server.session_store), │ │171 .@"user/new" => handleUserNew(io, request, allocator, path, server.admin_repo_path, server.users_dir, server.session_store), │ │172 .user => handleUserSettings(io, request, allocator, base, server.admin_repo_path, server.users_dir, server.session_store), │ │173 + .addkey => handleUserKey(io, request, allocator, base, server.admin_repo_path, server.users_dir, server.session_store, .add), │ │174 + .removekey => handleUserKey(io, request, allocator, base, server.admin_repo_path, server.users_dir, server.session_store, .remove),│ │175 .settings => handleRepoSettings(io, request, allocator, path, host), │ │176 .grant => handleRoles(io, request, allocator, base, host, .grant), │ │177 .revoke => handleRoles(io, request, allocator, base, host, .revoke), │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │897 }); │ │898 } │ │899 │ │900 +// add or remove one of the logged-in user's ssh keys, then return to the ssh │ │901 +// keys tab that posted the form, which a failure also goes back to. │ │902 +fn handleUserKey( │ │903 + io: std.Io, │ │904 + request: *std.http.Server.Request, │ │905 + allocator: std.mem.Allocator, │ │906 + form_location: []const u8, │ │907 + admin_repo_path: []const u8, │ │908 + users_dir: []const u8, │ │909 + session_store: SessionStore, │ │910 + action: evt.KeyAction, │ │911 +) !void { │ │912 + const user_id = requestUserId(request, session_store) orelse return respondLoginRequired(request); │ │913 + │ │914 + const body = try readFormBody(request, allocator); │ │915 + defer allocator.free(body); │ │916 + const key = (try parseFormField(allocator, body, "key")) orelse try allocator.dupe(u8, ""); │ │917 + defer allocator.free(key); │ │918 + │ │919 + { │ │920 + var admin_repo = try rp.Repo(.xit, evt.admin_repo_opts).open(io, allocator, .{ .path = admin_repo_path }); │ │921 + defer admin_repo.deinit(io, allocator); │ │922 + evt.changeUserKey(io, allocator, users_dir, &admin_repo, &user_id, action, key) catch |err| switch (err) { │ │923 + error.NotFound => return respondLoginRequired(request), │ │924 + else => { │ │925 + const failure = ui.Session.FormFeedback.KeyFailure.fromError(err) orelse return err; │ │926 + return respondFormFailure(request, allocator, session_store, form_location, .{ .user_keys = .{ .failure = failure, .key = key } });│ │927 + }, │ │928 + }; │ │929 + } │ │930 + │ │931 + try request.respond("", .{ │ │932 + .status = .see_other, │ │933 + .extra_headers = &.{.{ .name = "location", .value = form_location }}, │ │934 + }); │ │935 +} │ │936 + │ │937 // create a thread in the repo the form's page names and redirect to it. │ │938 fn handleThreadNew( │ │939 io: std.Io, │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯