Files
anti-plagiarism/services/frontend/src/api/client.ts
jze9 c1cf1ddd2f feat(auth): подтверждение email + актуализация SMTP/Ollama конфигов
- Резенд письма верификации (/auth/resend-verification), модалка на
  фронте с поллингом статуса, страница /verify-email/:token
- SMTP переведён на собственный Postfix (mail.jze9mail.ru, STARTTLS,
  SMTP_TLS_VERIFY) вместо Yandex-заглушки в дефолтах и .env.example
- OLLAMA_URL и модель в worker-gpu синхронизированы с новым GPU-хостом
  (llama3:8b -> qwen2.5:7b, которой раньше не было на сервере)

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-23 18:37:22 +05:00

123 lines
4.3 KiB
TypeScript

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}`),
resendVerification: () =>
api.post('/auth/resend-verification'),
};
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}`),
};
// ─── Админка ────────────────────────────────────────────────────────────────
export const adminApi = {
openSession: () => api.post('/admin/session'),
verifySession: (code: string) => api.get(`/admin/session/verify/${code}`),
health: () => api.get('/admin/health'),
stats: () => api.get('/admin/stats'),
users: (params?: { q?: string; limit?: number; offset?: number }) =>
api.get('/admin/users', { params }),
updateUser: (id: number, data: { plan?: string; is_verified?: boolean; is_admin?: boolean }) =>
api.patch(`/admin/users/${id}`, data),
deleteUser: (id: number) => api.delete(`/admin/users/${id}`),
tasks: (params?: { status?: string; type?: string; user_id?: number; limit?: number; offset?: number }) =>
api.get('/admin/tasks', { params }),
task: (publicId: string) => api.get(`/admin/tasks/${publicId}`),
requeueTask: (publicId: string) => api.post(`/admin/tasks/${publicId}/requeue`),
deleteTask: (publicId: string) => api.delete(`/admin/tasks/${publicId}`),
documents: (params?: { q?: string; source?: string; year?: number; limit?: number; offset?: number }) =>
api.get('/admin/documents', { params }),
deleteDocument: (id: number) => api.delete(`/admin/documents/${id}`),
storage: () => api.get('/admin/storage'),
sources: () => api.get('/admin/sources'),
createSource: (data: Record<string, unknown>) => api.post('/admin/sources', data),
updateSource: (id: number, data: Record<string, unknown>) => api.patch(`/admin/sources/${id}`, data),
deleteSource: (id: number) => api.delete(`/admin/sources/${id}`),
runSource: (id: number) => api.post(`/admin/sources/${id}/run`),
staging: (params?: { status?: string; limit?: number; offset?: number }) =>
api.get('/admin/staging', { params }),
stagingDetail: (id: number) => api.get(`/admin/staging/${id}`),
approveStaging: (id: number) => api.post(`/admin/staging/${id}/approve`),
rejectStaging: (id: number) => api.post(`/admin/staging/${id}/reject`),
};