feat(seed): require ADMIN_INITIAL_PASSWORD + convert setup-db to ESM (drop-public-setup) #13

Merged
varutasu merged 5 commits from convoy/drop-public-setup into main 2026-05-23 18:02:44 -04:00
varutasu commented 2026-05-23 16:09:28 -04:00 (Migrated from github.com)

Summary

Closes P0 #3 from .convoys/ship-readiness.md. Removes the last hardcoded admin credential from the source tree and fixes a pre-existing Node 22.x compatibility bug in the seed script so the new env-var gate actually fires.

  • Brief 1scripts/setup-neon-db.js now requires ADMIN_INITIAL_PASSWORD env var (fails loudly with an actionable message + process.exit(1) before any DB connection if unset). README.md env-example block lists the new var; the "Default Admin Account" section is replaced with "First-time admin setup" copy that documents the env var, generation suggestion (openssl rand -base64 24), CI-secret alternative, and operator-rotation note for envs that pre-date this change. Zero occurrences of admin123 remain in source or README.
  • Brief 2scripts/setup-neon-db.js converted from CommonJS (require()) to native ES modules (import). Brief 1's env-var gate is theatrical without this because package.json has "type": "module" (from bump-next-js for ESLint v9 flat config) and npm run setup-db was throwing ReferenceError: require is not defined in ES module scope on Node 22.x. Pure module-system conversion — no functional changes, same DDL, same env-var gate, same ON CONFLICT DO NOTHING.

Diff is 2 source files (scripts/setup-neon-db.js +21/-7, README.md +27/-5) plus 3 convoy planning docs (.convoys/drop-public-setup.md + 2 briefs) for the audit trail.

Decisions

Choice Why
A — Existing-admin rotation A1: going-forward only Matches Brief 1 of fix-auth-bypass (JWT_SECRET) — fix-loud the input, don't sweep data. Operators rotate manually via the app post-merge. Queue rotate-default-admin if a real audit demands it.
B — no-go-zones reading Operational edit allowed .cursor/rules/no-go-zones.mdc prohibits schema edits to setup-neon-db.js. Env-var gate + 2 console.log removals are operational. Zero DDL touched.
C — vitest coverage C1: no tests Setup script runs once per env; fails loudly via process.exit(1); manual smoke catches regressions. Unit-testing would require either extracting lib/seed-admin.js (scope creep) or spawning the script from vitest (slow, brittle). Same heuristic the existing 16-test suite uses.
D — Scope expansion for CJS→ESM Option B: expand convoy with brief 2 Mid-convoy discovery: setup-db doesn't actually run on Node 22.x. The two fixes are logically coupled (gate is theatrical without conversion); the conversion is mechanical (~6 LOC, same file); splitting would create a regression window where operators can't bootstrap a DB between PRs.

Full decision rationale lives in .convoys/drop-public-setup.md § Decisions A-D.

Smoke verification (Node 22.14.0, local)

Brief 1 + 2: fail-loud path

$ unset ADMIN_INITIAL_PASSWORD
$ npm run setup-db; echo "exit=$?"
[dotenv@17.2.1] injecting env (31) from .env.local
❌ ADMIN_INITIAL_PASSWORD environment variable is not set.

   Set it in .env.local for local dev, or as a CI secret if you run setup from CI.
   Generate a strong password with: openssl rand -base64 24
   See README.md → "First-time admin setup" for the full flow.

exit=1

Exit 1, no DB connection opened (no ✅ Connecting to Neon database...), brief-1 actionable stderr, no ReferenceError: require is not defined (the brief-2 fix).

Brief 1 + 2: happy path

$ export ADMIN_INITIAL_PASSWORD=temporary-strong-pw-for-smoke
$ npm run setup-db; echo "exit=$?"
✅ Connecting to Neon database...
✅ Created users table
✅ Created cards table
... (all DDL idempotent) ...
✅ Created admin user
🎉 Neon database setup completed successfully!

📋 Database Details:
   Database: Neon PostgreSQL
   Admin user ready (email: admin@tcgvault.com)

exit=0

Exit 0, admin123 and temporary-strong-pw-for-smoke both absent from stdout (R3 — stdout-leak prevention). Admin user ready (email: admin@tcgvault.com) replaces the two old credential-echo console.log lines.

End-to-end login (R1 case A — going-forward behavior)

Local DB has an admin row created 2025-07-23 with bcrypt.hash('admin123', 12). ON CONFLICT (email) DO NOTHING did not rotate it.

$ curl -sS -X POST http://localhost:3000/api/auth/login \
    -H 'content-type: application/json' \
    -d '{"email":"admin@tcgvault.com","password":"temporary-strong-pw-for-smoke"}'
HTTP 401  {"error":"Invalid credentials"}

$ curl -sS -X POST http://localhost:3000/api/auth/login \
    -H 'content-type: application/json' \
    -d '{"email":"admin@tcgvault.com","password":"admin123"}'
HTTP 200  {"success":true,"user":{...},"token":"eyJhbGci..."}

This is the documented Decision A behavior — re-running setup-db with the new env var does not rotate existing admin rows. See § Pre-merge operator checklist below.

Lint + tests

  • npm run lint → exit 0; matches pre-existing baseline (128 problems / 81 errors / 47 warnings, all pre-existing in React/pages files, none in scripts/setup-neon-db.js). Tracked by fix-lint-baseline (P1 #11.5 in ship-readiness).
  • npm run test:run3 test files, 16 tests, all passing (no test touches the files in this PR).

Pre-merge operator checklist

  • R1 callout — existing weak admin123 hashes are NOT rotated by this PR. Any environment (production, staging, dev branches) that currently has admin@tcgvault.com / admin123 in its database keeps that password after merge. Operators must rotate manually: log in with admin123, change the password via profile settings, verify the new hash. The queued rotate-default-admin follow-up convoy will ship an idempotent rotation script if a real audit finds a deploy still on the weak hash.
  • CI / Vercel env varADMIN_INITIAL_PASSWORD must be set as a Vercel project secret before merging if any branch runs npm run setup-db from CI. Today none do (it is a manual package.json script), but if a future CI step adds it, the secret must be in place first or the build will exit 1.
  • Local DB rotation (this reviewer's environment) — the convoy implementer confirmed the local DB still has admin123 working. Reviewer should rotate via the app before deploying any preview from this branch.

Resolved gotchas (AGENTS.md)

  • Gotcha #4 — "Default admin credentials are in the seed (admin@tcgvault.com / admin123)" → RESOLVED by brief 1. To be reflected in AGENTS.md by the doc-writer pass post-merge.

Flagged but deferred

  1. Sibling weak-credential references in scripts/reset-db.js, scripts/create-test-users.js, and TESTING_GUIDE.md — convoy out-of-scope per the no-go-zones rule (historical scripts) + the convoy spec. Queue purge-weak-creds-from-helpers (or fold into pick-a-name).
  2. admin@tcgvault.com hardcoded email in the SQL INSERT — not a credential issue; branding decision belongs to the queued pick-a-name convoy (P1 #12).
  3. Lint baseline (128 problems) — pre-existing per ship-readiness P1 #11.5. The L3 CI lint job is continue-on-error: true until fix-lint-baseline runs. Implementer confirmed this PR adds zero new lint issues.

Test plan (for reviewer)

  • Pull the branch, unset ADMIN_INITIAL_PASSWORD, run npm run setup-db. Confirm exit 1 + actionable stderr + no ReferenceError.
  • export ADMIN_INITIAL_PASSWORD=<your-test-value>, run npm run setup-db. Confirm exit 0 + ✅ Connecting to Neon database... + your value does not appear anywhere in stdout.
  • npm run test:run — all 16 tests pass.
  • Spot-check README.md rendering on the GitHub preview tab — confirm the "First-time admin setup" section reads clearly and the operator-rotation blockquote is visible.
  • Acknowledge the R1 callout above.

🤖 Generated with Cursor

Made with Cursor

## Summary Closes P0 #3 from `.convoys/ship-readiness.md`. Removes the last hardcoded admin credential from the source tree and fixes a pre-existing Node 22.x compatibility bug in the seed script so the new env-var gate actually fires. - **Brief 1** — `scripts/setup-neon-db.js` now requires `ADMIN_INITIAL_PASSWORD` env var (fails loudly with an actionable message + `process.exit(1)` *before* any DB connection if unset). `README.md` env-example block lists the new var; the "Default Admin Account" section is replaced with "First-time admin setup" copy that documents the env var, generation suggestion (`openssl rand -base64 24`), CI-secret alternative, and operator-rotation note for envs that pre-date this change. Zero occurrences of `admin123` remain in source or README. - **Brief 2** — `scripts/setup-neon-db.js` converted from CommonJS (`require()`) to native ES modules (`import`). Brief 1's env-var gate is theatrical without this because `package.json` has `"type": "module"` (from `bump-next-js` for ESLint v9 flat config) and `npm run setup-db` was throwing `ReferenceError: require is not defined in ES module scope` on Node 22.x. Pure module-system conversion — no functional changes, same DDL, same env-var gate, same `ON CONFLICT DO NOTHING`. Diff is **2 source files** (`scripts/setup-neon-db.js` +21/-7, `README.md` +27/-5) plus 3 convoy planning docs (`.convoys/drop-public-setup.md` + 2 briefs) for the audit trail. ## Decisions | | Choice | Why | |---|---|---| | **A — Existing-admin rotation** | A1: going-forward only | Matches Brief 1 of `fix-auth-bypass` (JWT_SECRET) — fix-loud the input, don't sweep data. Operators rotate manually via the app post-merge. Queue `rotate-default-admin` if a real audit demands it. | | **B — no-go-zones reading** | Operational edit allowed | `.cursor/rules/no-go-zones.mdc` prohibits **schema** edits to `setup-neon-db.js`. Env-var gate + 2 `console.log` removals are operational. Zero DDL touched. | | **C — vitest coverage** | C1: no tests | Setup script runs once per env; fails loudly via `process.exit(1)`; manual smoke catches regressions. Unit-testing would require either extracting `lib/seed-admin.js` (scope creep) or spawning the script from vitest (slow, brittle). Same heuristic the existing 16-test suite uses. | | **D — Scope expansion for CJS→ESM** | Option B: expand convoy with brief 2 | Mid-convoy discovery: setup-db doesn't actually run on Node 22.x. The two fixes are logically coupled (gate is theatrical without conversion); the conversion is mechanical (~6 LOC, same file); splitting would create a regression window where operators can't bootstrap a DB between PRs. | Full decision rationale lives in `.convoys/drop-public-setup.md` § Decisions A-D. ## Smoke verification (Node 22.14.0, local) ### Brief 1 + 2: fail-loud path ```bash $ unset ADMIN_INITIAL_PASSWORD $ npm run setup-db; echo "exit=$?" ``` ``` [dotenv@17.2.1] injecting env (31) from .env.local ❌ ADMIN_INITIAL_PASSWORD environment variable is not set. Set it in .env.local for local dev, or as a CI secret if you run setup from CI. Generate a strong password with: openssl rand -base64 24 See README.md → "First-time admin setup" for the full flow. exit=1 ``` ✅ Exit 1, no DB connection opened (no `✅ Connecting to Neon database...`), brief-1 actionable stderr, **no `ReferenceError: require is not defined`** (the brief-2 fix). ### Brief 1 + 2: happy path ```bash $ export ADMIN_INITIAL_PASSWORD=temporary-strong-pw-for-smoke $ npm run setup-db; echo "exit=$?" ``` ``` ✅ Connecting to Neon database... ✅ Created users table ✅ Created cards table ... (all DDL idempotent) ... ✅ Created admin user 🎉 Neon database setup completed successfully! 📋 Database Details: Database: Neon PostgreSQL Admin user ready (email: admin@tcgvault.com) exit=0 ``` ✅ Exit 0, `admin123` and `temporary-strong-pw-for-smoke` both **absent** from stdout (R3 — stdout-leak prevention). `Admin user ready (email: admin@tcgvault.com)` replaces the two old credential-echo `console.log` lines. ### End-to-end login (R1 case A — going-forward behavior) Local DB has an admin row created 2025-07-23 with `bcrypt.hash('admin123', 12)`. `ON CONFLICT (email) DO NOTHING` did **not** rotate it. ```bash $ curl -sS -X POST http://localhost:3000/api/auth/login \ -H 'content-type: application/json' \ -d '{"email":"admin@tcgvault.com","password":"temporary-strong-pw-for-smoke"}' HTTP 401 {"error":"Invalid credentials"} $ curl -sS -X POST http://localhost:3000/api/auth/login \ -H 'content-type: application/json' \ -d '{"email":"admin@tcgvault.com","password":"admin123"}' HTTP 200 {"success":true,"user":{...},"token":"eyJhbGci..."} ``` This is the **documented Decision A behavior** — re-running setup-db with the new env var does not rotate existing admin rows. See § Pre-merge operator checklist below. ### Lint + tests - `npm run lint` → exit 0; matches pre-existing baseline (128 problems / 81 errors / 47 warnings, all pre-existing in React/pages files, none in `scripts/setup-neon-db.js`). Tracked by `fix-lint-baseline` (P1 #11.5 in ship-readiness). - `npm run test:run` → **3 test files, 16 tests, all passing** (no test touches the files in this PR). ## Pre-merge operator checklist - [ ] **R1 callout — existing weak `admin123` hashes are NOT rotated by this PR.** Any environment (production, staging, dev branches) that currently has `admin@tcgvault.com / admin123` in its database keeps that password after merge. Operators must rotate manually: log in with `admin123`, change the password via profile settings, verify the new hash. The queued `rotate-default-admin` follow-up convoy will ship an idempotent rotation script if a real audit finds a deploy still on the weak hash. - [ ] **CI / Vercel env var** — `ADMIN_INITIAL_PASSWORD` must be set as a Vercel project secret **before** merging if any branch runs `npm run setup-db` from CI. Today none do (it is a manual `package.json` script), but if a future CI step adds it, the secret must be in place first or the build will exit 1. - [ ] **Local DB rotation (this reviewer's environment)** — the convoy implementer confirmed the local DB still has `admin123` working. Reviewer should rotate via the app before deploying any preview from this branch. ## Resolved gotchas (`AGENTS.md`) - **Gotcha #4** — "Default admin credentials are in the seed (`admin@tcgvault.com` / `admin123`)" → **RESOLVED** by brief 1. To be reflected in `AGENTS.md` by the doc-writer pass post-merge. ## Flagged but deferred 1. **Sibling weak-credential references** in `scripts/reset-db.js`, `scripts/create-test-users.js`, and `TESTING_GUIDE.md` — convoy out-of-scope per the no-go-zones rule (historical scripts) + the convoy spec. Queue `purge-weak-creds-from-helpers` (or fold into `pick-a-name`). 2. **`admin@tcgvault.com` hardcoded email** in the SQL `INSERT` — not a credential issue; branding decision belongs to the queued `pick-a-name` convoy (P1 #12). 3. **Lint baseline (128 problems)** — pre-existing per ship-readiness P1 #11.5. The L3 CI lint job is `continue-on-error: true` until `fix-lint-baseline` runs. Implementer confirmed this PR adds zero new lint issues. ## Test plan (for reviewer) - [ ] Pull the branch, `unset ADMIN_INITIAL_PASSWORD`, run `npm run setup-db`. Confirm exit 1 + actionable stderr + **no `ReferenceError`**. - [ ] `export ADMIN_INITIAL_PASSWORD=<your-test-value>`, run `npm run setup-db`. Confirm exit 0 + `✅ Connecting to Neon database...` + your value does **not** appear anywhere in stdout. - [ ] `npm run test:run` — all 16 tests pass. - [ ] Spot-check `README.md` rendering on the GitHub preview tab — confirm the "First-time admin setup" section reads clearly and the operator-rotation blockquote is visible. - [ ] Acknowledge the R1 callout above. 🤖 Generated with [Cursor](https://cursor.com) Made with [Cursor](https://cursor.com)
vercel[bot] commented 2026-05-23 16:09:33 -04:00 (Migrated from github.com)

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
tcg-vault Ready Ready Preview, Comment May 23, 2026 8:09pm

Request Review

[vc]: #9p4uQCD1Vus1wSyYxRcaiTZGL1JP08kxUCx82lBJj10=:eyJpc01vbm9yZXBvIjp0cnVlLCJ0eXBlIjoiZ2l0aHViIiwicHJvamVjdHMiOlt7Im5hbWUiOiJ0Y2ctdmF1bHQiLCJwcm9qZWN0SWQiOiJwcmpfRjZXOEVvRkd3Y0g3aWVGcnRvRlNlOXdVVkFhNSIsImxpdmVGZWVkYmFjayI6eyJyZXNvbHZlZCI6MCwidW5yZXNvbHZlZCI6MCwidG90YWwiOjAsImxpbmsiOiJ0Y2ctdmF1bHQtZ2l0LWNvbnZveS1kcm9wLXB1LTE0YmU3My1yYW5kYWxsLXN0aWxsd2VsbHMtcHJvamVjdHMudmVyY2VsLmFwcCJ9LCJpbnNwZWN0b3JVcmwiOiJodHRwczovL3ZlcmNlbC5jb20vcmFuZGFsbC1zdGlsbHdlbGxzLXByb2plY3RzL3RjZy12YXVsdC85NU01YjV3TjM5eXpQeVVFN25uWGZrYzhMMXY5IiwicHJldmlld1VybCI6InRjZy12YXVsdC1naXQtY29udm95LWRyb3AtcHUtMTRiZTczLXJhbmRhbGwtc3RpbGx3ZWxscy1wcm9qZWN0cy52ZXJjZWwuYXBwIiwibmV4dENvbW1pdFN0YXR1cyI6IkRFUExPWUVEIn1dLCJyZXF1ZXN0UmV2aWV3VXJsIjoiaHR0cHM6Ly92ZXJjZWwuY29tL3ZlcmNlbC1hZ2VudC9yZXF1ZXN0LXJldmlldz9vd25lcj12YXJ1dGFzdSZyZXBvPXRjZy12YXVsdCZwcj0xMyJ9 The latest updates on your projects. Learn more about [Vercel for GitHub](https://vercel.link/github-learn-more). | Project | Deployment | Actions | Updated (UTC) | | :--- | :----- | :------ | :------ | | [tcg-vault](https://vercel.com/randall-stillwells-projects/tcg-vault) | ![Ready](https://vercel.com/static/status/ready.svg) [Ready](https://vercel.com/randall-stillwells-projects/tcg-vault/95M5b5wN39yzPyUE7nnXfkc8L1v9) | [Preview](https://tcg-vault-git-convoy-drop-pu-14be73-randall-stillwells-projects.vercel.app), [Comment](https://vercel.live/open-feedback/tcg-vault-git-convoy-drop-pu-14be73-randall-stillwells-projects.vercel.app?via=pr-comment-feedback-link) | May 23, 2026 8:09pm | <a href="https://vercel.com/vercel-agent/request-review?owner=varutasu&repo=tcg-vault&pr=13" rel="noreferrer"><picture><source media="(prefers-color-scheme: dark)" srcset="https://agents-vade-review.vercel.sh/request-review-dark.svg"><source media="(prefers-color-scheme: light)" srcset="https://agents-vade-review.vercel.sh/request-review-light.svg"><img src="https://agents-vade-review.vercel.sh/request-review-light.svg" alt="Request Review"></picture></a>
github-actions[bot] commented 2026-05-23 16:09:38 -04:00 (Migrated from github.com)

Pipeline Health

Build + CI gates

Gate Status
Vercel build (Preview) pass
CI: Lint pass
CI: Schema map fresh skipped
Preview smoke skipped
Visual diff ⏭ skipped or pending

Build runs on Vercel; this CI runs lint and schema-map drift only (no duplicate build).

Role reports

Role Status
Reviewer report pending
A11y audit pending
Design system audit pending

See individual comments above for details. This rollup updates automatically.

<!-- pipeline-rollup --> ## Pipeline Health ### Build + CI gates | Gate | Status | | --- | --- | | Vercel build (Preview) | ✅ pass | | CI: Lint | ✅ pass | | CI: Schema map fresh | ❌ skipped | | Preview smoke | ❌ skipped | | Visual diff | ⏭ skipped or pending | _Build runs on Vercel; this CI runs lint and schema-map drift only (no duplicate build)._ ### Role reports | Role | Status | | --- | --- | | Reviewer report | ⏳ pending | | A11y audit | ⏳ pending | | Design system audit | ⏳ pending | See individual comments above for details. This rollup updates automatically.
Sign in to join this conversation.
No description provided.