import { Client } from 'pg'; import * as dotenv from 'dotenv'; import { readFileSync } from 'fs'; import { join } from 'path'; // Load environment variables dotenv.config({ path: '.env' }); async function checkAndCreateDatabases() { // Get connection info from DATABASE_URL or use defaults const mainDbUrl = process.env.DATABASE_URL || 'postgresql://postgres@localhost:5432/postgres'; const authDbUrl = process.env.DATABASE_URL_AUTH || 'postgresql://postgres@localhost:5432/postgres'; // Parse connection strings to get connection details const parseUrl = (url: string) => { const match = url.match(/postgresql:\/\/([^:]+):([^@]+)@([^:]+):(\d+)\/(.+)/) || url.match(/postgresql:\/\/([^@]+)@([^:]+):(\d+)\/(.+)/) || url.match(/postgresql:\/\/([^@]+)@([^:]+)\/(.+)/); if (match) { if (match.length === 6) { // With password return { user: match[1], password: match[2], host: match[3], port: parseInt(match[4]), database: match[5], }; } else if (match.length === 5) { // Without password, with port return { user: match[1], host: match[2], port: parseInt(match[3]), database: match[4], }; } else if (match.length === 4) { // Without password, without port return { user: match[1], host: match[2], port: 5432, database: match[3], }; } } // Fallback to defaults return { user: 'postgres', host: 'localhost', port: 5432, database: 'postgres', }; }; const mainConfig = parseUrl(mainDbUrl); const authConfig = parseUrl(authDbUrl); // Connect to postgres database to check/create databases const adminClient = new Client({ user: mainConfig.user, password: (mainConfig as any).password, host: mainConfig.host, port: mainConfig.port, database: 'postgres', // Connect to postgres database to create other databases }); try { console.log('šŸ” Checking databases...\n'); await adminClient.connect(); console.log('āœ… Connected to PostgreSQL\n'); // Check if punimtag database exists const mainDbCheck = await adminClient.query( "SELECT 1 FROM pg_database WHERE datname = 'punimtag'" ); if (mainDbCheck.rows.length === 0) { console.log('āš ļø punimtag database does not exist'); console.log(' This is the main database with photos - it should already exist.'); console.log(' If you need to create it, please do so manually or ensure your PunimTag setup is complete.\n'); } else { console.log('āœ… punimtag database exists\n'); } // Check if punimtag_auth database exists const authDbCheck = await adminClient.query( "SELECT 1 FROM pg_database WHERE datname = 'punimtag_auth'" ); if (authDbCheck.rows.length === 0) { console.log('šŸ“¦ Creating punimtag_auth database...'); await adminClient.query('CREATE DATABASE punimtag_auth'); console.log('āœ… punimtag_auth database created\n'); } else { console.log('āœ… punimtag_auth database exists\n'); } await adminClient.end(); // Now connect to punimtag_auth and create tables const authClient = new Client({ user: authConfig.user, password: (authConfig as any).password, host: authConfig.host, port: authConfig.port, database: 'punimtag_auth', }); try { await authClient.connect(); console.log('šŸ”— Connected to punimtag_auth database\n'); // Check if users table exists const usersTableCheck = await authClient.query(` SELECT EXISTS ( SELECT FROM information_schema.tables WHERE table_schema = 'public' AND table_name = 'users' ); `); if (!usersTableCheck.rows[0].exists) { console.log('šŸ“‹ Creating tables in punimtag_auth...'); // Read and execute setup-auth-tables.sql const setupScript = readFileSync( join(__dirname, '../setup-auth-tables.sql'), 'utf-8' ); // Split by semicolons and execute each statement const statements = setupScript .split(';') .map(s => s.trim()) .filter(s => s.length > 0 && !s.startsWith('--') && !s.startsWith('\\')); for (const statement of statements) { if (statement.length > 0) { try { await authClient.query(statement); } catch (error: any) { // Ignore "already exists" errors if (!error.message.includes('already exists')) { console.error(`Error executing: ${statement.substring(0, 50)}...`); throw error; } } } } console.log('āœ… Tables created\n'); } else { console.log('āœ… Tables already exist in punimtag_auth\n'); } // Check for required migrations console.log('šŸ” Checking for required migrations...\n'); // Check pending_photos table const pendingPhotosCheck = await authClient.query(` SELECT EXISTS ( SELECT FROM information_schema.tables WHERE table_schema = 'public' AND table_name = 'pending_photos' ); `); if (!pendingPhotosCheck.rows[0].exists) { console.log('šŸ“‹ Creating pending_photos table...'); const migrationScript = readFileSync( join(__dirname, '../migrations/add-pending-photos-table.sql'), 'utf-8' ); const statements = migrationScript .split(';') .map(s => s.trim()) .filter(s => s.length > 0 && !s.startsWith('--') && !s.startsWith('\\')); for (const statement of statements) { if (statement.length > 0) { try { await authClient.query(statement); } catch (error: any) { if (!error.message.includes('already exists')) { throw error; } } } } console.log('āœ… pending_photos table created\n'); } // Check email verification columns const emailVerificationCheck = await authClient.query(` SELECT column_name FROM information_schema.columns WHERE table_schema = 'public' AND table_name = 'users' AND column_name = 'email_verified' `); if (emailVerificationCheck.rows.length === 0) { console.log('šŸ“‹ Adding email verification columns...'); const migrationScript = readFileSync( join(__dirname, '../migrations/add-email-verification-columns.sql'), 'utf-8' ); const statements = migrationScript .split(';') .map(s => s.trim()) .filter(s => s.length > 0 && !s.startsWith('--') && !s.startsWith('\\')); for (const statement of statements) { if (statement.length > 0) { try { await authClient.query(statement); } catch (error: any) { if (!error.message.includes('already exists')) { throw error; } } } } console.log('āœ… Email verification columns added\n'); } console.log('šŸŽ‰ Database setup complete!\n'); console.log('Next steps:'); console.log('1. Ensure your .env file has correct DATABASE_URL_AUTH'); console.log('2. Run: npm run prisma:generate:all'); console.log('3. Create admin user: npx tsx scripts/create-admin-user.ts'); } catch (error: any) { console.error('\nāŒ Error setting up tables:', error.message); if (error.message.includes('permission denied')) { console.error('\nāš ļø Permission denied. You may need to run this as a PostgreSQL superuser.'); } throw error; } finally { await authClient.end(); } } catch (error: any) { console.error('\nāŒ Error:', error.message); if (error.message.includes('password authentication failed')) { console.error('\nāš ļø Authentication failed. Please check:'); console.error(' 1. Database credentials in .env file'); console.error(' 2. PostgreSQL is running'); console.error(' 3. User has permission to create databases'); } process.exit(1); } finally { await adminClient.end(); } } checkAndCreateDatabases();