import type { LocalContext } from "../context.js"; import { createDb } from "@reviq/db"; import { hashPassword } from "@reviq/utils"; import { buildCommand } from "@stricli/core"; import { writeConfig } from "../utils/config.js"; import { generateToken, hashToken } from "../utils/token.js"; interface BootstrapFlags { email: string; password: string; } async function bootstrap( this: LocalContext, flags: BootstrapFlags, ): Promise { console.log("RevIQ Bootstrap - Create Superuser"); console.log("===================================\n"); // Validate password length if (flags.password.length < 8) { console.error("Error: Password must be at least 8 characters"); this.process.exit(1); } const db = createDb(); try { // Check if user already exists const existing = await db .selectFrom("users") .where("email", "=", flags.email.toLowerCase()) .select("id") .executeTakeFirst(); if (existing) { console.error(`Error: User with email ${flags.email} already exists`); await db.destroy(); this.process.exit(1); } // Hash the password const passwordHash = await hashPassword(flags.password); // Create superuser const [user] = await db .insertInto("users") .values({ email: flags.email.toLowerCase(), password_hash: passwordHash, is_superuser: true, email_verified_at: new Date(), }) .returning(["id", "email"]) .execute(); if (!user) { console.error("Error: Failed to create user"); await db.destroy(); this.process.exit(1); } console.log(`Created superuser: ${user.email}`); // Create "reviq" org const [org] = await db .insertInto("orgs") .values({ slug: "reviq", display_name: "RevIQ", }) .returning(["id", "slug"]) .execute(); if (!org) { console.error("Error: Failed to create org"); await db.destroy(); this.process.exit(1); } // Add user as owner of the org await db .insertInto("org_members") .values({ org_id: org.id, user_id: user.id, role: "owner", }) .execute(); console.log(`Created org: ${org.slug}`); // Generate API token const token = generateToken(); const tokenHashValue = hashToken(token); await db .insertInto("api_tokens") .values({ user_id: user.id, token_hash: tokenHashValue, name: "CLI bootstrap token", expires_at: new Date(Date.now() + 365 * 24 * 60 * 60 * 1000), // 1 year }) .execute(); // Save to config await writeConfig({ apiUrl: Bun.env.API_URL ?? "http://localhost:9861", token, email: user.email, }); console.log("Saved credentials to ~/.config/reviq/credentials.json"); console.log("\nBootstrap complete! You can now use the CLI."); await db.destroy(); } catch (error) { console.error( "Error:", error instanceof Error ? error.message : String(error), ); await db.destroy(); this.process.exit(1); } } export const bootstrapCommand = buildCommand({ func: bootstrap, parameters: { flags: { email: { kind: "parsed", parse: String, brief: "Email address for the superuser", }, password: { kind: "parsed", parse: String, brief: "Password for the superuser", }, }, }, docs: { brief: "Create a superuser account and initial organization", fullDescription: "Creates a superuser with the 'reviq' organization. " + "Requires DATABASE_URL environment variable to be set.", }, });