migrate auth v2 code
This commit is contained in:
@@ -47,7 +47,9 @@
|
||||
- `/api/admin/signup` now creates a member record for the parent with `role: 'parent'`
|
||||
- `/api/admin/login` returns `memberName`, `memberColor`, `role` alongside session info
|
||||
- `/api/members/verify-token` returns `role` for the frontend
|
||||
- `requireAdmin` middleware unchanged (still checks `fam_admins`)
|
||||
- `requireAdmin` middleware checks `users` (`role='parent' && id = userId`, scoped by `famId`)
|
||||
- **Removed `fam_admins` collection (Aug 2026):** parent identity fully lives on the `users` record (`famId`, `role`, `name`, `color`, `email`). `requireAdmin`, `authorizeFamReq`, `resolveChatActor`, admin `/profile` get/patch, and the legacy proxy `/signup`/`/login` now read/write `users` instead. Signup no longer creates a `fam_admins` row; superadmin stats derive `parentEmail` from `users role='parent'`. Migrate step 34 drops the collection.
|
||||
- **Removed `fams.inviteCode` (Aug 2026):** join flow is OTP-based (`user_configs.otp`), so the stored/displayed/regenerated invite code was never consumed. Removed `randomCode()` at signup, the `/regen-invite` endpoint + `hono.admin.regenInvite` action, the `inviteCode` type/field, and added migrate step 34 to drop the field.
|
||||
- Frontend sidebar is role-aware: `isParent = session !== null`
|
||||
- **No more `/admin` prefix** — admin pages live under `/{fam}/{parent-username}/chores` etc.
|
||||
|
||||
@@ -221,3 +223,23 @@
|
||||
- Reverted `docker/Dockerfile` `POCKETBASE_VERSION` back to `0.25.8` (matches `docker/Dockerfile.dev`). Dev `pb-dev` and the docker app internal PB `:8091` share host `./pb_data`.
|
||||
- **Migration schema scripts (DO NOT FORGET)**: the schema single source of truth is `shared/pb/schema.ts` (`SCHEMA_PLAN`), iterated by `proxy/src/migrate.ts` (`ensureSchema`) and `proxy/scripts/seed.ts`. The chat `messages` / `chat_typing` collections are defined there **without** an explicit `createdAt` — they rely on PB auto-adding it on first create.
|
||||
- The 0.39 prod `messages` drift (missing `createdAt`, then `id` "Cannot be blank" after a raw field PATCH) came from schema mismatch during the bump. When migrating 0.25→0.39, reconcile the schema scripts against 0.39's field semantics (incl. system `id` `autogeneratePattern`) instead of patching collections by hand.
|
||||
|
||||
### 2026-08-15 — Members→users migration, OTP child login, cookie httpOnly, slugify
|
||||
|
||||
- **Migration (members → users, run live + verified):** deleted the `members` collection and repointed the 5 `memberId` relations (`assigned_chores`, `completions`, `rewards`, `weekly_history`, `bonus_configs`) → `users`. Added `users.name` + `users.color`, backfilled child name/color; backfilled parent `role` (`''` → `'parent'`). Scoped `users` rules: list/view = `famId = @request.auth.famId`, update/delete = `famId = @request.auth.famId && @request.auth.role = 'parent'`. Re-runs are idempotent.
|
||||
- **PB relation quirk:** PB forbids changing a relation's target collection in place (`validation_field_relation_change`) — you must **drop the field and re-add it** targeting the new collection in two separate collection updates.
|
||||
- **Child (member) auth:** children are `users` records with `role='child'`, PB `username = {famSlug}:{handle}` (composite, globally unique), `name` = raw display name. Server derives the PB password = `MEMBER_SECRET + famSlug + handle`; the join gate is a 20-min OTP in `user_configs`, then `authWithPassword`. Children now hold a `pb_token` cookie (previously a `device_token` cookie with no session). `SessionUser` gained `username` (set in `hooks.server.ts`, stores `handleOf(record.username)`); the kanban child redirect compares `session.username`, not `session.name`.
|
||||
- **Cookie httpOnly:** `pb_token` is now `httpOnly:true`. The browser PB SDK is seeded from `page.data.pbToken` via `initPb(token)` (layout onMount) — **not** from `document.cookie` (the client can no longer read it). Logout is server-side only. Note: the JWT is still shipped to the client in SSR HTML via the `pbToken` prop.
|
||||
- **Typecheck baselines:** frontend `svelte-check` = 20 pre-existing canary errors (chat `json(status)` ResponseInit, `$types` Action/SubmitFunction, qrcode decl, vite.config, RewardType/Frequency casts, implicitly-any); proxy `tsc --noEmit` = pre-existing record-typing + implicit-any errors. No new errors in edited files.
|
||||
- **`crypto.randomUUID()` unavailable over plain HTTP** (non-secure context) → added `genClientId()` fallback (randomUUID if available, else `Date.now().toString(36)+random`) in `chat.svelte.ts`.
|
||||
- **Shared slugify util:** `shared/slugify.ts` (`@shared/slugify` alias) — used by proxy signup + fam rename, frontend signup + settings `renameFam`, and `member-otp.createChild` (child username + password gen). Removed the 3 local duplicate `slugify` helpers.
|
||||
- **Settings UI:** CardGrid/Card are now responsive (media queries + `--grid-cols`/`--card-cols`; Cards use `container-type: inline-size`). Members card split into two columns: add-child form | member list (space-between rows, larger 22px colour circle, "Preview" CTA → `/{famSlug}/{m.username}`, Remove). Family Name card gained an explainer + mono `/{fam.slug}` slug line.
|
||||
- **Hono audit:** the proxy is the live data layer — admin CRUD/reads via `hono.admin.*` (kanban load, bonuses ~17 calls, ledger 6, preferences 2, fam dash 4, settings `complete-week`/`debug/generate-data`), member actions via `memberApi.*` (toggleCompletion/claimReward/payday) + direct `/api` fetches (chores assigned-chores, kanban `/api/members/me`). **Not implemented:** `/api/weekly-cron` CRON, Stripe (`create-checkout` + webhook), WhatsApp — weekly settlement is manual via `complete-week`/`simulateEow`. Chat endpoints exist in the proxy but the frontend talks to PB directly.
|
||||
|
||||
### 2026-08-15 — Signup: multi-step flow restored + family-creation bug fixed
|
||||
|
||||
- **Bug (blocker):** new family creation failed with PB `{"username":{"code":"validation_required","message":"Cannot be blank."}}` — the `users.create` in `/signup` omitted the auth `username` field (PB 0.39 requires it). Reproduced directly against PB: create WITHOUT `username` → `validation_required`; WITH `username` → succeeds.
|
||||
- **Fix:** `signup/+page.server.ts` now includes `username` on the parent `users` create.
|
||||
- **Username convention (composite + handle):** PB `users.username` is the composite `{famSlug}:{handle}` for BOTH parents and children — globally unique (PB auth-identity needs a single-column unique index) even though the URL segment is per-family. `handle(name)` = lowercase, strips all non-`[a-z0-9]` (`"Jakey Boy"` → `jakeyboy`); `slugify()` (hyphenated) is kept only for fam slugs. URL segment = `handleOf(username)` (part after the last `:`) → `/{famSlug}/{handle}`. `name` keeps the raw display name, read from DB via `authRefresh` (not plucked into the cookie). Parent's handle captured at signup step 1 (`yourName`) → `username = famUsername(famSlug, handle(yourName))`; parents authenticate email+password and land on the fam dashboard `/{famSlug}` (not username-routed). Children authenticate via OTP → `authWithPassword(famUsername(...), derivePassword(famSlug, handle))`. Redirects in `login/+page.server.ts`, `[fam]/[username]/+page.server.ts` (parent + child branches) and `preferences/+page.server.ts` use `session.username` (the handle). Member-list URLs in `settings`, `[fam]/+page.svelte`, `[fam]/[username]/+page.svelte` build `/{famSlug}/{handleOf(m.username)}`. `handle`/`handleOf`/`famUsername` live in `shared/slugify.ts` (`@shared/slugify`).
|
||||
- **Restored intended multi-step signup** (from the guide, adapted to current OTP model): `/signup` steps — (1) `?/signup` familyName/yourName/email/password → create fam + parent (name=yourName, username=famUsername(famSlug, handle(yourName))) + settings, set `pb_token`; (2) `?/child` optional child → `issueAccess` returns `{ code, joinUrl }`; (3) show OTP join code + "Go to dashboard" link. Uses named actions + `use:enhance` (callback typed `any` to avoid the pre-existing canary `$types` SubmitFunction error).
|
||||
- **Typecheck:** frontend `svelte-check` stays at 20 pre-existing errors (no new in edited files).
|
||||
|
||||
Reference in New Issue
Block a user