╭──────────╮ │← previous│ ╰──────────╯ ╭ changed ──╮ │src/web.zig│ ╰───────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │540 const id = requestUserId(request, server.session_store) orelse break :blk null; │ │541 break :blk if (try ui.activeUser(moment, &arena, id) != null) id else null; │ │542 }; │ │533 - const owner_repo = evt.parseOwnerRepoPath(repo_base[repo_prefix.len..]) orelse return false; │ │543 + const owner_repo = evt.parseOwnerRepoPath(identity) orelse return false; │ │544 const repo = (try evt.readRepoByOwnerAndName(io, allocator, &arena, moment, server.users_dir, owner_repo.owner, owner_repo.name, user_id)) orelse return false;│ │545 return repo.role != .none; │ │546 } │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │588 base: []const u8, │ │589 host: Host, │ │590 ) !void { │ │581 - if (commentBaseParts(base) != null) return handleCommentNew(io, request, allocator, base, host); │ │591 + if (commentBaseParts(host, base) != null) return handleCommentNew(io, request, allocator, base, host);│ │592 │ │593 const issues_suffix = "/issues"; │ │594 if (std.mem.endsWith(u8, base, issues_suffix)) │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │651 } } }); │ │652 }; │ │653 │ │644 - const location = try std.fmt.allocPrint(allocator, "/repo/{s}/{s}", .{ user.event.name, name });│ │654 + const location = try std.fmt.allocPrint(allocator, "/{s}:{s}", .{ user.event.name, name }); │ │655 defer allocator.free(location); │ │656 try request.respond("", .{ │ │657 .status = .see_other, │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │687 defer allocator.free(access_value); │ │688 const access = std.meta.stringToEnum(evt.Repo.Access, access_value) orelse return request.respond("invalid access", .{ .status = .bad_request, .extra_headers = &.{.{ .name = "content-type", .value = "text/plain" }} });│ │689 │ │680 - const repo_prefix = "/repo/"; │ │681 - const route = ui.Repo.Settings.update(io, allocator, server.users_dir, actor, repo_base[repo_prefix.len..], name, description, access) catch |err| switch (err) { │ │690 + // authorizeWrite refused any other base │ │691 + const identity = repoBaseIdentity(repo_base) orelse unreachable; │ │692 + const route = ui.Repo.Settings.update(io, allocator, server.users_dir, actor, identity, name, description, access) catch |err| switch (err) { │ │693 error.NotFound => return respondRepoNotFound(request), │ │694 else => { │ │695 const failure = ui.Session.FormFeedback.RepoFailure.fromError(err) orelse return err; │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────╮ │749 const token = try session_store.create(&user_id); │ │750 var cookie_buf: [256]u8 = undefined; │ │751 const cookie = try std.fmt.bufPrint(&cookie_buf, session_cookie_fmt, .{token});│ │741 - const location = try std.fmt.allocPrint(allocator, "/user/{s}", .{name}); │ │752 + const location = try std.fmt.allocPrint(allocator, "/{s}", .{name}); │ │753 defer allocator.free(location); │ │754 try request.respond("", .{ │ │755 .status = .see_other, │ ╰────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │908 comment_id: ?[evt.event_id_size]u8, │ │909 }; │ │910 │ │900 -fn commentBaseParts(base: []const u8) ?CommentBaseParts { │ │901 - const route = ui.RoutablePage.fromUrl(base) orelse ui.RoutablePage.fromUrlLocal(base) orelse return null; │ │911 +fn commentBaseParts(host: Host, base: []const u8) ?CommentBaseParts { │ │912 + const route = routeFromUrl(host, base) orelse return null; │ │913 const identity, const thread_kind, const thread_hex, const comment_hex = switch (route) { │ │914 .repo_issues => |*issue| .{ issue.name.slice(), evt.EventKind.issue, issue.selected.slice(), issue.comment.slice() }, │ │915 .repo_patches => |*patch| .{ patch.name.slice(), evt.EventKind.patch, patch.selected.slice(), patch.comment.slice() },│ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │918 }; │ │919 const thread_id = evt.parseEventId(thread_hex) catch return null; │ │920 const comment_id = if (comment_hex.len == 0) null else evt.parseEventId(comment_hex) catch return null; │ │910 - const repo_base_len = if (identity.len == 0) 0 else "/repo/".len + identity.len; │ │921 + const repo_base_len = if (identity.len == 0) 0 else "/".len + identity.len; │ │922 return .{ .repo_base = base[0..repo_base_len], .thread_kind = thread_kind, .thread_id = thread_id, .comment_id = comment_id };│ │923 } │ │924 │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │929 base: []const u8, │ │930 host: Host, │ │931 ) !void { │ │921 - const parts = commentBaseParts(base) orelse return respondRemoveNotFound(request); │ │932 + const parts = commentBaseParts(host, base) orelse return respondRemoveNotFound(request); │ │933 if (parts.thread_kind != .patch or parts.comment_id != null) return respondRemoveNotFound(request);│ │934 const server = switch (host) { │ │935 .server => |server| server, │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │973 host: Host, │ │974 revision: evt.Patch.MergeRevision, │ │975 ) !void { │ │965 - const parts = commentBaseParts(base) orelse return respondRemoveNotFound(request); │ │976 + const parts = commentBaseParts(host, base) orelse return respondRemoveNotFound(request); │ │977 if (parts.thread_kind != .patch or parts.comment_id != null) return respondRemoveNotFound(request);│ │978 const server = switch (host) { │ │979 .server => |server| server, │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────╮ │1025 host: Host, │ │1026 ) !void { │ │1027 const not_found = "comment parent not found"; │ │1017 - const parts = commentBaseParts(base) orelse { │ │1028 + const parts = commentBaseParts(host, base) orelse { │ │1029 try request.respond(not_found, .{ │ │1030 .status = .not_found, │ │1031 .extra_headers = &.{.{ .name = "content-type", .value = "text/plain" }},│ ╰──────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────╮ │next →│ ╰──────╯