From 84781c44281a6b78889bcc495a9b8cb64757b88d Mon Sep 17 00:00:00 2001 From: Keyvan Chamani Date: Tue, 24 Sep 2024 18:47:55 -0400 Subject: [PATCH] UNTRACKED/k1ch/ fix knex config, release idle db connection and minor JWKS API bug (#124) * bug-fix: untracked/k1ch/ fix knex config, release idle db connection and minor JWKS API bug * chore: bump version to v2.2.1 * chore: untracked/k1ch/introduce new knex envs * chore: add additional comment to explain idleTimeoutMillis: 0 --- database/.env.sample | 7 +++++ database/knexfile.js | 11 +++++-- database/layer/knex.js | 27 +++++++++++++---- database/package-lock.json | 4 +-- database/package.json | 2 +- database/utils/pgErrorHandler.js | 4 +-- docs/INSTALL.md | 29 +++++++++++-------- server/.env.sample | 7 ++++- server/package-lock.json | 6 ++-- server/package.json | 2 +- server/src/api_endpoints/endpoint_jwksjson.js | 2 +- 11 files changed, 71 insertions(+), 30 deletions(-) diff --git a/database/.env.sample b/database/.env.sample index 47b1e1c..ad777e9 100644 --- a/database/.env.sample +++ b/database/.env.sample @@ -1,3 +1,10 @@ PGURI=postgres://postgres:tehsecure@usher-db:5432/postgres?sslmode=disable # Needed by knex for storing migrations table PGSCHEMA=usher +KNEX_POOL_MIN=0 +KNEX_POOL_MAX=100 +KNEX_POOL_PROPAGATE_CREATE_ERROR=false +KNEX_POOL_CREATE_RETRY_INTERVAL_MILLIS=500 +KNEX_POOL_CREATE_TIMEOUT_MILLIS=5000 +KNEX_POOL_ACQUIRE_TIMEOUT_MILLIS=5000 +KNEX_POOL_REAP_INTERVAL_MILLIS=1000 diff --git a/database/knexfile.js b/database/knexfile.js index fee92be..9fe4589 100644 --- a/database/knexfile.js +++ b/database/knexfile.js @@ -9,7 +9,14 @@ module.exports = { schemaName: env.PGSCHEMA, }, pool: { - min: process.env.KNEX_POOL_MIN || 1, - max: process.env.KNEX_POOL_MAX || 100, + min: +process.env.KNEX_POOL_MIN || 0, + max: +process.env.KNEX_POOL_MAX || 100, + + // tarn config (https://www.npmjs.com/package/tarn) + propagateCreateError: process.env.KNEX_POOL_PROPAGATE_CREATE_ERROR === 'true' || false, + createRetryIntervalMillis: +process.env.KNEX_POOL_CREATE_RETRY_INTERVAL_MILLIS || 500, + createTimeoutMillis: +process.env.KNEX_POOL_CREATE_TIMEOUT_MILLIS || 5000, + acquireTimeoutMillis: +process.env.KNEX_POOL_ACQUIRE_TIMEOUT_MILLIS || 5000, + reapIntervalMillis: +process.env.KNEX_POOL_REAP_INTERVAL_MILLIS || 1000, }, } diff --git a/database/layer/knex.js b/database/layer/knex.js index 1ac1794..c65f61b 100644 --- a/database/layer/knex.js +++ b/database/layer/knex.js @@ -1,5 +1,5 @@ -const knex = require('knex'); -const knexDbConfig = require('../knexfile'); +const knex = require('knex') +const knexDbConfig = require('../knexfile') /** * Usher DB connection instance. @@ -7,10 +7,27 @@ const knexDbConfig = require('../knexfile'); * @type {import('knex')} * @desc This instance provides a connection to the Usher database using Knex.js * @example // To import usherDb instance - * const { usherDb } = require('./knex'); + * const { usherDb } = require('./knex') * @example // To perform a database query - * const persona = await usherDb('personas').where('key', personaKey).first(); + * const persona = await usherDb('personas').where('key', personaKey).first() */ -const usherDb = knex(knexDbConfig); +let usherDb +try { + usherDb = knex(knexDbConfig) + if (usherDb?.client?.pool) { + const pool = usherDb.client.pool + // Set idle timeout to 0 to release connections immediately. This can't be configured through Knex. + pool.idleTimeoutMillis = 0 + + // Check the pool for idle connections on 'release' event + pool.on('release', () => { + process.nextTick(() => { + pool.check() // Ensures the pool checks for idle connections immediately + }) + }) + } +} catch (err) { + console.error('Failed to create knex instance: ', JSON.stringify(err)) +} module.exports = { usherDb } diff --git a/database/package-lock.json b/database/package-lock.json index 775ebf2..47dd9c1 100644 --- a/database/package-lock.json +++ b/database/package-lock.json @@ -1,12 +1,12 @@ { "name": "@dmgt-tech/the-usher-server-database", - "version": "2.2.0", + "version": "2.2.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@dmgt-tech/the-usher-server-database", - "version": "2.2.0", + "version": "2.2.1", "license": "MIT", "dependencies": { "dotenv": "16.4.5", diff --git a/database/package.json b/database/package.json index 8055fcc..ab0083b 100644 --- a/database/package.json +++ b/database/package.json @@ -1,6 +1,6 @@ { "name": "@dmgt-tech/the-usher-server-database", - "version": "2.2.0", + "version": "2.2.1", "description": "Database layer for TheUsher", "scripts": { "test": "mocha --exit", diff --git a/database/utils/pgErrorHandler.js b/database/utils/pgErrorHandler.js index baebb39..2573171 100644 --- a/database/utils/pgErrorHandler.js +++ b/database/utils/pgErrorHandler.js @@ -69,8 +69,8 @@ const pgErrorHandler = (pgDbError) => { break default: - error.message = `Unexpected DB Error ${pgDbError?.code}, message is: ${pgDbError?.message}!. ${JSON.stringify(pgDbError)}` - error.httpStatusCode = 500 + error.message = `Unexpected DB Error - Code: ${pgDbError?.code}, Message: ${pgDbError?.message}, Error: ${JSON.stringify(pgDbError)}` + error.httpStatusCode = 503 break } diff --git a/docs/INSTALL.md b/docs/INSTALL.md index 6444a01..afe3737 100644 --- a/docs/INSTALL.md +++ b/docs/INSTALL.md @@ -53,18 +53,23 @@ Create a `.env` file that will contain the settings to configure the Usher. You The following variables are required to be configured. -| Parameter | Description | -|--------------------------|----------------------------------------------------------| -| PGURI | Database connection string | -| PGSCHEMA | Database schema name | -| KNEX_POOL_MIN | (Optional) Min number of db pool connections, default to 1 | -| KNEX_POOL_MAX | (Optional) Max number of db pool connections, default to 100 | -| TOKEN_LIFETIME_SECONDS | Number of seconds Access Token is valid | -| SESSION_LIFETIME_SECONDS | Number of seconds Refresh Token is valid | -| ISSUER_WHITELIST | Comma separated list of authorized Issuer Servers | -| THEUSHER_AUD_CLAIMS | (Optional) Comma separated list of authorized audience (aud) claims | -| PRESET_SERVER_URL | (Optional) URI to use as `iss` claim for issued tokens | -| ISSUER_ALIASES | (Optional && Experimental) [Hostname aliases](USAGE.md#migrating-idenitity-provider-domain-names-issuer-aliases-experimental) for IdP tokens issuer | +| Parameter | Description | +|-----------------------------------------|----------------------------------------------------------| +| PGURI | Database connection string | +| PGSCHEMA | Database schema name | +| KNEX_POOL_MIN | (Optional) Min number of db pool connections, default 0 | +| KNEX_POOL_MAX | (Optional) Max number of db pool connections, default 100 | +| KNEX_POOL_PROPAGATE_CREATE_ERROR | (Optional) Propagate create error, default false | +| KNEX_POOL_CREATE_RETRY_INTERVAL_MILLIS | (Optional) Interval in milliseconds to retry creating connection, default 500 | +| KNEX_POOL_CREATE_TIMEOUT_MILLIS | (Optional) Timeout in milliseconds for creating connection, default 5000 | +| KNEX_POOL_ACQUIRE_TIMEOUT_MILLIS | (Optional) Timeout in milliseconds for acquiring connection, default 5000 | +| KNEX_POOL_REAP_INTERVAL_MILLIS | (Optional) Interval in milliseconds to reap connection pool, default 1000 | +| TOKEN_LIFETIME_SECONDS | Number of seconds Access Token is valid | +| SESSION_LIFETIME_SECONDS | Number of seconds Refresh Token is valid | +| ISSUER_WHITELIST | Comma separated list of authorized Issuer Servers | +| THEUSHER_AUD_CLAIMS | (Optional) Comma separated list of authorized audience (aud) claims | +| PRESET_SERVER_URL | (Optional) URI to use as `iss` claim for issued tokens | +| ISSUER_ALIASES | (Optional && Experimental) [Hostname aliases](USAGE.md#migrating-idenitity-provider-domain-names-issuer-aliases-experimental) for IdP tokens issuer | ## Generic Installation Steps diff --git a/server/.env.sample b/server/.env.sample index 7117d62..2ce50cd 100644 --- a/server/.env.sample +++ b/server/.env.sample @@ -13,8 +13,13 @@ NODE_ENV=development PGURI=postgres://postgres:tehsecure@localhost:5432/postgres?sslmode=disable PGSCHEMA=usher -KNEX_POOL_MIN=1 +KNEX_POOL_MIN=0 KNEX_POOL_MAX=100 +KNEX_POOL_PROPAGATE_CREATE_ERROR=false +KNEX_POOL_CREATE_RETRY_INTERVAL_MILLIS=500 +KNEX_POOL_CREATE_TIMEOUT_MILLIS=5000 +KNEX_POOL_ACQUIRE_TIMEOUT_MILLIS=5000 +KNEX_POOL_REAP_INTERVAL_MILLIS=1000 # TOKEN LIFETIMES # Duration the access_token is valid: diff --git a/server/package-lock.json b/server/package-lock.json index 0b15123..15ee032 100644 --- a/server/package-lock.json +++ b/server/package-lock.json @@ -1,12 +1,12 @@ { "name": "@dmgt-tech/the-usher-server", - "version": "2.2.0", + "version": "2.2.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@dmgt-tech/the-usher-server", - "version": "2.2.0", + "version": "2.2.1", "license": "MIT", "dependencies": { "cors": "2.8.5", @@ -39,7 +39,7 @@ }, "../database": { "name": "@dmgt-tech/the-usher-server-database", - "version": "2.2.0", + "version": "2.2.1", "license": "MIT", "dependencies": { "dotenv": "16.4.5", diff --git a/server/package.json b/server/package.json index fc15e33..e945b24 100644 --- a/server/package.json +++ b/server/package.json @@ -1,6 +1,6 @@ { "name": "@dmgt-tech/the-usher-server", - "version": "2.2.0", + "version": "2.2.1", "description": "The Usher Authorization Server", "engines": { "node": ">=18" diff --git a/server/src/api_endpoints/endpoint_jwksjson.js b/server/src/api_endpoints/endpoint_jwksjson.js index 0a50233..6b76a59 100644 --- a/server/src/api_endpoints/endpoint_jwksjson.js +++ b/server/src/api_endpoints/endpoint_jwksjson.js @@ -2,7 +2,7 @@ const keystore = require('database/layer/db-keys') const { pem2jwk } = require('pem-jwk') const createError = require('http-errors') -const getJwks = async (req, res) => { +const getJwks = async (req, res, next) => { try { const keyPairs = await keystore.selectAllKeys() const publicKeys = keyPairs?.map(keyPair => {