Files

263 lines
6.6 KiB
JavaScript
Raw Permalink Normal View History

2025-09-02 21:43:00 +10:00
import fs from "node:fs";
import NodeRSA from "node-rsa";
import { global as logger } from "../logger.js";
2023-03-21 16:53:39 +10:00
2026-02-18 08:32:17 +10:00
const keysFile = '/data/keys.json';
const mysqlEngine = 'mysql2';
const postgresEngine = 'pg';
const sqliteClientName = 'better-sqlite3';
// Not used for new setups anymore but may exist in legacy setups
const legacySqliteClientName = 'sqlite3';
2023-03-21 16:53:39 +10:00
let instance = null;
// 1. Load from config file first (not recommended anymore)
// 2. Use config env variables next
const configure = () => {
2025-09-02 21:43:00 +10:00
const filename = `${process.env.NODE_CONFIG_DIR || "./config"}/${process.env.NODE_ENV || "default"}.json`;
2023-03-21 16:53:39 +10:00
if (fs.existsSync(filename)) {
let configData;
try {
2025-09-02 21:43:00 +10:00
// Load this json synchronously
const rawData = fs.readFileSync(filename);
configData = JSON.parse(rawData);
2024-12-24 08:57:54 +10:00
} catch (_) {
2023-03-21 16:53:39 +10:00
// do nothing
}
2023-03-21 17:11:16 +10:00
2025-09-02 21:43:00 +10:00
if (configData?.database) {
2023-03-21 16:53:39 +10:00
logger.info(`Using configuration from file: ${filename}`);
2025-11-05 10:56:23 +10:00
// Migrate those who have "mysql" engine to "mysql2"
if (configData.database.engine === "mysql") {
configData.database.engine = mysqlEngine;
}
2025-09-02 21:43:00 +10:00
instance = configData;
instance.keys = getKeys();
2023-03-21 16:53:39 +10:00
return;
}
}
2025-10-08 11:26:43 +11:00
const toBool = (v) => /^(1|true|yes|on)$/i.test((v || '').trim());
2025-10-08 10:57:58 +11:00
2025-11-05 12:33:59 +10:00
const envMysqlHost = process.env.DB_MYSQL_HOST || null;
const envMysqlUser = process.env.DB_MYSQL_USER || null;
const envMysqlName = process.env.DB_MYSQL_NAME || null;
const envMysqlSSL = toBool(process.env.DB_MYSQL_SSL);
2025-11-05 10:56:23 +10:00
const envMysqlSSLRejectUnauthorized = process.env.DB_MYSQL_SSL_REJECT_UNAUTHORIZED === undefined ? true : toBool(process.env.DB_MYSQL_SSL_REJECT_UNAUTHORIZED);
const envMysqlSSLVerifyIdentity = process.env.DB_MYSQL_SSL_VERIFY_IDENTITY === undefined ? true : toBool(process.env.DB_MYSQL_SSL_VERIFY_IDENTITY);
2023-03-21 16:53:39 +10:00
if (envMysqlHost && envMysqlUser && envMysqlName) {
// we have enough mysql creds to go with mysql
2025-09-02 21:43:00 +10:00
logger.info("Using MySQL configuration");
2023-03-21 16:53:39 +10:00
instance = {
database: {
2025-09-02 21:43:00 +10:00
engine: mysqlEngine,
host: envMysqlHost,
port: process.env.DB_MYSQL_PORT || 3306,
user: envMysqlUser,
2023-03-21 16:53:39 +10:00
password: process.env.DB_MYSQL_PASSWORD,
2025-10-08 10:57:58 +11:00
name: envMysqlName,
2025-10-08 11:26:43 +11:00
ssl: envMysqlSSL ? { rejectUnauthorized: envMysqlSSLRejectUnauthorized, verifyIdentity: envMysqlSSLVerifyIdentity } : false,
},
keys: getKeys(),
2023-03-21 16:53:39 +10:00
};
return;
}
2024-12-24 08:57:54 +10:00
const envPostgresHost = process.env.DB_POSTGRES_HOST || null;
const envPostgresUser = process.env.DB_POSTGRES_USER || null;
const envPostgresName = process.env.DB_POSTGRES_NAME || null;
if (envPostgresHost && envPostgresUser && envPostgresName) {
// we have enough postgres creds to go with postgres
2025-09-02 21:43:00 +10:00
logger.info("Using Postgres configuration");
2024-12-24 08:57:54 +10:00
instance = {
database: {
2025-09-02 21:43:00 +10:00
engine: postgresEngine,
host: envPostgresHost,
port: process.env.DB_POSTGRES_PORT || 5432,
user: envPostgresUser,
2024-12-24 08:57:54 +10:00
password: process.env.DB_POSTGRES_PASSWORD,
2025-09-02 21:43:00 +10:00
name: envPostgresName,
2024-12-24 08:57:54 +10:00
},
keys: getKeys(),
};
return;
}
2025-09-02 21:43:00 +10:00
const envSqliteFile = process.env.DB_SQLITE_FILE || "/data/database.sqlite";
2023-03-21 16:53:39 +10:00
logger.info(`Using Sqlite: ${envSqliteFile}`);
instance = {
database: {
2025-09-02 21:43:00 +10:00
engine: "knex-native",
knex: {
client: sqliteClientName,
2023-03-21 16:53:39 +10:00
connection: {
2025-09-02 21:43:00 +10:00
filename: envSqliteFile,
2023-03-21 16:53:39 +10:00
},
2025-09-02 21:43:00 +10:00
useNullAsDefault: true,
},
},
keys: getKeys(),
2023-03-21 16:53:39 +10:00
};
};
2023-03-21 16:53:39 +10:00
const getKeys = () => {
2023-03-21 16:53:39 +10:00
// Get keys from file
2025-11-04 13:43:52 +10:00
if (isDebugMode()) {
logger.debug("Checking for keys file:", keysFile);
}
2023-03-21 16:53:39 +10:00
if (!fs.existsSync(keysFile)) {
generateKeys();
2023-03-21 17:11:16 +10:00
} else if (process.env.DEBUG) {
2025-09-02 21:43:00 +10:00
logger.info("Keys file exists OK");
2023-03-21 16:53:39 +10:00
}
try {
2025-09-02 21:43:00 +10:00
// Load this json keysFile synchronously and return the json object
const rawData = fs.readFileSync(keysFile);
return JSON.parse(rawData);
2023-03-21 16:53:39 +10:00
} catch (err) {
2025-09-02 21:43:00 +10:00
logger.error(`Could not read JWT key pair from config file: ${keysFile}`, err);
2023-03-21 16:53:39 +10:00
process.exit(1);
}
};
const generateKeys = () => {
2025-09-02 21:43:00 +10:00
logger.info("Creating a new JWT key pair...");
2023-03-21 16:53:39 +10:00
// Now create the keys and save them in the config.
const key = new NodeRSA({ b: 2048 });
key.generateKeyPair();
const keys = {
2025-09-02 21:43:00 +10:00
key: key.exportKey("private").toString(),
pub: key.exportKey("public").toString(),
2023-03-21 16:53:39 +10:00
};
// Write keys config
try {
fs.writeFileSync(keysFile, JSON.stringify(keys, null, 2));
} catch (err) {
2025-09-02 21:43:00 +10:00
logger.error(`Could not write JWT key pair to config file: ${keysFile}: ${err.message}`);
2023-03-21 16:53:39 +10:00
process.exit(1);
}
2025-09-02 21:43:00 +10:00
logger.info(`Wrote JWT key pair to config file: ${keysFile}`);
2023-03-21 16:53:39 +10:00
};
2025-09-02 21:43:00 +10:00
/**
*
* @param {string} key ie: 'database' or 'database.engine'
* @returns {boolean}
*/
const configHas = (key) => {
instance === null && configure();
const keys = key.split(".");
let level = instance;
let has = true;
keys.forEach((keyItem) => {
if (typeof level[keyItem] === "undefined") {
has = false;
} else {
level = level[keyItem];
2024-10-16 11:06:29 +10:00
}
2025-09-02 21:43:00 +10:00
});
return has;
};
/**
* Gets a specific key from the top level
*
* @param {string} key
* @returns {*}
*/
const configGet = (key) => {
instance === null && configure();
if (key && typeof instance[key] !== "undefined") {
return instance[key];
2023-03-21 16:53:39 +10:00
}
2025-09-02 21:43:00 +10:00
return instance;
2023-03-21 16:53:39 +10:00
};
2025-09-02 21:43:00 +10:00
/**
* Is this a sqlite configuration?
*
* @returns {boolean}
*/
const isSqlite = () => {
instance === null && configure();
2026-02-18 08:32:17 +10:00
return instance.database.knex && [sqliteClientName, legacySqliteClientName].includes(instance.database.knex.client);
2025-09-02 21:43:00 +10:00
};
/**
* Is this a mysql configuration?
*
* @returns {boolean}
*/
const isMysql = () => {
instance === null && configure();
return instance.database.engine === mysqlEngine;
};
/**
* Is this a postgres configuration?
*
* @returns {boolean}
*/
const isPostgres = () => {
instance === null && configure();
return instance.database.engine === postgresEngine;
};
/**
2026-03-18 13:47:03 +01:00
* Are we running in debug mode?
2025-09-02 21:43:00 +10:00
*
* @returns {boolean}
*/
const isDebugMode = () => !!process.env.DEBUG;
/**
* Are we running in CI?
*
* @returns {boolean}
*/
const isCI = () => process.env.CI === 'true' && process.env.DEBUG === 'true';
2025-09-02 21:43:00 +10:00
/**
* Returns a public key
*
* @returns {string}
*/
const getPublicKey = () => {
instance === null && configure();
return instance.keys.pub;
};
/**
* Returns a private key
*
* @returns {string}
*/
const getPrivateKey = () => {
instance === null && configure();
return instance.keys.key;
};
/**
* @returns {boolean}
*/
const useLetsencryptStaging = () => !!process.env.LE_STAGING;
/**
* @returns {string|null}
*/
const useLetsencryptServer = () => {
if (process.env.LE_SERVER) {
return process.env.LE_SERVER;
}
return null;
};
export { isCI, configHas, configGet, isSqlite, isMysql, isPostgres, isDebugMode, getPrivateKey, getPublicKey, useLetsencryptStaging, useLetsencryptServer };