From 4164964fd1da73c395888ee7b6839e5410288c79 Mon Sep 17 00:00:00 2001 From: GEgorov Date: Tue, 21 Oct 2025 12:59:45 +0300 Subject: [PATCH] modified: backend/src/services/AqlExecutor.ts new file: backend/src/services/AqlExecutor.ts.backup --- backend/src/services/AqlExecutor.ts | 17 +- backend/src/services/AqlExecutor.ts.backup | 260 +++++++++++++++++++++ 2 files changed, 273 insertions(+), 4 deletions(-) create mode 100644 backend/src/services/AqlExecutor.ts.backup diff --git a/backend/src/services/AqlExecutor.ts b/backend/src/services/AqlExecutor.ts index 84a39ea..b17f46f 100644 --- a/backend/src/services/AqlExecutor.ts +++ b/backend/src/services/AqlExecutor.ts @@ -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; diff --git a/backend/src/services/AqlExecutor.ts.backup b/backend/src/services/AqlExecutor.ts.backup new file mode 100644 index 0000000..84a39ea --- /dev/null +++ b/backend/src/services/AqlExecutor.ts.backup @@ -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; + parameters?: Record; +} + +export class AqlExecutor { + /** + * Получает конфигурацию AQL базы данных + */ + private async getDatabaseConfig(databaseId: string): Promise { + 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 { + 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, requestParams: Record): string { + const processedParams: Record = {}; + + 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 { + 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 = { + '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();