UNPKG

@speckle/shared

Version:

Shared code between various Speckle JS packages

201 lines 9.57 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.obfuscateConnectionString = exports.getConnectionSettings = exports.configureKnexClient = exports.createKnexConfig = exports.loadMultiRegionsConfig = exports.regionConfigSchema = void 0; const zod_1 = require("zod"); const promises_1 = __importDefault(require("node:fs/promises")); const Knex = __importStar(require("knex")); // knex has broken types, hence the * as import const _lodash_1 = require("#lodash"); // cause of knex's ESM/CJS interop issues const knex = (0, _lodash_1.get)(Knex, 'knex') || (0, _lodash_1.get)(Knex, 'default'); exports.regionConfigSchema = zod_1.z.object({ postgres: zod_1.z.object({ connectionUri: zod_1.z .string() .describe('Full Postgres connection URI (e.g. "postgres://user:password@host:port/dbname")'), databaseName: zod_1.z .string() .describe('Name of the database to connect to. Used where the connection string is to a connection pool, and does not include the database name.') .optional(), privateConnectionUri: zod_1.z .string() .describe('Full Postgres connection URI in VPN or Docker networks (e.g. "postgres://user:password@host:port/dbname")') .optional(), publicTlsCertificate: zod_1.z .string() .describe('Public TLS ("CA") certificate for the Postgres server') .optional(), skipInitialization: zod_1.z .boolean() .optional() .describe('Skip database initialization (migration run & replication setup). Only used in tests.') .refine((val) => val !== true || process.env.NODE_ENV === 'test', { message: 'skipInitialization can only be set when NODE_ENV is "test"' }) }), blobStorage: zod_1.z.object({ endpoint: zod_1.z .string() .url() .describe('URL of the S3-compatible storage endpoint, accessible from the server'), publicEndpoint: zod_1.z .string() .url() .optional() .describe('Public URL of the S3-compatible storage endpoint, accessible from clients via the public internet'), accessKey: zod_1.z.string().describe('Access key for the S3-compatible storage endpoint'), secretKey: zod_1.z.string().describe('Secret key for the S3-compatible storage endpoint'), bucket: zod_1.z.string().describe('Name of the S3-compatible storage bucket'), createBucketIfNotExists: zod_1.z .boolean() .describe('Whether to create the bucket if it does not exist'), s3Region: zod_1.z.string().describe('Region of the S3-compatible storage endpoint') }) }); const multiRegionConfigSchema = zod_1.z.object({ main: exports.regionConfigSchema, regions: zod_1.z.record(zod_1.z.string(), exports.regionConfigSchema), defaultProjectRegionKey: zod_1.z.string().min(3).nullish() }); const loadMultiRegionsConfig = async ({ path }) => { let file; try { file = await promises_1.default.readFile(path, 'utf-8'); } catch (e) { if (e instanceof Error && 'code' in e && e.code === 'ENOENT') { throw new Error(`Multi-region config file not found at path: ${path}`); } throw e; } let parsedJson; try { parsedJson = JSON.parse(file); // This will throw if the file is not valid JSON } catch { throw new Error(`Multi-region config file at path '${path}' is not valid JSON`); } const schema = multiRegionConfigSchema; const multiRegionConfigFileResult = schema.safeParse(parsedJson); // This will throw if the config is invalid if (!multiRegionConfigFileResult.success) throw new Error(`Multi-region config file at path '${path}' does not fit the schema: ${multiRegionConfigFileResult.error}`); return multiRegionConfigFileResult.data; }; exports.loadMultiRegionsConfig = loadMultiRegionsConfig; const createKnexConfig = ({ connectionString, migrationDirs, isTestEnv, isDevOrTestEnv, logger, maxConnections, caCertificate, connectionAcquireTimeoutMillis, connectionCreateTimeoutMillis, asyncStackTraces, migrationSource }) => { const shouldEnableAsyncStackTraces = (0, _lodash_1.isUndefined)(asyncStackTraces) ? isDevOrTestEnv : asyncStackTraces; return { client: 'pg', migrations: { extension: 'ts', // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment migrationSource, // these warnings are annoying locally when switching branches: disableMigrationsListValidation: !!isDevOrTestEnv, ...(migrationSource ? {} : { // Can only be set if migrationSource is not set 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); } }, connection: { connectionString, ssl: caCertificate ? { ca: caCertificate, rejectUnauthorized: true } : undefined, // eslint-disable-next-line camelcase application_name: 'speckle_server' }, // we wish to avoid leaking sql queries in the logs: https://knexjs.org/guide/#compilesqlonerror compileSqlOnError: isDevOrTestEnv, asyncStackTraces: shouldEnableAsyncStackTraces, pool: { min: 0, max: maxConnections, acquireTimeoutMillis: connectionAcquireTimeoutMillis, // If the maximum number of connections is reached, it wait for 16 seconds trying to acquire an existing connection before throwing a timeout error. createTimeoutMillis: connectionCreateTimeoutMillis // If no existing connection is available and the maximum number of connections is not yet reached, the pool will try to create a new connection for 5 seconds before throwing a timeout error. // createRetryIntervalMillis: 200, // Irrelevant & ignored because propagateCreateError is true. // propagateCreateError: true // The propagateCreateError is set to true by default in Knex and throws a TimeoutError if the first create connection to the database fails. Knex recommends that this value is NOT set to false, despite what 'helpful' people on Stackoverflow tell you: https://github.com/knex/knex/issues/3455#issuecomment-535554401 } }; }; exports.createKnexConfig = createKnexConfig; const configureKnexClient = (config, configArgs) => { const knexConfig = (0, exports.createKnexConfig)({ connectionString: config.postgres.connectionUri, caCertificate: config.postgres.publicTlsCertificate, ...configArgs }); const privateConfig = config.postgres.privateConnectionUri ? knex((0, exports.createKnexConfig)({ connectionString: config.postgres.privateConnectionUri, caCertificate: config.postgres.publicTlsCertificate, ...configArgs })) : undefined; return { public: knex(knexConfig), private: privateConfig }; }; exports.configureKnexClient = configureKnexClient; const getConnectionSettings = (knex) => { return knex.client.connectionSettings; }; exports.getConnectionSettings = getConnectionSettings; const obfuscateConnectionString = (connectionString) => { const url = new URL(connectionString); const obfuscatedUrl = new URL(url); obfuscatedUrl.password = '****'; return obfuscatedUrl.toString(); }; exports.obfuscateConnectionString = obfuscateConnectionString; //# sourceMappingURL=db.js.map