- Add LogoutHandler: deletes session row (D-06), clears cookie, redirects to /login
- Protect GET / inside RequireAuth group; remove old top-level registration
- Add POST /logout inside same RequireAuth group (D-22: POST-only logout)
- Update Layout signature to accept *auth.User; render logout form + email when authed
- Update Index template to accept *auth.User and show "Signed in as {email}"
- Update SignupPage/LoginPage to pass nil to Layout (auth pages are unauthed)
- Update IndexHandler to pull user from auth.Authed(ctx) and pass to template
- Update TestIndex_RendersHxGet -> TestIndex_UnauthRedirects (GET / now protected)
- AUTH-04 (logout) and AUTH-05 (protected /) are now closed
51 lines
2 KiB
Text
51 lines
2 KiB
Text
// Package templates owns the server-rendered HTML for the Phase 1 walking
|
|
// skeleton. Each *.templ file compiles to a *_templ.go file via `templ
|
|
// generate`; generated files are gitignored.
|
|
package templates
|
|
|
|
import "backend/internal/auth"
|
|
|
|
// Layout is the base HTML shell every page renders inside. The structural
|
|
// classes, container width (max-w-5xl), horizontal padding, header strip,
|
|
// footer, and asset references (/static/tailwind.css, /static/htmx.min.js)
|
|
// are locked by UI-SPEC §Base Layout Contract and CONTEXT D-10 — do NOT
|
|
// load HTMX from a CDN.
|
|
//
|
|
// user is non-nil when the request context carries an authenticated session.
|
|
// When non-nil, the header renders a Log out POST form (D-22). Auth pages
|
|
// pass nil since they're gated behind RedirectIfAuthed and never shown to
|
|
// authed users.
|
|
templ Layout(title string, user *auth.User) {
|
|
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="utf-8"/>
|
|
<meta name="viewport" content="width=device-width, initial-scale=1"/>
|
|
<title>{ title }</title>
|
|
<link rel="stylesheet" href="/static/tailwind.css"/>
|
|
</head>
|
|
<body class="min-h-screen bg-white text-slate-900 antialiased">
|
|
<header class="bg-slate-50 border-b border-slate-200">
|
|
<div class="mx-auto max-w-5xl px-4 sm:px-6 py-4 flex items-center justify-between">
|
|
<span class="text-sm font-semibold text-slate-800">Xtablo</span>
|
|
if user != nil {
|
|
<div class="flex items-center gap-3">
|
|
<span class="text-sm text-slate-600">{ user.Email }</span>
|
|
<form method="POST" action="/logout" class="inline">
|
|
<!-- CSRF field added in Plan 07 -->
|
|
<button type="submit" class="text-sm text-slate-700 hover:underline">Log out</button>
|
|
</form>
|
|
</div>
|
|
}
|
|
</div>
|
|
</header>
|
|
<main class="mx-auto max-w-5xl px-4 sm:px-6 py-8">
|
|
{ children... }
|
|
</main>
|
|
<footer class="mx-auto max-w-5xl px-4 sm:px-6 py-6 text-sm text-slate-600">
|
|
Phase 2 · Authentication
|
|
</footer>
|
|
<script src="/static/htmx.min.js" defer></script>
|
|
</body>
|
|
</html>
|
|
}
|