config.js 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244
  1. import fs from "node:fs";
  2. import NodeRSA from "node-rsa";
  3. import { global as logger } from "../logger.js";
  4. const keysFile = '/data/keys.json';
  5. const mysqlEngine = 'mysql2';
  6. const postgresEngine = 'pg';
  7. const sqliteClientName = 'sqlite3';
  8. let instance = null;
  9. // 1. Load from config file first (not recommended anymore)
  10. // 2. Use config env variables next
  11. const configure = () => {
  12. const filename = `${process.env.NODE_CONFIG_DIR || "./config"}/${process.env.NODE_ENV || "default"}.json`;
  13. if (fs.existsSync(filename)) {
  14. let configData;
  15. try {
  16. // Load this json synchronously
  17. const rawData = fs.readFileSync(filename);
  18. configData = JSON.parse(rawData);
  19. } catch (_) {
  20. // do nothing
  21. }
  22. if (configData?.database) {
  23. logger.info(`Using configuration from file: ${filename}`);
  24. instance = configData;
  25. instance.keys = getKeys();
  26. return;
  27. }
  28. }
  29. const envMysqlHost = process.env.DB_MYSQL_HOST || null;
  30. const envMysqlUser = process.env.DB_MYSQL_USER || null;
  31. const envMysqlName = process.env.DB_MYSQL_NAME || null;
  32. if (envMysqlHost && envMysqlUser && envMysqlName) {
  33. // we have enough mysql creds to go with mysql
  34. logger.info("Using MySQL configuration");
  35. instance = {
  36. database: {
  37. engine: mysqlEngine,
  38. host: envMysqlHost,
  39. port: process.env.DB_MYSQL_PORT || 3306,
  40. user: envMysqlUser,
  41. password: process.env.DB_MYSQL_PASSWORD,
  42. name: envMysqlName,
  43. },
  44. keys: getKeys(),
  45. };
  46. return;
  47. }
  48. const envPostgresHost = process.env.DB_POSTGRES_HOST || null;
  49. const envPostgresUser = process.env.DB_POSTGRES_USER || null;
  50. const envPostgresName = process.env.DB_POSTGRES_NAME || null;
  51. if (envPostgresHost && envPostgresUser && envPostgresName) {
  52. // we have enough postgres creds to go with postgres
  53. logger.info("Using Postgres configuration");
  54. instance = {
  55. database: {
  56. engine: postgresEngine,
  57. host: envPostgresHost,
  58. port: process.env.DB_POSTGRES_PORT || 5432,
  59. user: envPostgresUser,
  60. password: process.env.DB_POSTGRES_PASSWORD,
  61. name: envPostgresName,
  62. },
  63. keys: getKeys(),
  64. };
  65. return;
  66. }
  67. const envSqliteFile = process.env.DB_SQLITE_FILE || "/data/database.sqlite";
  68. logger.info(`Using Sqlite: ${envSqliteFile}`);
  69. instance = {
  70. database: {
  71. engine: "knex-native",
  72. knex: {
  73. client: sqliteClientName,
  74. connection: {
  75. filename: envSqliteFile,
  76. },
  77. useNullAsDefault: true,
  78. },
  79. },
  80. keys: getKeys(),
  81. };
  82. };
  83. const getKeys = () => {
  84. // Get keys from file
  85. logger.debug("Cheecking for keys file:", keysFile);
  86. if (!fs.existsSync(keysFile)) {
  87. generateKeys();
  88. } else if (process.env.DEBUG) {
  89. logger.info("Keys file exists OK");
  90. }
  91. try {
  92. // Load this json keysFile synchronously and return the json object
  93. const rawData = fs.readFileSync(keysFile);
  94. return JSON.parse(rawData);
  95. } catch (err) {
  96. logger.error(`Could not read JWT key pair from config file: ${keysFile}`, err);
  97. process.exit(1);
  98. }
  99. };
  100. const generateKeys = () => {
  101. logger.info("Creating a new JWT key pair...");
  102. // Now create the keys and save them in the config.
  103. const key = new NodeRSA({ b: 2048 });
  104. key.generateKeyPair();
  105. const keys = {
  106. key: key.exportKey("private").toString(),
  107. pub: key.exportKey("public").toString(),
  108. };
  109. // Write keys config
  110. try {
  111. fs.writeFileSync(keysFile, JSON.stringify(keys, null, 2));
  112. } catch (err) {
  113. logger.error(`Could not write JWT key pair to config file: ${keysFile}: ${err.message}`);
  114. process.exit(1);
  115. }
  116. logger.info(`Wrote JWT key pair to config file: ${keysFile}`);
  117. };
  118. /**
  119. *
  120. * @param {string} key ie: 'database' or 'database.engine'
  121. * @returns {boolean}
  122. */
  123. const configHas = (key) => {
  124. instance === null && configure();
  125. const keys = key.split(".");
  126. let level = instance;
  127. let has = true;
  128. keys.forEach((keyItem) => {
  129. if (typeof level[keyItem] === "undefined") {
  130. has = false;
  131. } else {
  132. level = level[keyItem];
  133. }
  134. });
  135. return has;
  136. };
  137. /**
  138. * Gets a specific key from the top level
  139. *
  140. * @param {string} key
  141. * @returns {*}
  142. */
  143. const configGet = (key) => {
  144. instance === null && configure();
  145. if (key && typeof instance[key] !== "undefined") {
  146. return instance[key];
  147. }
  148. return instance;
  149. };
  150. /**
  151. * Is this a sqlite configuration?
  152. *
  153. * @returns {boolean}
  154. */
  155. const isSqlite = () => {
  156. instance === null && configure();
  157. return instance.database.knex && instance.database.knex.client === sqliteClientName;
  158. };
  159. /**
  160. * Is this a mysql configuration?
  161. *
  162. * @returns {boolean}
  163. */
  164. const isMysql = () => {
  165. instance === null && configure();
  166. return instance.database.engine === mysqlEngine;
  167. };
  168. /**
  169. * Is this a postgres configuration?
  170. *
  171. * @returns {boolean}
  172. */
  173. const isPostgres = () => {
  174. instance === null && configure();
  175. return instance.database.engine === postgresEngine;
  176. };
  177. /**
  178. * Are we running in debug mdoe?
  179. *
  180. * @returns {boolean}
  181. */
  182. const isDebugMode = () => !!process.env.DEBUG;
  183. /**
  184. * Are we running in CI?
  185. *
  186. * @returns {boolean}
  187. */
  188. const isCI = () => process.env.CI === 'true' && process.env.DEBUG === 'true';
  189. /**
  190. * Returns a public key
  191. *
  192. * @returns {string}
  193. */
  194. const getPublicKey = () => {
  195. instance === null && configure();
  196. return instance.keys.pub;
  197. };
  198. /**
  199. * Returns a private key
  200. *
  201. * @returns {string}
  202. */
  203. const getPrivateKey = () => {
  204. instance === null && configure();
  205. return instance.keys.key;
  206. };
  207. /**
  208. * @returns {boolean}
  209. */
  210. const useLetsencryptStaging = () => !!process.env.LE_STAGING;
  211. /**
  212. * @returns {string|null}
  213. */
  214. const useLetsencryptServer = () => {
  215. if (process.env.LE_SERVER) {
  216. return process.env.LE_SERVER;
  217. }
  218. return null;
  219. };
  220. export { isCI, configHas, configGet, isSqlite, isMysql, isPostgres, isDebugMode, getPrivateKey, getPublicKey, useLetsencryptStaging, useLetsencryptServer };