import { createClient } from 'redis'; import pkg from 'pg'; const { Pool } = pkg; export interface DatabaseConfig { redis: { url: string; maxRetriesPerRequest: number; }; postgres: { host: string; port: number; database: string; user: string; password: string; max: number; }; } export const databaseConfig: DatabaseConfig = { redis: { url: process.env.REDIS_URL || 'redis://localhost:6379', maxRetriesPerRequest: 3, }, postgres: { host: process.env.POSTGRES_HOST || 'localhost', port: parseInt(process.env.POSTGRES_PORT || '5432'), database: process.env.POSTGRES_DB || 'gaplace', user: process.env.POSTGRES_USER || 'gaplace', password: process.env.POSTGRES_PASSWORD || 'password', max: 20, }, }; // Redis client export const redisClient = createClient({ url: databaseConfig.redis.url, }); redisClient.on('error', (err) => { console.error('Redis Client Error:', err); }); redisClient.on('connect', () => { console.log('✅ Connected to Redis'); }); // PostgreSQL client export const pgPool = new Pool(databaseConfig.postgres); pgPool.on('error', (err) => { console.error('PostgreSQL Pool Error:', err); }); export async function initializeDatabase(): Promise { try { // Connect to Redis await redisClient.connect(); // Test PostgreSQL connection const client = await pgPool.connect(); console.log('✅ Connected to PostgreSQL'); client.release(); // Create tables if they don't exist await createTables(); } catch (error) { console.error('❌ Database initialization failed:', error); throw error; } } async function createTables(): Promise { const client = await pgPool.connect(); try { await client.query(` CREATE TABLE IF NOT EXISTS users ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), username VARCHAR(50) UNIQUE NOT NULL, email VARCHAR(255) UNIQUE, password_hash VARCHAR(255), avatar_url TEXT, is_guest BOOLEAN DEFAULT TRUE, created_at TIMESTAMP DEFAULT NOW(), last_seen TIMESTAMP DEFAULT NOW() ); `); await client.query(` CREATE TABLE IF NOT EXISTS canvases ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), name VARCHAR(100) NOT NULL, width INTEGER NOT NULL DEFAULT 1000, height INTEGER NOT NULL DEFAULT 1000, chunk_size INTEGER NOT NULL DEFAULT 64, is_public BOOLEAN DEFAULT TRUE, created_by UUID REFERENCES users(id), created_at TIMESTAMP DEFAULT NOW(), updated_at TIMESTAMP DEFAULT NOW() ); `); await client.query(` CREATE TABLE IF NOT EXISTS user_sessions ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), user_id UUID REFERENCES users(id), canvas_id UUID REFERENCES canvases(id), session_token VARCHAR(255) UNIQUE NOT NULL, is_active BOOLEAN DEFAULT TRUE, last_activity TIMESTAMP DEFAULT NOW(), created_at TIMESTAMP DEFAULT NOW() ); `); await client.query(` CREATE INDEX IF NOT EXISTS idx_users_username ON users(username); CREATE INDEX IF NOT EXISTS idx_canvases_public ON canvases(is_public); CREATE INDEX IF NOT EXISTS idx_sessions_active ON user_sessions(is_active, last_activity); `); console.log('✅ Database tables created/verified'); } finally { client.release(); } }