plugin-postgresql-connector
Version:
NocoBase plugin for connecting to external PostgreSQL databases
371 lines • 14.3 kB
JavaScript
;
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