- Migration 010: test_* columns on databases table, environment column on request_logs - DatabasePoolManager: dual-pool strategy (prod + test), getPool(id, env) with fallback - All executors (SQL, Script, AQL): environment param threaded through execution paths - dynamicApiController: X-Environment header detection, environment in logging - databaseManagementController: CRUD for test credentials, testConnection with ?env=test - Frontend: test env form in DatabaseModal, env toggle in EndpointEditor test panel Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
750 lines
24 KiB
TypeScript
750 lines
24 KiB
TypeScript
import { Response } from 'express';
|
|
import { AuthRequest } from '../middleware/auth';
|
|
import { mainPool } from '../config/database';
|
|
import { v4 as uuidv4 } from 'uuid';
|
|
import { ExportedEndpoint, ExportedScriptQuery, ScriptExecutionError, Environment } from '../types';
|
|
import { encryptEndpointData, decryptEndpointData } from '../services/endpointCrypto';
|
|
|
|
export const getEndpoints = async (req: AuthRequest, res: Response) => {
|
|
try {
|
|
const { search, folder_id } = req.query;
|
|
|
|
let query = `
|
|
SELECT e.*, f.name as folder_name
|
|
FROM endpoints e
|
|
LEFT JOIN folders f ON e.folder_id = f.id
|
|
WHERE 1=1
|
|
`;
|
|
const params: any[] = [];
|
|
|
|
if (folder_id) {
|
|
query += ` AND e.folder_id = $${params.length + 1}`;
|
|
params.push(folder_id);
|
|
}
|
|
|
|
if (search) {
|
|
const searchIndex = params.length + 1;
|
|
query += ` AND (
|
|
e.name ILIKE $${searchIndex} OR
|
|
e.description ILIKE $${searchIndex} OR
|
|
e.sql_query ILIKE $${searchIndex} OR
|
|
e.path ILIKE $${searchIndex}
|
|
)`;
|
|
params.push(`%${search}%`);
|
|
}
|
|
|
|
query += ` ORDER BY e.created_at DESC`;
|
|
|
|
const result = await mainPool.query(query, params);
|
|
res.json(result.rows);
|
|
} catch (error) {
|
|
console.error('Get endpoints error:', error);
|
|
res.status(500).json({ error: 'Internal server error' });
|
|
}
|
|
};
|
|
|
|
export const getEndpoint = async (req: AuthRequest, res: Response) => {
|
|
try {
|
|
const { id } = req.params;
|
|
|
|
const result = await mainPool.query(
|
|
`SELECT e.*, f.name as folder_name
|
|
FROM endpoints e
|
|
LEFT JOIN folders f ON e.folder_id = f.id
|
|
WHERE e.id = $1`,
|
|
[id]
|
|
);
|
|
|
|
if (result.rows.length === 0) {
|
|
return res.status(404).json({ error: 'Endpoint not found' });
|
|
}
|
|
|
|
res.json(result.rows[0]);
|
|
} catch (error) {
|
|
console.error('Get endpoint error:', error);
|
|
res.status(500).json({ error: 'Internal server error' });
|
|
}
|
|
};
|
|
|
|
export const createEndpoint = async (req: AuthRequest, res: Response) => {
|
|
try {
|
|
const {
|
|
name,
|
|
description,
|
|
method,
|
|
path,
|
|
database_id,
|
|
sql_query,
|
|
parameters,
|
|
folder_id,
|
|
is_public,
|
|
enable_logging,
|
|
execution_type,
|
|
script_language,
|
|
script_code,
|
|
script_queries,
|
|
aql_method,
|
|
aql_endpoint,
|
|
aql_body,
|
|
aql_query_params,
|
|
detailed_response,
|
|
response_schema,
|
|
} = req.body;
|
|
|
|
if (!name || !method || !path) {
|
|
return res.status(400).json({ error: 'Missing required fields' });
|
|
}
|
|
|
|
const execType = execution_type || 'sql';
|
|
|
|
// Валидация для типа SQL
|
|
if (execType === 'sql') {
|
|
if (!database_id || !sql_query) {
|
|
return res.status(400).json({ error: 'Database ID and SQL query are required for SQL execution type' });
|
|
}
|
|
}
|
|
|
|
// Валидация для типа Script
|
|
if (execType === 'script') {
|
|
if (!script_language || !script_code || !script_queries) {
|
|
return res.status(400).json({ error: 'Script language, code, and queries are required for script execution type' });
|
|
}
|
|
}
|
|
|
|
// Валидация для типа 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,
|
|
aql_method, aql_endpoint, aql_body, aql_query_params, detailed_response, response_schema
|
|
)
|
|
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21)
|
|
RETURNING *`,
|
|
[
|
|
name,
|
|
description || '',
|
|
method,
|
|
path,
|
|
database_id || null,
|
|
sql_query || '',
|
|
JSON.stringify(parameters || []),
|
|
folder_id || null,
|
|
req.user!.id,
|
|
is_public || false,
|
|
enable_logging || false,
|
|
execType,
|
|
script_language || null,
|
|
script_code || null,
|
|
JSON.stringify(script_queries || []),
|
|
aql_method || null,
|
|
aql_endpoint || null,
|
|
aql_body || null,
|
|
JSON.stringify(aql_query_params || {}),
|
|
detailed_response || false,
|
|
response_schema ? JSON.stringify(response_schema) : null,
|
|
]
|
|
);
|
|
|
|
res.status(201).json(result.rows[0]);
|
|
} catch (error: any) {
|
|
console.error('Create endpoint error:', error);
|
|
if (error.code === '23505') {
|
|
return res.status(400).json({ error: 'Endpoint path already exists' });
|
|
}
|
|
res.status(500).json({ error: 'Internal server error' });
|
|
}
|
|
};
|
|
|
|
export const updateEndpoint = async (req: AuthRequest, res: Response) => {
|
|
try {
|
|
const { id } = req.params;
|
|
const {
|
|
name,
|
|
description,
|
|
method,
|
|
path,
|
|
database_id,
|
|
sql_query,
|
|
parameters,
|
|
folder_id,
|
|
is_public,
|
|
enable_logging,
|
|
execution_type,
|
|
script_language,
|
|
script_code,
|
|
script_queries,
|
|
aql_method,
|
|
aql_endpoint,
|
|
aql_body,
|
|
aql_query_params,
|
|
detailed_response,
|
|
response_schema,
|
|
} = req.body;
|
|
|
|
const result = await mainPool.query(
|
|
`UPDATE endpoints
|
|
SET name = $1,
|
|
description = $2,
|
|
method = $3,
|
|
path = $4,
|
|
database_id = $5,
|
|
sql_query = $6,
|
|
parameters = $7,
|
|
folder_id = $8,
|
|
is_public = $9,
|
|
enable_logging = $10,
|
|
execution_type = $11,
|
|
script_language = $12,
|
|
script_code = $13,
|
|
script_queries = $14,
|
|
aql_method = $15,
|
|
aql_endpoint = $16,
|
|
aql_body = $17,
|
|
aql_query_params = $18,
|
|
detailed_response = $19,
|
|
response_schema = $20,
|
|
updated_at = CURRENT_TIMESTAMP
|
|
WHERE id = $21
|
|
RETURNING *`,
|
|
[
|
|
name,
|
|
description,
|
|
method,
|
|
path,
|
|
database_id || null,
|
|
sql_query,
|
|
parameters ? JSON.stringify(parameters) : null,
|
|
folder_id || null,
|
|
is_public,
|
|
enable_logging,
|
|
execution_type,
|
|
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,
|
|
detailed_response || false,
|
|
response_schema ? JSON.stringify(response_schema) : null,
|
|
id,
|
|
]
|
|
);
|
|
|
|
if (result.rows.length === 0) {
|
|
return res.status(404).json({ error: 'Endpoint not found' });
|
|
}
|
|
|
|
res.json(result.rows[0]);
|
|
} catch (error: any) {
|
|
console.error('Update endpoint error:', error);
|
|
if (error.code === '23505') {
|
|
return res.status(400).json({ error: 'Endpoint path already exists' });
|
|
}
|
|
res.status(500).json({ error: 'Internal server error' });
|
|
}
|
|
};
|
|
|
|
export const deleteEndpoint = async (req: AuthRequest, res: Response) => {
|
|
try {
|
|
const { id } = req.params;
|
|
|
|
const result = await mainPool.query(
|
|
'DELETE FROM endpoints WHERE id = $1 RETURNING id',
|
|
[id]
|
|
);
|
|
|
|
if (result.rows.length === 0) {
|
|
return res.status(404).json({ error: 'Endpoint not found' });
|
|
}
|
|
|
|
res.json({ message: 'Endpoint deleted successfully' });
|
|
} catch (error) {
|
|
console.error('Delete endpoint error:', error);
|
|
res.status(500).json({ error: 'Internal server error' });
|
|
}
|
|
};
|
|
|
|
export const testEndpoint = async (req: AuthRequest, res: Response) => {
|
|
try {
|
|
const {
|
|
database_id,
|
|
sql_query,
|
|
parameters,
|
|
endpoint_parameters,
|
|
execution_type,
|
|
script_language,
|
|
script_code,
|
|
script_queries,
|
|
aql_method,
|
|
aql_endpoint,
|
|
aql_body,
|
|
aql_query_params,
|
|
environment: reqEnv,
|
|
} = req.body;
|
|
|
|
const environment: Environment = reqEnv === 'prod' ? 'prod' : 'test';
|
|
const execType = execution_type || 'sql';
|
|
|
|
if (execType === 'sql') {
|
|
if (!database_id) {
|
|
return res.status(400).json({ error: 'Missing database_id for SQL execution' });
|
|
}
|
|
if (!sql_query) {
|
|
return res.status(400).json({ error: 'Missing sql_query' });
|
|
}
|
|
|
|
// Преобразуем именованные параметры ($paramName) в позиционные ($1, $2, $3...)
|
|
let processedQuery = sql_query;
|
|
|
|
if (endpoint_parameters && Array.isArray(endpoint_parameters)) {
|
|
endpoint_parameters.forEach((param: any, index: number) => {
|
|
const paramName = param.name;
|
|
const position = index + 1;
|
|
|
|
// Заменяем все вхождения $paramName на $position
|
|
const regex = new RegExp(`\\$${paramName}\\b`, 'g');
|
|
processedQuery = processedQuery.replace(regex, `$${position}`);
|
|
});
|
|
}
|
|
|
|
const { sqlExecutor } = require('../services/SqlExecutor');
|
|
const result = await sqlExecutor.executeQuery(database_id, processedQuery, parameters || [], environment);
|
|
|
|
res.json({
|
|
success: true,
|
|
data: result.rows,
|
|
rowCount: result.rowCount,
|
|
executionTime: result.executionTime,
|
|
logs: [
|
|
{ type: 'info', message: `Query executed in ${result.executionTime}ms, returned ${result.rowCount} rows`, timestamp: Date.now() },
|
|
],
|
|
queries: [],
|
|
processedQuery,
|
|
});
|
|
} else if (execType === 'script') {
|
|
if (!script_language || !script_code) {
|
|
return res.status(400).json({ error: 'Missing script_language or script_code' });
|
|
}
|
|
|
|
// Собираем параметры из тестовых значений
|
|
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 { scriptExecutor } = require('../services/ScriptExecutor');
|
|
const scriptResult = await scriptExecutor.execute(script_language, script_code, {
|
|
databaseId: database_id,
|
|
scriptQueries: script_queries || [],
|
|
requestParams,
|
|
endpointParameters: endpoint_parameters || [],
|
|
environment,
|
|
});
|
|
|
|
res.json({
|
|
success: true,
|
|
data: scriptResult.result,
|
|
logs: scriptResult.logs,
|
|
queries: scriptResult.queries,
|
|
});
|
|
} 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,
|
|
}, environment);
|
|
|
|
res.json({
|
|
success: true,
|
|
data: result.rows,
|
|
rowCount: result.rowCount,
|
|
executionTime: result.executionTime,
|
|
logs: [
|
|
{ type: 'info', message: `AQL ${aql_method} ${aql_endpoint} executed in ${result.executionTime}ms`, timestamp: Date.now() },
|
|
],
|
|
queries: [],
|
|
});
|
|
} else {
|
|
return res.status(400).json({ error: 'Invalid execution_type' });
|
|
}
|
|
} catch (error: any) {
|
|
const isScriptError = error instanceof ScriptExecutionError;
|
|
res.status(400).json({
|
|
success: false,
|
|
error: error.message,
|
|
detail: error.detail || undefined,
|
|
hint: error.hint || undefined,
|
|
logs: isScriptError ? error.logs : [],
|
|
queries: isScriptError ? error.queries : [],
|
|
});
|
|
}
|
|
};
|
|
|
|
export const exportEndpoint = async (req: AuthRequest, res: Response) => {
|
|
try {
|
|
const { id } = req.params;
|
|
|
|
const endpointResult = await mainPool.query(
|
|
'SELECT * FROM endpoints WHERE id = $1',
|
|
[id]
|
|
);
|
|
if (endpointResult.rows.length === 0) {
|
|
return res.status(404).json({ error: 'Endpoint not found' });
|
|
}
|
|
const endpoint = endpointResult.rows[0];
|
|
|
|
// Resolve database_id -> name & type
|
|
let databaseName: string | null = null;
|
|
let databaseType: string | null = null;
|
|
if (endpoint.database_id) {
|
|
const dbResult = await mainPool.query(
|
|
'SELECT name, type FROM databases WHERE id = $1',
|
|
[endpoint.database_id]
|
|
);
|
|
if (dbResult.rows.length > 0) {
|
|
databaseName = dbResult.rows[0].name;
|
|
databaseType = dbResult.rows[0].type;
|
|
}
|
|
}
|
|
|
|
// Resolve folder_id -> name
|
|
let folderName: string | null = null;
|
|
if (endpoint.folder_id) {
|
|
const folderResult = await mainPool.query(
|
|
'SELECT name FROM folders WHERE id = $1',
|
|
[endpoint.folder_id]
|
|
);
|
|
if (folderResult.rows.length > 0) {
|
|
folderName = folderResult.rows[0].name;
|
|
}
|
|
}
|
|
|
|
// Resolve database_ids in script_queries
|
|
const scriptQueries = endpoint.script_queries || [];
|
|
const exportedScriptQueries: ExportedScriptQuery[] = [];
|
|
for (const sq of scriptQueries) {
|
|
let sqDbName: string | undefined;
|
|
let sqDbType: string | undefined;
|
|
if (sq.database_id) {
|
|
const sqDbResult = await mainPool.query(
|
|
'SELECT name, type FROM databases WHERE id = $1',
|
|
[sq.database_id]
|
|
);
|
|
if (sqDbResult.rows.length > 0) {
|
|
sqDbName = sqDbResult.rows[0].name;
|
|
sqDbType = sqDbResult.rows[0].type;
|
|
}
|
|
}
|
|
exportedScriptQueries.push({
|
|
name: sq.name,
|
|
sql: sq.sql,
|
|
database_name: sqDbName,
|
|
database_type: sqDbType,
|
|
aql_method: sq.aql_method,
|
|
aql_endpoint: sq.aql_endpoint,
|
|
aql_body: sq.aql_body,
|
|
aql_query_params: sq.aql_query_params,
|
|
});
|
|
}
|
|
|
|
const exportData: ExportedEndpoint = {
|
|
_format: 'kabe_v1',
|
|
name: endpoint.name,
|
|
description: endpoint.description || '',
|
|
method: endpoint.method,
|
|
path: endpoint.path,
|
|
execution_type: endpoint.execution_type || 'sql',
|
|
database_name: databaseName,
|
|
database_type: databaseType,
|
|
sql_query: endpoint.sql_query || '',
|
|
parameters: endpoint.parameters || [],
|
|
script_language: endpoint.script_language || null,
|
|
script_code: endpoint.script_code || null,
|
|
script_queries: exportedScriptQueries,
|
|
aql_method: endpoint.aql_method || null,
|
|
aql_endpoint: endpoint.aql_endpoint || null,
|
|
aql_body: endpoint.aql_body || null,
|
|
aql_query_params: endpoint.aql_query_params || null,
|
|
is_public: endpoint.is_public || false,
|
|
enable_logging: endpoint.enable_logging || false,
|
|
detailed_response: endpoint.detailed_response || false,
|
|
response_schema: endpoint.response_schema || null,
|
|
folder_name: folderName,
|
|
};
|
|
|
|
const encrypted = encryptEndpointData(exportData);
|
|
|
|
const safeFileName = endpoint.name.replace(/[^a-zA-Z0-9_\-]/g, '_');
|
|
const encodedFileName = encodeURIComponent(endpoint.name.replace(/[\/\\:*?"<>|]/g, '_')) + '.kabe';
|
|
res.setHeader('Content-Type', 'application/octet-stream');
|
|
res.setHeader('Content-Disposition', `attachment; filename="${safeFileName}.kabe"; filename*=UTF-8''${encodedFileName}`);
|
|
res.send(encrypted);
|
|
} catch (error) {
|
|
console.error('Export endpoint error:', error);
|
|
res.status(500).json({ error: 'Internal server error' });
|
|
}
|
|
};
|
|
|
|
export const importPreview = async (req: AuthRequest, res: Response) => {
|
|
try {
|
|
const buffer = req.body as Buffer;
|
|
if (!buffer || buffer.length === 0) {
|
|
return res.status(400).json({ error: 'No file uploaded' });
|
|
}
|
|
|
|
let exportData: ExportedEndpoint;
|
|
try {
|
|
exportData = decryptEndpointData(buffer) as ExportedEndpoint;
|
|
} catch (err) {
|
|
return res.status(400).json({ error: 'Invalid or corrupted .kabe file' });
|
|
}
|
|
|
|
if (exportData._format !== 'kabe_v1') {
|
|
return res.status(400).json({ error: 'Unsupported file format version' });
|
|
}
|
|
|
|
// Collect all referenced database names
|
|
const referencedDatabases: { name: string; type: string }[] = [];
|
|
|
|
if (exportData.database_name) {
|
|
referencedDatabases.push({
|
|
name: exportData.database_name,
|
|
type: exportData.database_type || 'unknown',
|
|
});
|
|
}
|
|
|
|
for (const sq of exportData.script_queries || []) {
|
|
if (sq.database_name && !referencedDatabases.find(d => d.name === sq.database_name)) {
|
|
referencedDatabases.push({
|
|
name: sq.database_name,
|
|
type: sq.database_type || 'unknown',
|
|
});
|
|
}
|
|
}
|
|
|
|
// Check which databases exist locally
|
|
const localDatabases = await mainPool.query(
|
|
'SELECT id, name, type FROM databases WHERE is_active = true'
|
|
);
|
|
|
|
const databaseMapping = referencedDatabases.map(ref => {
|
|
const found = localDatabases.rows.find(
|
|
(db: any) => db.name === ref.name && db.type === ref.type
|
|
);
|
|
return {
|
|
name: ref.name,
|
|
type: ref.type,
|
|
found: !!found,
|
|
local_id: found?.id || null,
|
|
};
|
|
});
|
|
|
|
// Check folder
|
|
let folder: { name: string; found: boolean; local_id: string | null } | null = null;
|
|
if (exportData.folder_name) {
|
|
const folderResult = await mainPool.query(
|
|
'SELECT id FROM folders WHERE name = $1',
|
|
[exportData.folder_name]
|
|
);
|
|
folder = {
|
|
name: exportData.folder_name,
|
|
found: folderResult.rows.length > 0,
|
|
local_id: folderResult.rows.length > 0 ? folderResult.rows[0].id : null,
|
|
};
|
|
}
|
|
|
|
// Check if path already exists
|
|
const pathCheck = await mainPool.query(
|
|
'SELECT id FROM endpoints WHERE path = $1',
|
|
[exportData.path]
|
|
);
|
|
|
|
res.json({
|
|
endpoint: {
|
|
name: exportData.name,
|
|
description: exportData.description,
|
|
method: exportData.method,
|
|
path: exportData.path,
|
|
execution_type: exportData.execution_type,
|
|
is_public: exportData.is_public,
|
|
enable_logging: exportData.enable_logging,
|
|
detailed_response: exportData.detailed_response,
|
|
folder_name: exportData.folder_name,
|
|
},
|
|
databases: databaseMapping,
|
|
all_databases_found: databaseMapping.every(d => d.found),
|
|
local_databases: localDatabases.rows.map((db: any) => ({
|
|
id: db.id,
|
|
name: db.name,
|
|
type: db.type,
|
|
})),
|
|
folder,
|
|
path_exists: pathCheck.rows.length > 0,
|
|
});
|
|
} catch (error) {
|
|
console.error('Import preview error:', error);
|
|
res.status(500).json({ error: 'Internal server error' });
|
|
}
|
|
};
|
|
|
|
export const importEndpoint = async (req: AuthRequest, res: Response) => {
|
|
try {
|
|
const {
|
|
file_data,
|
|
database_mapping,
|
|
folder_id,
|
|
override_path,
|
|
} = req.body;
|
|
|
|
if (!file_data) {
|
|
return res.status(400).json({ error: 'No file data provided' });
|
|
}
|
|
|
|
const buffer = Buffer.from(file_data, 'base64');
|
|
let exportData: ExportedEndpoint;
|
|
try {
|
|
exportData = decryptEndpointData(buffer) as ExportedEndpoint;
|
|
} catch (err) {
|
|
return res.status(400).json({ error: 'Invalid or corrupted .kabe file' });
|
|
}
|
|
|
|
// Resolve main database_id
|
|
let databaseId: string | null = null;
|
|
if (exportData.database_name) {
|
|
const mappedId = database_mapping?.[exportData.database_name];
|
|
if (mappedId) {
|
|
databaseId = mappedId;
|
|
} else {
|
|
const dbResult = await mainPool.query(
|
|
'SELECT id FROM databases WHERE name = $1 AND is_active = true',
|
|
[exportData.database_name]
|
|
);
|
|
if (dbResult.rows.length > 0) {
|
|
databaseId = dbResult.rows[0].id;
|
|
} else {
|
|
return res.status(400).json({
|
|
error: `Database "${exportData.database_name}" not found and no mapping provided`
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
// Resolve script_queries database_ids
|
|
const resolvedScriptQueries = [];
|
|
for (const sq of exportData.script_queries || []) {
|
|
let sqDatabaseId: string | undefined;
|
|
if (sq.database_name) {
|
|
const mappedId = database_mapping?.[sq.database_name];
|
|
if (mappedId) {
|
|
sqDatabaseId = mappedId;
|
|
} else {
|
|
const sqDbResult = await mainPool.query(
|
|
'SELECT id FROM databases WHERE name = $1 AND is_active = true',
|
|
[sq.database_name]
|
|
);
|
|
if (sqDbResult.rows.length > 0) {
|
|
sqDatabaseId = sqDbResult.rows[0].id;
|
|
} else {
|
|
return res.status(400).json({
|
|
error: `Database "${sq.database_name}" (script query "${sq.name}") not found and no mapping provided`
|
|
});
|
|
}
|
|
}
|
|
}
|
|
resolvedScriptQueries.push({
|
|
name: sq.name,
|
|
sql: sq.sql,
|
|
database_id: sqDatabaseId,
|
|
aql_method: sq.aql_method,
|
|
aql_endpoint: sq.aql_endpoint,
|
|
aql_body: sq.aql_body,
|
|
aql_query_params: sq.aql_query_params,
|
|
});
|
|
}
|
|
|
|
// Resolve folder
|
|
let resolvedFolderId: string | null = folder_id || null;
|
|
if (!resolvedFolderId && exportData.folder_name) {
|
|
const folderResult = await mainPool.query(
|
|
'SELECT id FROM folders WHERE name = $1',
|
|
[exportData.folder_name]
|
|
);
|
|
if (folderResult.rows.length > 0) {
|
|
resolvedFolderId = folderResult.rows[0].id;
|
|
}
|
|
}
|
|
|
|
const finalPath = override_path || exportData.path;
|
|
|
|
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,
|
|
aql_method, aql_endpoint, aql_body, aql_query_params, detailed_response, response_schema
|
|
)
|
|
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21)
|
|
RETURNING *`,
|
|
[
|
|
exportData.name,
|
|
exportData.description || '',
|
|
exportData.method,
|
|
finalPath,
|
|
databaseId,
|
|
exportData.sql_query || '',
|
|
JSON.stringify(exportData.parameters || []),
|
|
resolvedFolderId,
|
|
req.user!.id,
|
|
exportData.is_public || false,
|
|
exportData.enable_logging || false,
|
|
exportData.execution_type || 'sql',
|
|
exportData.script_language || null,
|
|
exportData.script_code || null,
|
|
JSON.stringify(resolvedScriptQueries),
|
|
exportData.aql_method || null,
|
|
exportData.aql_endpoint || null,
|
|
exportData.aql_body || null,
|
|
JSON.stringify(exportData.aql_query_params || {}),
|
|
exportData.detailed_response || false,
|
|
exportData.response_schema ? JSON.stringify(exportData.response_schema) : null,
|
|
]
|
|
);
|
|
|
|
res.status(201).json(result.rows[0]);
|
|
} catch (error: any) {
|
|
console.error('Import endpoint error:', error);
|
|
if (error.code === '23505') {
|
|
return res.status(400).json({ error: 'Endpoint path already exists' });
|
|
}
|
|
res.status(500).json({ error: 'Internal server error' });
|
|
}
|
|
};
|