import mysql from 'mysql2/promise'; import { config } from '../config/config.js'; import { logger } from '../logger/logger.js'; let pool = null; /** * Initializes the MySQL database pool and ensures schema exists. */ export async function initializeDatabase() { if (!config.dbUsername) { logger.warn('db_disabled', { reason: 'No DB_USERNAME configured, database logging is disabled' }); return; } try { pool = mysql.createPool({ host: config.dbHost, port: config.dbPort, database: config.dbDatabase, user: config.dbUsername, password: config.dbPassword, waitForConnections: true, connectionLimit: 10, queueLimit: 0 }); // Test connection and auto-create the table if not exists const conn = await pool.getConnection(); await conn.query(` CREATE TABLE IF NOT EXISTS \`call_logs\` ( \`id\` INT AUTO_INCREMENT PRIMARY KEY, \`session_id\` VARCHAR(36) NOT NULL UNIQUE, \`ride_id\` VARCHAR(255) NOT NULL, \`driver_id\` VARCHAR(255) NOT NULL, \`passenger_id\` VARCHAR(255) NOT NULL, \`driver_ip\` VARCHAR(45) NULL DEFAULT NULL, \`passenger_ip\` VARCHAR(45) NULL DEFAULT NULL, \`driver_conn_ip\` VARCHAR(45) NULL DEFAULT NULL, \`passenger_conn_ip\` VARCHAR(45) NULL DEFAULT NULL, \`status\` VARCHAR(50) NOT NULL DEFAULT 'created', \`initiated_by\` VARCHAR(255) NULL DEFAULT NULL, \`end_reason\` VARCHAR(255) NULL DEFAULT NULL, \`created_at\` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, \`connected_at\` TIMESTAMP NULL DEFAULT NULL, \`ended_at\` TIMESTAMP NULL DEFAULT NULL, INDEX \`idx_session_id\` (\`session_id\`), INDEX \`idx_ride_id\` (\`ride_id\`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; `); conn.release(); logger.info('db_initialized', { host: config.dbHost, database: config.dbDatabase }); } catch (err) { logger.error('db_initialization_failed', { error: err.message }); // Soft fallback: log the failure but do not crash the signaling server pool = null; } } /** * Logs session creation to the database with pre-registered client IPs. */ export async function logSessionCreated(sessionID, rideID, driverID, passengerID, driverIP = null, passengerIP = null) { if (!pool) return; try { await pool.query( `INSERT INTO call_logs (session_id, ride_id, driver_id, passenger_id, driver_ip, passenger_ip, status, created_at) VALUES (?, ?, ?, ?, ?, ?, 'created', NOW())`, [sessionID, rideID, driverID, passengerID, driverIP, passengerIP] ); } catch (err) { logger.error('db_log_session_created_failed', { session_id: sessionID, error: err.message }); } } /** * Logs session status transition to active and records the connection IPs. */ export async function logSessionActive(sessionID, driverConnIP = null, passengerConnIP = null) { if (!pool) return; try { await pool.query( `UPDATE call_logs SET status = 'active', connected_at = NOW(), driver_conn_ip = ?, passenger_conn_ip = ? WHERE session_id = ?`, [driverConnIP, passengerConnIP, sessionID] ); } catch (err) { logger.error('db_log_session_active_failed', { session_id: sessionID, error: err.message }); } } /** * Logs the initiator (caller) of the call when the first SDP offer is received. */ export async function logSessionInitiator(sessionID, initiatorID) { if (!pool) return; try { await pool.query( `UPDATE call_logs SET initiated_by = ? WHERE session_id = ? AND initiated_by IS NULL`, [initiatorID, sessionID] ); } catch (err) { logger.error('db_log_session_initiator_failed', { session_id: sessionID, initiator_id: initiatorID, error: err.message }); } } /** * Logs call termination reason and end time. */ export async function logSessionEnded(sessionID, endReason) { if (!pool) return; try { await pool.query( `UPDATE call_logs SET status = 'ended', ended_at = NOW(), end_reason = ? WHERE session_id = ?`, [endReason, sessionID] ); } catch (err) { logger.error('db_log_session_ended_failed', { session_id: sessionID, error: err.message }); } }