modified: backend/src/controllers/databaseManagementController.ts
modified: backend/src/controllers/dynamicApiController.ts modified: backend/src/controllers/endpointController.ts new file: backend/src/migrations/005_add_aql_support.sql new file: backend/src/services/AqlExecutor.ts modified: backend/src/types/index.ts modified: frontend/src/components/EndpointModal.tsx modified: frontend/src/pages/Databases.tsx modified: frontend/src/types/index.ts
This commit is contained in:
@@ -7,7 +7,10 @@ import { databasePoolManager } from '../services/DatabasePoolManager';
|
||||
export const getDatabases = async (req: AuthRequest, res: Response) => {
|
||||
try {
|
||||
const result = await mainPool.query(
|
||||
'SELECT id, name, type, host, port, database_name, username, ssl, is_active, created_at, updated_at FROM databases ORDER BY name'
|
||||
`SELECT id, name, type, host, port, database_name, username, ssl, is_active,
|
||||
aql_base_url, aql_auth_type, aql_auth_value, aql_headers,
|
||||
created_at, updated_at
|
||||
FROM databases ORDER BY name`
|
||||
);
|
||||
|
||||
res.json(result.rows);
|
||||
@@ -22,7 +25,10 @@ export const getDatabase = async (req: AuthRequest, res: Response) => {
|
||||
const { id } = req.params;
|
||||
|
||||
const result = await mainPool.query(
|
||||
'SELECT id, name, type, host, port, database_name, username, ssl, is_active, created_at, updated_at FROM databases WHERE id = $1',
|
||||
`SELECT id, name, type, host, port, database_name, username, ssl, is_active,
|
||||
aql_base_url, aql_auth_type, aql_auth_value, aql_headers,
|
||||
created_at, updated_at
|
||||
FROM databases WHERE id = $1`,
|
||||
[id]
|
||||
);
|
||||
|
||||
@@ -39,26 +45,58 @@ export const getDatabase = async (req: AuthRequest, res: Response) => {
|
||||
|
||||
export const createDatabase = async (req: AuthRequest, res: Response) => {
|
||||
try {
|
||||
const { name, type, host, port, database_name, username, password, ssl } = req.body;
|
||||
const {
|
||||
name, type, host, port, database_name, username, password, ssl,
|
||||
aql_base_url, aql_auth_type, aql_auth_value, aql_headers
|
||||
} = req.body;
|
||||
|
||||
if (!name || !host || !port || !database_name || !username || !password) {
|
||||
return res.status(400).json({ error: 'Не заполнены обязательные поля' });
|
||||
const dbType = type || 'postgresql';
|
||||
|
||||
// Валидация для обычных БД
|
||||
if (dbType !== 'aql') {
|
||||
if (!name || !host || !port || !database_name || !username || !password) {
|
||||
return res.status(400).json({ error: 'Не заполнены обязательные поля' });
|
||||
}
|
||||
} else {
|
||||
// Валидация для AQL
|
||||
if (!name || !aql_base_url) {
|
||||
return res.status(400).json({ error: 'Не заполнены обязательные поля для AQL базы' });
|
||||
}
|
||||
}
|
||||
|
||||
const result = await mainPool.query(
|
||||
`INSERT INTO databases (name, type, host, port, database_name, username, password, ssl, is_active)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, true)
|
||||
`INSERT INTO databases (
|
||||
name, type, host, port, database_name, username, password, ssl, is_active,
|
||||
aql_base_url, aql_auth_type, aql_auth_value, aql_headers
|
||||
)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, true, $9, $10, $11, $12)
|
||||
RETURNING *`,
|
||||
[name, type || 'postgresql', host, port, database_name, username, password, ssl || false]
|
||||
[
|
||||
name,
|
||||
dbType,
|
||||
host || '',
|
||||
port || 0,
|
||||
database_name || '',
|
||||
username || '',
|
||||
password || '',
|
||||
ssl || false,
|
||||
aql_base_url || null,
|
||||
aql_auth_type || null,
|
||||
aql_auth_value || null,
|
||||
aql_headers ? JSON.stringify(aql_headers) : null
|
||||
]
|
||||
);
|
||||
|
||||
const newDb = result.rows[0];
|
||||
|
||||
// Добавить пул подключений
|
||||
await databasePoolManager.reloadPool(newDb.id);
|
||||
// Добавить пул подключений (только для не-AQL баз)
|
||||
if (dbType !== 'aql') {
|
||||
await databasePoolManager.reloadPool(newDb.id);
|
||||
}
|
||||
|
||||
// Не возвращаем пароль
|
||||
delete newDb.password;
|
||||
delete newDb.aql_auth_value; // Также не возвращаем auth value
|
||||
|
||||
res.status(201).json(newDb);
|
||||
} catch (error: any) {
|
||||
@@ -73,13 +111,16 @@ export const createDatabase = async (req: AuthRequest, res: Response) => {
|
||||
export const updateDatabase = async (req: AuthRequest, res: Response) => {
|
||||
try {
|
||||
const { id } = req.params;
|
||||
const { name, type, host, port, database_name, username, password, ssl, is_active } = req.body;
|
||||
const {
|
||||
name, type, host, port, database_name, username, password, ssl, is_active,
|
||||
aql_base_url, aql_auth_type, aql_auth_value, aql_headers
|
||||
} = req.body;
|
||||
|
||||
// Если пароль не передан, не обновляем его
|
||||
// Если пароль/auth не передан, не обновляем его
|
||||
let query;
|
||||
let params;
|
||||
|
||||
if (password) {
|
||||
if (password || aql_auth_value) {
|
||||
query = `
|
||||
UPDATE databases
|
||||
SET name = COALESCE($1, name),
|
||||
@@ -88,14 +129,22 @@ export const updateDatabase = async (req: AuthRequest, res: Response) => {
|
||||
port = COALESCE($4, port),
|
||||
database_name = COALESCE($5, database_name),
|
||||
username = COALESCE($6, username),
|
||||
password = $7,
|
||||
password = COALESCE($7, password),
|
||||
ssl = COALESCE($8, ssl),
|
||||
is_active = COALESCE($9, is_active),
|
||||
aql_base_url = COALESCE($10, aql_base_url),
|
||||
aql_auth_type = COALESCE($11, aql_auth_type),
|
||||
aql_auth_value = COALESCE($12, aql_auth_value),
|
||||
aql_headers = COALESCE($13, aql_headers),
|
||||
updated_at = CURRENT_TIMESTAMP
|
||||
WHERE id = $10
|
||||
RETURNING id, name, type, host, port, database_name, username, ssl, is_active, created_at, updated_at
|
||||
WHERE id = $14
|
||||
RETURNING id, name, type, host, port, database_name, username, ssl, is_active,
|
||||
aql_base_url, aql_auth_type, aql_headers, created_at, updated_at
|
||||
`;
|
||||
params = [name, type, host, port, database_name, username, password, ssl, is_active, id];
|
||||
params = [
|
||||
name, type, host, port, database_name, username, password || aql_auth_value, ssl, is_active,
|
||||
aql_base_url, aql_auth_type, aql_auth_value, aql_headers ? JSON.stringify(aql_headers) : null, id
|
||||
];
|
||||
} else {
|
||||
query = `
|
||||
UPDATE databases
|
||||
@@ -107,11 +156,18 @@ export const updateDatabase = async (req: AuthRequest, res: Response) => {
|
||||
username = COALESCE($6, username),
|
||||
ssl = COALESCE($7, ssl),
|
||||
is_active = COALESCE($8, is_active),
|
||||
aql_base_url = COALESCE($9, aql_base_url),
|
||||
aql_auth_type = COALESCE($10, aql_auth_type),
|
||||
aql_headers = COALESCE($11, aql_headers),
|
||||
updated_at = CURRENT_TIMESTAMP
|
||||
WHERE id = $9
|
||||
RETURNING id, name, type, host, port, database_name, username, ssl, is_active, created_at, updated_at
|
||||
WHERE id = $12
|
||||
RETURNING id, name, type, host, port, database_name, username, ssl, is_active,
|
||||
aql_base_url, aql_auth_type, aql_headers, created_at, updated_at
|
||||
`;
|
||||
params = [name, type, host, port, database_name, username, ssl, is_active, id];
|
||||
params = [
|
||||
name, type, host, port, database_name, username, ssl, is_active,
|
||||
aql_base_url, aql_auth_type, aql_headers ? JSON.stringify(aql_headers) : null, id
|
||||
];
|
||||
}
|
||||
|
||||
const result = await mainPool.query(query, params);
|
||||
@@ -120,8 +176,10 @@ export const updateDatabase = async (req: AuthRequest, res: Response) => {
|
||||
return res.status(404).json({ error: 'База данных не найдена' });
|
||||
}
|
||||
|
||||
// Перезагрузить пул
|
||||
await databasePoolManager.reloadPool(id);
|
||||
// Перезагрузить пул (только для не-AQL баз)
|
||||
if (result.rows[0].type !== 'aql') {
|
||||
await databasePoolManager.reloadPool(id);
|
||||
}
|
||||
|
||||
res.json(result.rows[0]);
|
||||
} catch (error: any) {
|
||||
@@ -172,12 +230,33 @@ export const testDatabaseConnection = async (req: AuthRequest, res: Response) =>
|
||||
try {
|
||||
const { id } = req.params;
|
||||
|
||||
const isConnected = await databasePoolManager.testConnection(id);
|
||||
// Получаем тип БД
|
||||
const dbResult = await mainPool.query('SELECT type FROM databases WHERE id = $1', [id]);
|
||||
|
||||
res.json({
|
||||
success: isConnected,
|
||||
message: isConnected ? 'Подключение успешно' : 'Ошибка подключения',
|
||||
});
|
||||
if (dbResult.rows.length === 0) {
|
||||
return res.status(404).json({ success: false, error: 'База данных не найдена' });
|
||||
}
|
||||
|
||||
const dbType = dbResult.rows[0].type;
|
||||
|
||||
if (dbType === 'aql') {
|
||||
// Для AQL используем aqlExecutor
|
||||
const { aqlExecutor } = require('../services/AqlExecutor');
|
||||
const result = await aqlExecutor.testConnection(id);
|
||||
|
||||
res.json({
|
||||
success: result.success,
|
||||
message: result.success ? 'Подключение успешно' : result.error || 'Ошибка подключения',
|
||||
});
|
||||
} else {
|
||||
// Для обычных БД используем databasePoolManager
|
||||
const isConnected = await databasePoolManager.testConnection(id);
|
||||
|
||||
res.json({
|
||||
success: isConnected,
|
||||
message: isConnected ? 'Подключение успешно' : 'Ошибка подключения',
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Test connection error:', error);
|
||||
res.status(500).json({ error: 'Ошибка тестирования подключения' });
|
||||
|
||||
@@ -141,7 +141,38 @@ export const executeDynamicEndpoint = async (req: ApiKeyRequest, res: Response)
|
||||
let result;
|
||||
const executionType = endpoint.execution_type || 'sql';
|
||||
|
||||
if (executionType === 'script') {
|
||||
if (executionType === 'aql') {
|
||||
// Execute AQL query
|
||||
const aqlMethod = endpoint.aql_method;
|
||||
const aqlEndpoint = endpoint.aql_endpoint;
|
||||
const aqlBody = endpoint.aql_body;
|
||||
|
||||
let aqlQueryParams: Record<string, string> = {};
|
||||
if (endpoint.aql_query_params) {
|
||||
if (typeof endpoint.aql_query_params === 'string') {
|
||||
try {
|
||||
aqlQueryParams = JSON.parse(endpoint.aql_query_params);
|
||||
} catch (e) {
|
||||
aqlQueryParams = {};
|
||||
}
|
||||
} else if (typeof endpoint.aql_query_params === 'object') {
|
||||
aqlQueryParams = endpoint.aql_query_params;
|
||||
}
|
||||
}
|
||||
|
||||
if (!aqlMethod || !aqlEndpoint) {
|
||||
return res.status(500).json({ error: 'AQL configuration is incomplete' });
|
||||
}
|
||||
|
||||
const { aqlExecutor } = require('../services/AqlExecutor');
|
||||
result = await aqlExecutor.executeAqlQuery(endpoint.database_id, {
|
||||
method: aqlMethod,
|
||||
endpoint: aqlEndpoint,
|
||||
body: aqlBody,
|
||||
queryParams: aqlQueryParams,
|
||||
parameters: requestParams,
|
||||
});
|
||||
} else if (executionType === 'script') {
|
||||
// Execute script
|
||||
const scriptLanguage = endpoint.script_language;
|
||||
const scriptCode = endpoint.script_code;
|
||||
|
||||
@@ -81,6 +81,10 @@ export const createEndpoint = async (req: AuthRequest, res: Response) => {
|
||||
script_language,
|
||||
script_code,
|
||||
script_queries,
|
||||
aql_method,
|
||||
aql_endpoint,
|
||||
aql_body,
|
||||
aql_query_params,
|
||||
} = req.body;
|
||||
|
||||
if (!name || !method || !path) {
|
||||
@@ -103,13 +107,21 @@ export const createEndpoint = async (req: AuthRequest, res: Response) => {
|
||||
}
|
||||
}
|
||||
|
||||
// Валидация для типа AQL
|
||||
if (execType === 'aql') {
|
||||
if (!database_id || !aql_method || !aql_endpoint) {
|
||||
return res.status(400).json({ error: 'Database ID, AQL method, and AQL endpoint are required for AQL execution type' });
|
||||
}
|
||||
}
|
||||
|
||||
const result = await mainPool.query(
|
||||
`INSERT INTO endpoints (
|
||||
name, description, method, path, database_id, sql_query, parameters,
|
||||
folder_id, user_id, is_public, enable_logging,
|
||||
execution_type, script_language, script_code, script_queries
|
||||
execution_type, script_language, script_code, script_queries,
|
||||
aql_method, aql_endpoint, aql_body, aql_query_params
|
||||
)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19)
|
||||
RETURNING *`,
|
||||
[
|
||||
name,
|
||||
@@ -127,6 +139,10 @@ export const createEndpoint = async (req: AuthRequest, res: Response) => {
|
||||
script_language || null,
|
||||
script_code || null,
|
||||
JSON.stringify(script_queries || []),
|
||||
aql_method || null,
|
||||
aql_endpoint || null,
|
||||
aql_body || null,
|
||||
JSON.stringify(aql_query_params || {}),
|
||||
]
|
||||
);
|
||||
|
||||
@@ -158,6 +174,10 @@ export const updateEndpoint = async (req: AuthRequest, res: Response) => {
|
||||
script_language,
|
||||
script_code,
|
||||
script_queries,
|
||||
aql_method,
|
||||
aql_endpoint,
|
||||
aql_body,
|
||||
aql_query_params,
|
||||
} = req.body;
|
||||
|
||||
const result = await mainPool.query(
|
||||
@@ -176,8 +196,12 @@ export const updateEndpoint = async (req: AuthRequest, res: Response) => {
|
||||
script_language = $12,
|
||||
script_code = $13,
|
||||
script_queries = $14,
|
||||
aql_method = $15,
|
||||
aql_endpoint = $16,
|
||||
aql_body = $17,
|
||||
aql_query_params = $18,
|
||||
updated_at = CURRENT_TIMESTAMP
|
||||
WHERE id = $15
|
||||
WHERE id = $19
|
||||
RETURNING *`,
|
||||
[
|
||||
name,
|
||||
@@ -194,6 +218,10 @@ export const updateEndpoint = async (req: AuthRequest, res: Response) => {
|
||||
script_language || null,
|
||||
script_code || null,
|
||||
script_queries ? JSON.stringify(script_queries) : null,
|
||||
aql_method || null,
|
||||
aql_endpoint || null,
|
||||
aql_body || null,
|
||||
aql_query_params ? JSON.stringify(aql_query_params) : null,
|
||||
id,
|
||||
]
|
||||
);
|
||||
@@ -242,7 +270,11 @@ export const testEndpoint = async (req: AuthRequest, res: Response) => {
|
||||
execution_type,
|
||||
script_language,
|
||||
script_code,
|
||||
script_queries
|
||||
script_queries,
|
||||
aql_method,
|
||||
aql_endpoint,
|
||||
aql_body,
|
||||
aql_query_params,
|
||||
} = req.body;
|
||||
|
||||
const execType = execution_type || 'sql';
|
||||
@@ -305,6 +337,37 @@ export const testEndpoint = async (req: AuthRequest, res: Response) => {
|
||||
rowCount: scriptResult.rowCount || (Array.isArray(scriptResult.data) ? scriptResult.data.length : 0),
|
||||
executionTime: scriptResult.executionTime || 0,
|
||||
});
|
||||
} else if (execType === 'aql') {
|
||||
if (!database_id) {
|
||||
return res.status(400).json({ error: 'Missing database_id for AQL execution' });
|
||||
}
|
||||
if (!aql_method || !aql_endpoint) {
|
||||
return res.status(400).json({ error: 'Missing aql_method or aql_endpoint' });
|
||||
}
|
||||
|
||||
// Собираем параметры из тестовых значений
|
||||
const requestParams: Record<string, any> = {};
|
||||
if (endpoint_parameters && Array.isArray(endpoint_parameters) && parameters && Array.isArray(parameters)) {
|
||||
endpoint_parameters.forEach((param: any, index: number) => {
|
||||
requestParams[param.name] = parameters[index];
|
||||
});
|
||||
}
|
||||
|
||||
const { aqlExecutor } = require('../services/AqlExecutor');
|
||||
const result = await aqlExecutor.executeAqlQuery(database_id, {
|
||||
method: aql_method,
|
||||
endpoint: aql_endpoint,
|
||||
body: aql_body,
|
||||
queryParams: aql_query_params,
|
||||
parameters: requestParams,
|
||||
});
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
data: result.rows,
|
||||
rowCount: result.rowCount,
|
||||
executionTime: result.executionTime,
|
||||
});
|
||||
} else {
|
||||
return res.status(400).json({ error: 'Invalid execution_type' });
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user