chore(migrations): renumber 090 → 096 + small notes from #403 review

Post-merge cleanups after #403 (customer portal):

- Renumber 090_backfill_photo_dimensions_v2.js → 096 to follow #403's
  090_add_customer_accounts ... 095_add_customer_portal_flag chain.
- customerAccountsService.js: TODO note on must_change_password
  documenting that the column is decorative until an admin
  pre-loaded-password flow ships (mirrors what adminAuth does for
  must_change_password today).
- customerAuth.js: doc-comment on the /login route explaining why the
  customerPortal feature flag deliberately doesn't gate it (toggle off
  hides UI, doesn't revoke existing-customer access; deactivate
  individual accounts to lock out).
- 095_add_customer_portal_flag.js: header comment said "Migration 094"
  (copy-paste from 094) — now matches the filename.
This commit is contained in:
Paul Nothaft
2026-05-11 09:59:47 +02:00
parent 936a277eb8
commit 49b36a0352
4 changed files with 13 additions and 1 deletions
@@ -1,5 +1,5 @@
/**
* Migration 094: Add `customerPortal` to feature_flags.
* Migration 095: Add `customerPortal` to feature_flags.
*
* The customer portal (#354) is the foundation feature for the
* customer-side UI surface — login, dashboard, profile, password reset,
+6
View File
@@ -48,6 +48,12 @@ const TOKEN_TTL_SECONDS = 24 * 60 * 60; // mirrors admin tokens
// ---- login -------------------------------------------------------------
// The customerPortal feature flag deliberately does NOT gate this route.
// Flipping the master toggle off in Settings → Features hides the admin
// UI surface (sidebar entry, /admin/customers page) but does not revoke
// access for customers who already accepted an invitation. To lock out
// existing customers, deactivate their accounts individually
// (customer_accounts.is_active = false) — which IS enforced below.
router.post('/login', [
body('email').isEmail().normalizeEmail().withMessage('Valid email is required'),
body('password').isString().notEmpty(),
@@ -220,6 +220,12 @@ async function acceptInvitation({ token, name, password, profile }) {
country_code: merged.country_code || null,
password_hash: passwordHash,
is_active: formatBoolean(true),
// must_change_password is decorative today — accept-invite always
// sets a customer-chosen password, so this flag is never true and
// customerAuth doesn't read it. TODO when we ship an "admin
// pre-loads a temporary password" flow: surface a code in the
// login response (mirroring adminAuth's MUST_CHANGE_PASSWORD) and
// add a /change-password gate to customerAuth.
must_change_password: formatBoolean(false),
// Leave password_changed_at NULL on initial accept. Setting it here
// creates a millisecond/second-rounding race with the JWT issued