UNPKG

plugin-postgresql-connector

Version:

NocoBase plugin for connecting to external PostgreSQL databases

371 lines 14.3 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.ConnectionController = void 0; const Connection_1 = __importDefault(require("../models/Connection")); const joi_1 = __importDefault(require("joi")); // Validation schema for connection data const connectionSchema = joi_1.default.object({ name: joi_1.default.string().required().min(1).max(100).trim(), host: joi_1.default.string().required().min(1).max(255).trim(), port: joi_1.default.number().integer().min(1).max(65535).default(5432), database: joi_1.default.string().required().min(1).max(63).trim(), username: joi_1.default.string().required().min(1).max(63).trim(), password: joi_1.default.string().required().min(1), ssl: joi_1.default.boolean().default(false), connectionOptions: joi_1.default.object().default({}), }); const connectionUpdateSchema = connectionSchema.fork([ 'name', 'host', 'database', 'username', 'password' ], (schema) => schema.optional()); class ConnectionController { constructor(connectionManager) { this.connectionManager = connectionManager; } /** * Create a new database connection */ async create(ctx) { try { // Validate input data const { error, value } = connectionSchema.validate(ctx.request.body); if (error) { ctx.status = 400; ctx.body = { success: false, error: error.details[0].message, }; return; } // Test connection first const testResult = await this.connectionManager.testConnection(value); if (!testResult.success) { ctx.status = 400; ctx.body = { success: false, error: `Connection test failed: ${testResult.error}`, }; return; } // Check if connection name already exists const existingConnection = await Connection_1.default.findOne({ where: { name: value.name, isActive: true }, }); if (existingConnection) { ctx.status = 409; ctx.body = { success: false, error: 'Connection name already exists', }; return; } // Encrypt password before saving const encryptedPassword = this.connectionManager.encryptPassword(value.password); // Save to database const connection = await Connection_1.default.create({ ...value, password: encryptedPassword, }); ctx.status = 201; ctx.body = { success: true, data: { id: connection.getDataValue('id'), name: connection.getDataValue('name'), host: connection.getDataValue('host'), port: connection.getDataValue('port'), database: connection.getDataValue('database'), username: connection.getDataValue('username'), ssl: connection.getDataValue('ssl'), isActive: connection.getDataValue('isActive'), createdAt: connection.getDataValue('createdAt'), }, message: 'Connection created successfully', }; } catch (error) { console.error('Create connection error:', error); ctx.status = 500; ctx.body = { success: false, error: `Failed to create connection: ${error instanceof Error ? error.message : 'Unknown error'}`, }; } } /** * Test database connection without saving */ async test(ctx) { try { // Validate input data const { error, value } = connectionSchema.validate(ctx.request.body); if (error) { ctx.status = 400; ctx.body = { success: false, error: error.details[0].message, }; return; } // Test connection const testResult = await this.connectionManager.testConnection(value); if (testResult.success) { ctx.body = { success: true, message: 'Connection test successful', }; } else { ctx.status = 400; ctx.body = { success: false, error: testResult.error, }; } } catch (error) { console.error('Test connection error:', error); ctx.status = 500; ctx.body = { success: false, error: `Connection test failed: ${error instanceof Error ? error.message : 'Unknown error'}`, }; } } /** * List all active connections */ async list(ctx) { try { const { page = 1, pageSize = 10, search } = ctx.query; const offset = (parseInt(page) - 1) * parseInt(pageSize); const limit = parseInt(pageSize); const whereClause = { isActive: true }; // Add search functionality if (search) { whereClause.name = { [Op.iLike]: `%${search}%`, }; } const { count, rows } = await Connection_1.default.findAndCountAll({ where: whereClause, attributes: ['id', 'name', 'host', 'port', 'database', 'username', 'ssl', 'isActive', 'createdAt', 'updatedAt'], offset, limit, order: [['createdAt', 'DESC']], }); ctx.body = { success: true, data: { connections: rows, pagination: { total: count, page: parseInt(page), pageSize: parseInt(pageSize), totalPages: Math.ceil(count / parseInt(pageSize)), }, }, }; } catch (error) { console.error('List connections error:', error); ctx.status = 500; ctx.body = { success: false, error: `Failed to list connections: ${error instanceof Error ? error.message : 'Unknown error'}`, }; } } /** * Get a specific connection by ID */ async get(ctx) { try { const { id } = ctx.params; const connection = await Connection_1.default.findOne({ where: { id, isActive: true }, attributes: ['id', 'name', 'host', 'port', 'database', 'username', 'ssl', 'isActive', 'createdAt', 'updatedAt'], }); if (!connection) { ctx.status = 404; ctx.body = { success: false, error: 'Connection not found', }; return; } ctx.body = { success: true, data: connection, }; } catch (error) { console.error('Get connection error:', error); ctx.status = 500; ctx.body = { success: false, error: `Failed to get connection: ${error instanceof Error ? error.message : 'Unknown error'}`, }; } } /** * Update a connection */ async update(ctx) { try { const { id } = ctx.params; // Validate input data const { error, value } = connectionUpdateSchema.validate(ctx.request.body); if (error) { ctx.status = 400; ctx.body = { success: false, error: error.details[0].message, }; return; } // Find connection const connection = await Connection_1.default.findOne({ where: { id, isActive: true }, }); if (!connection) { ctx.status = 404; ctx.body = { success: false, error: 'Connection not found', }; return; } // If password is being updated, encrypt it if (value.password) { value.password = this.connectionManager.encryptPassword(value.password); } // If connection details are being updated, test the new connection if (value.host || value.port || value.database || value.username || value.password || value.ssl !== undefined) { const currentConfig = { host: value.host || connection.getDataValue('host'), port: value.port || connection.getDataValue('port'), database: value.database || connection.getDataValue('database'), username: value.username || connection.getDataValue('username'), password: value.password ? this.connectionManager.decryptPassword(value.password) : this.connectionManager.decryptPassword(connection.getDataValue('password')), ssl: value.ssl !== undefined ? value.ssl : connection.getDataValue('ssl'), }; const testResult = await this.connectionManager.testConnection(currentConfig); if (!testResult.success) { ctx.status = 400; ctx.body = { success: false, error: `Connection test failed: ${testResult.error}`, }; return; } } // Update connection await connection.update(value); ctx.body = { success: true, data: { id: connection.getDataValue('id'), name: connection.getDataValue('name'), host: connection.getDataValue('host'), port: connection.getDataValue('port'), database: connection.getDataValue('database'), username: connection.getDataValue('username'), ssl: connection.getDataValue('ssl'), isActive: connection.getDataValue('isActive'), updatedAt: connection.getDataValue('updatedAt'), }, message: 'Connection updated successfully', }; } catch (error) { console.error('Update connection error:', error); ctx.status = 500; ctx.body = { success: false, error: `Failed to update connection: ${error instanceof Error ? error.message : 'Unknown error'}`, }; } } /** * Delete (deactivate) a connection */ async delete(ctx) { try { const { id } = ctx.params; const connection = await Connection_1.default.findOne({ where: { id, isActive: true }, }); if (!connection) { ctx.status = 404; ctx.body = { success: false, error: 'Connection not found', }; return; } // Soft delete by setting isActive to false await connection.update({ isActive: false }); // Close any active connection pools for this connection try { const activeConnections = this.connectionManager.getActiveConnections(); const connectionToClose = activeConnections.find(connId => connId.includes(id)); if (connectionToClose) { await this.connectionManager.closeConnection(connectionToClose); } } catch (poolError) { console.warn('Warning: Could not close connection pool:', poolError); } ctx.body = { success: true, message: 'Connection deleted successfully', }; } catch (error) { console.error('Delete connection error:', error); ctx.status = 500; ctx.body = { success: false, error: `Failed to delete connection: ${error instanceof Error ? error.message : 'Unknown error'}`, }; } } /** * Get connection statistics */ async stats(ctx) { try { const totalConnections = await Connection_1.default.count({ where: { isActive: true }, }); const activePoolConnections = this.connectionManager.getActiveConnections().length; ctx.body = { success: true, data: { totalConnections, activePoolConnections, connections: this.connectionManager.getActiveConnections().map(connId => ({ connectionId: connId, stats: this.connectionManager.getConnectionStats(connId), })), }, }; } catch (error) { console.error('Get connection stats error:', error); ctx.status = 500; ctx.body = { success: false, error: `Failed to get connection statistics: ${error instanceof Error ? error.message : 'Unknown error'}`, }; } } } exports.ConnectionController = ConnectionController; exports.default = ConnectionController; //# sourceMappingURL=connection.js.map