import * as dotenv from 'dotenv'; import mysql from 'mysql2'; import {Kysely, MysqlDialect} from 'kysely'; import {AdminDatabase} from './admin.schema.js'; import logger from '../../middleware/logger.js'; dotenv.config(); /** * The admin module is the one place in this API that does not use the * `mariadb` driver: better-auth talks to the database through Kysely, whose * MySQL dialect expects a mysql2 pool. The other domains keep their own * `mariadb` pools (see Feedback.db.ts) - this is an addition, not a migration. * * The pool is the callback-style `mysql2` one, NOT `mysql2/promise`: Kysely's * MysqlDialect calls `pool.getConnection((err, conn) => ...)`. The promise * wrapper ignores that callback and returns a Promise instead, so every query * through Kysely would hang forever with no error - which is exactly what it * did until the integration tests caught it. * * timezone 'Z' matters: better-auth computes session and token expiry in UTC. * Without it mysql2 would write and read those DATETIMEs in the process's local * zone, so sessions would expire an hour early or late depending on DST. */ export namespace NachklangAdminDB { export const pool = mysql.createPool({ host: process.env.DB_HOST, user: process.env.DB_USER, password: process.env.DB_PASSWORD, database: process.env.ADMIN_DB, // The other modules' pools default to 3306. This one is configurable so // the integration tests can point at a throwaway container on another // port without touching a developer's real .env. port: parseInt(process.env.DB_PORT || '3306', 10), connectionLimit: 5, timezone: 'Z' }); // mysql2 emits connection trouble as an event on the pool, not only as a // rejected query. Without a listener Node turns that into an // uncaughtException, so a database restart would take the whole API - and // with it the calendar, feedback and tickets domains - down with it. // Individual queries still reject, and their callers still answer 500. pool.on('error', (err: unknown) => { logger.error('Admin database pool error', {detail: (err as any)?.message}); }); // Handed to better-auth as `database: {dialect, type: 'mysql'}`. export const dialect = new MysqlDialect({pool}); // Used by this module's own services for the two custom tables and for // permission lookups that join better-auth's `user`. export const db = new Kysely({dialect}); }