d4641e27aa
- /setup Server Component + SetupClient form + createFirstAdmin Server Action: zero-users guard (TOCTOU-safe), argon2 hash, ADMIN user creation, redirects to /auth/signin after setup - scripts/setup-admin.mjs: CLI alternative for headless/container setups - docs/installation.md: 7-section install guide (clone → configure → run → verify) Co-Authored-By: claude-flow <ruv@ruv.net>
83 lines
2.2 KiB
JavaScript
83 lines
2.2 KiB
JavaScript
#!/usr/bin/env node
|
|
// scripts/setup-admin.mjs
|
|
// Usage: node scripts/setup-admin.mjs --email admin@example.com --name "Admin" --password secret123
|
|
|
|
import { loadWorkspaceEnv } from "./load-env.mjs";
|
|
|
|
// Load .env if DATABASE_URL is not already set
|
|
if (!process.env.DATABASE_URL) {
|
|
const loaded = loadWorkspaceEnv();
|
|
if (loaded.length === 0 && !process.env.DATABASE_URL) {
|
|
console.error("ERROR: DATABASE_URL is not set. Create a .env file or export DATABASE_URL before running this script.");
|
|
process.exit(1);
|
|
}
|
|
}
|
|
|
|
// Parse CLI args
|
|
const args = process.argv.slice(2);
|
|
|
|
function getArg(flag) {
|
|
const index = args.indexOf(flag);
|
|
if (index === -1 || index + 1 >= args.length) return null;
|
|
return args[index + 1];
|
|
}
|
|
|
|
const email = getArg("--email");
|
|
const name = getArg("--name");
|
|
const password = getArg("--password");
|
|
|
|
const missing = [];
|
|
if (!email) missing.push("--email");
|
|
if (!name) missing.push("--name");
|
|
if (!password) missing.push("--password");
|
|
|
|
if (missing.length > 0) {
|
|
console.error(`ERROR: Missing required arguments: ${missing.join(", ")}`);
|
|
console.error("Usage: node scripts/setup-admin.mjs --email admin@example.com --name \"Admin\" --password secret123");
|
|
process.exit(1);
|
|
}
|
|
|
|
if (!email.includes("@")) {
|
|
console.error("ERROR: Invalid email address.");
|
|
process.exit(1);
|
|
}
|
|
|
|
if (password.length < 8) {
|
|
console.error("ERROR: Password must be at least 8 characters.");
|
|
process.exit(1);
|
|
}
|
|
|
|
const { PrismaClient } = await import("@prisma/client");
|
|
const { hash } = await import("@node-rs/argon2");
|
|
|
|
const prisma = new PrismaClient();
|
|
|
|
try {
|
|
const count = await prisma.user.count();
|
|
|
|
if (count > 0) {
|
|
console.log("Admin user already exists. Skipping.");
|
|
process.exit(0);
|
|
}
|
|
|
|
const passwordHash = await hash(password);
|
|
|
|
await prisma.user.create({
|
|
data: {
|
|
email: email.toLowerCase().trim(),
|
|
name: name.trim(),
|
|
passwordHash,
|
|
systemRole: "ADMIN",
|
|
isActive: true,
|
|
},
|
|
});
|
|
|
|
console.log(`Admin user created: ${email.toLowerCase().trim()}`);
|
|
} catch (err) {
|
|
const message = err instanceof Error ? err.message : String(err);
|
|
console.error(`ERROR: ${message}`);
|
|
process.exit(1);
|
|
} finally {
|
|
await prisma.$disconnect();
|
|
}
|