modified: backend/src/services/AqlExecutor.ts
new file: backend/src/services/AqlExecutor.ts.backup
This commit is contained in:
@@ -140,12 +140,21 @@ export class AqlExecutor {
|
||||
console.log('Body:', processedBody || '(no body)');
|
||||
console.log('===================\n');
|
||||
|
||||
// Выполняем HTTP запрос
|
||||
const response = await fetch(fullUrl, {
|
||||
// Формируем опции для fetch
|
||||
// ВАЖНО: body не должен передаваться для GET запросов
|
||||
// и если он undefined, иначе fetch может изменить метод на GET
|
||||
const fetchOptions: RequestInit = {
|
||||
method: config.method,
|
||||
headers,
|
||||
body: processedBody,
|
||||
});
|
||||
};
|
||||
|
||||
// Добавляем body только если он есть и метод поддерживает body
|
||||
if (processedBody && config.method !== 'GET') {
|
||||
fetchOptions.body = processedBody;
|
||||
}
|
||||
|
||||
// Выполняем HTTP запрос
|
||||
const response = await fetch(fullUrl, fetchOptions);
|
||||
|
||||
const executionTime = Date.now() - startTime;
|
||||
|
||||
|
||||
260
backend/src/services/AqlExecutor.ts.backup
Normal file
260
backend/src/services/AqlExecutor.ts.backup
Normal file
@@ -0,0 +1,260 @@
|
||||
import { QueryResult, DatabaseConfig } from '../types';
|
||||
import { mainPool } from '../config/database';
|
||||
|
||||
interface AqlRequestConfig {
|
||||
method: 'GET' | 'POST' | 'PUT' | 'DELETE';
|
||||
endpoint: string;
|
||||
body?: string;
|
||||
queryParams?: Record<string, string>;
|
||||
parameters?: Record<string, any>;
|
||||
}
|
||||
|
||||
export class AqlExecutor {
|
||||
/**
|
||||
* Получает конфигурацию AQL базы данных
|
||||
*/
|
||||
private async getDatabaseConfig(databaseId: string): Promise<DatabaseConfig | null> {
|
||||
const result = await mainPool.query(
|
||||
'SELECT * FROM databases WHERE id = $1 AND type = $2',
|
||||
[databaseId, 'aql']
|
||||
);
|
||||
|
||||
if (result.rows.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return result.rows[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Заменяет параметры вида $paramName в строке на значения из объекта parameters
|
||||
*/
|
||||
private replaceParameters(template: string, parameters: Record<string, any>): string {
|
||||
let result = template;
|
||||
|
||||
// Находим все параметры вида $paramName
|
||||
const paramMatches = template.match(/\$\w+/g) || [];
|
||||
const uniqueParams = [...new Set(paramMatches.map(p => p.substring(1)))];
|
||||
|
||||
uniqueParams.forEach((paramName) => {
|
||||
const regex = new RegExp(`\\$${paramName}\\b`, 'g');
|
||||
const value = parameters[paramName];
|
||||
|
||||
if (value !== undefined && value !== null) {
|
||||
// Для строк в JSON нужны кавычки, для чисел - нет
|
||||
const replacement = typeof value === 'string'
|
||||
? value
|
||||
: JSON.stringify(value);
|
||||
result = result.replace(regex, replacement);
|
||||
} else {
|
||||
result = result.replace(regex, '');
|
||||
}
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Строит query string из объекта параметров
|
||||
*/
|
||||
private buildQueryString(params: Record<string, string>, requestParams: Record<string, any>): string {
|
||||
const processedParams: Record<string, string> = {};
|
||||
|
||||
for (const [key, value] of Object.entries(params)) {
|
||||
processedParams[key] = this.replaceParameters(value, requestParams);
|
||||
}
|
||||
|
||||
const queryString = new URLSearchParams(processedParams).toString();
|
||||
return queryString ? `?${queryString}` : '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Выполняет AQL запрос
|
||||
*/
|
||||
async executeAqlQuery(
|
||||
databaseId: string,
|
||||
config: AqlRequestConfig
|
||||
): Promise<QueryResult> {
|
||||
const startTime = Date.now();
|
||||
|
||||
try {
|
||||
// Получаем конфигурацию БД
|
||||
const dbConfig = await this.getDatabaseConfig(databaseId);
|
||||
|
||||
if (!dbConfig) {
|
||||
throw new Error(`AQL database with id ${databaseId} not found or not configured`);
|
||||
}
|
||||
|
||||
if (!dbConfig.aql_base_url) {
|
||||
throw new Error(`AQL base URL not configured for database ${databaseId}`);
|
||||
}
|
||||
|
||||
const parameters = config.parameters || {};
|
||||
|
||||
// Обрабатываем endpoint с параметрами
|
||||
const processedEndpoint = this.replaceParameters(config.endpoint, parameters);
|
||||
|
||||
// Обрабатываем query параметры
|
||||
const queryString = config.queryParams
|
||||
? this.buildQueryString(config.queryParams, parameters)
|
||||
: '';
|
||||
|
||||
// Формируем полный URL
|
||||
const fullUrl = `${dbConfig.aql_base_url}${processedEndpoint}${queryString}`;
|
||||
|
||||
// Обрабатываем body с параметрами
|
||||
let processedBody: string | undefined;
|
||||
if (config.body) {
|
||||
processedBody = this.replaceParameters(config.body, parameters);
|
||||
}
|
||||
|
||||
// Формируем заголовки
|
||||
const headers: Record<string, string> = {
|
||||
'Content-Type': 'application/json',
|
||||
'Accept': 'application/json',
|
||||
};
|
||||
|
||||
// Добавляем аутентификацию
|
||||
if (dbConfig.aql_auth_type === 'basic' && dbConfig.aql_auth_value) {
|
||||
headers['Authorization'] = `Basic ${dbConfig.aql_auth_value}`;
|
||||
} else if (dbConfig.aql_auth_type === 'bearer' && dbConfig.aql_auth_value) {
|
||||
headers['Authorization'] = `Bearer ${dbConfig.aql_auth_value}`;
|
||||
} else if (dbConfig.aql_auth_type === 'custom' && dbConfig.aql_auth_value) {
|
||||
headers['Authorization'] = dbConfig.aql_auth_value;
|
||||
}
|
||||
|
||||
// Добавляем кастомные заголовки из конфигурации БД
|
||||
if (dbConfig.aql_headers) {
|
||||
const customHeaders = typeof dbConfig.aql_headers === 'string'
|
||||
? JSON.parse(dbConfig.aql_headers)
|
||||
: dbConfig.aql_headers;
|
||||
|
||||
Object.assign(headers, customHeaders);
|
||||
}
|
||||
|
||||
// Логируем запрос
|
||||
console.log('\n=== AQL Request ===');
|
||||
console.log('URL:', fullUrl);
|
||||
console.log('Method:', config.method);
|
||||
console.log('Headers:', JSON.stringify(headers, null, 2));
|
||||
console.log('Body:', processedBody || '(no body)');
|
||||
console.log('===================\n');
|
||||
|
||||
// Выполняем HTTP запрос
|
||||
const response = await fetch(fullUrl, {
|
||||
method: config.method,
|
||||
headers,
|
||||
body: processedBody,
|
||||
});
|
||||
|
||||
const executionTime = Date.now() - startTime;
|
||||
|
||||
// Проверяем статус ответа
|
||||
if (!response.ok) {
|
||||
const errorText = await response.text();
|
||||
console.log('\n=== AQL Error Response ===');
|
||||
console.log('Status:', response.status);
|
||||
console.log('Response:', errorText);
|
||||
console.log('==========================\n');
|
||||
throw new Error(`AQL API error (${response.status}): ${errorText}`);
|
||||
}
|
||||
|
||||
// Обрабатываем пустой ответ (204 No Content)
|
||||
if (response.status === 204) {
|
||||
console.log('\n=== AQL Response ===');
|
||||
console.log('Status: 204 (No Content)');
|
||||
console.log('====================\n');
|
||||
return {
|
||||
rows: [],
|
||||
rowCount: 0,
|
||||
executionTime,
|
||||
};
|
||||
}
|
||||
|
||||
// Парсим JSON ответ
|
||||
const responseText = await response.text();
|
||||
console.log('\n=== AQL Response ===');
|
||||
console.log('Status:', response.status);
|
||||
console.log('Raw Response:', responseText);
|
||||
console.log('====================\n');
|
||||
|
||||
// Если ответ пустой, возвращаем пустой результат
|
||||
if (!responseText || responseText.trim() === '') {
|
||||
return {
|
||||
rows: [],
|
||||
rowCount: 0,
|
||||
executionTime,
|
||||
};
|
||||
}
|
||||
|
||||
let data;
|
||||
try {
|
||||
data = JSON.parse(responseText);
|
||||
} catch (e) {
|
||||
console.error('Failed to parse JSON response:', e);
|
||||
throw new Error(`Invalid JSON response: ${responseText.substring(0, 200)}`);
|
||||
}
|
||||
|
||||
// Возвращаем данные как есть
|
||||
return {
|
||||
rows: data,
|
||||
rowCount: Array.isArray(data) ? data.length : (data ? 1 : 0),
|
||||
executionTime,
|
||||
};
|
||||
} catch (error: any) {
|
||||
console.error('AQL Execution Error:', error);
|
||||
throw new Error(`AQL Error: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Тестирует AQL запрос
|
||||
*/
|
||||
async testAqlQuery(
|
||||
databaseId: string,
|
||||
config: AqlRequestConfig
|
||||
): Promise<{ success: boolean; error?: string }> {
|
||||
try {
|
||||
await this.executeAqlQuery(databaseId, config);
|
||||
return { success: true };
|
||||
} catch (error: any) {
|
||||
return { success: false, error: error.message };
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Тестирует подключение к AQL базе
|
||||
*/
|
||||
async testConnection(databaseId: string): Promise<{ success: boolean; error?: string }> {
|
||||
try {
|
||||
const dbConfig = await this.getDatabaseConfig(databaseId);
|
||||
|
||||
if (!dbConfig) {
|
||||
return { success: false, error: 'Database not found' };
|
||||
}
|
||||
|
||||
if (!dbConfig.aql_base_url) {
|
||||
return { success: false, error: 'AQL base URL not configured' };
|
||||
}
|
||||
|
||||
// Пробуем выполнить простой запрос для проверки соединения
|
||||
const response = await fetch(dbConfig.aql_base_url, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Accept': 'application/json',
|
||||
},
|
||||
});
|
||||
|
||||
if (response.ok || response.status === 404) {
|
||||
// 404 тоже OK - это значит что сервер доступен
|
||||
return { success: true };
|
||||
} else {
|
||||
return { success: false, error: `HTTP ${response.status}` };
|
||||
}
|
||||
} catch (error: any) {
|
||||
return { success: false, error: error.message };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const aqlExecutor = new AqlExecutor();
|
||||
Reference in New Issue
Block a user