feat: Enhance database initialization script to create target database if it doesn't exist and improve error handling

This commit is contained in:
ethan.chen
2026-01-08 11:29:00 +08:00
parent ca6fa71f38
commit c47e59378b

View File

@@ -1,29 +1,101 @@
/**
* Database initialization script
* Creates all required tables in PostgreSQL
* 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");
console.error(
" export DATABASE_URL=postgresql://user:password@host:port/database"
);
process.exit(1);
}
console.log("Connecting to PostgreSQL...");
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 PostgreSQL");
console.log("✓ Connected to database");
// Read schema file
const schemaPath = join(process.cwd(), "src", "storage", "schema.sql");
@@ -47,8 +119,13 @@ async function initDatabase() {
} 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)}...`);
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)}...`);