╭ changed ──╮ │src/web.zig│ ╰───────────╯ ╭───────────╮ │← all files│ ╰───────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────╮ │425 }); │ │426 } │ │427 │ │428 -// the author for an event created by this request, or null when the request │ │429 -// may not create one. local mode has no accounts, so it authors anonymously. │ │428 +const RequestAuthor = struct { │ │429 + author: evt.CommitAuthor, │ │430 + user_id: ?[evt.event_id_size]u8 = null, │ │431 +}; │ │432 + │ │433 +// the author and user id for an event created by this request, or null when the│ │434 +// request may not create one. local mode has no accounts, so it has no user id.│ │435 fn eventAuthor( │ │436 io: std.Io, │ │437 allocator: std.mem.Allocator, │ ╰─────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────╮ │438 arena: *std.heap.ArenaAllocator, │ │439 request: *std.http.Server.Request, │ │440 host: Host, │ │436 -) !?evt.CommitAuthor { │ │441 +) !?RequestAuthor { │ │442 const remote = switch (host) { │ │443 .remote => |remote| remote, │ │439 - .local => |src| return try ui.localAuthor(src, io, allocator, arena), │ │444 + .local => |src| return .{ .author = try ui.localAuthor(src, io, allocator, arena) },│ │445 }; │ │446 const token = getCookieValue(request, cookie_name) orelse return null; │ │447 var user_id: [evt.event_id_size]u8 = undefined; │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │451 defer repo.deinit(io, allocator); │ │452 const moment = try evt.currentMoment(evt.admin_repo_opts, &repo); │ │453 const user = (try evt.User.readById(evt.AdminDB, evt.admin_repo_opts.hash, moment, arena, &user_id)) orelse return null;│ │449 - return .{ .name = user.event.name, .email = user.event.email }; │ │454 + return .{ │ │455 + .author = .{ .name = user.event.name, .email = user.event.email }, │ │456 + .user_id = user_id, │ │457 + }; │ │458 } │ │459 │ │460 // refuse a write from a request with no logged-in user. the body goes unread, │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────╮ │467 }); │ │468 } │ │469 │ │462 -// new actions share a suffix; the base identifies whether the form creates an│ │463 -// issue or comment. │ │470 +// new actions share a suffix; the base identifies whether the form creates a │ │471 +// thread or comment. │ │472 fn handleNew( │ │473 io: std.Io, │ │474 request: *std.http.Server.Request, │ ╰───────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │480 │ │481 const issues_suffix = "/issues"; │ │482 if (std.mem.endsWith(u8, base, issues_suffix)) │ │475 - return handleTopicNew(io, request, allocator, base[0 .. base.len - issues_suffix.len], host, .issue); │ │483 + return handleThreadNew(io, request, allocator, base[0 .. base.len - issues_suffix.len], host, .issue); │ │484 const discussions_suffix = "/discussions"; │ │485 if (std.mem.endsWith(u8, base, discussions_suffix)) │ │478 - return handleTopicNew(io, request, allocator, base[0 .. base.len - discussions_suffix.len], host, .discuss); │ │486 + return handleThreadNew(io, request, allocator, base[0 .. base.len - discussions_suffix.len], host, .discuss);│ │487 const patches_suffix = "/patches"; │ │488 if (std.mem.endsWith(u8, base, patches_suffix)) │ │481 - return handleTopicNew(io, request, allocator, base[0 .. base.len - patches_suffix.len], host, .patch); │ │489 + return handleThreadNew(io, request, allocator, base[0 .. base.len - patches_suffix.len], host, .patch); │ │490 │ │491 try request.respond("new event target not found", .{ │ │492 .status = .not_found, │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────╮ │494 }); │ │495 } │ │496 │ │489 -// create an issue or discussion in the repo the form's page names and redirect to it.│ │490 -fn handleTopicNew( │ │497 +// create a thread in the repo the form's page names and redirect to it. │ │498 +fn handleThreadNew( │ │499 io: std.Io, │ │500 request: *std.http.Server.Request, │ │501 allocator: std.mem.Allocator, │ ╰───────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │505 ) !void { │ │506 var author_arena = std.heap.ArenaAllocator.init(allocator); │ │507 defer author_arena.deinit(); │ │500 - const author = (try eventAuthor(io, allocator, &author_arena, request, host)) orelse return respondLoginRequired(request); │ │501 - var patch_user_id: ?[evt.event_id_size]u8 = null; │ │502 - if (kind == .patch) { │ │503 - const remote = switch (host) { │ │504 - .remote => |remote| remote, │ │505 - .local => return respondRemoveNotFound(request), │ │506 - }; │ │507 - const token = getCookieValue(request, cookie_name) orelse return respondLoginRequired(request); │ │508 - var user_id: [evt.event_id_size]u8 = undefined; │ │509 - if (!remote.session_store.lookup(token, &user_id)) return respondLoginRequired(request); │ │510 - patch_user_id = user_id; │ │511 - } │ │508 + const request_author = (try eventAuthor(io, allocator, &author_arena, request, host)) orelse return respondLoginRequired(request);│ │509 + const author = request_author.author; │ │510 const body = try readFormBody(request, allocator); │ │511 defer allocator.free(body); │ │512 │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │552 .remote => |remote| remote, │ │553 .local => return respondRemoveNotFound(request), │ │554 }; │ │557 - const user_id = patch_user_id orelse return respondLoginRequired(request); │ │555 + const user_id = request_author.user_id orelse return respondLoginRequired(request); │ │556 const request_repo = (try requestRepoSource(io, allocator, host, base)) orelse return respondRemoveNotFound(request); │ │557 defer request_repo.deinit(allocator); │ │558 const repo_id = evt.parseEventId(std.fs.path.basename(request_repo.source.path)) catch return respondRemoveNotFound(request);│ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │560 defer admin_repo.deinit(io, allocator); │ │561 const repos_dir = try std.fs.path.join(allocator, &.{ std.fs.path.dirname(remote.admin_repo_path) orelse ".", "repos" });│ │562 defer allocator.free(repos_dir); │ │565 - const path = try fork.create(.{}, io, allocator, repos_dir, &admin_repo, .{ │ │563 + const fork_path = try fork.create(.{}, io, allocator, repos_dir, &admin_repo, .{ │ │564 .id = event_id_hex, │ │565 .user_id = user_id, │ │566 .repo_id = repo_id, │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │570 .author = author, │ │571 .timestamp = @intCast(std.Io.Timestamp.now(io, .real).toSeconds()), │ │572 }); │ │575 - defer allocator.free(path); │ │573 + defer allocator.free(fork_path); │ │574 const location = try std.fmt.allocPrint(allocator, "{s}/patch:{s}", .{ base, &event_id_hex }); │ │575 defer allocator.free(location); │ │576 try request.respond("", .{ .status = .see_other, .extra_headers = &.{.{ .name = "location", .value = location }} });│ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────╮ │next →│ ╰──────╯