╭ changed ──╮ │src/web.zig│ ╰───────────╯ ╭───────────╮ │← all files│ ╰───────────╯ ╭────────────────────────────────────────╮ │41 local: ui.RepoSource, │ │42 server: struct { │ │43 admin_repo_path: []const u8,│ │44 + repo_root_path: []const u8, │ │45 session_store: SessionStore,│ │46 git_http_port: ?u16, │ │47 git_ssh_port: ?u16, │ ╰────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │64 .server => |server| blk: { │ │65 const repo_prefix = "/repo/"; │ │66 if (!std.mem.startsWith(u8, repo_base, repo_prefix)) break :blk null; │ │66 - const repos_dir = try std.fs.path.join(allocator, &.{ std.fs.path.dirname(server.admin_repo_path) orelse ".", "repos" }); │ │67 - defer allocator.free(repos_dir); │ │68 - const path = switch (try serve_common.resolveRepoPath(io, allocator, repos_dir, server.admin_repo_path, repo_base[repo_prefix.len..], false)) { │ │67 + const path = switch (try serve_common.resolveRepoPath(io, allocator, server.repo_root_path, server.admin_repo_path, repo_base[repo_prefix.len..], false)) {│ │68 .ok => |value| value, │ │69 .invalid, .not_found => break :blk null, │ │70 }; │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │522 var admin_repo = try rp.Repo(.xit, evt.admin_repo_opts).open(io, allocator, .{ .path = server.admin_repo_path }); │ │523 defer admin_repo.deinit(io, allocator); │ │524 const moment = try evt.currentMoment(evt.admin_repo_opts, &admin_repo); │ │526 - break :blk try ui.authorizeUser(moment, arena, user_id, repo_base[repo_prefix.len..], min_role); │ │525 + break :blk try ui.authorizeUser(io, moment, arena, server.repo_root_path, user_id, repo_base[repo_prefix.len..], min_role);│ │526 }; │ │527 switch (authorization) { │ │528 .actor => |actor| return actor, │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │558 const id = requestUserId(request, server.session_store) orelse break :blk null; │ │559 break :blk if (try ui.activeUser(moment, &arena, id) != null) id else null; │ │560 }; │ │562 - const role = (try ui.repoRole(moment, &arena, user_id, repo_base[repo_prefix.len..])) orelse return false; │ │563 - return role != .none; │ │561 + const owner_repo = evt.parseOwnerRepoPath(repo_base[repo_prefix.len..]) orelse return false; │ │562 + const repo = (try evt.readRepoByOwnerAndName(io, allocator, &arena, moment, server.repo_root_path, owner_repo.owner, owner_repo.name)) orelse return false;│ │563 + return evt.Repo.roleOf(repo.repo, user_id) != .none; │ │564 } │ │565 │ │566 // the logged-in user the request's session cookie names │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │707 return respondThreadFormFailure(request, allocator, host, form_location, patchFeedback(.invalid_target_branch, title, labels, description, target_branch, null));│ │708 } │ │709 const repo_id = evt.parseEventId(std.fs.path.basename(request_repo.source.path)) catch return respondRemoveNotFound(request); │ │710 - var admin_repo = try rp.Repo(.xit, evt.admin_repo_opts).open(io, allocator, .{ .path = server.admin_repo_path }); │ │711 - defer admin_repo.deinit(io, allocator); │ │712 - const repos_dir = try std.fs.path.join(allocator, &.{ std.fs.path.dirname(server.admin_repo_path) orelse ".", "repos" }); │ │713 - defer allocator.free(repos_dir); │ │714 - const fork_path = try fork.create(.{}, io, allocator, repos_dir, &admin_repo, .{ │ │710 + const repo_user_id = actor.repo_user_id orelse unreachable; │ │711 + var user_repo = (try evt.openUserRepo(io, allocator, server.repo_root_path, &user_id)) orelse return respondRemoveNotFound(request); │ │712 + defer user_repo.deinit(io, allocator); │ │713 + const fork_path = try fork.create(.{}, io, allocator, server.repo_root_path, &user_repo, .{ │ │714 .id = event_id_hex, │ │715 .user_id = user_id, │ │716 .repo_id = repo_id, │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────╮ │717 + .repo_user_id = repo_user_id,│ │718 .title = title, │ │719 .description = description, │ │720 .labels = labels, │ ╰──────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │816 var admin_repo = try rp.Repo(.xit, evt.admin_repo_opts).open(io, allocator, .{ .path = server.admin_repo_path }); │ │817 defer admin_repo.deinit(io, allocator); │ │818 const id = std.fmt.bytesToHex(parts.thread_id, .lower); │ │819 - const repos_dir = try std.fs.path.join(allocator, &.{ std.fs.path.dirname(server.admin_repo_path) orelse ".", "repos" }); │ │820 - defer allocator.free(repos_dir); │ │821 - const fork_path = try fork.forkPath(allocator, repos_dir, &id); │ │819 + const fork_path = try fork.forkPath(allocator, server.repo_root_path, &id); │ │820 defer allocator.free(fork_path); │ │823 - try pch.publish(.{}, io, allocator, &admin_repo, &target_repo, fork_path, .{ │ │821 + var user_repo = (try evt.openUserRepo(io, allocator, server.repo_root_path, &user_id)) orelse return respondRemoveNotFound(request);│ │822 + defer user_repo.deinit(io, allocator); │ │823 + try pch.publish(.{}, io, allocator, &admin_repo, &user_repo, &target_repo, fork_path, .{ │ │824 .id = id, │ │825 .user_id = user_id, │ │826 .repo_id = repo_id, │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │855 defer request_repo.deinit(allocator); │ │856 var target_repo = try rp.Repo(.xit, .{}).open(io, allocator, request_repo.source.localInitOpts()); │ │857 defer target_repo.deinit(io, allocator); │ │858 - var admin_repo = try rp.Repo(.xit, evt.admin_repo_opts).open(io, allocator, .{ .path = server.admin_repo_path }); │ │859 - defer admin_repo.deinit(io, allocator); │ │858 │ │859 const id = std.fmt.bytesToHex(parts.thread_id, .lower); │ │862 - const repos_dir = try std.fs.path.join(allocator, &.{ std.fs.path.dirname(server.admin_repo_path) orelse ".", "repos" });│ │863 - defer allocator.free(repos_dir); │ │864 - pch.mergeAndRemoveFork(.{}, io, allocator, repos_dir, &admin_repo, &target_repo, .{ │ │860 + pch.mergeAndRemoveFork(.{}, io, allocator, server.repo_root_path, &target_repo, .{ │ │861 .id = id, │ │862 .revision = revision, │ │863 .author = author, │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │1333 .server => |server| { │ │1334 const user_id = actor.user_id orelse return respondLoginRequired(request); │ │1335 │ │1340 - var admin_repo = try rp.Repo(.xit, evt.admin_repo_opts).open(io, allocator, .{ .path = server.admin_repo_path }); │ │1341 - defer admin_repo.deinit(io, allocator); │ │1342 - const moment = try evt.currentMoment(evt.admin_repo_opts, &admin_repo); │ │1343 - const record = try evt.Fork.readById(evt.AdminDB, evt.admin_repo_opts.hash, moment, &author_arena, &parts.id); │ │1344 - if (record) |fork_record| { │ │1336 + // a draft lives in its author's user repo │ │1337 + if (try evt.readForkById(io, allocator, &author_arena, server.repo_root_path, &user_id, &parts.id)) |fork_record| { │ │1338 const repo_id = evt.parseEventId(std.fs.path.basename(source.path)) catch return respondRemoveNotFound(request); │ │1339 if (!fork_record.removed and fork_record.event.stage == .draft and std.mem.eql(u8, fork_record.event.repo_id, &repo_id)) {│ │1340 const id = std.fmt.bytesToHex(parts.id, .lower); │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │1348 - const repos_dir = try std.fs.path.join(allocator, &.{ std.fs.path.dirname(server.admin_repo_path) orelse ".", "repos" });│ │1349 - defer allocator.free(repos_dir); │ │1350 - try fork.remove(io, allocator, repos_dir, &admin_repo, &id, &user_id, author); │ │1341 + var user_repo = (try evt.openUserRepo(io, allocator, server.repo_root_path, &user_id)) orelse unreachable; │ │1342 + defer user_repo.deinit(io, allocator); │ │1343 + try fork.remove(io, allocator, server.repo_root_path, &user_repo, &id, author); │ │1344 │ │1345 const location = try std.fmt.allocPrint(allocator, "{s}/patches/drafts", .{parts.repo_base}); │ │1346 defer allocator.free(location); │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────╮ │next →│ ╰──────╯