DataExtract / artifacts /api-server /src /lib /bootstrapAdmin.ts
Taha Saif
Deploy Ecom Hub API backend (Express + Drizzle + Clerk + R2)
74bfb65
Raw History Blame Contribute Delete
2.58 kB
import { clerkClient } from '@clerk/express';
import { logger } from './logger';
/**
* Owner-configured bootstrap runs independently in each Clerk environment.
* The completion marker prevents a restart from restoring a revoked role.
* Passwords are read only from Secrets and never logged or reset on restart.
*/
export async function bootstrapAdmin() {
const email = process.env.ADMIN_EMAIL?.trim().toLowerCase();
if (!email || !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) {
throw new Error('Set a valid ADMIN_EMAIL before starting admin authentication.');
}
try {
const client = clerkClient;
const { data: users } = await client.users.getUserList({ emailAddress: [email], limit: 2 });
if (users.length > 1) throw new Error('Admin bootstrap found multiple matching accounts.');
const existing = users[0];
if (existing?.privateMetadata.ecomAdminBootstrapComplete) {
logger.info('Admin account bootstrap already completed; existing permissions preserved.');
return;
}
if (existing) {
const verified = existing.emailAddresses.some(address =>
address.emailAddress.toLowerCase() === email && address.verification?.status === 'verified',
);
if (!verified) throw new Error('Verify ownership of the configured admin email before granting access.');
await client.users.updateUserMetadata(existing.id, {
publicMetadata: { ...existing.publicMetadata, role: 'admin' },
privateMetadata: { ...existing.privateMetadata, ecomAdminBootstrapComplete: true },
});
} else {
const password = process.env.ADMIN_INITIAL_PASSWORD;
if (!password) throw new Error('Set ADMIN_INITIAL_PASSWORD in Secrets to create the initial admin account.');
await client.users.createUser({
emailAddress: [email],
password,
firstName: 'Admin',
publicMetadata: { role: 'admin' },
privateMetadata: { ecomAdminBootstrapComplete: true },
});
}
logger.info('Owner-configured admin account is ready.');
} catch (error) {
// Do not log SDK request/response objects: they can contain credentials.
if (error && typeof error === 'object' && 'errors' in error) {
const errors = error.errors as { code?: string }[];
const codes = errors.map(item => item.code?.replace(/[^a-zA-Z0-9_]/g, '')).filter(Boolean).join(', ');
throw new Error(`Admin account setup was rejected by the login provider (${codes || 'request_failed'}). Check the admin settings and use a strong ADMIN_INITIAL_PASSWORD.`);
}
throw error;
}
}