feat: initial microservices project structure
Services: - api: FastAPI gateway with JWT auth, async endpoints, WebSocket - worker-gpu: CUDA sentence-transformers, FAISS IVFFlat, Ollama LLM - worker-indexer: Winnowing+MinHash plagiarism detection, PDF/DOCX extraction - worker-notifier: SMTP email notifications - worker-gost: GOST 7.1-2003 and GOST R 7.0.5-2008 formatting Infrastructure: - docker-compose.yml (production) + docker-compose.dev.yml (hot reload) - Nginx reverse proxy + WebSocket support - PostgreSQL 16 with Alembic migrations - Elasticsearch 8 with Russian/English analyzers - MinIO, RabbitMQ, Redis, Ollama Frontend: - React 18 + Vite + TypeScript + TailwindCSS + Zustand + React Query v5 - 9 pages: Home, Search, Cabinet, Task, Check, Bibliography, Pricing, Login, Register Scripts: - Parser stubs: OpenAlex, КиберЛенинка, arXiv (Phase 0 - to be filled) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
80
services/frontend/src/api/client.ts
Normal file
80
services/frontend/src/api/client.ts
Normal file
@@ -0,0 +1,80 @@
|
||||
import axios from 'axios';
|
||||
import { useAuthStore } from '../store/auth';
|
||||
|
||||
export const api = axios.create({
|
||||
baseURL: import.meta.env.VITE_API_URL || '/api',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
timeout: 30000,
|
||||
});
|
||||
|
||||
// Добавить JWT токен к каждому запросу
|
||||
api.interceptors.request.use((config) => {
|
||||
const token = useAuthStore.getState().token;
|
||||
if (token) {
|
||||
config.headers.Authorization = `Bearer ${token}`;
|
||||
}
|
||||
return config;
|
||||
});
|
||||
|
||||
// Обработка ответов: при 401 — разлогинить
|
||||
api.interceptors.response.use(
|
||||
(response) => response,
|
||||
(error) => {
|
||||
if (error.response?.status === 401) {
|
||||
useAuthStore.getState().logout();
|
||||
}
|
||||
return Promise.reject(error);
|
||||
}
|
||||
);
|
||||
|
||||
// ─── API методы ───────────────────────────────────────────────────────────────
|
||||
|
||||
export const authApi = {
|
||||
register: (data: { email: string; password: string; name: string }) =>
|
||||
api.post('/auth/register', data),
|
||||
|
||||
login: (data: { email: string; password: string }) =>
|
||||
api.post('/auth/login', data),
|
||||
|
||||
me: () => api.get('/auth/me'),
|
||||
|
||||
verifyEmail: (token: string) =>
|
||||
api.post(`/auth/verify-email/${token}`),
|
||||
};
|
||||
|
||||
export const tasksApi = {
|
||||
list: (limit = 20, offset = 0) =>
|
||||
api.get('/tasks/', { params: { limit, offset } }),
|
||||
|
||||
get: (taskId: string) =>
|
||||
api.get(`/tasks/${taskId}`),
|
||||
|
||||
delete: (taskId: string) =>
|
||||
api.delete(`/tasks/${taskId}`),
|
||||
};
|
||||
|
||||
export const searchApi = {
|
||||
create: (data: {
|
||||
query: string;
|
||||
lang?: string;
|
||||
year_from?: number;
|
||||
year_to?: number;
|
||||
category?: string;
|
||||
}) => api.post('/search/', data),
|
||||
};
|
||||
|
||||
export const documentsApi = {
|
||||
uploadForCheck: (file: File) => {
|
||||
const formData = new FormData();
|
||||
formData.append('file', file);
|
||||
return api.post('/documents/check', formData, {
|
||||
headers: { 'Content-Type': 'multipart/form-data' },
|
||||
timeout: 120000, // 2 минуты для загрузки
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
export const reportsApi = {
|
||||
get: (taskId: string) =>
|
||||
api.get(`/reports/${taskId}`),
|
||||
};
|
||||
Reference in New Issue
Block a user