From 659ebf71ea8a90916f8b98a9e16aa40d2ae10808 Mon Sep 17 00:00:00 2001 From: eshmeshek Date: Sun, 1 Mar 2026 19:32:23 +0300 Subject: [PATCH] modified: backend/src/types/index.ts modified: frontend/src/App.tsx deleted: frontend/src/components/EndpointModal.tsx new file: frontend/src/pages/EndpointEditor.tsx modified: frontend/src/pages/Endpoints.tsx modified: frontend/src/pages/Folders.tsx modified: frontend/src/types/index.ts --- backend/src/types/index.ts | 1 + frontend/src/App.tsx | 21 + frontend/src/components/EndpointModal.tsx | 1210 ------------------- frontend/src/pages/EndpointEditor.tsx | 1326 +++++++++++++++++++++ frontend/src/pages/Endpoints.tsx | 36 +- frontend/src/pages/Folders.tsx | 28 +- frontend/src/types/index.ts | 1 + 7 files changed, 1360 insertions(+), 1263 deletions(-) delete mode 100644 frontend/src/components/EndpointModal.tsx create mode 100644 frontend/src/pages/EndpointEditor.tsx diff --git a/backend/src/types/index.ts b/backend/src/types/index.ts index acbde92..999d5a1 100644 --- a/backend/src/types/index.ts +++ b/backend/src/types/index.ts @@ -81,6 +81,7 @@ export interface EndpointParameter { required: boolean; default_value?: any; description?: string; + example_value?: string; in: 'query' | 'body' | 'path'; } diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index d5e625d..b8200ff 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -9,6 +9,7 @@ import Sidebar from '@/components/Sidebar'; import Login from '@/pages/Login'; import Dashboard from '@/pages/Dashboard'; import Endpoints from '@/pages/Endpoints'; +import EndpointEditor from '@/pages/EndpointEditor'; import ApiKeys from '@/pages/ApiKeys'; import Folders from '@/pages/Folders'; import Logs from '@/pages/Logs'; @@ -97,6 +98,26 @@ function App() { } /> + + + + + + } + /> + + + + + + } + /> void; -} - -export default function EndpointModal({ - endpoint, - databases, - folderId, - onClose, -}: EndpointModalProps) { - const queryClient = useQueryClient(); - const [formData, setFormData] = useState({ - name: endpoint?.name || '', - description: endpoint?.description || '', - method: endpoint?.method || 'GET', - path: endpoint?.path || '', - database_id: endpoint?.database_id || '', - sql_query: endpoint?.sql_query || '', - parameters: endpoint?.parameters || [], - folder_id: endpoint?.folder_id || folderId || '', - is_public: endpoint?.is_public || false, - enable_logging: endpoint?.enable_logging || false, - execution_type: endpoint?.execution_type || 'sql', - script_language: endpoint?.script_language || 'javascript', - script_code: endpoint?.script_code || '', - script_queries: endpoint?.script_queries || [], - // AQL-specific fields - aql_method: endpoint?.aql_method || 'GET', - aql_endpoint: endpoint?.aql_endpoint || '', - aql_body: endpoint?.aql_body || '', - aql_query_params: endpoint?.aql_query_params || {}, - // Response format - detailed_response: endpoint?.detailed_response || false, - }); - - const [editingQueryIndex, setEditingQueryIndex] = useState(null); - const [showScriptCodeEditor, setShowScriptCodeEditor] = useState(false); - const [parametersExpanded, setParametersExpanded] = useState(true); - const [queriesExpanded, setQueriesExpanded] = useState(true); - const [testResult, setTestResult] = useState(null); - const [activeResultTab, setActiveResultTab] = useState<'data' | 'logs' | 'queries'>('data'); - - // Определяем тип выбранной базы данных - const selectedDatabase = databases.find(db => db.id === formData.database_id); - const isAqlDatabase = selectedDatabase?.type === 'aql'; - - const saveMutation = useMutation({ - mutationFn: (data: any) => - endpoint ? endpointsApi.update(endpoint.id, data) : endpointsApi.create(data), - onSuccess: () => { - queryClient.invalidateQueries({ queryKey: ['endpoints'] }); - toast.success(endpoint ? 'Эндпоинт обновлен' : 'Эндпоинт создан'); - onClose(); - }, - onError: () => toast.error('Не удалось сохранить эндпоинт'), - }); - - // Restore test params and result from localStorage - const storageKey = endpoint?.id ? `test_${endpoint.id}` : null; - const [testParams, setTestParams] = useState(() => { - if (storageKey) { - try { - const saved = localStorage.getItem(storageKey); - if (saved) return JSON.parse(saved).testParams || {}; - } catch {} - } - return {}; - }); - - // Restore testResult from localStorage on mount - useEffect(() => { - if (storageKey) { - try { - const saved = localStorage.getItem(storageKey); - if (saved) { - const parsed = JSON.parse(saved); - if (parsed.testResult) setTestResult(parsed.testResult); - } - } catch {} - } - }, [storageKey]); - - // Save testParams and testResult to localStorage - useEffect(() => { - if (storageKey) { - try { - localStorage.setItem(storageKey, JSON.stringify({ testParams, testResult })); - } catch {} - } - }, [storageKey, testParams, testResult]); - - const testMutation = useMutation({ - mutationFn: () => { - // Собираем тестовые значения параметров в массив в правильном порядке - const paramValues = formData.parameters.map((param: any) => { - const value = testParams[param.name]; - if (value === undefined || value === '') return null; - - // Преобразуем тип если нужно - switch (param.type) { - case 'number': - return Number(value); - case 'boolean': - return value === 'true' || value === true; - default: - return value; - } - }); - - if (formData.execution_type === 'aql') { - return endpointsApi.test({ - database_id: formData.database_id || '', - execution_type: 'aql', - aql_method: formData.aql_method || 'GET', - aql_endpoint: formData.aql_endpoint || '', - aql_body: formData.aql_body || '', - aql_query_params: typeof formData.aql_query_params === 'string' ? {} : formData.aql_query_params || {}, - parameters: paramValues, - endpoint_parameters: formData.parameters, - } as any); - } else if (formData.execution_type === 'script') { - // Для скриптов используем database_id из первого запроса или пустую строку - const scriptQueries = formData.script_queries || []; - const firstDbId = scriptQueries.length > 0 ? scriptQueries[0].database_id : ''; - return endpointsApi.test({ - database_id: firstDbId || '', - execution_type: 'script', - script_language: formData.script_language || 'javascript', - script_code: formData.script_code || '', - script_queries: scriptQueries, - parameters: paramValues, - endpoint_parameters: formData.parameters, - }); - } else { - return endpointsApi.test({ - database_id: formData.database_id || '', - execution_type: 'sql', - sql_query: formData.sql_query || '', - parameters: paramValues, - endpoint_parameters: formData.parameters, - }); - } - }, - onSuccess: (response) => { - setTestResult(response.data); - setActiveResultTab('data'); - }, - onError: (error: any) => { - const errorData = error.response?.data; - setTestResult({ - success: false, - error: errorData?.error || error.message || 'Ошибка тестирования запроса', - detail: errorData?.detail, - hint: errorData?.hint, - logs: errorData?.logs || [], - queries: errorData?.queries || [], - }); - setActiveResultTab('data'); - }, - }); - - const handleSubmit = (e: React.FormEvent) => { - e.preventDefault(); - saveMutation.mutate(formData); - }; - - return ( - <> -
-
-
-

- {endpoint ? 'Редактировать эндпоинт' : 'Создать новый эндпоинт'} -

-
- -
-
-
- - setFormData({ ...formData, name: e.target.value })} - className="input w-full" - /> -
-
- - -
-
- -
- -