modified: backend/src/services/DatabasePoolManager.ts

modified:   backend/src/services/ScriptExecutor.ts
	modified:   backend/src/types/index.ts
	modified:   frontend/src/components/CodeEditor.tsx
	modified:   frontend/src/components/EndpointModal.tsx
	deleted:    frontend/src/pages/Databases.tsx
	modified:   frontend/src/pages/Settings.tsx
	modified:   frontend/src/types/index.ts
This commit is contained in:
GEgorov
2025-10-07 20:16:53 +03:00
parent 65e4f5b423
commit c85fa20634
8 changed files with 311 additions and 562 deletions

View File

@@ -3,7 +3,7 @@ import Editor from '@monaco-editor/react';
interface CodeEditorProps {
value: string;
onChange: (value: string) => void;
language: 'javascript' | 'python';
language: 'javascript' | 'python' | 'json';
height?: string;
}

View File

@@ -377,38 +377,43 @@ export default function EndpointModal({
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">AQL Body (JSON)</label>
<label className="block text-sm font-medium text-gray-700 mb-2">AQL Body (JSON)</label>
<div className="mb-2 p-2 bg-blue-50 border border-blue-200 rounded text-xs text-blue-700">
<div>Используйте <code className="bg-blue-100 px-1 rounded">$параметр</code> в JSON для подстановки</div>
</div>
<textarea
<CodeEditor
value={formData.aql_body}
onChange={(e) => setFormData({ ...formData, aql_body: e.target.value })}
className="input w-full font-mono text-sm"
rows={6}
placeholder='{"aql": "select c from COMPOSITION c where c/uid/value= &apos;$compositionId&apos;"}'
onChange={(value) => setFormData({ ...formData, aql_body: value })}
language="json"
height="150px"
/>
<p className="text-xs text-gray-500 mt-1">
Пример: {`{"aql": "select c from COMPOSITION c where c/uid/value='$compositionId'"}`}
</p>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">AQL Query Parameters (JSON)</label>
<label className="block text-sm font-medium text-gray-700 mb-2">AQL Query Parameters (JSON)</label>
<div className="mb-2 p-2 bg-blue-50 border border-blue-200 rounded text-xs text-blue-700">
<div>Формат: <code className="bg-blue-100 px-1 rounded">{`{"key": "value", "CompositionLink": "$linkValue"}`}</code></div>
</div>
<textarea
<CodeEditor
value={typeof formData.aql_query_params === 'string' ? formData.aql_query_params : JSON.stringify(formData.aql_query_params, null, 2)}
onChange={(e) => {
onChange={(value) => {
try {
const parsed = JSON.parse(e.target.value);
const parsed = JSON.parse(value);
setFormData({ ...formData, aql_query_params: parsed });
} catch {
// Игнорируем невалидный JSON - не обновляем состояние
// Сохраняем невалидный JSON как строку для последующего редактирования
setFormData({ ...formData, aql_query_params: value as any });
}
}}
className="input w-full font-mono text-sm"
rows={4}
placeholder='{"CompositionLink": "ehr:compositions/$compositionId"}'
language="json"
height="120px"
/>
<p className="text-xs text-gray-500 mt-1">
Пример: {`{"CompositionLink": "ehr:compositions/$compositionId"}`}
</p>
</div>
</>
) : formData.execution_type === 'sql' ? (
@@ -450,7 +455,15 @@ export default function EndpointModal({
<button
type="button"
onClick={() => {
const newQueries = [...formData.script_queries, { name: '', sql: '', database_id: '' }];
const newQueries = [...formData.script_queries, {
name: '',
sql: '',
database_id: '',
aql_method: 'GET' as 'GET' | 'POST' | 'PUT' | 'DELETE',
aql_endpoint: '',
aql_body: '',
aql_query_params: {}
}];
setFormData({ ...formData, script_queries: newQueries });
setEditingQueryIndex(newQueries.length - 1);
}}
@@ -469,9 +482,14 @@ export default function EndpointModal({
<div className="flex items-center gap-2 mb-1">
<code className="text-sm font-semibold text-gray-900">{query.name || 'Безымянный запрос'}</code>
{query.database_id && (
<span className="text-xs text-gray-500">
{databases.find(db => db.id === query.database_id)?.name || 'БД не выбрана'}
</span>
<>
<span className="text-xs text-gray-500">
{databases.find(db => db.id === query.database_id)?.name || 'БД не выбрана'}
</span>
{databases.find(db => db.id === query.database_id)?.type === 'aql' && (
<span className="text-xs bg-purple-100 text-purple-700 px-2 py-0.5 rounded">AQL</span>
)}
</>
)}
</div>
{query.sql && (
@@ -479,6 +497,11 @@ export default function EndpointModal({
{query.sql.substring(0, 100)}{query.sql.length > 100 ? '...' : ''}
</div>
)}
{query.aql_endpoint && (
<div className="text-xs text-gray-600 font-mono bg-purple-50 p-2 rounded mt-1">
<span className="text-purple-700 font-semibold">{query.aql_method}</span> {query.aql_endpoint}
</div>
)}
</div>
<div className="flex gap-2 ml-4">
<button
@@ -666,22 +689,112 @@ export default function EndpointModal({
))}
</select>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">SQL Запрос</label>
<div className="mb-2 p-2 bg-blue-50 border border-blue-200 rounded text-xs text-blue-700">
<div><strong>Совет:</strong> Используйте <code className="bg-blue-100 px-1 rounded">$имяПараметра</code> для параметров из запроса или дополнительных параметров из скрипта.</div>
</div>
<SqlEditor
value={formData.script_queries[editingQueryIndex]?.sql || ''}
onChange={(value) => {
const newQueries = [...formData.script_queries];
newQueries[editingQueryIndex].sql = value;
setFormData({ ...formData, script_queries: newQueries });
}}
databaseId={formData.script_queries[editingQueryIndex]?.database_id || ''}
height="400px"
/>
</div>
{/* Определяем тип выбранной базы данных */}
{(() => {
const selectedDb = databases.find(db => db.id === formData.script_queries[editingQueryIndex]?.database_id);
const isAql = selectedDb?.type === 'aql';
return isAql ? (
<>
{/* AQL Fields */}
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">AQL HTTP Метод</label>
<select
value={formData.script_queries[editingQueryIndex]?.aql_method || 'GET'}
onChange={(e) => {
const newQueries = [...formData.script_queries];
newQueries[editingQueryIndex].aql_method = e.target.value as any;
setFormData({ ...formData, script_queries: newQueries });
}}
className="input w-full"
>
<option value="GET">GET</option>
<option value="POST">POST</option>
<option value="PUT">PUT</option>
<option value="DELETE">DELETE</option>
</select>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">AQL Endpoint URL</label>
<div className="mb-2 p-2 bg-blue-50 border border-blue-200 rounded text-xs text-blue-700">
<div>Используйте <code className="bg-blue-100 px-1 rounded">$параметр</code> для подстановки</div>
</div>
<input
type="text"
required
value={formData.script_queries[editingQueryIndex]?.aql_endpoint || ''}
onChange={(e) => {
const newQueries = [...formData.script_queries];
newQueries[editingQueryIndex].aql_endpoint = e.target.value;
setFormData({ ...formData, script_queries: newQueries });
}}
className="input w-full"
placeholder="/query"
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">AQL Body (JSON)</label>
<div className="mb-2 p-2 bg-blue-50 border border-blue-200 rounded text-xs text-blue-700">
<div>Используйте <code className="bg-blue-100 px-1 rounded">$параметр</code> в JSON для подстановки</div>
</div>
<CodeEditor
value={formData.script_queries[editingQueryIndex]?.aql_body || ''}
onChange={(value) => {
const newQueries = [...formData.script_queries];
newQueries[editingQueryIndex].aql_body = value;
setFormData({ ...formData, script_queries: newQueries });
}}
language="json"
height="200px"
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">AQL Query Parameters (JSON)</label>
<CodeEditor
value={typeof formData.script_queries[editingQueryIndex]?.aql_query_params === 'string'
? formData.script_queries[editingQueryIndex]?.aql_query_params
: JSON.stringify(formData.script_queries[editingQueryIndex]?.aql_query_params || {}, null, 2)}
onChange={(value) => {
const newQueries = [...formData.script_queries];
try {
const parsed = JSON.parse(value);
newQueries[editingQueryIndex].aql_query_params = parsed;
} catch {
newQueries[editingQueryIndex].aql_query_params = value as any;
}
setFormData({ ...formData, script_queries: newQueries });
}}
language="json"
height="150px"
/>
</div>
</>
) : (
<>
{/* SQL Field */}
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">SQL Запрос</label>
<div className="mb-2 p-2 bg-blue-50 border border-blue-200 rounded text-xs text-blue-700">
<div><strong>Совет:</strong> Используйте <code className="bg-blue-100 px-1 rounded">$имяПараметра</code> для параметров из запроса или дополнительных параметров из скрипта.</div>
</div>
<SqlEditor
value={formData.script_queries[editingQueryIndex]?.sql || ''}
onChange={(value) => {
const newQueries = [...formData.script_queries];
newQueries[editingQueryIndex].sql = value;
setFormData({ ...formData, script_queries: newQueries });
}}
databaseId={formData.script_queries[editingQueryIndex]?.database_id || ''}
height="400px"
/>
</div>
</>
);
})()}
</div>
<div className="p-6 border-t border-gray-200 flex gap-3">
<button