fix(redirects, settings): /whatsnew alias + /settings/{tab} deep-links (t-paliad-040)
Bug 7 — /whatsnew was bare 404. Sidebar uses /changelog (canonical) but
users typing /whatsnew from memory hit the not-found chrome. Added
/whatsnew → /changelog as a 301 to internal/handlers/redirects.go,
following the existing legacy-redirect pattern. Wired on the OUTER mux so
unauthenticated bookmarks redirect one-hop instead of round-tripping
through /login. /search left as-is per the brief — sidebar's global-search
overlay is the live UX, /search would only be hit via typo and falls back
to the chromed 404 from t-paliad-037.
Bug 8 — /settings/caldav worked (200 → 301 → /settings?tab=caldav) but
/settings/notifications, /settings/dezernat, /settings/profile all 404'd.
Tabs themselves were fine in-page; only the deep-link form was broken.
Replaced the single CalDAV-only handler with a generic /settings/{tab}
redirector backed by a slug→canonical map that accepts both the German
tab IDs the client TS understands (profil, benachrichtigungen, dezernat)
and intuitive English aliases (profile, notifications, department).
Unknown slugs fall back to /settings (default tab) instead of 404 so
typos don't break.
Bug 10 — login form 401 console replay: skipped per brief permission.
Reproduced in Playwright; the console message is the browser's automatic
"Failed to load resource: the server responded with a status of 401"
emitted by the network stack itself. login.ts has no console.error call.
The only workarounds (server returns 200 with {ok:false}, or 422 instead
of 401) either compromise the security pattern or don't actually suppress
the browser log. Documented in the smoke delta report.
Verified: go build/vet/test clean, bun run build clean.
This commit is contained in:
@@ -24,16 +24,36 @@ func handleAppointmentsCalendarPage(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
// handleSettingsPage serves the unified settings page with tabs for
|
||||
// Profil / Benachrichtigungen / CalDAV. The active tab is picked client-side
|
||||
// from ?tab=<name> so switching tabs doesn't round-trip.
|
||||
// Profil / Benachrichtigungen / CalDAV / Dezernat. The active tab is picked
|
||||
// client-side from ?tab=<name> so switching tabs doesn't round-trip.
|
||||
func handleSettingsPage(w http.ResponseWriter, r *http.Request) {
|
||||
http.ServeFile(w, r, "dist/settings.html")
|
||||
}
|
||||
|
||||
// handleSettingsCalDAVRedirect keeps /settings/caldav working for
|
||||
// bookmarks and any external links while the canonical URL moves to
|
||||
// /settings?tab=caldav. 301 Moved Permanently — browsers cache the hop
|
||||
// so the redirect only costs once per bookmark.
|
||||
func handleSettingsCalDAVRedirect(w http.ResponseWriter, r *http.Request) {
|
||||
http.Redirect(w, r, "/settings?tab=caldav", http.StatusMovedPermanently)
|
||||
// settingsTabAliases maps every supported /settings/<slug> deep-link to its
|
||||
// canonical ?tab=<name> value the client TS understands. Both the German tab
|
||||
// IDs (profil/benachrichtigungen/dezernat) and intuitive English aliases
|
||||
// (profile/notifications/department) are accepted so bookmarks, smoke tests,
|
||||
// and manually-typed URLs all land on the right tab.
|
||||
var settingsTabAliases = map[string]string{
|
||||
"profil": "profil",
|
||||
"profile": "profil",
|
||||
"benachrichtigungen": "benachrichtigungen",
|
||||
"notifications": "benachrichtigungen",
|
||||
"caldav": "caldav",
|
||||
"dezernat": "dezernat",
|
||||
"department": "dezernat",
|
||||
}
|
||||
|
||||
// handleSettingsTabRedirect turns /settings/<slug> into /settings?tab=<canonical>
|
||||
// as 301 Moved Permanently. Unknown slugs fall back to the bare /settings page
|
||||
// (the client picks the default tab) so a typo doesn't 404.
|
||||
func handleSettingsTabRedirect(w http.ResponseWriter, r *http.Request) {
|
||||
slug := r.PathValue("tab")
|
||||
canonical, ok := settingsTabAliases[slug]
|
||||
if !ok {
|
||||
http.Redirect(w, r, "/settings", http.StatusMovedPermanently)
|
||||
return
|
||||
}
|
||||
http.Redirect(w, r, "/settings?tab="+canonical, http.StatusMovedPermanently)
|
||||
}
|
||||
|
||||
@@ -245,7 +245,7 @@ func Register(mux *http.ServeMux, client *auth.Client, giteaAPIToken string, svc
|
||||
|
||||
// Settings
|
||||
protected.HandleFunc("GET /settings", gateOnboarded(handleSettingsPage))
|
||||
protected.HandleFunc("GET /settings/caldav", handleSettingsCalDAVRedirect)
|
||||
protected.HandleFunc("GET /settings/{tab}", handleSettingsTabRedirect)
|
||||
|
||||
// Catch-all 404 — runs for any authenticated path that no more-specific
|
||||
// pattern claimed. Renders the chromed shell with HTTP 404 (Bug 9 from
|
||||
|
||||
@@ -28,6 +28,9 @@ func registerLegacyRedirects(mux *http.ServeMux) {
|
||||
"/parteien": "/parties",
|
||||
"/gerichte": "/courts",
|
||||
"/glossar": "/glossary",
|
||||
// Memorable aliases — sidebar uses the canonical path but users
|
||||
// type these from memory and would otherwise hit the 404 chrome.
|
||||
"/whatsnew": "/changelog",
|
||||
}
|
||||
for oldPrefix, newPrefix := range prefixes {
|
||||
mux.Handle("GET "+oldPrefix, redirectPrefix(oldPrefix, newPrefix))
|
||||
|
||||
Reference in New Issue
Block a user