plugin-postgresql-connector
Version:
NocoBase plugin for connecting to external PostgreSQL databases
210 lines • 7.38 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.ConnectionManager = void 0;
const pg_1 = require("pg");
const crypto_js_1 = __importDefault(require("crypto-js"));
class ConnectionManager {
constructor() {
this.pools = new Map();
this.encryptionKey = process.env.ENCRYPTION_KEY || 'default-key-change-in-production';
this.maxConnections = parseInt(process.env.POSTGRESQL_CONNECTOR_MAX_CONNECTIONS || '10');
this.connectionTimeout = parseInt(process.env.POSTGRESQL_CONNECTOR_CONNECTION_TIMEOUT || '5000');
this.idleTimeout = parseInt(process.env.POSTGRESQL_CONNECTOR_IDLE_TIMEOUT || '30000');
if (this.encryptionKey === 'default-key-change-in-production') {
console.warn('WARNING: Using default encryption key. Please set ENCRYPTION_KEY environment variable.');
}
}
/**
* Create a new connection pool and test the connection
*/
async createConnection(connectionConfig) {
const connectionId = this.generateConnectionId();
try {
// Create connection pool configuration
const poolConfig = {
host: connectionConfig.host,
port: connectionConfig.port,
database: connectionConfig.database,
user: connectionConfig.username,
password: connectionConfig.password,
max: this.maxConnections,
idleTimeoutMillis: this.idleTimeout,
connectionTimeoutMillis: this.connectionTimeout,
ssl: connectionConfig.ssl ? { rejectUnauthorized: false } : false,
...connectionConfig.connectionOptions,
};
// Create pool
const pool = new pg_1.Pool(poolConfig);
// Test connection
const client = await pool.connect();
try {
await client.query('SELECT NOW()');
console.log(`Connection ${connectionId} established successfully`);
}
finally {
client.release();
}
// Store pool
this.pools.set(connectionId, pool);
return {
success: true,
connectionId,
};
}
catch (error) {
console.error(`Connection ${connectionId} failed:`, error);
return {
success: false,
error: error instanceof Error ? error.message : 'Unknown connection error',
};
}
}
/**
* Get a client from the connection pool
*/
async getConnection(connectionId) {
const pool = this.pools.get(connectionId);
if (!pool) {
throw new Error(`Connection ${connectionId} not found`);
}
try {
return await pool.connect();
}
catch (error) {
console.error(`Failed to get connection from pool ${connectionId}:`, error);
throw new Error(`Failed to get connection: ${error instanceof Error ? error.message : 'Unknown error'}`);
}
}
/**
* Close a specific connection pool
*/
async closeConnection(connectionId) {
const pool = this.pools.get(connectionId);
if (pool) {
try {
await pool.end();
this.pools.delete(connectionId);
console.log(`Connection ${connectionId} closed successfully`);
}
catch (error) {
console.error(`Error closing connection ${connectionId}:`, error);
throw error;
}
}
}
/**
* Close all connection pools
*/
async closeAllConnections() {
const closePromises = Array.from(this.pools.keys()).map(connectionId => this.closeConnection(connectionId));
await Promise.all(closePromises);
console.log('All connections closed');
}
/**
* Test connection without creating a pool
*/
async testConnection(connectionConfig) {
const poolConfig = {
host: connectionConfig.host,
port: connectionConfig.port,
database: connectionConfig.database,
user: connectionConfig.username,
password: connectionConfig.password,
max: 1, // Only one connection for testing
idleTimeoutMillis: 1000,
connectionTimeoutMillis: this.connectionTimeout,
ssl: connectionConfig.ssl ? { rejectUnauthorized: false } : false,
};
const pool = new pg_1.Pool(poolConfig);
try {
const client = await pool.connect();
try {
await client.query('SELECT NOW()');
return { success: true };
}
finally {
client.release();
}
}
catch (error) {
return {
success: false,
error: error instanceof Error ? error.message : 'Connection test failed',
};
}
finally {
await pool.end();
}
}
/**
* Get connection pool statistics
*/
getConnectionStats(connectionId) {
const pool = this.pools.get(connectionId);
if (!pool) {
return null;
}
return {
totalCount: pool.totalCount,
idleCount: pool.idleCount,
waitingCount: pool.waitingCount,
};
}
/**
* Get all active connection IDs
*/
getActiveConnections() {
return Array.from(this.pools.keys());
}
/**
* Encrypt password for storage
*/
encryptPassword(password) {
try {
return crypto_js_1.default.AES.encrypt(password, this.encryptionKey).toString();
}
catch (error) {
console.error('Password encryption failed:', error);
throw new Error('Failed to encrypt password');
}
}
/**
* Decrypt password from storage
*/
decryptPassword(encryptedPassword) {
try {
const bytes = crypto_js_1.default.AES.decrypt(encryptedPassword, this.encryptionKey);
const decrypted = bytes.toString(crypto_js_1.default.enc.Utf8);
if (!decrypted) {
throw new Error('Failed to decrypt password - invalid key or corrupted data');
}
return decrypted;
}
catch (error) {
console.error('Password decryption failed:', error);
throw new Error('Failed to decrypt password');
}
}
/**
* Generate unique connection ID
*/
generateConnectionId() {
const timestamp = Date.now();
const random = Math.random().toString(36).substr(2, 9);
return `conn_${timestamp}_${random}`;
}
/**
* Cleanup method for graceful shutdown
*/
async destroy() {
console.log('ConnectionManager: Starting cleanup...');
await this.closeAllConnections();
console.log('ConnectionManager: Cleanup completed');
}
}
exports.ConnectionManager = ConnectionManager;
exports.default = ConnectionManager;
//# sourceMappingURL=ConnectionManager.js.map