ESLint, server-action tests, export-my-data, enriched healthcheck, CHANGELOG
ESLint
- Pinned eslint@^8 + eslint-config-next@^14 to match Next 14's `next lint`.
ESLint 9's flat-config breaks `next lint` for legacy projects.
- .eslintrc.json extends next/core-web-vitals; ignores tests/, scripts/,
prisma/data/, .next/, node_modules.
- 7 pre-existing warnings surfaced (exhaustive-deps + alt-text + img tag
in user-written components). Left as warnings — pre-existing, not
breaking. CI runs lint; warnings don't fail the job.
Server action tests (tests/actions-admin.test.ts, tests/actions-auth.test.ts)
- Vitest setup file (tests/helpers/setup-actions.ts) sets DATABASE_URL
to a per-process temp SQLite DB and runs `prisma db push` BEFORE
lib/prisma instantiates its global PrismaClient. Tests then call the
real server actions against an isolated DB.
- vi.mock + vi.hoisted to mock @/lib/auth.getCurrentUser, next/headers
cookies+headers, next/navigation redirect, next/cache revalidatePath.
- Coverage:
- admin: setUserAdmin (Forbidden, promote, last-admin demote refused,
demote-with-other-admin allowed), deleteUser (last-admin guard,
self-delete refused, cascading delete to exercises + workouts),
adminResetPassword (hash-and-revoke, short-password rejected).
- auth flows: signupAction (closed by default, opens-and-creates,
mismatched confirm rejected, short pwd rejected, malformed email
rejected, no email-enumeration leak), changePasswordAction
(rotate-and-revoke-others, wrong current pwd rejected, no-op pwd
rejected), deleteMyAccountAction (phrase required, password required,
last-admin refused, success cascades + clears cookie + redirects).
- Total suite: 34 tests, ~2s.
Export my data (/api/me/export + Settings -> Export my data)
- Downloads a JSON dump of every workout/set/exercise/program tied to
the user. Excludes password hash and sessions. Filename includes
email + date. content-disposition: attachment, no-store cache.
- Exported shape matches the underlying tables 1:1 so a future "import
my data" flow can round-trip without ambiguity.
Enriched /api/health
- Now reports: database.connected, database.journalMode (and walEnabled
shortcut), users count, instanceSettings.signupsOpen, library.available
+ sizeBytes. Surfaces a `warnings` array if journal_mode != 'wal' but
doesn't fail the check (app still works without WAL — just unsafe for
online backups). Returns 503 only on hard DB failure.
CHANGELOG.md
- Single Unreleased section documenting everything that will ship as
v1.0.0:1 once the maintainer drops a fresh /data snapshot. Added /
Changed / Removed / Compat-notes sections.
This commit is contained in:
+105
@@ -0,0 +1,105 @@
|
||||
# Changelog
|
||||
|
||||
All notable changes to Proof of Work and its StartOS package wrapper.
|
||||
The format roughly follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/);
|
||||
versions track the StartOS package release rev (`upstream:rev` per ExVer).
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
This is everything in `master` since the last published `.s9pk`. Will
|
||||
ship as v1.0.0:1 once the maintainer drops a fresh `/data` snapshot
|
||||
into `start9/0.4/seed/data/app.db` and runs `make x86 && make install`.
|
||||
|
||||
### Added
|
||||
- **Multi-user support.** Every install starts with one admin
|
||||
(`admin@local`) and sign-ups closed. The admin opens sign-ups via
|
||||
Settings -> Instance Settings or via the new StartOS package action
|
||||
"Set new signups". New users start with no admin privileges and the
|
||||
full curated exercise library auto-seeded.
|
||||
- **Curated exercise library** at `proof-of-work/prisma/exercises.seed.json`.
|
||||
Used by `prisma/seed.ts` for fresh installs and by
|
||||
`ensureExerciseLibrary.cjs` (run from `docker_entrypoint.sh` on every
|
||||
boot) so library updates flow to existing installs additively, never
|
||||
overwriting users' own custom exercises.
|
||||
- `npm run sync-library` regenerates the JSON from the live snapshot.
|
||||
- **In-app password change** at Settings -> Change password. Verifies
|
||||
current password, requires 8+ char new password, auto-revokes every
|
||||
other session for the user.
|
||||
- **Admin user management** at `/main/admin/users`. List / promote /
|
||||
demote / reset-password / delete with last-admin guard and self-delete
|
||||
guard. Admin-initiated password reset force-revokes all the target's
|
||||
sessions.
|
||||
- **Self-serve account deletion** at Settings -> Danger Zone. Requires
|
||||
current password AND typing the literal phrase "delete my account".
|
||||
Refused for the last admin. Cascades through Prisma onDelete.
|
||||
- **Last-login tracking** (`User.lastLoginAt`). Stamped on every session
|
||||
creation, displayed as a relative-age cell in the admin Users table.
|
||||
- **Export my data** at Settings -> Export my data. Downloads a JSON
|
||||
with every workout, set, exercise, program tied to the user. Password
|
||||
hash and sessions excluded.
|
||||
- **Rate limits** on `/auth/login` (10/IP/15min) and `/auth/signup`
|
||||
(5/IP/15min). In-process sliding window, no deps.
|
||||
- **Security headers**: Content-Security-Policy (with frame-ancestors
|
||||
'none', form-action 'self', object-src 'none'),
|
||||
Strict-Transport-Security, Referrer-Policy, Permissions-Policy.
|
||||
- **SQLite WAL mode** + `synchronous=NORMAL` enabled in entrypoint.
|
||||
Keeps readers from blocking on a concurrent backup-time writer.
|
||||
- **StartOS Package Action** `change-admin-credentials` now keys on
|
||||
`WHERE isAdmin = 1 ORDER BY createdAt ASC LIMIT 1` (previously oldest
|
||||
user regardless of role).
|
||||
- **StartOS Package Action** `toggle-signups`: same setter as the
|
||||
in-app admin toggle, accessible from the StartOS UI without an admin
|
||||
login. Asserts read-back matches written value.
|
||||
- **Test suite** (Vitest, 34 tests, ~2s): rate limit, hashing,
|
||||
curated-library multi-user idempotency, admin actions including
|
||||
last-admin guard, signup gate + email-enumeration leak check,
|
||||
password-change, account deletion.
|
||||
- **GitHub Actions CI**: app job runs prisma validate + prisma generate
|
||||
+ tsc + lint + tests; startos job runs the package's `npm run check`
|
||||
(tsc --noEmit). Both on push and PR to master/main.
|
||||
- **ESLint config** (`.eslintrc.json` extending `next/core-web-vitals`).
|
||||
Wired into CI.
|
||||
- **Enriched `/api/health`** reports DB connection, journal mode (WAL
|
||||
status), library JSON availability, instance signup state, and user
|
||||
count. 503 if DB is unreachable; warnings field for non-fatal issues
|
||||
(e.g. journal_mode != wal).
|
||||
|
||||
### Changed
|
||||
- **Rebranded `workout-log` -> `proof-of-work`** end-to-end. Folder,
|
||||
npm package name, StartOS package id. StartOS treats this as a brand
|
||||
new service; cutover from the legacy package is via baked seed in
|
||||
v1.0.0:1, not in-place upgrade.
|
||||
- **Session tokens** now 256-bit `crypto.randomBytes` hex. Were derived
|
||||
from `Math.random() + Date.now()` — predictable enough that a
|
||||
determined attacker could enumerate other users' tokens. **Existing
|
||||
sessions invalidate on upgrade by design** — token format/length
|
||||
changed, old tokens won't validate.
|
||||
- **Version graph reset** to `1.0.0:1` (was on the legacy `workout-log`
|
||||
v0.1.0:18 / :19 / :20 line).
|
||||
|
||||
### Removed
|
||||
- Legacy `start9/0.3.5/` package (StartOS 0.3.5 wrapper, 65MB image
|
||||
artifacts, no longer the deploy target).
|
||||
- `start9-example-packaging/` template from another project.
|
||||
- Workout-planner standalone Dockerfile + docker-compose.yml (replaced
|
||||
by the StartOS package's own Dockerfile).
|
||||
- Various planning docs replaced by `start9/0.4/DEPLOY_040.md` and the
|
||||
root `README.md`.
|
||||
|
||||
### Compat notes for cutover
|
||||
The boot-time entrypoint runs idempotent ALTERs that:
|
||||
- Add `User.isAdmin` and auto-promote the oldest user to admin if no
|
||||
admin exists (preserves admin functionality across the cutover).
|
||||
- Add `User.lastLoginAt`.
|
||||
- Create the `InstanceSettings` table + singleton row (signupsOpen=0).
|
||||
- Switch SQLite to WAL mode (persists in DB header thereafter).
|
||||
|
||||
So a snapshot pulled off the legacy `workout-log` host comes up as a
|
||||
working multi-user `proof-of-work` install with no manual SQL.
|
||||
|
||||
---
|
||||
|
||||
## v0.1.0:17 and earlier (legacy `workout-log` package)
|
||||
|
||||
Out of scope for this changelog. The legacy package's history lives in
|
||||
the git log; nothing in this repo references it after v1.0.0:1 ships.
|
||||
Reference in New Issue
Block a user