/* eslint-disable no-undef */ require('dotenv').config(); const fs = require('fs'); const path = require('path'); // Calculate secrets directory path once at module load const DOCKER_SECRETS_PATH = '/run/secrets'; const LOCAL_SECRETS_PATH = '../secrets'; const SECRETS_PATH = fs.existsSync(DOCKER_SECRETS_PATH) ? DOCKER_SECRETS_PATH : LOCAL_SECRETS_PATH; /** * Gets a secret value from either a Docker secret file or environment variable * @param secretName - Name of the secret (e.g. 'postgres_password') * @param envVar - Name of the fallback environment variable * @param defaultValue - Optional default value if neither source exists * @returns The secret value as a string */ function getSecret(secretName, envVar, defaultValue = '') { const secretPath = path.join(SECRETS_PATH, secretName); try { return fs.readFileSync(secretPath, 'utf8').trim(); } catch (error) { if (process.env[envVar]) { console.warn(`Using ${envVar} environment variable instead of Docker secret`); const envVal = process.env[envVar] || defaultValue; console.log(`Using ${envVar} environment variable: ${envVal}`); return envVal; } console.warn(`Neither secret file ${secretPath} nor ${envVar} environment variable found, using default value`); return defaultValue; } } const DatabaseType = { postgres: 'postgres' }; const externals = { [DatabaseType.postgres]: 'pg' // Add more alternatives as needed }; const isValidDbType = (type) => { return type !== undefined && Object.keys(externals).includes(type); }; const getClient = () => { const dbType = process.env.DB_TYPE; if (isValidDbType(dbType)) { return externals[dbType]; } console.warn(`Invalid or missing DB_TYPE: ${dbType}. Defaulting to postgres.`); return externals[DatabaseType.postgres]; }; const { validate: uuidValidate } = require('uuid'); const seedsDirectory = process.env.SEEDS_DIR || './seeds/dev'; function isValidTenantId(tenantId) { if (!tenantId) return true; if (tenantId === 'default') return true; return uuidValidate(tenantId); } const createConnectionWithTenant = (config, tenant) => { if (!isValidTenantId(tenant)) { throw new Error('Invalid tenant ID format'); } return { ...config, asyncStackTraces: true, pool: { ...config.pool, afterCreate: (conn, done) => { // With CitusDB, tenant isolation is handled automatically at the shard level // No need to set app.current_tenant session variable console.log(`Connection created for tenant: ${tenant} (CitusDB handles isolation automatically)`); done(null, conn); }, }, }; }; // Base configuration for migrations (uses postgres user) const migrationConfig = { client: 'pg', connection: { host: process.env.DB_HOST, port: process.env.DB_PORT, user: process.env.DB_USER_ADMIN || 'postgres', password: getSecret('postgres_password', 'DB_PASSWORD_ADMIN'), database: process.env.DB_NAME_SERVER, }, pool: { min: 0, max: 20, }, migrations: { directory: process.env.MIGRATIONS_DIR || "./migrations", loadExtensions: ['.cjs', '.js'] }, seeds: { directory: seedsDirectory, loadExtensions: ['.cjs', '.js'] } }; // Base configuration for application (uses app_user) const appConfig = { client: 'pg', connection: { host: process.env.DB_HOST, port: process.env.DB_PORT, user: process.env.DB_USER_SERVER || 'app_user', password: getSecret('db_password_server', 'DB_PASSWORD_SERVER'), database: process.env.DB_NAME_SERVER, }, pool: { min: 2, max: 20, } }; const knexfile = { development: { // Development uses app_user for normal operations ...appConfig, // But keeps postgres user connection for migrations migrations: migrationConfig.migrations, seeds: { directory: seedsDirectory, loadExtensions: ['.cjs', '.js'] } }, test: { client: 'pg', connection: { host: process.env.DB_HOST || 'localhost', port: process.env.DB_PORT || '5432', user: process.env.DB_USER_ADMIN || 'postgres', password: getSecret('db_password_server', 'DB_PASSWORD_SERVER', 'test_password'), database: process.env.DB_NAME_SERVER || 'sebastian_test', }, pool: { min: 2, max: 20, }, migrations: migrationConfig.migrations, }, production: { // Production uses app_user for normal operations ...appConfig, // But keeps postgres user connection for migrations migrations: migrationConfig.migrations, }, local: { client: 'postgresql', connection: { host: 'localhost', port: '5432', user: process.env.DB_USER_SERVER || 'postgres', password: getSecret('db_password_server', 'DB_PASSWORD_SERVER', 'abcd1234!'), database: process.env.DB_NAME_SERVER || 'server', }, migrations: migrationConfig.migrations, }, // Special config just for running migrations (uses postgres user) migration: migrationConfig }; // console.log('/server knexfile', knexfile); module.exports = { ...knexfile, createConnectionWithTenant, };