╭ changed ──╮ │src/web.zig│ ╰───────────╯ ╭───────────╮ │← all files│ ╰───────────╯ ╭──────────╮ │← previous│ ╰──────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │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" }},│ ╰──────────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │1335 base: []const u8, │ │1336 host: Host, │ │1337 ) !void { │ │1327 - const parts = commentBaseParts(base) orelse return respondRemoveNotFound(request); │ │1338 + const parts = commentBaseParts(host, base) orelse return respondRemoveNotFound(request); │ │1339 if (parts.comment_id != null) return handleCommentEdit(io, request, allocator, base, host, parts);│ │1340 return handleThreadEdit(io, request, allocator, base, host, parts); │ │1341 } │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────╮ │1428 }; │ │1429 │ │1430 // split a remove url into the repo and event it names │ │1420 -fn removeParts(base: []const u8) ?RemoveParts { │ │1431 +fn removeParts(host: Host, base: []const u8) ?RemoveParts { │ │1432 if (attachmentRequest(base)) |attachment| { │ │1433 const parent = attachParentParts(attachment.repo_base) orelse return null;│ │1434 return .{ │ ╰────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────╮ │1437 .id = attachment.id, │ │1438 }; │ │1439 } │ │1429 - if (commentBaseParts(base)) |thread| return .{ │ │1440 + if (commentBaseParts(host, base)) |thread| return .{ │ │1441 .repo_base = thread.repo_base, │ │1442 .kind = if (thread.comment_id != null) .comment else thread.thread_kind,│ │1443 .id = thread.comment_id orelse thread.thread_id, │ ╰──────────────────────────────────────────────────────────────────────────────────────╯ ╭──────╮ │next →│ ╰──────╯