Commit graph

10 commits

Author SHA1 Message Date
Arthur Belleville
b61f36f17e
fix(07): WR-01 NewRouter returns error instead of panicking on bad static FS
Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
2026-05-15 18:54:49 +02:00
Arthur Belleville
bdd3cba314
feat(07-01): wire embed.FS into NewRouter and RunMigrations into cmd/web/main.go
- backend/internal/web/router.go: staticDir string -> staticFS fs.FS; /healthz uses HealthzHandler(); /readyz registered with ReadyzHandler(pinger); embedded FS served via fs.Sub()
- backend/cmd/web/main.go: import assets "backend"; db.RunMigrations(ctx, pool, assets.Migrations) before router; web.NewRouter now receives assets.Static
- All *_test.go NewRouter call sites updated from "./static" to os.DirFS("./static"); "os" import added where missing
2026-05-15 18:14:33 +02:00
Arthur Belleville
49e84c8176
fix(05-WR-01): raise ReadTimeout/WriteTimeout to 120s for large uploads
15s was too short for 25MB uploads on slow connections (~256KB/s takes
~100s). Both timeouts are raised to 120s to accommodate MAX_UPLOAD_SIZE_MB
at worst-case bandwidth with headroom.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
2026-05-15 12:50:25 +02:00
Arthur Belleville
a12c5abea6
feat(05-02): 3-tab layout + files templates + router + main.go S3 wiring
- tablos.templ: TabloDetailPage gains files+activeTab params, 3-tab nav with hx-push-url
- tablos.templ: TabloOverviewTabFragment + TasksTabFragment (wraps KanbanBoard) added
- files.templ: FilesTabFragment, FileUploadForm (hx-encoding=multipart/form-data),
  FileListRow, FileListEmpty, FileRowGone, UploadErrorFragment
- files_helpers.go: formatBytes() converts int64 bytes to human-readable string
- router.go: fileDeps FilesDeps param added; TabloTasksTabHandler + file routes wired
- handlers_tablos.go: both TabloDetailPage call sites updated (nil, 'overview')
- main.go: S3_ENDPOINT/S3_BUCKET/S3_REGION env vars read; files.NewStore constructed;
  fileDeps wired; nil filesStore allowed when S3 env unset (503 from handlers)
- All test routers updated to pass FilesDeps{} in new param position
2026-05-15 12:28:33 +02:00
Arthur Belleville
181ae79369
feat(04-02): TasksDeps, task handlers, router task routes
- Add handlers_tasks.go: TasksDeps, TaskNewFormHandler, TaskCancelNewHandler, TaskCreateHandler, TaskShowHandler, TaskDeleteConfirmHandler, TaskDeleteHandler, plus stub Edit/Update/Reorder handlers
- Add task routes to router.go (static before parametric per Pitfall 1)
- Add TasksDeps param to NewRouter; update main.go and all test callers
- Move TaskColumns/TaskColumnLabels to templates package to avoid import cycle
2026-05-15 09:31:59 +02:00
Arthur Belleville
c8f44b1ad2
test(03-01): add TablosDeps stub and RED integration test scaffold for TABLO-01..06
- handlers_tablos.go: TablosDeps stub type enabling test compilation
- handlers_tablos_test.go: 10 integration tests (RED baseline) for all TABLO-01..06 paths
  - TestTabloList, TestTabloList_Empty, TestTabloCreate, TestTabloCreate_Validation
  - TestTabloDetail_Owner, TestTabloDetail_NonOwner, TestTabloDetail_InvalidID
  - TestTabloUpdate, TestTabloDeleteConfirm, TestTabloDelete
- router.go: NewRouter accepts TablosDeps as second deps parameter
- handlers_auth_test.go, handlers_test.go, csrf_test.go: update NewRouter call sites
- cmd/web/main.go: construct and pass TablosDeps to NewRouter
2026-05-15 00:13:31 +02:00
Arthur Belleville
389e1bc8b4
feat(02-07): gorilla/csrf integration — mount middleware, wire all forms, env-driven key
- auth.Mount(env, key) wraps csrf.Protect with locked D-14/D-24 options
- auth.LoadKeyFromEnv() reads SESSION_SECRET, hex-decodes, validates 32 bytes; fails fast on error
- ui.CSRFField(token) templ component renders hidden _csrf input
- Layout, LoginPage/Fragment, SignupPage/Fragment, Index all embed @ui.CSRFField(csrfToken)
- Handlers thread csrf.Token(r) into every page/fragment render call
- NewRouter mounts auth.Mount after ResolveSession, before all route groups (D-24)
- main.go calls auth.LoadKeyFromEnv(); logs.Fatalf on missing/invalid SESSION_SECRET
- SESSION_SECRET documented in .env.example with openssl rand -hex 32 instruction
- go.mod: gorilla/csrf v1.7.3 (direct); prior tests updated with getCSRFToken helper
- All Plan 04/05/06 tests updated to acquire and submit valid _csrf tokens
2026-05-14 22:59:06 +02:00
Arthur Belleville
7d8c498980
feat(02-05): login vertical slice with rate limiting
- auth_login.templ: LoginPage + LoginFormFragment (mirrors signup shape)
- LoginForm + LoginErrors types added to templates/auth_forms.go
- LoginPageHandler + LoginPostHandler in handlers_auth.go
  - Rate-limit check before user lookup (D-16, T-2-14)
  - Single errInvalidCreds constant for D-20 enumeration defense
  - Session rotation via Store.Rotate on success (D-10, T-2-04)
  - HTMX-aware redirect and fragment responses (D-19, D-21)
- AuthDeps extended with Limiter *auth.LimiterStore field
- router.go: GET /login in RedirectIfAuthed group (D-23)
- main.go: LimiterStore created with janitor goroutine (D-16)
- Export NewLimiterStoreWithClock + SetLimiterClock for cross-package tests
- 12 TestLogin_* integration tests all pass with real DB
2026-05-14 22:27:54 +02:00
Arthur Belleville
efdc16babe
feat(02-04): signup handler, router wiring, and integration tests
- Add handlers_auth.go: SignupPageHandler + SignupPostHandler (validate -> hash -> insert -> session -> redirect)
- Add AuthDeps struct; wire argon2id hash, InsertUser, Store.Create, SetSessionCookie
- Update router.go: NewRouter accepts AuthDeps; mount ResolveSession (D-24); wire /signup routes behind RedirectIfAuthed
- Update cmd/web/main.go: build AuthDeps (sqlc.Queries + auth.Store + secure flag) and pass to NewRouter
- Add nil-Store guard to auth.ResolveSession for Phase 1 unit-test compatibility
- Update handlers_test.go: pass AuthDeps{} zero value to NewRouter (Phase 1 routes unaffected)
- Add testdb_test.go: isolated-schema test helper for web package integration tests
- Add handlers_auth_test.go: 8 TestSignup_* integration tests (all pass against real Postgres)
2026-05-14 22:17:50 +02:00
Arthur Belleville
08a2c3cd96
feat(01-03): cmd/web entrypoint with graceful shutdown
- Reads DATABASE_URL (required), PORT (default 8080), ENV (default
  development) from os.Getenv only — no third-party env loader (D-15:
  .env exported by just dev, prod injects real env)
- slog.SetDefault wired before fatal-on-missing-DSN so even startup errors
  emit structured output; sanitization per T-01-12 (never log DSN)
- pgxpool opened via db.NewPool; chi router mounted with ./static as the
  asset root
- http.Server: ReadTimeout=15s, WriteTimeout=15s, IdleTimeout=60s
  (T-01-10 slow-client mitigation)
- signal.NotifyContext (Go 1.21+) traps SIGINT/SIGTERM, propagates through
  ctx; on signal: srv.Shutdown with 10s timeout, then explicit pool.Close
  (Pitfall 4 — never via defer)
- No /readyz route (Phase 7 scope)
2026-05-14 19:26:22 +02:00