Compare commits

...

2 Commits

Author SHA1 Message Date
e8aab43a13 Merge branch 'main' of ssh://gitea.esh-service.ru:2222/public/api_builder 2026-03-01 19:32:37 +03:00
659ebf71ea 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
2026-03-01 19:32:23 +03:00
7 changed files with 1360 additions and 1263 deletions

View File

@@ -81,6 +81,7 @@ export interface EndpointParameter {
required: boolean; required: boolean;
default_value?: any; default_value?: any;
description?: string; description?: string;
example_value?: string;
in: 'query' | 'body' | 'path'; in: 'query' | 'body' | 'path';
} }

View File

@@ -9,6 +9,7 @@ import Sidebar from '@/components/Sidebar';
import Login from '@/pages/Login'; import Login from '@/pages/Login';
import Dashboard from '@/pages/Dashboard'; import Dashboard from '@/pages/Dashboard';
import Endpoints from '@/pages/Endpoints'; import Endpoints from '@/pages/Endpoints';
import EndpointEditor from '@/pages/EndpointEditor';
import ApiKeys from '@/pages/ApiKeys'; import ApiKeys from '@/pages/ApiKeys';
import Folders from '@/pages/Folders'; import Folders from '@/pages/Folders';
import Logs from '@/pages/Logs'; import Logs from '@/pages/Logs';
@@ -97,6 +98,26 @@ function App() {
</PrivateRoute> </PrivateRoute>
} }
/> />
<Route
path="/endpoints/new"
element={
<PrivateRoute>
<Layout>
<EndpointEditor />
</Layout>
</PrivateRoute>
}
/>
<Route
path="/endpoints/:id"
element={
<PrivateRoute>
<Layout>
<EndpointEditor />
</Layout>
</PrivateRoute>
}
/>
<Route <Route
path="/endpoints" path="/endpoints"
element={ element={

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,18 +1,17 @@
import { useState, useRef } from 'react'; import { useState, useRef } from 'react';
import { useNavigate } from 'react-router-dom';
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
import { endpointsApi, databasesApi } from '@/services/api'; import { endpointsApi } from '@/services/api';
import { Endpoint, ImportPreviewResponse } from '@/types'; import { ImportPreviewResponse } from '@/types';
import { Plus, Search, Edit2, Trash2, Download, Upload } from 'lucide-react'; import { Plus, Search, Edit2, Trash2, Download, Upload } from 'lucide-react';
import toast from 'react-hot-toast'; import toast from 'react-hot-toast';
import EndpointModal from '@/components/EndpointModal';
import ImportEndpointModal from '@/components/ImportEndpointModal'; import ImportEndpointModal from '@/components/ImportEndpointModal';
import Dialog from '@/components/Dialog'; import Dialog from '@/components/Dialog';
export default function Endpoints() { export default function Endpoints() {
const queryClient = useQueryClient(); const queryClient = useQueryClient();
const navigate = useNavigate();
const [search, setSearch] = useState(''); const [search, setSearch] = useState('');
const [showModal, setShowModal] = useState(false);
const [editingEndpoint, setEditingEndpoint] = useState<Endpoint | null>(null);
const [showImportModal, setShowImportModal] = useState(false); const [showImportModal, setShowImportModal] = useState(false);
const [importFile, setImportFile] = useState<File | null>(null); const [importFile, setImportFile] = useState<File | null>(null);
const [importPreview, setImportPreview] = useState<ImportPreviewResponse | null>(null); const [importPreview, setImportPreview] = useState<ImportPreviewResponse | null>(null);
@@ -35,11 +34,6 @@ export default function Endpoints() {
queryFn: () => endpointsApi.getAll(search).then(res => res.data), queryFn: () => endpointsApi.getAll(search).then(res => res.data),
}); });
const { data: databases } = useQuery({
queryKey: ['databases'],
queryFn: () => databasesApi.getAll().then(res => res.data),
});
const deleteMutation = useMutation({ const deleteMutation = useMutation({
mutationFn: (id: string) => endpointsApi.delete(id), mutationFn: (id: string) => endpointsApi.delete(id),
onSuccess: () => { onSuccess: () => {
@@ -61,16 +55,6 @@ export default function Endpoints() {
}); });
}; };
const handleEdit = (endpoint: Endpoint) => {
setEditingEndpoint(endpoint);
setShowModal(true);
};
const handleCreate = () => {
setEditingEndpoint(null);
setShowModal(true);
};
const handleExport = async (endpointId: string, endpointName: string) => { const handleExport = async (endpointId: string, endpointName: string) => {
try { try {
const response = await endpointsApi.exportEndpoint(endpointId); const response = await endpointsApi.exportEndpoint(endpointId);
@@ -129,7 +113,7 @@ export default function Endpoints() {
<Upload size={20} /> <Upload size={20} />
Импорт Импорт
</button> </button>
<button onClick={handleCreate} className="btn btn-primary flex items-center gap-2"> <button onClick={() => navigate('/endpoints/new')} className="btn btn-primary flex items-center gap-2">
<Plus size={20} /> <Plus size={20} />
Новый эндпоинт Новый эндпоинт
</button> </button>
@@ -203,7 +187,7 @@ export default function Endpoints() {
<Download size={18} className="text-gray-600" /> <Download size={18} className="text-gray-600" />
</button> </button>
<button <button
onClick={() => handleEdit(endpoint)} onClick={() => navigate(`/endpoints/${endpoint.id}`)}
className="p-2 hover:bg-gray-100 rounded-lg transition-colors" className="p-2 hover:bg-gray-100 rounded-lg transition-colors"
title="Редактировать" title="Редактировать"
> >
@@ -229,14 +213,6 @@ export default function Endpoints() {
</div> </div>
)} )}
{showModal && (
<EndpointModal
endpoint={editingEndpoint}
databases={databases || []}
onClose={() => setShowModal(false)}
/>
)}
{showImportModal && importPreview && importFile && ( {showImportModal && importPreview && importFile && (
<ImportEndpointModal <ImportEndpointModal
preview={importPreview} preview={importPreview}

View File

@@ -1,18 +1,17 @@
import { useState } from 'react'; import { useState } from 'react';
import { useNavigate } from 'react-router-dom';
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
import { foldersApi, endpointsApi, databasesApi } from '@/services/api'; import { foldersApi, endpointsApi } from '@/services/api';
import { Folder, Endpoint } from '@/types'; import { Folder, Endpoint } from '@/types';
import { Plus, Edit2, Trash2, Folder as FolderIcon, FolderOpen, FileCode, ChevronRight, ChevronDown } from 'lucide-react'; import { Plus, Edit2, Trash2, Folder as FolderIcon, FolderOpen, FileCode, ChevronRight, ChevronDown } from 'lucide-react';
import toast from 'react-hot-toast'; import toast from 'react-hot-toast';
import EndpointModal from '@/components/EndpointModal';
import Dialog from '@/components/Dialog'; import Dialog from '@/components/Dialog';
export default function Folders() { export default function Folders() {
const queryClient = useQueryClient(); const queryClient = useQueryClient();
const navigate = useNavigate();
const [showFolderModal, setShowFolderModal] = useState(false); const [showFolderModal, setShowFolderModal] = useState(false);
const [showEndpointModal, setShowEndpointModal] = useState(false);
const [editingFolder, setEditingFolder] = useState<Folder | null>(null); const [editingFolder, setEditingFolder] = useState<Folder | null>(null);
const [editingEndpoint, setEditingEndpoint] = useState<Endpoint | null>(null);
const [selectedFolderId, setSelectedFolderId] = useState<string | null>(null); const [selectedFolderId, setSelectedFolderId] = useState<string | null>(null);
const [expandedFolders, setExpandedFolders] = useState<Set<string>>(new Set()); const [expandedFolders, setExpandedFolders] = useState<Set<string>>(new Set());
const [dialog, setDialog] = useState<{ const [dialog, setDialog] = useState<{
@@ -38,11 +37,6 @@ export default function Folders() {
queryFn: () => endpointsApi.getAll().then(res => res.data), queryFn: () => endpointsApi.getAll().then(res => res.data),
}); });
const { data: databases } = useQuery({
queryKey: ['databases'],
queryFn: () => databasesApi.getAll().then(res => res.data),
});
const deleteFolderMutation = useMutation({ const deleteFolderMutation = useMutation({
mutationFn: (id: string) => foldersApi.delete(id), mutationFn: (id: string) => foldersApi.delete(id),
onSuccess: () => { onSuccess: () => {
@@ -74,14 +68,11 @@ export default function Folders() {
}; };
const handleCreateEndpoint = (folderId?: string) => { const handleCreateEndpoint = (folderId?: string) => {
setSelectedFolderId(folderId || null); navigate(folderId ? `/endpoints/new?folder=${folderId}` : '/endpoints/new');
setEditingEndpoint(null);
setShowEndpointModal(true);
}; };
const handleEditEndpoint = (endpoint: Endpoint) => { const handleEditEndpoint = (endpoint: Endpoint) => {
setEditingEndpoint(endpoint); navigate(`/endpoints/${endpoint.id}`);
setShowEndpointModal(true);
}; };
const handleDeleteFolder = (id: string) => { const handleDeleteFolder = (id: string) => {
@@ -226,15 +217,6 @@ export default function Folders() {
/> />
)} )}
{showEndpointModal && (
<EndpointModal
endpoint={editingEndpoint}
folderId={selectedFolderId}
databases={databases || []}
onClose={() => setShowEndpointModal(false)}
/>
)}
<Dialog <Dialog
isOpen={dialog.isOpen} isOpen={dialog.isOpen}
onClose={() => setDialog({ ...dialog, isOpen: false })} onClose={() => setDialog({ ...dialog, isOpen: false })}

View File

@@ -41,6 +41,7 @@ export interface EndpointParameter {
required: boolean; required: boolean;
default_value?: any; default_value?: any;
description?: string; description?: string;
example_value?: string;
in: 'query' | 'body' | 'path'; in: 'query' | 'body' | 'path';
} }