/** * Seed an admin user for development. * Run: npx tsx scripts/seed-admin.ts [email] [password] [name] */ import { config } from "dotenv"; config({ path: ".env.local" }); import pg from "pg"; const { Pool } = pg; async function main() { const email = process.argv[2] ?? "admin@test.com"; const password = process.argv[3] ?? "Admin1234!"; const name = process.argv[4] ?? "Admin"; const baseUrl = process.env.NEON_AUTH_BASE_URL!; const pool = new Pool({ connectionString: process.env.DATABASE_URL }); try { // 1. Create or get brand const brandResult = await pool.query(` INSERT INTO brands (name, slug, plan_tier) VALUES ('Demo Brand', 'demo', 'enterprise') ON CONFLICT (slug) DO UPDATE SET name = EXCLUDED.name RETURNING id, name, slug `); const brand = brandResult.rows[0]; console.log("✓ Brand:", brand.name, `(${brand.id})`); // 2. Create user in Neon Auth via direct API const res = await fetch(`${baseUrl}/sign-up/email`, { method: "POST", headers: { "Content-Type": "application/json", "Origin": "http://localhost:4000", "x-neon-auth-proxy": "node", }, body: JSON.stringify({ email, password, name, callbackURL: "/admin" }), }); const data = await res.json(); if (!res.ok && data.code !== "USER_ALREADY_EXISTS_USE_ANOTHER_EMAIL") { console.error("✗ Failed to create user:", data); process.exit(1); } const userId = data.user?.id; console.log("✓ Neon Auth user:", email, userId ? `(${userId})` : "(already existed)"); // 3. Verify email in Neon Auth DB if (userId) { await pool.query( `UPDATE neon_auth.user SET "emailVerified" = true WHERE id = $1`, [userId] ); console.log("✓ Email verified in DB"); } // 4. Get user ID from neon_auth.user by email const neonUser = await pool.query( `SELECT id FROM neon_auth.user WHERE email = $1`, [email] ); const neonUserId = neonUser.rows[0]?.id; if (!neonUserId) { console.error("✗ Could not find user in neon_auth.user"); process.exit(1); } // 5. Insert into admin_users (upsert) const adminResult = await pool.query(` INSERT INTO admin_users (user_id, email, name, role) VALUES ($1, $2, $3, 'brand_admin') ON CONFLICT (email) DO UPDATE SET user_id = EXCLUDED.user_id, name = EXCLUDED.name, role = EXCLUDED.role RETURNING id `, [neonUserId, email, name]); const adminUser = adminResult.rows[0]; console.log("✓ Admin user:", email, `(${adminUser.id})`); // 6. Link to brand await pool.query(` INSERT INTO admin_user_brands (admin_user_id, brand_id) VALUES ($1, $2) ON CONFLICT DO NOTHING `, [adminUser.id, brand.id]); console.log("✓ Linked to brand:", brand.name); console.log(`\n🎉 All set! Log in at http://localhost:4000/login`); console.log(` Email: ${email}`); console.log(` Password: ${password}`); } finally { await pool.end(); } } main();