new file: .claude/settings.local.json

new file:   .gitignore
	new file:   backend/.env.example
	new file:   backend/.gitignore
	new file:   backend/ecosystem.config.js
	new file:   backend/nodemon.json
	new file:   backend/package-lock.json
	new file:   backend/package.json
	new file:   backend/src/config/database.ts
	new file:   backend/src/config/dynamicSwagger.ts
	new file:   backend/src/config/environment.ts
	new file:   backend/src/config/swagger.ts
	new file:   backend/src/controllers/apiKeyController.ts
	new file:   backend/src/controllers/authController.ts
	new file:   backend/src/controllers/databaseController.ts
	new file:   backend/src/controllers/databaseManagementController.ts
	new file:   backend/src/controllers/dynamicApiController.ts
	new file:   backend/src/controllers/endpointController.ts
	new file:   backend/src/controllers/folderController.ts
	new file:   backend/src/controllers/logsController.ts
	new file:   backend/src/controllers/userController.ts
	new file:   backend/src/middleware/apiKey.ts
	new file:   backend/src/middleware/auth.ts
	new file:   backend/src/middleware/logging.ts
	new file:   backend/src/migrations/001_initial_schema.sql
	new file:   backend/src/migrations/002_add_logging.sql
	new file:   backend/src/migrations/003_add_scripting.sql
	new file:   backend/src/migrations/004_add_superadmin.sql
	new file:   backend/src/migrations/run.ts
	new file:   backend/src/migrations/seed.ts
	new file:   backend/src/routes/apiKeys.ts
	new file:   backend/src/routes/auth.ts
	new file:   backend/src/routes/databaseManagement.ts
	new file:   backend/src/routes/databases.ts
	new file:   backend/src/routes/dynamic.ts
	new file:   backend/src/routes/endpoints.ts
	new file:   backend/src/routes/folders.ts
	new file:   backend/src/routes/logs.ts
	new file:   backend/src/routes/users.ts
	new file:   backend/src/server.ts
	new file:   backend/src/services/DatabasePoolManager.ts
	new file:   backend/src/services/ScriptExecutor.ts
	new file:   backend/src/services/SqlExecutor.ts
	new file:   backend/src/types/index.ts
	new file:   backend/tsconfig.json
	new file:   frontend/.gitignore
	new file:   frontend/index.html
	new file:   frontend/nginx.conf
	new file:   frontend/package-lock.json
	new file:   frontend/package.json
	new file:   frontend/postcss.config.js
	new file:   frontend/src/App.tsx
	new file:   frontend/src/components/CodeEditor.tsx
This commit is contained in:
GEgorov
2025-10-07 00:04:04 +03:00
commit 8943f5a070
79 changed files with 17032 additions and 0 deletions

View File

@@ -0,0 +1,317 @@
import { Response } from 'express';
import { AuthRequest } from '../middleware/auth';
import { mainPool } from '../config/database';
import { v4 as uuidv4 } from 'uuid';
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,
} = 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' });
}
}
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
)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15)
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 || []),
]
);
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,
} = 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,
updated_at = CURRENT_TIMESTAMP
WHERE id = $15
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,
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
} = req.body;
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 || []);
res.json({
success: true,
data: result.rows,
rowCount: result.rowCount,
executionTime: result.executionTime,
});
} 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 || [],
});
res.json({
success: true,
data: scriptResult.data || scriptResult,
rowCount: scriptResult.rowCount || (Array.isArray(scriptResult.data) ? scriptResult.data.length : 0),
executionTime: scriptResult.executionTime || 0,
});
} else {
return res.status(400).json({ error: 'Invalid execution_type' });
}
} catch (error: any) {
res.status(400).json({
success: false,
error: error.message,
});
}
};