wulf-pulse/lib/bootstrap.ts
lorentz 3dd379de36 fix: "user" table writes use "updatedAt" not updated_at
Two more sites with the same bug as the theme route — Better Auth's "user"
table column is quoted camelCase. Caught via UAT after the theme PUT fix.

- app/api/settings/profile/route.ts:26 (PATCH admin profile name)
- lib/bootstrap.ts:97 (clearSetupFlag — first-login setup wizard)
2026-05-10 23:03:36 -04:00

103 lines
2.5 KiB
TypeScript

import { Pool } from "pg";
const pool = new Pool({
connectionString: process.env.DATABASE_URL,
});
/**
* Check if any users exist in the database
*/
export async function hasUsers(): Promise<boolean> {
try {
const result = await pool.query('SELECT COUNT(*) as count FROM "user"');
return parseInt(result.rows[0].count) > 0;
} catch (error) {
// Table might not exist yet
return false;
}
}
/**
* Create the default super-admin user from environment variables
*/
export async function createDefaultAdmin(): Promise<boolean> {
const email = process.env.DEFAULT_ADMIN_EMAIL;
const name = process.env.DEFAULT_ADMIN_NAME || "System Administrator";
if (!email) {
console.warn("DEFAULT_ADMIN_EMAIL not set, skipping admin creation");
return false;
}
try {
// Check if user already exists
const existing = await pool.query(
'SELECT id FROM "user" WHERE email = $1',
[email]
);
if (existing.rows.length > 0) {
console.log("Default admin already exists");
return false;
}
// Create the admin user
const id = crypto.randomUUID();
await pool.query(
`INSERT INTO "user" (id, name, email, role, email_verified, requires_setup, created_at, updated_at)
VALUES ($1, $2, $3, 'super-admin', false, true, NOW(), NOW())`,
[id, name, email]
);
console.log(`Created default admin: ${email}`);
return true;
} catch (error) {
console.error("Failed to create default admin:", error);
return false;
}
}
/**
* Run bootstrap checks and setup
*/
export async function bootstrap(): Promise<void> {
try {
const usersExist = await hasUsers();
if (!usersExist) {
console.log("No users found, running initial setup...");
await createDefaultAdmin();
}
} catch (error) {
console.error("Bootstrap failed:", error);
}
}
/**
* Check if a user requires setup (has requires_setup flag)
*/
export async function userRequiresSetup(userId: string): Promise<boolean> {
try {
const result = await pool.query(
'SELECT requires_setup FROM "user" WHERE id = $1',
[userId]
);
return result.rows[0]?.requires_setup === true;
} catch (error) {
return false;
}
}
/**
* Clear the requires_setup flag for a user
*/
export async function clearSetupFlag(userId: string): Promise<void> {
try {
await pool.query(
'UPDATE "user" SET requires_setup = false, "updatedAt" = NOW() WHERE id = $1',
[userId]
);
} catch (error) {
console.error("Failed to clear setup flag:", error);
}
}