multibridge
Version:
A multi-database connection framework with centralized configuration
83 lines (82 loc) • 3.16 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.createPostgresConnection = createPostgresConnection;
const pg_1 = require("pg");
const loggers_1 = __importDefault(require("../utils/loggers"));
const envConfig_1 = require("../config/envConfig");
const errors_1 = require("../utils/errors");
/**
* Sanitize and validate schema name to prevent SQL injection
* Uses PostgreSQL identifier quoting
*/
function sanitizeSchemaName(schema) {
// Validate schema name contains only safe characters
if (!/^[a-zA-Z0-9_]+$/.test(schema)) {
throw new errors_1.ValidationError(`Invalid schema name: ${schema}. Only alphanumeric and underscore are allowed.`);
}
// Use PostgreSQL identifier quoting to safely escape
// Replace any double quotes with escaped quotes and wrap in quotes
const escaped = schema.replace(/"/g, '""');
return `"${escaped}"`;
}
async function createPostgresConnection(config) {
const sanitizedSchema = sanitizeSchemaName(config.schema);
const pool = new pg_1.Pool({
host: config.host,
port: config.port,
user: config.username,
password: config.password,
database: config.database,
max: envConfig_1.envConfig.POSTGRES_POOL_MAX,
min: envConfig_1.envConfig.POSTGRES_POOL_MIN,
idleTimeoutMillis: 600000, // 10 minutes
connectionTimeoutMillis: 5000, // 5 seconds
});
// For every new connection, set the search_path
pool.on("connect", async (client) => {
try {
// Use sanitized schema name with proper quoting
await client.query(`SET search_path TO ${sanitizedSchema}`);
loggers_1.default.debug(`Search path set to ${config.schema} for new connection`);
}
catch (error) {
loggers_1.default.error(`Failed to set search path on new connection: ${error.message}`, {
schema: config.schema,
});
}
});
// Add pool event listeners for monitoring
pool.on("error", (err) => {
loggers_1.default.error(`Unexpected error on idle PostgreSQL client: ${err.message}`, {
host: config.host,
database: config.database,
});
});
// Test the pool by acquiring a client once
try {
const client = await pool.connect();
try {
await client.query(`SET search_path TO ${sanitizedSchema}`);
loggers_1.default.info("Connected to PostgreSQL and search_path set", {
schema: config.schema,
host: config.host,
database: config.database,
});
}
finally {
client.release();
}
return pool;
}
catch (error) {
loggers_1.default.error(`Error establishing initial connection to PostgreSQL: ${error.message}`, {
host: config.host,
database: config.database,
schema: config.schema,
});
throw error;
}
}