/** * Database initialization script * Creates database and all required tables in PostgreSQL */ import postgres from "postgres"; import { readFileSync } from "fs"; import { join } from "path"; /** * Parse DATABASE_URL and extract components */ function parseDatabaseUrl(url: string): { protocol: string; user: string; password: string; host: string; port: string; database: string; } { const match = url.match( /^postgresql:\/\/([^:]+):([^@]+)@([^:]+):(\d+)\/(.+)$/ ); if (!match) { throw new Error( "Invalid DATABASE_URL format. Expected: postgresql://user:password@host:port/database" ); } return { protocol: "postgresql", user: match[1], password: match[2], host: match[3], port: match[4], database: match[5], }; } async function initDatabase() { const dbUrl = process.env.DATABASE_URL; if (!dbUrl) { console.error("Error: DATABASE_URL environment variable is required"); console.error("Please set it in your .env file or export it:"); console.error( " export DATABASE_URL=postgresql://user:password@host:port/database" ); process.exit(1); } const dbConfig = parseDatabaseUrl(dbUrl); const targetDatabase = dbConfig.database; console.log(`Target database: ${targetDatabase}`); console.log( `Connecting to PostgreSQL server (${dbConfig.host}:${dbConfig.port})...` ); // Connect to postgres database (default database) to create target database const adminUrl = `postgresql://${dbConfig.user}:${dbConfig.password}@${dbConfig.host}:${dbConfig.port}/postgres`; const adminSql = postgres(adminUrl); try { // Check if database exists const dbExists = await adminSql` SELECT 1 FROM pg_database WHERE datname = ${targetDatabase} `; if (dbExists.length === 0) { console.log(`Database "${targetDatabase}" does not exist. Creating...`); // Create database (escape database name to prevent SQL injection) // PostgreSQL identifiers are case-insensitive unless quoted, so we quote it const escapedDbName = `"${targetDatabase.replace(/"/g, '""')}"`; await (adminSql as any).unsafe(`CREATE DATABASE ${escapedDbName}`); console.log(`✓ Database "${targetDatabase}" created successfully`); } else { console.log(`✓ Database "${targetDatabase}" already exists`); } } catch (error) { const errorMessage = (error as Error).message; if (errorMessage.includes("already exists")) { console.log(`✓ Database "${targetDatabase}" already exists`); } else { console.error(`✗ Failed to create database: ${errorMessage}`); throw error; } } finally { await adminSql.end(); } // Now connect to target database and create tables console.log(`\nConnecting to database "${targetDatabase}"...`); const sql = postgres(dbUrl); try { // Test connection await sql`SELECT 1`; console.log("✓ Connected to database"); // Read schema file const schemaPath = join(process.cwd(), "src", "storage", "schema.sql"); console.log(`Reading schema from ${schemaPath}...`); const schema = readFileSync(schemaPath, "utf-8"); // Split by semicolons and execute each statement const statements = schema .split(";") .map((s) => s.trim()) .filter((s) => s.length > 0 && !s.startsWith("--")); console.log(`Executing ${statements.length} SQL statements...`); for (const statement of statements) { if (statement) { try { // Use postgres.unsafe() to execute raw SQL await (sql as any).unsafe(statement); console.log(`✓ Executed: ${statement.substring(0, 50)}...`); } catch (error) { // Check if it's a "already exists" error (which is OK) const errorMessage = (error as Error).message; if ( errorMessage.includes("already exists") || errorMessage.includes("duplicate") ) { console.log( `⚠ Skipped (already exists): ${statement.substring(0, 50)}...` ); } else { console.error(`✗ Error executing statement: ${errorMessage}`); console.error(` Statement: ${statement.substring(0, 100)}...`); throw error; } } } } console.log("\n✓ Database initialization completed successfully!"); console.log("\nTables created:"); console.log(" - code_snippets"); console.log(" - notes"); console.log(" - tasks"); console.log(" - baby_milestones"); console.log(" - math_resources"); console.log(" - game_wishlist"); } catch (error) { console.error("\n✗ Database initialization failed:", error); process.exit(1); } finally { await sql.end(); } } // Run initialization initDatabase().catch((error) => { console.error("Fatal error:", error); process.exit(1); });