These were committed in cleartext and pushed to remote history. Rotate the
dev server SSH password and Postgres 'ladmin' password separately; this
only stops the leak going forward and points at Infisical/vault instead.
Closes three items from the outstanding e2e/CI gap list:
- Timing budgets: timings.measure() only ever recorded durations for the
(still-unwired) Pushgateway export — nothing failed CI when a step got
slow. Add e2e/timing-budgets.ts (expectWithinBudget + shared BUDGET_MS
buckets) and wire it into every measure() call site across the suite.
Mail-wait steps are deliberately left unbudgeted (external mail-trap
delivery latency, not a code performance signal).
- actions/upload-artifact@v4 doesn't work against this Gitea/act runner's
artifact backend — pin to v3 for the e2e failure-report upload.
- Shared act_runner npm cache has corrupted platform-native tarballs before
(@next/swc-linux-x64-musl) and reds viewer-unit/admin-unit/e2e with no
product bug involved. All three npm ci steps now retry once after
`npm cache clean --force` on first failure.
Verified: full local suite green against DEV (37 passed, 6 skipped, no
budget assertion failures) before wiring into CI.
- Wire E2E_API_USERNAME/PASSWORD (admin) + E2E_API_VIEWER_USERNAME/PASSWORD
(viewer) into the Gitea Actions e2e job so FastAPI-authed specs run in CI.
- Add api.role-permissions.spec.ts: viewer 403 vs admin 200/200/200 on
/api/v1/users, /api/v1/role-permissions, and /api/v1/photos/bulk-delete
(safe no-op via a non-existent photo id — proves the gate, not deletion).
- Add gallery.search-filters.spec.ts: tag_id and person_id filters on the
public /api/search route return correct subsets, combined filters narrow
results, and the /search UI tag-filter interaction updates the URL/count.
- Update ROADMAP/README/.env.example for the new env vars and coverage.
Wire viewer-frontend Vitest into Gitea Actions, centralize DEV base URLs
in env-defaults.json, expand playkit API catalog specs, and land auth/mail
flows plus verify-email public-host redirect and idle-logout e2e hooks.
Establishes frontend unit testing infra (jsdom env, RTL, jest-dom
matchers, common Radix/jsdom polyfills) and seeds it with tests for
lib/utils.ts, lib/photo-utils.ts, hooks/useFocusTrap.ts, and
ThemeToggle.tsx (46 tests total).
Adds test_api_integration.py (person identification, tag+search,
favorites+search, bulk favorites, and role-permission-grant workflows
chained across multiple endpoints) and test_api_errors.py (404/401/403/422
consistency sweeps, SQL-injection/XSS-style input handling, large-payload
handling) per the two suites the existing tests/API_TEST_PLAN.md already
documented but never implemented.
Along the way, found and documented (via a passing test, not a fix) that
GET /api/v1/faces/unidentified has no auth dependency, unlike its sibling
routes.
Adds pytest coverage for metrics, click_log, role_permissions, videos,
pending-identifications, pending-linkages, and reported-photos — the
last three required extending conftest with a second (auth) test
database + DDL mirroring the Prisma schema-auth.prisma tables, since
those endpoints read/write DATABASE_URL_AUTH via raw SQL.
pending-photos.py is intentionally left uncovered for now (heavy
filesystem/hash-based duplicate-detection logic needs its own mocking
strategy) and tracked as a follow-up.
Add an e2e/ Playwright harness (via @levkin/playkit) that smoke-tests
the public DEV host: login page loads, sign-out never redirects to a
LAN host, FastAPI /health responds, and forgot-password mail lands in
Mailtrap with a public reset link (Kolby #56/#57 regressions).
Wire it into Gitea Actions as a new `e2e` job (self-hosted runner,
Chromium via Playwright), gated behind the existing skip-ci-check.
Add npm root scripts (install:e2e, test:e2e) and gitignore e2e build
artifacts (node_modules, reports, .env).
Add next-themes light/dark/system theming with an accessible toggle
in every header, and close out an a11y pass across the viewer app:
skip-to-content link, main landmarks and heading hierarchy on every
route, focus-trapped dialog semantics for the photo viewer modals,
aria-label/live-region coverage for dynamic messages and icon
buttons, and dark: variants for previously light-only colors.
- NextAuth session/jwt callbacks no longer return null (the client
useSession() hook chokes on a null session, crashing the app on
sign-out for deactivated/refreshed sessions) — fixes#57
- UserMenu/Header sign-out handlers close the Manage Users overlay and
popover before calling signOut(), and no longer let a rejected
signOut() promise go unhandled
- Added an app-level error boundary as a safety net for any remaining
client render errors
- Registration and forgot-password flows now tell the user honestly
when the confirmation/reset email failed to send instead of always
claiming success — related to #56
- lib/email.ts throws a clear, actionable error when neither
SMTP nor Resend is configured, instead of a cryptic SDK error
- Documented required SMTP/Resend env vars in DEPLOYMENT_CHECKLIST.md
(were missing entirely, which is why no confirmation/reset emails
were ever sent in deployed environments) — fixes#56
- #45: re-read local folder when recursive checkbox toggles
- #47: clear Identify crop spinner when image is already cached
- #46: search selected people by person_ids; AND for "First Last"
- #21: enqueue network import without blocking API walk
- #26: serve resized grid thumbnails for photos
- #30/#31/#33: re-enable small-face filters in auto-match