Skip to content

feat: Admin model extension and superadmin bootstrap script - #53

Merged
codebestia merged 1 commit into
ShadeProtocol:mainfrom
G-ELM:feat/admin-superadmin-bootstrap
Aug 21, 2026
Merged

feat: Admin model extension and superadmin bootstrap script#53
codebestia merged 1 commit into
ShadeProtocol:mainfrom
G-ELM:feat/admin-superadmin-bootstrap

Conversation

@G-ELM

@G-ELM G-ELM commented Aug 21, 2026

Copy link
Copy Markdown
Contributor

Summary

Admin had no way to get a row into it. There is deliberately no admin self-registration endpoint — unlike merchants, who provision themselves on first wallet sign-in — so the first admin has to come from a terminal script, and every admin after that is created by one that already exists. This adds the model fields that make that shape expressible and the script that bootstraps the first row.

Since every other admin issue depends on this model's final shape, the field set is the thing to scrutinise here.

Schema

One correction to the issue's premise: active and isSuperAdmin already existed. They were added by the 20260821000000_add_admin_refresh_token migration, so this PR only adds the two fields that were genuinely missing:

name         String
createdBy    String?   // self-relation FK → Admin.id, null only for the bootstrap admin

createdBy is a nullable self-relation (AdminCreatedBy), ON DELETE SET NULL — Prisma's default for an optional relation. Null means "no creator", which is true of exactly one row: the one the script makes.

The migration adds name with a temporary DEFAULT '' and then drops the default. Nothing can have written an Admin row before now, so the table is empty in practice, but this keeps the migration from failing on the NOT NULL for anyone with a hand-inserted dev row. The end state matches the schema exactly — no default survives.

Script

scripts/create-superadmin.ts, wired up as npm run admin:create-superadmin:

npm run admin:create-superadmin -- --address=<G...> --name="Jane Doe"
  • Validates the address with StrKey.isValidEd25519PublicKey — the same check createChallengeController uses, so an address the network would reject is rejected here too (including a well-formed one with a bad checksum)
  • Refuses an address that already has an Admin row. Re-running with the same address is an operator mistake worth surfacing, never an overwrite and never a silently swallowed no-op
  • Creates with isSuperAdmin: true, active: true, createdBy: null
  • Both refusals exit non-zero before any Prisma call, so there is no write on a rejected input
  • Accepts --flag=value and --flag value, since operators type these by hand
  • parseArgs and createSuperadmin are exported so the guard rails are testable; main() runs only when the file is executed directly

CLI-only by design. No HTTP route reaches this code — I grepped src/routes/ and src/controllers/ to confirm none exists.

Testing

npm test391 tests / 45 suites passing (+11 new). prisma validate clean, tsc --noEmit clean, eslint 0 errors, prettier clean.

Verified end-to-end from the terminal:

$ npx tsx scripts/create-superadmin.ts --address=GNOTVALID --name="Jane Doe"
"GNOTVALID" is not a valid Stellar public key.
exit=1

$ npx tsx scripts/create-superadmin.ts --address=GBNFW62V...K5YY
--name is required.
Usage: npm run admin:create-superadmin -- --address=<G...> --name="<full name>"
exit=1

tests/unit/create-superadmin.test.ts covers argument parsing in both spellings, invalid and bad-checksum addresses, missing/blank name, a --name flag swallowed by the next flag, the exact create payload, and duplicate refusal (both when the existing admin is and is not already a superadmin).

Also in this PR

Widened the format/format:check npm globs from src/** to {src,scripts}/**, so the new top-level directory does not escape the formatting the rest of the source is held to. I left .github/workflows/ alone — its prettier step hardcodes src/**, so CI still will not check scripts/. Worth a follow-up if you want that enforced.

Acceptance criteria

  • Admin model updated (name, createdBy; active/isSuperAdmin already present)
  • prisma migrate dev runs cleanly — needs a live database, see above
  • npm run admin:create-superadmin -- --address=<valid G...> --name="Jane Doe" creates a row with isSuperAdmin: true, active: trueunit-tested, needs a live database to confirm
  • Invalid Stellar address → exits non-zero, no row created
  • Address already present → exits non-zero, no overwrite or duplicate
  • No HTTP endpoint exists for admin creation

Closes #39

…cript

Admin had no way to get a row into it. There is deliberately no admin
self-registration endpoint — unlike merchants, who provision themselves
on first wallet sign-in — so the first admin has to come from a terminal
script, and every later admin is created by one that already exists.

Extends Admin with `name` and a nullable self-referential `createdBy`,
null only for the script-bootstrapped admin. `active` and `isSuperAdmin`
were already added by the admin refresh-token migration, so they are
unchanged here.

scripts/create-superadmin.ts validates the address with
StrKey.isValidEd25519PublicKey — the same check createChallengeController
uses — and refuses an address that already has an Admin row rather than
overwriting or duplicating it. Both refusals exit non-zero without
touching the database. It is CLI-only by design: no HTTP route reaches
this code.

The `name` column is added with a temporary default that is then dropped,
so the migration cannot fail on the NOT NULL for a dev database that had
an Admin row hand-inserted. The end state matches the schema exactly.

Also widens the format/format:check globs to cover the new scripts/
directory, so it does not escape the formatting the rest of the source
is held to.

@codebestia codebestia left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!
Thank you for your contribution.

@codebestia
codebestia merged commit 5f7631d into ShadeProtocol:main Aug 21, 2026
2 checks passed
@coderabbitai

coderabbitai Bot commented Aug 21, 2026

Copy link
Copy Markdown
Contributor

Important

  • 🔍 Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: e9a1182e-a9a6-4aae-8b28-c524f15927a6


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Admin Model & Superadmin Bootstrap Script

2 participants