NextMyDay/internal/core/server/handlers.go
Claus Lohmar d11e14295b feat(core): add the store, identity plumbing and the door (SPEC-003/004)
- store: SQLite (WAL, foreign keys ON, CGO-free driver) + the embedded
  migration runner; each module's migrations are name-prefixed (D-012)
- audit: the append-only audit log; the actor threads via context (R-1)
- users: the user_id/GUID/email anchors (D-013)
- config: the YAML settings file (D-035) + validation
- httpx: cookies · loopback detection · trusted-proxy XFF · CSRF · security
  headers · redaction
- middleware: correlation id · audit · rate limiting
- mail: the invite/recovery mailer seam
- server: the core DOOR — the resolution switch, the dispatch registry
  (RegisterModule), the role gates, /setup · enroll/login · recovery flows,
  the reserved `static` workflow, and the loopback-only bootstrap (D-025)
2026-10-14 10:53:51 +00:00

66 lines
2.7 KiB
Go

package server
import (
"net/http"
"git.lohmar.co.uk/cclohmar/NextMyDay/internal/core/web"
)
// The core handlers below are the door's minimal landing surfaces. SPEC-003 §4
// says the door renders no dynamic HTML — it resolves, gates, and hands off;
// these are the hand-off targets for the routes SPEC-003 must classify, and the
// real screens arrive with SPEC-005 (setup), SPEC-006 (enroll/login) and
// SPEC-007 (access). SPEC-009 §4 renders them inside the shared shell.
// Home is the neutral landing page. It is PUBLIC: it takes no session and
// touches no database (SPEC-003 §9 AC-2). It is registered both as the core
// `home` workflow (level 1, /home) and as the `home` module's dashboard (level
// 2, /{GUID}).
func Home(w http.ResponseWriter, _ *http.Request, _ *RequestContext) error {
return web.Render(w, http.StatusOK, web.Page{
Title: "MyDay",
Guest: true,
Content: web.Markup(`<h1>MyDay</h1>
<p>Welcome. Sign in with your personal link.</p>`),
})
}
// LoginPlaceholder stands in for the login screen (SPEC-006). It answers 200:
// a rendered page is a successful response — only a failed credential attempt
// is 401/400 (F-1).
func LoginPlaceholder(w http.ResponseWriter, _ *http.Request, _ *RequestContext) error {
w.Header().Set("Content-Type", "text/html; charset=utf-8")
w.WriteHeader(http.StatusOK)
_, err := w.Write([]byte(`<!doctype html><html><body><h1>Sign in</h1>` +
`<p>This link requires a valid session.</p></body></html>`))
return err
}
// EnrollPlaceholder stands in for the enrollment screen (SPEC-006). The GUID is
// found but not yet enrolled, so no session is required to see it.
func EnrollPlaceholder(w http.ResponseWriter, _ *http.Request, _ *RequestContext) error {
w.Header().Set("Content-Type", "text/html; charset=utf-8")
w.WriteHeader(http.StatusOK)
_, err := w.Write([]byte(`<!doctype html><html><body><h1>Enrollment</h1>` +
`<p>Passkey and TOTP enrollment.</p></body></html>`))
return err
}
// SetupPlaceholder stands in for the /setup wizard (SPEC-005). The door only
// reaches it in setup mode.
func SetupPlaceholder(w http.ResponseWriter, _ *http.Request, _ *RequestContext) error {
w.Header().Set("Content-Type", "text/html; charset=utf-8")
w.WriteHeader(http.StatusOK)
_, err := w.Write([]byte(`<!doctype html><html><body><h1>Setup</h1>` +
`<p>Bootstrap wizard.</p></body></html>`))
return err
}
// AccessPlaceholder stands in for the recovery entry (SPEC-007).
func AccessPlaceholder(w http.ResponseWriter, _ *http.Request, _ *RequestContext) error {
w.Header().Set("Content-Type", "text/html; charset=utf-8")
w.WriteHeader(http.StatusOK)
_, err := w.Write([]byte(`<!doctype html><html><body><h1>Account access</h1>` +
`<p>Recovery.</p></body></html>`))
return err
}