import axios, { AxiosError } from 'axios'; import type { AiModelId, Frame, Mask, Project, Template } from '../store/useStore'; import { API_BASE_URL } from './config'; const apiClient = axios.create({ baseURL: API_BASE_URL, headers: { 'Content-Type': 'application/json', }, timeout: 30000, }); // Request interceptor: attach token apiClient.interceptors.request.use( (config) => { const token = localStorage.getItem('token'); if (token) { config.headers.Authorization = `Bearer ${token}`; } return config; }, (error) => Promise.reject(error) ); // Response interceptor: handle errors apiClient.interceptors.response.use( (response) => response, (error: AxiosError) => { if (error.response?.status === 401) { localStorage.removeItem('token'); window.location.reload(); } return Promise.reject(error); } ); // Auth export async function login(username: string, password: string): Promise<{ token: string }> { const response = await apiClient.post('/api/auth/login', { username, password }); return response.data; } // Projects function normalizeProjectStatus(status?: string): Project['status'] { const value = (status || 'pending').toLowerCase(); if (value === 'ready') return 'ready'; if (value === 'parsing' || value === 'queued' || value === 'running') return 'parsing'; if (value === 'error' || value === 'failed') return 'error'; return 'pending'; } function mapProject(p: any): Project { return { id: String(p.id), name: p.name, description: p.description, status: normalizeProjectStatus(p.status), frames: p.frame_count ?? 0, fps: p.original_fps ? `${Math.round(p.original_fps)}FPS` : '30FPS', thumbnail_url: p.thumbnail_url, video_path: p.video_path, source_type: p.source_type, original_fps: p.original_fps, parse_fps: p.parse_fps, createdAt: p.created_at, updatedAt: p.updated_at, }; } export async function getProjects(): Promise { const response = await apiClient.get('/api/projects'); return response.data.map(mapProject); } export async function createProject(payload: { name: string; description?: string; parse_fps?: number; }): Promise { const response = await apiClient.post('/api/projects', payload); return mapProject(response.data); } export async function updateProject(id: string, payload: Partial): Promise { const response = await apiClient.patch(`/api/projects/${id}`, payload); return mapProject(response.data); } export async function deleteProject(id: string): Promise { await apiClient.delete(`/api/projects/${id}`); } // Templates function _mapTemplate(t: any): Template { const mapping = t.mapping_rules || {}; return { id: String(t.id), name: t.name, description: t.description, classes: mapping.classes || [], rules: mapping.rules || [], createdAt: t.created_at, updatedAt: t.updated_at, }; } export async function getTemplates(): Promise { const response = await apiClient.get('/api/templates'); return response.data.map(_mapTemplate); } export async function createTemplate(payload: { name: string; description?: string; color: string; z_index: number; classes?: { name: string; color: string; zIndex: number; category?: string }[]; rules?: any[]; }): Promise