hotfix(checklists): disambiguate /checklists/{slug}/edit → /checklists/templates/{slug}/edit (production-down route conflict)

Go ServeMux refused to register patterns 'GET /checklists/{slug}/edit' (from
dirac's Slice A merge b418705) and 'GET /checklists/instances/{id}' (existing)
because both match '/checklists/instances/edit'. Container crash-looped on
boot since 13:32 UTC; paliad.de returned 404 from Traefik because no app was
listening.

Renaming the new template-edit route to /checklists/templates/{slug}/edit
disambiguates — '/templates/...' is a literal segment so the {slug} is now
strictly under a fixed prefix that can't collide with 'instances'.

Touches:
- internal/handlers/handlers.go:257 — route pattern
- frontend/src/client/checklists.ts:290 — Bearbeiten link
- frontend/src/client/checklists-author.ts:52 — URL parser regex
- frontend/src/checklists-author.tsx — doc comment

go build + bun run build clean.
This commit is contained in:
mAi
2026-05-20 15:34:00 +02:00
parent b418705775
commit 794617cbfd
4 changed files with 5 additions and 5 deletions

View File

@@ -6,7 +6,7 @@ import { Footer } from "./components/Footer";
import { PWAHead } from "./components/PWAHead";
// Authoring wizard for paliad.checklists. Both /checklists/new and
// /checklists/{slug}/edit serve this same bundle; the client reads
// /checklists/templates/{slug}/edit serve this same bundle; the client reads
// window.location.pathname to decide create vs edit mode.
export function renderChecklistsAuthor(): string {
return "<!DOCTYPE html>" + (

View File

@@ -1,5 +1,5 @@
// Authoring wizard for paliad.checklists. Serves both /checklists/new
// (create) and /checklists/{slug}/edit (edit). The HTML bundle is the
// (create) and /checklists/templates/{slug}/edit (edit). The HTML bundle is the
// same; this client reads location.pathname to decide which mode to
// boot into.
@@ -49,7 +49,7 @@ function detectMode(): { mode: "create" | "edit"; slug?: string } {
if (path === "/checklists/new") {
return { mode: "create" };
}
const m = path.match(/^\/checklists\/([^/]+)\/edit$/);
const m = path.match(/^\/checklists\/templates\/([^/]+)\/edit$/);
if (m) {
return { mode: "edit", slug: m[1] };
}

View File

@@ -287,7 +287,7 @@ function renderMyTemplates() {
<p class="checklist-card-desc">${esc(tpl.description || "")}</p>
<p class="checklist-card-court">${esc(tpl.court || "")}</p>
<div class="checklist-card-actions">
<a class="btn btn-small" href="/checklists/${esc(tpl.slug)}/edit" data-i18n="checklisten.mine.edit">Bearbeiten</a>
<a class="btn btn-small" href="/checklists/templates/${esc(tpl.slug)}/edit" data-i18n="checklisten.mine.edit">Bearbeiten</a>
<button class="btn btn-small btn-danger" data-action="delete" data-slug="${esc(tpl.slug)}" data-title="${escAttr(tpl.title)}" data-i18n="checklisten.mine.delete">L&ouml;schen</button>
</div>
</article>`;

View File

@@ -254,7 +254,7 @@ func Register(mux *http.ServeMux, client *auth.Client, giteaAPIToken string, svc
protected.HandleFunc("GET /checklists", handleChecklistsPage)
protected.HandleFunc("GET /checklists/new", handleChecklistsAuthorPage)
protected.HandleFunc("GET /checklists/instances/{id}", handleChecklistInstancePage)
protected.HandleFunc("GET /checklists/{slug}/edit", handleChecklistsAuthorPage)
protected.HandleFunc("GET /checklists/templates/{slug}/edit", handleChecklistsAuthorPage)
protected.HandleFunc("GET /checklists/{slug}", handleChecklistDetailPage)
protected.HandleFunc("GET /api/checklists", handleChecklistsAPI)
protected.HandleFunc("GET /api/checklists/{slug}", handleChecklistAPI)