/* eslint-disable camelcase */ /* istanbul ignore file */ 'use strict' const { packageRoot } = require('./bootstrap') const fs = require('fs') const path = require('path') const { isTestEnv, ignoreMissingMigrations, postgresMaxConnections } = require('@/modules/shared/helpers/envHelper') const { dbLogger: logger } = require('./logging/logging') function walk(dir) { let results = [] const list = fs.readdirSync(dir) list.forEach(function (file) { const fullFile = path.join(dir, file) const stat = fs.statSync(fullFile) if (stat && stat.isDirectory()) { if (file === 'migrations') results.push(fullFile) else results = results.concat(walk(fullFile)) } }) return results } // Always read migrations from /dist, otherwise we risk the same migration being applied twice // once with the .ts extension and the 2nd time with the .js one // The only exception is when running tests in the test DB, cause the stakes are way lower there and we always // run them through ts-node anyway, so it doesn't make sense forcing the app to be built const migrationModulesDir = path.resolve( packageRoot, isTestEnv() ? './modules' : './dist/modules' ) const migrationDirsExist = fs.existsSync(migrationModulesDir) if (!migrationDirsExist && !ignoreMissingMigrations()) { throw new Error('App must be built into /dist, to enable work with migrations') } const migrationDirs = migrationDirsExist ? walk(migrationModulesDir) : [] // this is for readability, many users struggle to set the postgres connection uri // in the env variables. This way its a bit easier to understand, also backward compatible. const env = process.env let connectionUri if (env.POSTGRES_USER && env.POSTGRES_PASSWORD) { connectionUri = `postgres://${encodeURIComponent( env.POSTGRES_USER )}:${encodeURIComponent(env.POSTGRES_PASSWORD)}@${ env.POSTGRES_URL }/${encodeURIComponent(env.POSTGRES_DB)}` } else { connectionUri = env.POSTGRES_URL } // NOTE: fixes time pagination, breaks graphql DateTime parsing :/ // The pg driver (& knex?) parses dates for us and it breaks precision. This // disables any date parsing and we guarantee values are returned as strings. // const types = require('pg').types // const TIMESTAMPTZ_OID = 1184 // const TIMESTAMP_OID = 1114 // types.setTypeParser(TIMESTAMPTZ_OID, (val) => val) // types.setTypeParser(TIMESTAMP_OID, (val) => val) // Another NOTE: // this is why the new datetime columns are created like this // table.specificType('createdAt', 'TIMESTAMPTZ(3)').defaultTo(knex.fn.now()) /** @type {import('knex').Knex.Config} */ const commonConfig = { client: 'pg', migrations: { extension: 'ts', loadExtensions: isTestEnv() ? ['.js', '.ts'] : ['.js'], directory: migrationDirs }, log: { warn(message) { logger.warn(message) }, error(message) { logger.error(message) }, deprecate(message) { logger.info(message) }, debug(message) { logger.debug(message) } }, // we wish to avoid leaking sql queries in the logs: https://knexjs.org/guide/#compilesqlonerror compileSqlOnError: false, pool: { min: 0, max: postgresMaxConnections(), acquireTimeoutMillis: 16000, //allows for 3x creation attempts plus idle time between attempts createTimeoutMillis: 5000 } } /** @type {Object} */ const config = { test: { ...commonConfig, connection: { connectionString: connectionUri || 'postgres://127.0.0.1/speckle2_test', application_name: 'speckle_server' }, compileSqlOnError: true, asyncStackTraces: true }, development: { ...commonConfig, connection: { connectionString: connectionUri || 'postgres://127.0.0.1/speckle2_dev', application_name: 'speckle_server' }, compileSqlOnError: true, asyncStackTraces: true }, production: { ...commonConfig, connection: { connectionString: connectionUri, application_name: 'speckle_server' } } } module.exports = config