const fs = require('fs') const path = require('path') const mariadb = require('mariadb') require('dotenv').config() const log = require('./logger')('db') const pool = mariadb.createPool({ host: process.env.DB_HOST || '127.0.0.1', port: Number(process.env.DB_PORT) || 3306, user: process.env.DB_USER || 'root', password: process.env.DB_PASSWORD || '', database: process.env.DB_NAME || 'runic_gateway', connectionLimit: 5, // Return plain JS numbers, never BigInt — keeps JSON responses clean. insertIdAsNumber: true, bigIntAsNumber: true, decimalAsNumber: true, // The driver defaults to 'local' — silently serializing bound JS Date // params using the HOST MACHINE's local offset instead of the DB session's // timezone (discovered via the Discord bot's temp_roles.expires_at coming // back hours off in dev). 'auto' negotiates the actual session timezone so // Date round-trips correctly regardless of host TZ — affects any write of // a JS Date param, e.g. botConfig.model.js's last_connected_at. timezone: 'auto', }) /** * Run a parameterized query and release the connection. * @param {string} sql * @param {Array} [params] */ async function query(sql, params) { const conn = await pool.getConnection() try { return await conn.query(sql, params) } finally { conn.release() } } const SCHEMA_PATH = path.join(__dirname, '..', '..', 'db', 'schema.sql') /** * Create tables if they do not exist. Idempotent. Retries while the DB is still * coming up (important under docker-compose even with a healthcheck). */ async function ensureSchema({ retries = 10, delayMs = 2000 } = {}) { for (let attempt = 1; attempt <= retries; attempt++) { try { const conn = await pool.getConnection() try { const sql = fs.readFileSync(SCHEMA_PATH, 'utf8') // Strip full-line comments first, then split — so a leading comment block // doesn't get glued onto (and discard) the statement that follows it. const statements = sql .split('\n') .filter((line) => !line.trim().startsWith('--')) .join('\n') .split(';') .map((s) => s.trim()) .filter((s) => s.length > 0) for (const statement of statements) { await conn.query(statement) } log.info('schema ensured') return } finally { conn.release() } } catch (err) { if (attempt === retries) throw err log.warn(`database not ready, retrying (attempt ${attempt}/${retries})`, { code: err.code || err.message, }) await new Promise((r) => setTimeout(r, delayMs)) } } } async function close() { await pool.end() } module.exports = { pool, query, ensureSchema, close }