Skip to main content
Glama
bootstrap.ts5.53 kB
import { Pool } from 'pg'; import { readFileSync, existsSync } from 'fs'; import { join } from 'path'; import { parse as parseDotenv } from 'dotenv'; /** * Bootstrap Database Connection * * Reads minimal config from .env.bootstrap file for initial DB connection * This is the ONLY service that reads from file - all others use ConfigService */ interface BootstrapConfig { DB_HOST: string; DB_PORT: string; DB_NAME: string; DB_USER: string; DB_PASSWORD: string; DB_SSL?: string; CONFIG_ENCRYPTION_KEY: string; } class BootstrapDB { private pool: Pool | null = null; private config: BootstrapConfig | null = null; /** * Load bootstrap configuration from environment variables or .env.bootstrap file * Priority: environment variables > .env.bootstrap file * This supports both Docker containers (using env vars) and local development (using file) */ private loadBootstrapConfig(): BootstrapConfig { const required = ['DB_HOST', 'DB_PORT', 'DB_NAME', 'DB_USER', 'DB_PASSWORD', 'CONFIG_ENCRYPTION_KEY']; // Try loading from environment variables first (for Docker containers) const fromEnv: Partial<BootstrapConfig> = { DB_HOST: process.env.DB_HOST, DB_PORT: process.env.DB_PORT, DB_NAME: process.env.DB_NAME, DB_USER: process.env.DB_USER, DB_PASSWORD: process.env.DB_PASSWORD, DB_SSL: process.env.DB_SSL, CONFIG_ENCRYPTION_KEY: process.env.CONFIG_ENCRYPTION_KEY, }; // Check if all required fields are available from environment const allFromEnv = required.every(field => fromEnv[field as keyof BootstrapConfig] !== undefined); if (allFromEnv) { return fromEnv as BootstrapConfig; } // Fallback: try loading from .env.bootstrap file const bootstrapPath = join(process.cwd(), '.env.bootstrap'); if (!existsSync(bootstrapPath)) { // Debug information for troubleshooting const envStatus = { DB_HOST: !!process.env.DB_HOST, DB_PORT: !!process.env.DB_PORT, DB_NAME: !!process.env.DB_NAME, DB_USER: !!process.env.DB_USER, DB_PASSWORD: !!process.env.DB_PASSWORD, CONFIG_ENCRYPTION_KEY: !!process.env.CONFIG_ENCRYPTION_KEY, bootstrapFileExists: false, cwd: process.cwd(), }; throw new Error( 'Bootstrap configuration not found.\n' + 'Environment status: ' + JSON.stringify(envStatus, null, 2) + '\n' + 'Please provide either:\n' + '1. Environment variables: DB_HOST, DB_PORT, DB_NAME, DB_USER, DB_PASSWORD, CONFIG_ENCRYPTION_KEY\n' + '2. Or .env.bootstrap file by running: npm run setup:config' ); } const bootstrapContent = readFileSync(bootstrapPath, 'utf8'); const parsed = parseDotenv(bootstrapContent) as any; // Validate required fields in file for (const field of required) { if (!parsed[field]) { throw new Error(`Missing required field in .env.bootstrap: ${field}`); } } return parsed as BootstrapConfig; } /** * Initialize database pool with bootstrap config */ async initialize(): Promise<void> { console.log('🔄 Starting BootstrapDB initialization...'); if (this.pool) { console.log('✓ BootstrapDB already initialized'); return; // Already initialized } this.config = this.loadBootstrapConfig(); this.pool = new Pool({ host: this.config.DB_HOST, port: parseInt(this.config.DB_PORT, 10), database: this.config.DB_NAME, user: this.config.DB_USER, password: this.config.DB_PASSWORD, ssl: this.config.DB_SSL === 'true' ? { rejectUnauthorized: false } : false, max: 20, idleTimeoutMillis: 30000, connectionTimeoutMillis: 5000, }); // Test connection try { const client = await this.pool.connect(); await client.query('SELECT NOW()'); client.release(); console.log('✅ Database connection established'); } catch (error) { console.error('❌ Database connection failed:', error); throw error; } } /** * Get database pool (must call initialize() first) */ getPool(): Pool { if (!this.pool) { throw new Error('BootstrapDB not initialized. Call initialize() first.'); } return this.pool; } /** * Get encryption key from bootstrap config */ getEncryptionKey(): string { if (!this.config) { throw new Error('BootstrapDB not initialized.'); } return this.config.CONFIG_ENCRYPTION_KEY; } /** * Close database connection */ async close(): Promise<void> { if (this.pool) { await this.pool.end(); this.pool = null; } } } // Singleton instance export const bootstrapDB = new BootstrapDB(); // Re-export getPool for backward compatibility with existing code export async function getPool(): Promise<Pool> { if (!bootstrapDB.getPool) { await bootstrapDB.initialize(); } return bootstrapDB.getPool(); }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/babasida246/ai-mcp-gateway'

If you have feedback or need assistance with the MCP directory API, please join our Discord server