╭ changed ──╮ │src/web.zig│ ╰───────────╯ ╭───────────╮ │← all files│ ╰───────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │45 clone_ssh_port: ?u16, │ │46 }, │ │47 local: ui.RepoSource, │ │48 +}; │ │49 + │ │50 +// an on-disk repo resolved from a request url. remote paths are owned. │ │51 +const RequestRepoSource = struct { │ │52 + source: ui.RepoSource, │ │53 + owned_path: ?[]const u8 = null, │ │54 + │ │55 + fn deinit(self: RequestRepoSource, allocator: std.mem.Allocator) void { │ │56 + if (self.owned_path) |path| allocator.free(path); │ │57 + } │ │58 }; │ │59 + │ │60 +fn requestRepoSource(io: std.Io, allocator: std.mem.Allocator, host: Host, repo_base: []const u8) !?RequestRepoSource { │ │61 + return switch (host) { │ │62 + .remote => |remote| blk: { │ │63 + const repo_prefix = "/repo/"; │ │64 + if (!std.mem.startsWith(u8, repo_base, repo_prefix)) break :blk null; │ │65 + const repos_dir = try std.fs.path.join(allocator, &.{ std.fs.path.dirname(remote.admin_repo_path) orelse ".", "repos" }); │ │66 + defer allocator.free(repos_dir); │ │67 + const path = switch (try serve_common.resolveRepoPath(io, allocator, repos_dir, remote.admin_repo_path, repo_base[repo_prefix.len..], false)) {│ │68 + .ok => |value| value, │ │69 + .invalid, .not_found => break :blk null, │ │70 + }; │ │71 + break :blk .{ │ │72 + .source = .{ .path = path, .repo_kind = .xit }, │ │73 + .owned_path = path, │ │74 + }; │ │75 + }, │ │76 + .local => |source| if (repo_base.len == 0) .{ .source = source } else null, │ │77 + }; │ │78 +} │ │79 │ │80 pub fn handleConnection( │ │81 io: std.Io, │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │539 }; │ │540 │ │541 const not_found = "repo not found"; │ │512 - switch (host) { │ │513 - .remote => |remote| { │ │514 - const repo_prefix = "/repo/"; │ │515 - if (!std.mem.startsWith(u8, base, repo_prefix)) { │ │516 - try request.respond(not_found, .{ │ │517 - .status = .not_found, │ │518 - .extra_headers = &.{.{ .name = "content-type", .value = "text/plain" }}, │ │519 - }); │ │520 - return; │ │542 + const request_repo = (try requestRepoSource(io, allocator, host, base)) orelse { │ │543 + try request.respond(not_found, .{ │ │544 + .status = .not_found, │ │545 + .extra_headers = &.{.{ .name = "content-type", .value = "text/plain" }}, │ │546 + }); │ │547 + return; │ │548 + }; │ │549 + defer request_repo.deinit(allocator); │ │550 + const source = request_repo.source; │ │551 + switch (source.repo_kind) { │ │552 + inline else => |repo_kind| { │ │553 + var any_repo = try rp.AnyRepo(repo_kind, .{}).open(io, allocator, source.localInitOpts()); │ │554 + defer any_repo.deinit(io, allocator); │ │555 + switch (any_repo) { │ │556 + inline else => |*repo| try evt.consume(repo_kind, repo.self_repo_opts, io, allocator, repo, evt.events_ref, &[_]evt.EventWithId{event}), │ │557 } │ │522 - │ │523 - const repos_dir = try std.fs.path.join(allocator, &.{ std.fs.path.dirname(remote.admin_repo_path) orelse ".", "repos" }); │ │524 - defer allocator.free(repos_dir); │ │525 - const repo_path = switch (try serve_common.resolveRepoPath(io, allocator, repos_dir, remote.admin_repo_path, base[repo_prefix.len..], false)) { │ │526 - .ok => |p| p, │ │527 - .invalid, .not_found => { │ │528 - try request.respond(not_found, .{ │ │529 - .status = .not_found, │ │530 - .extra_headers = &.{.{ .name = "content-type", .value = "text/plain" }}, │ │531 - }); │ │532 - return; │ │533 - }, │ │534 - }; │ │535 - defer allocator.free(repo_path); │ │536 - │ │537 - var repo = try rp.Repo(.xit, .{}).open(io, allocator, .{ .path = repo_path }); │ │538 - defer repo.deinit(io, allocator); │ │539 - try evt.consume(.xit, .{}, io, allocator, &repo, evt.events_ref, &[_]evt.EventWithId{event}); │ │558 }, │ │541 - .local => |src| { │ │542 - // local routes elide the repo base │ │543 - if (base.len != 0) { │ │544 - try request.respond(not_found, .{ │ │545 - .status = .not_found, │ │546 - .extra_headers = &.{.{ .name = "content-type", .value = "text/plain" }}, │ │547 - }); │ │548 - return; │ │549 - } │ │550 - switch (src.repo_kind) { │ │551 - inline else => |repo_kind| { │ │552 - var any_repo = try rp.AnyRepo(repo_kind, .{}).open(io, allocator, src.localInitOpts()); │ │553 - defer any_repo.deinit(io, allocator); │ │554 - switch (any_repo) { │ │555 - inline else => |*repo| try evt.consume(repo_kind, repo.self_repo_opts, io, allocator, repo, evt.events_ref, &[_]evt.EventWithId{event}),│ │556 - } │ │557 - }, │ │558 - } │ │559 - }, │ │559 } │ │560 │ │561 const location = try std.fmt.allocPrint(allocator, "{s}/{s}:{s}", .{ base, @tagName(kind), &event_id_hex }); │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │634 const thread_id_hex = std.fmt.bytesToHex(parts.thread_id, .lower); │ │635 const parent_id_hex = std.fmt.bytesToHex(parts.comment_id orelse parts.thread_id, .lower); │ │636 │ │637 + const request_repo = (try requestRepoSource(io, allocator, host, parts.repo_base)) orelse { │ │638 + try request.respond(not_found, .{ │ │639 + .status = .not_found, │ │640 + .extra_headers = &.{.{ .name = "content-type", .value = "text/plain" }}, │ │641 + }); │ │642 + return; │ │643 + }; │ │644 + defer request_repo.deinit(allocator); │ │645 + const source = request_repo.source; │ │646 var event_id_hex: [evt.event_id_size * 2]u8 = undefined; │ │639 - switch (host) { │ │640 - .remote => |remote| { │ │641 - const repo_prefix = "/repo/"; │ │642 - if (!std.mem.startsWith(u8, parts.repo_base, repo_prefix)) { │ │643 - try request.respond(not_found, .{ │ │644 - .status = .not_found, │ │645 - .extra_headers = &.{.{ .name = "content-type", .value = "text/plain" }}, │ │646 - }); │ │647 - return; │ │648 - } │ │649 - const repos_dir = try std.fs.path.join(allocator, &.{ std.fs.path.dirname(remote.admin_repo_path) orelse ".", "repos" }); │ │650 - defer allocator.free(repos_dir); │ │651 - const repo_path = switch (try serve_common.resolveRepoPath(io, allocator, repos_dir, remote.admin_repo_path, parts.repo_base[repo_prefix.len..], false)) { │ │652 - .ok => |p| p, │ │653 - .invalid, .not_found => { │ │654 - try request.respond(not_found, .{ │ │655 - .status = .not_found, │ │656 - .extra_headers = &.{.{ .name = "content-type", .value = "text/plain" }}, │ │657 - }); │ │658 - return; │ │659 - }, │ │660 - }; │ │661 - defer allocator.free(repo_path); │ │662 - │ │663 - var repo = try rp.Repo(.xit, .{}).open(io, allocator, .{ .path = repo_path }); │ │664 - defer repo.deinit(io, allocator); │ │665 - event_id_hex = try evt.Comment.create(.xit, .{}, io, allocator, &repo, &thread_id_hex, &parent_id_hex, body, author); │ │666 - }, │ │667 - .local => |src| { │ │668 - if (parts.repo_base.len != 0) { │ │669 - try request.respond(not_found, .{ │ │670 - .status = .not_found, │ │671 - .extra_headers = &.{.{ .name = "content-type", .value = "text/plain" }}, │ │672 - }); │ │673 - return; │ │674 - } │ │675 - switch (src.repo_kind) { │ │676 - inline else => |repo_kind| { │ │677 - var any_repo = try rp.AnyRepo(repo_kind, .{}).open(io, allocator, src.localInitOpts()); │ │678 - defer any_repo.deinit(io, allocator); │ │679 - switch (any_repo) { │ │680 - inline else => |*repo| event_id_hex = try evt.Comment.create(repo_kind, repo.self_repo_opts, io, allocator, repo, &thread_id_hex, &parent_id_hex, body, author),│ │681 - } │ │682 - }, │ │647 + switch (source.repo_kind) { │ │648 + inline else => |repo_kind| { │ │649 + var any_repo = try rp.AnyRepo(repo_kind, .{}).open(io, allocator, source.localInitOpts()); │ │650 + defer any_repo.deinit(io, allocator); │ │651 + switch (any_repo) { │ │652 + inline else => |*repo| event_id_hex = try evt.Comment.create(repo_kind, repo.self_repo_opts, io, allocator, repo, &thread_id_hex, &parent_id_hex, body, author), │ │653 } │ │654 }, │ │655 } │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │687 attachment: AttachmentRequest, │ │688 host: Host, │ │689 ) !void { │ │720 - switch (host) { │ │721 - .remote => |remote| { │ │722 - const repo_prefix = "/repo/"; │ │723 - if (!std.mem.startsWith(u8, attachment.repo_base, repo_prefix)) return respondAttachmentNotFound(request); │ │724 - │ │725 - const repos_dir = try std.fs.path.join(allocator, &.{ std.fs.path.dirname(remote.admin_repo_path) orelse ".", "repos" }); │ │726 - defer allocator.free(repos_dir); │ │727 - const repo_path = switch (try serve_common.resolveRepoPath(io, allocator, repos_dir, remote.admin_repo_path, attachment.repo_base[repo_prefix.len..], false)) {│ │728 - .ok => |p| p, │ │729 - .invalid, .not_found => return respondAttachmentNotFound(request), │ │730 - }; │ │731 - defer allocator.free(repo_path); │ │732 - │ │733 - var repo = try rp.Repo(.xit, .{}).open(io, allocator, .{ .path = repo_path }); │ │734 - defer repo.deinit(io, allocator); │ │735 - try sendAttachment(.xit, .{}, io, request, allocator, &repo, &attachment.id); │ │736 - }, │ │737 - .local => |src| { │ │738 - // local routes elide the repo base │ │739 - if (attachment.repo_base.len != 0) return respondAttachmentNotFound(request); │ │740 - switch (src.repo_kind) { │ │741 - inline else => |repo_kind| { │ │742 - var any_repo = try rp.AnyRepo(repo_kind, .{}).open(io, allocator, src.localInitOpts()); │ │743 - defer any_repo.deinit(io, allocator); │ │744 - switch (any_repo) { │ │745 - inline else => |*repo| try sendAttachment(repo_kind, repo.self_repo_opts, io, request, allocator, repo, &attachment.id), │ │746 - } │ │747 - }, │ │690 + const request_repo = (try requestRepoSource(io, allocator, host, attachment.repo_base)) orelse return respondAttachmentNotFound(request); │ │691 + defer request_repo.deinit(allocator); │ │692 + const source = request_repo.source; │ │693 + switch (source.repo_kind) { │ │694 + inline else => |repo_kind| { │ │695 + var any_repo = try rp.AnyRepo(repo_kind, .{}).open(io, allocator, source.localInitOpts()); │ │696 + defer any_repo.deinit(io, allocator); │ │697 + switch (any_repo) { │ │698 + inline else => |*repo| try sendAttachment(repo_kind, repo.self_repo_opts, io, request, allocator, repo, &attachment.id), │ │699 } │ │700 }, │ │701 } │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │842 │ │843 var body_buf: [4096]u8 = undefined; │ │844 const blob: evt.Blob = .{ .name = name, .size = size, .reader = try request.readerExpectContinue(&body_buf) }; │ │894 - │ │895 - switch (host) { │ │896 - .remote => |remote| { │ │897 - const repo_prefix = "/repo/"; │ │898 - if (!std.mem.startsWith(u8, parts.repo_base, repo_prefix)) return respondAttachmentParentNotFound(request); │ │899 - const repos_dir = try std.fs.path.join(allocator, &.{ std.fs.path.dirname(remote.admin_repo_path) orelse ".", "repos" }); │ │900 - defer allocator.free(repos_dir); │ │901 - const repo_path = switch (try serve_common.resolveRepoPath(io, allocator, repos_dir, remote.admin_repo_path, parts.repo_base[repo_prefix.len..], false)) { │ │902 - .ok => |p| p, │ │903 - .invalid, .not_found => return respondAttachmentParentNotFound(request), │ │904 - }; │ │905 - defer allocator.free(repo_path); │ │845 │ │907 - var repo = try rp.Repo(.xit, .{}).open(io, allocator, .{ .path = repo_path }); │ │908 - defer repo.deinit(io, allocator); │ │909 - _ = evt.Attachment.create(.xit, .{}, io, allocator, &repo, &parent_id_hex, blob, author) catch |err| switch (err) { │ │910 - error.ParentNotFound => return respondAttachmentParentNotFound(request), │ │911 - else => return err, │ │912 - }; │ │913 - }, │ │914 - .local => |src| { │ │915 - if (parts.repo_base.len != 0) return respondAttachmentParentNotFound(request); │ │916 - switch (src.repo_kind) { │ │917 - inline else => |repo_kind| { │ │918 - var any_repo = try rp.AnyRepo(repo_kind, .{}).open(io, allocator, src.localInitOpts()); │ │919 - defer any_repo.deinit(io, allocator); │ │920 - switch (any_repo) { │ │921 - inline else => |*repo| _ = evt.Attachment.create(repo_kind, repo.self_repo_opts, io, allocator, repo, &parent_id_hex, blob, author) catch |err| switch (err) {│ │922 - error.ParentNotFound => return respondAttachmentParentNotFound(request), │ │923 - else => return err, │ │924 - }, │ │925 - } │ │846 + const request_repo = (try requestRepoSource(io, allocator, host, parts.repo_base)) orelse return respondAttachmentParentNotFound(request); │ │847 + defer request_repo.deinit(allocator); │ │848 + const source = request_repo.source; │ │849 + switch (source.repo_kind) { │ │850 + inline else => |repo_kind| { │ │851 + var any_repo = try rp.AnyRepo(repo_kind, .{}).open(io, allocator, source.localInitOpts()); │ │852 + defer any_repo.deinit(io, allocator); │ │853 + switch (any_repo) { │ │854 + inline else => |*repo| _ = evt.Attachment.create(repo_kind, repo.self_repo_opts, io, allocator, repo, &parent_id_hex, blob, author) catch |err| switch (err) { │ │855 + error.ParentNotFound => return respondAttachmentParentNotFound(request), │ │856 + else => return err, │ │857 }, │ │858 } │ │859 }, │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │895 ) !void { │ │896 const comment_id = parts.comment_id orelse return error.NotFound; │ │897 const thread_id = std.fmt.bytesToHex(parts.thread_id, .lower); │ │967 - switch (host) { │ │968 - .remote => |remote| { │ │969 - const repo_prefix = "/repo/"; │ │970 - if (!std.mem.startsWith(u8, parts.repo_base, repo_prefix)) return error.NotFound; │ │971 - const repos_dir = try std.fs.path.join(allocator, &.{ std.fs.path.dirname(remote.admin_repo_path) orelse ".", "repos" }); │ │972 - defer allocator.free(repos_dir); │ │973 - const repo_path = switch (try serve_common.resolveRepoPath(io, allocator, repos_dir, remote.admin_repo_path, parts.repo_base[repo_prefix.len..], false)) {│ │974 - .ok => |p| p, │ │975 - .invalid, .not_found => return error.NotFound, │ │976 - }; │ │977 - defer allocator.free(repo_path); │ │978 - │ │979 - var repo = try rp.Repo(.xit, .{}).open(io, allocator, .{ .path = repo_path }); │ │980 - defer repo.deinit(io, allocator); │ │981 - try evt.Comment.update(.xit, .{}, io, allocator, &repo, &thread_id, &comment_id, body, author); │ │982 - }, │ │983 - .local => |src| { │ │984 - if (parts.repo_base.len != 0) return error.NotFound; │ │985 - switch (src.repo_kind) { │ │986 - inline else => |repo_kind| { │ │987 - var any_repo = try rp.AnyRepo(repo_kind, .{}).open(io, allocator, src.localInitOpts()); │ │988 - defer any_repo.deinit(io, allocator); │ │989 - switch (any_repo) { │ │990 - inline else => |*repo| try evt.Comment.update(repo_kind, repo.self_repo_opts, io, allocator, repo, &thread_id, &comment_id, body, author), │ │991 - } │ │992 - }, │ │898 + const request_repo = (try requestRepoSource(io, allocator, host, parts.repo_base)) orelse return error.NotFound; │ │899 + defer request_repo.deinit(allocator); │ │900 + const source = request_repo.source; │ │901 + switch (source.repo_kind) { │ │902 + inline else => |repo_kind| { │ │903 + var any_repo = try rp.AnyRepo(repo_kind, .{}).open(io, allocator, source.localInitOpts()); │ │904 + defer any_repo.deinit(io, allocator); │ │905 + switch (any_repo) { │ │906 + inline else => |*repo| try evt.Comment.update(repo_kind, repo.self_repo_opts, io, allocator, repo, &thread_id, &comment_id, body, author), │ │907 } │ │908 }, │ │909 } │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │1018 defer author_arena.deinit(); │ │1019 const author = (try eventAuthor(io, allocator, &author_arena, request, host)) orelse return respondLoginRequired(request); │ │1020 │ │1107 - switch (host) { │ │1108 - .remote => |remote| { │ │1109 - const repo_prefix = "/repo/"; │ │1110 - if (!std.mem.startsWith(u8, parts.repo_base, repo_prefix)) return respondRemoveNotFound(request); │ │1111 - const repos_dir = try std.fs.path.join(allocator, &.{ std.fs.path.dirname(remote.admin_repo_path) orelse ".", "repos" }); │ │1112 - defer allocator.free(repos_dir); │ │1113 - const repo_path = switch (try serve_common.resolveRepoPath(io, allocator, repos_dir, remote.admin_repo_path, parts.repo_base[repo_prefix.len..], false)) { │ │1114 - .ok => |p| p, │ │1115 - .invalid, .not_found => return respondRemoveNotFound(request), │ │1116 - }; │ │1117 - defer allocator.free(repo_path); │ │1118 - │ │1119 - var repo = try rp.Repo(.xit, .{}).open(io, allocator, .{ .path = repo_path }); │ │1120 - defer repo.deinit(io, allocator); │ │1121 - evt.remove(.xit, .{}, io, allocator, &repo, &parts.id, parts.kind, author) catch |err| switch (err) { │ │1122 - error.EventNotFound => return respondRemoveNotFound(request), │ │1123 - else => |e| return e, │ │1124 - }; │ │1125 - }, │ │1126 - .local => |src| { │ │1127 - if (parts.repo_base.len != 0) return respondRemoveNotFound(request); │ │1128 - switch (src.repo_kind) { │ │1129 - inline else => |repo_kind| { │ │1130 - var any_repo = try rp.AnyRepo(repo_kind, .{}).open(io, allocator, src.localInitOpts()); │ │1131 - defer any_repo.deinit(io, allocator); │ │1132 - switch (any_repo) { │ │1133 - inline else => |*repo| evt.remove(repo_kind, repo.self_repo_opts, io, allocator, repo, &parts.id, parts.kind, author) catch |err| switch (err) {│ │1134 - error.EventNotFound => return respondRemoveNotFound(request), │ │1135 - else => |e| return e, │ │1136 - }, │ │1137 - } │ │1021 + const request_repo = (try requestRepoSource(io, allocator, host, parts.repo_base)) orelse return respondRemoveNotFound(request); │ │1022 + defer request_repo.deinit(allocator); │ │1023 + const source = request_repo.source; │ │1024 + switch (source.repo_kind) { │ │1025 + inline else => |repo_kind| { │ │1026 + var any_repo = try rp.AnyRepo(repo_kind, .{}).open(io, allocator, source.localInitOpts()); │ │1027 + defer any_repo.deinit(io, allocator); │ │1028 + switch (any_repo) { │ │1029 + inline else => |*repo| evt.remove(repo_kind, repo.self_repo_opts, io, allocator, repo, &parts.id, parts.kind, author) catch |err| switch (err) { │ │1030 + error.EventNotFound => return respondRemoveNotFound(request), │ │1031 + else => |e| return e, │ │1032 }, │ │1033 } │ │1034 }, │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │1062 update: evt.Issue.Update, │ │1063 author: evt.CommitAuthor, │ │1064 ) !void { │ │1171 - switch (host) { │ │1172 - .remote => |remote| { │ │1173 - const repo_prefix = "/repo/"; │ │1174 - if (!std.mem.startsWith(u8, parts.repo_base, repo_prefix)) return error.NotFound; │ │1175 - const repos_dir = try std.fs.path.join(allocator, &.{ std.fs.path.dirname(remote.admin_repo_path) orelse ".", "repos" }); │ │1176 - defer allocator.free(repos_dir); │ │1177 - const repo_path = switch (try serve_common.resolveRepoPath(io, allocator, repos_dir, remote.admin_repo_path, parts.repo_base[repo_prefix.len..], false)) {│ │1178 - .ok => |p| p, │ │1179 - .invalid, .not_found => return error.NotFound, │ │1180 - }; │ │1181 - defer allocator.free(repo_path); │ │1182 - │ │1183 - var repo = try rp.Repo(.xit, .{}).open(io, allocator, .{ .path = repo_path }); │ │1184 - defer repo.deinit(io, allocator); │ │1185 - try evt.Issue.update(.xit, .{}, io, allocator, &repo, &parts.id_bytes, update, author); │ │1186 - }, │ │1187 - .local => |src| { │ │1188 - // the local forms post to "/issue:<id>/...", so the repo base │ │1189 - // is empty │ │1190 - if (parts.repo_base.len != 0) return error.NotFound; │ │1191 - switch (src.repo_kind) { │ │1192 - inline else => |repo_kind| { │ │1193 - var any_repo = try rp.AnyRepo(repo_kind, .{}).open(io, allocator, src.localInitOpts()); │ │1194 - defer any_repo.deinit(io, allocator); │ │1195 - switch (any_repo) { │ │1196 - inline else => |*repo| try evt.Issue.update(repo_kind, repo.self_repo_opts, io, allocator, repo, &parts.id_bytes, update, author), │ │1197 - } │ │1198 - }, │ │1065 + const request_repo = (try requestRepoSource(io, allocator, host, parts.repo_base)) orelse return error.NotFound; │ │1066 + defer request_repo.deinit(allocator); │ │1067 + const source = request_repo.source; │ │1068 + switch (source.repo_kind) { │ │1069 + inline else => |repo_kind| { │ │1070 + var any_repo = try rp.AnyRepo(repo_kind, .{}).open(io, allocator, source.localInitOpts()); │ │1071 + defer any_repo.deinit(io, allocator); │ │1072 + switch (any_repo) { │ │1073 + inline else => |*repo| try evt.Issue.update(repo_kind, repo.self_repo_opts, io, allocator, repo, &parts.id_bytes, update, author), │ │1074 } │ │1075 }, │ │1076 } │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │1087 description: []const u8, │ │1088 author: evt.CommitAuthor, │ │1089 ) !void { │ │1215 - switch (host) { │ │1216 - .remote => |remote| { │ │1217 - const repo_prefix = "/repo/"; │ │1218 - if (!std.mem.startsWith(u8, repo_base, repo_prefix)) return error.NotFound; │ │1219 - const repos_dir = try std.fs.path.join(allocator, &.{ std.fs.path.dirname(remote.admin_repo_path) orelse ".", "repos" }); │ │1220 - defer allocator.free(repos_dir); │ │1221 - const repo_path = switch (try serve_common.resolveRepoPath(io, allocator, repos_dir, remote.admin_repo_path, repo_base[repo_prefix.len..], false)) { │ │1222 - .ok => |p| p, │ │1223 - .invalid, .not_found => return error.NotFound, │ │1224 - }; │ │1225 - defer allocator.free(repo_path); │ │1226 - │ │1227 - var repo = try rp.Repo(.xit, .{}).open(io, allocator, .{ .path = repo_path }); │ │1228 - defer repo.deinit(io, allocator); │ │1229 - try evt.Discussion.update(.xit, .{}, io, allocator, &repo, id, title, tags, description, author); │ │1230 - }, │ │1231 - .local => |src| { │ │1232 - if (repo_base.len != 0) return error.NotFound; │ │1233 - switch (src.repo_kind) { │ │1234 - inline else => |repo_kind| { │ │1235 - var any_repo = try rp.AnyRepo(repo_kind, .{}).open(io, allocator, src.localInitOpts()); │ │1236 - defer any_repo.deinit(io, allocator); │ │1237 - switch (any_repo) { │ │1238 - inline else => |*repo| try evt.Discussion.update(repo_kind, repo.self_repo_opts, io, allocator, repo, id, title, tags, description, author),│ │1239 - } │ │1240 - }, │ │1090 + const request_repo = (try requestRepoSource(io, allocator, host, repo_base)) orelse return error.NotFound; │ │1091 + defer request_repo.deinit(allocator); │ │1092 + const source = request_repo.source; │ │1093 + switch (source.repo_kind) { │ │1094 + inline else => |repo_kind| { │ │1095 + var any_repo = try rp.AnyRepo(repo_kind, .{}).open(io, allocator, source.localInitOpts()); │ │1096 + defer any_repo.deinit(io, allocator); │ │1097 + switch (any_repo) { │ │1098 + inline else => |*repo| try evt.Discussion.update(repo_kind, repo.self_repo_opts, io, allocator, repo, id, title, tags, description, author), │ │1099 } │ │1100 }, │ │1101 } │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯