import axios, { AxiosError } from 'axios'; const API_BASE = import.meta.env.VITE_API_BASE_URL || 'https://qiyuan-log-center-api.airlabs.art'; const api = axios.create({ baseURL: API_BASE, timeout: 10000, }); // ==================== 自身错误上报 ==================== export function reportError(error: Error, context?: Record) { const stackLines = error.stack?.split('\n') || []; const match = stackLines[1]?.match(/at\s+.*\s+\((.+):(\d+):\d+\)/); const payload = { project_id: 'log_center_web', environment: import.meta.env.MODE, level: 'ERROR', error: { type: error.name, message: error.message, file_path: match?.[1] || 'unknown', line_number: parseInt(match?.[2] || '0'), stack_trace: stackLines, }, context: { url: window.location.href, userAgent: navigator.userAgent, ...context, }, }; const blob = new Blob([JSON.stringify(payload)], { type: 'application/json' }); if (navigator.sendBeacon) { navigator.sendBeacon(`${API_BASE}/api/v1/logs/report`, blob); } else { fetch(`${API_BASE}/api/v1/logs/report`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(payload), keepalive: true, }).catch(() => {}); } } // Axios 拦截器:上报 5xx 服务端错误 api.interceptors.response.use( (response) => response, (error: AxiosError) => { if (error.response && error.response.status >= 500) { reportError(error, { api_url: error.config?.url, method: error.config?.method, status: error.response.status, }); } return Promise.reject(error); }, ); // Types export interface ErrorLog { id: number; project_id: string; environment: string; level: string; source: string; error_type: string; error_message: string; file_path: string | null; line_number: number | null; stack_trace: string; context: Record; version?: string; commit_hash?: string; timestamp: string; fingerprint: string; status: string; retry_count: number; failure_reason: string | null; // Severity (1-10) severity?: number | null; severity_reason?: string | null; // PR Tracking pr_number?: number | null; pr_url?: string | null; branch_name?: string | null; // Rejection Tracking rejection_reason?: string | null; rejection_count?: number; last_rejected_at?: string | null; merged_at?: string | null; } export interface Project { id: number; project_id: string; name: string | null; repo_url: string | null; local_path: string | null; description: string | null; created_at: string; updated_at: string; } export interface DashboardStats { total_bugs: number; today_bugs: number; fix_rate: number; status_distribution: Record; source_distribution: Record; } export interface PaginatedResponse { items: T[]; total: number; page: number; page_size: number; total_pages: number; } export interface RepairReport { id: number; error_log_ids: number[]; project_id: string; status: string; ai_analysis: string; fix_plan: string; code_diff: string; modified_files: string[]; test_output: string; test_passed: boolean; created_at: string; repair_round: number; failure_reason: string | null; pr_url?: string | null; pr_number?: number | null; branch_name?: string | null; } // API Functions export const getStats = () => api.get('/api/v1/dashboard/stats'); export const getBugs = (params: { page?: number; page_size?: number; status?: string; project_id?: string; source?: string; }) => api.get>('/api/v1/bugs', { params }); export const getBugDetail = (id: number) => api.get(`/api/v1/bugs/${id}`); export const getProjects = () => api.get<{ projects: Project[] }>('/api/v1/projects'); export const getProjectDetail = (projectId: string) => api.get(`/api/v1/projects/${projectId}`); export const updateProject = (projectId: string, data: Partial) => api.put(`/api/v1/projects/${projectId}`, data); export const updateTaskStatus = (taskId: number, status: string, message?: string) => api.put(`/api/v1/tasks/${taskId}/status`, { status, message }); export const triggerRepair = (bugId: number) => updateTaskStatus(bugId, 'PENDING_FIX', 'Triggered from UI'); export const getRepairReports = (params: { page?: number; page_size?: number; project_id?: string; }) => api.get>('/api/v1/repair/reports', { params }); export const getRepairReportDetail = (id: number) => api.get(`/api/v1/repair/reports/${id}`); export const getRepairReportsByBug = (errorLogId: number) => api.get>('/api/v1/repair/reports', { params: { error_log_id: errorLogId, page_size: 100 } }); // PR Operations export const mergePR = (bugId: number) => api.post(`/api/v1/bugs/${bugId}/merge-pr`); export const closePR = (bugId: number, reason: string) => api.post(`/api/v1/bugs/${bugId}/close-pr`, { reason }); export const retryFix = (bugId: number) => api.post(`/api/v1/bugs/${bugId}/retry`); // Manual review operations (without PR) export const approveFix = (bugId: number) => api.post(`/api/v1/bugs/${bugId}/approve-fix`); export const rejectFix = (bugId: number, reason: string) => api.post(`/api/v1/bugs/${bugId}/reject-fix`, { reason }); // Report-level approve/reject (batch operation) export const approveReport = (reportId: number) => api.post(`/api/v1/repair/reports/${reportId}/approve`); export const rejectReport = (reportId: number, reason: string) => api.post(`/api/v1/repair/reports/${reportId}/reject`, { reason }); export default api;