modified: backend/src/config/dynamicSwagger.ts

modified:   backend/src/controllers/endpointController.ts
	new file:   backend/src/migrations/009_add_response_schema.sql
	modified:   backend/src/types/index.ts
	modified:   frontend/src/pages/EndpointEditor.tsx
	modified:   frontend/src/types/index.ts
This commit is contained in:
2026-03-13 15:22:32 +03:00
parent b6b7064a41
commit 727c6765f8
6 changed files with 94 additions and 15 deletions

View File

@@ -75,9 +75,17 @@ export default function EndpointEditor() {
aql_query_params: endpointData.aql_query_params || {},
detailed_response: endpointData.detailed_response || false,
});
setResponseSchemaText(
endpointData.response_schema
? JSON.stringify(endpointData.response_schema, null, 2)
: ''
);
}
}, [endpointData]);
const [responseSchemaText, setResponseSchemaText] = useState('');
const [responseSchemaExpanded, setResponseSchemaExpanded] = useState(false);
const [responseSchemaError, setResponseSchemaError] = useState('');
const [editingQueryIndex, setEditingQueryIndex] = useState<number | null>(null);
const [showScriptCodeEditor, setShowScriptCodeEditor] = useState(false);
const [parametersExpanded, setParametersExpanded] = useState(true);
@@ -215,7 +223,17 @@ export default function EndpointEditor() {
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
saveMutation.mutate(formData);
let parsedSchema = null;
if (responseSchemaText.trim()) {
try {
parsedSchema = JSON.parse(responseSchemaText);
} catch {
setResponseSchemaError('Некорректный JSON');
setResponseSchemaExpanded(true);
return;
}
}
saveMutation.mutate({ ...formData, response_schema: parsedSchema });
};
// cURL generator
@@ -738,6 +756,45 @@ export default function EndpointEditor() {
</>
)}
{/* Response schema */}
<div className="border border-gray-200 rounded-lg">
<div
className="flex items-center justify-between p-3 bg-gray-50 cursor-pointer hover:bg-gray-100 rounded-t-lg"
onClick={() => setResponseSchemaExpanded(!responseSchemaExpanded)}
>
<div className="flex items-center gap-2">
{responseSchemaExpanded ? <ChevronUp size={18} /> : <ChevronDown size={18} />}
<label className="text-sm font-medium text-gray-700 cursor-pointer">
Схема ответа 200 (Swagger)
</label>
{responseSchemaText.trim() && (
<span className="px-2 py-0.5 bg-green-100 text-green-700 rounded-full text-xs">задана</span>
)}
{responseSchemaError && (
<span className="px-2 py-0.5 bg-red-100 text-red-700 rounded-full text-xs">{responseSchemaError}</span>
)}
</div>
</div>
{responseSchemaExpanded && (
<div className="p-4 bg-white rounded-b-lg space-y-2">
<p className="text-xs text-gray-500">
JSON Schema в формате OpenAPI для документирования ответа. Если не задана используется схема по умолчанию.
</p>
<textarea
value={responseSchemaText}
onChange={(e) => {
setResponseSchemaText(e.target.value);
setResponseSchemaError('');
}}
className="input w-full font-mono text-xs"
rows={10}
placeholder={'{\n "type": "array",\n "items": {\n "type": "object",\n "properties": {\n "id": { "type": "number" },\n "name": { "type": "string" }\n }\n }\n}'}
spellCheck={false}
/>
</div>
)}
</div>
{/* Checkboxes */}
<div className="flex flex-wrap items-center gap-6">
<label className="flex items-center gap-2">

View File

@@ -81,6 +81,7 @@ export interface Endpoint {
aql_query_params?: Record<string, string>;
// Response format
detailed_response?: boolean;
response_schema?: object | null;
created_at: string;
updated_at: string;
}