#!/usr/bin/env node /** * Upsert a platform admin with a tenant company (fixes empty-company 400s). * Loads monorepo-root `.env` only for DATABASE_URL. * Email/password are CLI flags only — never read from .env. * * Usage (from repo root): * npm run seed:platform-admin -- --email you@example.com --password '…' * npm run seed:platform-admin -- --email you@example.com --promote-only * npm run seed:platform-admin -- --email you@example.com --password '…' --company 'Acme Ops' */ import { spawnSync } from "node:child_process"; import path from "node:path"; import { detectHostProfile, withGoLowMem } from "./lowmem-env.mjs"; import { loadRootEnv, repoRoot } from "./root-env.mjs"; const env = withGoLowMem(loadRootEnv(process.env), detectHostProfile()); function usage(msg) { if (msg) console.error(msg); console.error(`Usage: npm run seed:platform-admin -- --email you@example.com --password 'strong-password' npm run seed:platform-admin -- --email you@example.com --promote-only npm run seed:platform-admin -- --email you@example.com --password '…' --company 'Acme Ops' Email/password must be passed on the CLI (not stored in .env). DATABASE_URL comes from the shell or monorepo-root .env. Creates a company + admin membership + Free plan unless --no-company.`); process.exit(1); } /** Parse `--key value` / `--flag` from argv after npm's `--`. */ function parseArgs(argv) { const out = { email: "", password: "", name: "", company: "", promoteOnly: false, noCompany: false, }; for (let i = 0; i < argv.length; i++) { const a = argv[i]; if (a === "--promote-only") { out.promoteOnly = true; continue; } if (a === "--no-company") { out.noCompany = true; continue; } if (a === "--email" || a === "-email") { out.email = String(argv[++i] || "").trim(); continue; } if (a === "--password" || a === "-password") { out.password = String(argv[++i] || ""); continue; } if (a === "--name" || a === "-name") { out.name = String(argv[++i] || "").trim(); continue; } if (a === "--company" || a === "-company") { // Join remaining tokens until next --flag so unquoted "Admin Company" works. const parts = []; while (i + 1 < argv.length && !String(argv[i + 1]).startsWith("-")) { parts.push(String(argv[++i])); } out.company = parts.join(" ").trim(); continue; } if (a === "--help" || a === "-h") usage(); usage(`Unknown argument: ${a}`); } return out; } const opts = parseArgs(process.argv.slice(2)); if (!env.DATABASE_URL) { usage("DATABASE_URL is required (shell or monorepo-root .env)"); } if (!opts.email || !opts.email.includes("@")) { usage("--email is required"); } if (!opts.promoteOnly && opts.password.length < 8) { usage("--password is required (min 8 characters), or pass --promote-only"); } const apiDir = path.join(repoRoot, "apps", "api"); const args = [ "run", "./cmd/seed-platform-admin", "-postgres", env.DATABASE_URL, "-email", opts.email, "-confirm", ]; if (opts.name) args.push("-name", opts.name); if (opts.company) args.push("-company", opts.company); if (opts.noCompany) args.push("-no-company"); if (opts.promoteOnly) { args.push("-promote-only"); } else { args.push("-password", opts.password); } console.log(`==> seed-platform-admin ${opts.email}`); const r = spawnSync("go", args, { cwd: apiDir, env, stdio: "inherit", shell: false, }); process.exit(r.status ?? 1);