Some checks failed
Build and Deploy Log Center / build-and-deploy (push) Failing after 1m19s
- 新增 app/self_report.py:后端运行时异常直接写入自身数据库 - main.py:添加全局异常处理器 + 启动时注册 log_center_api/web 项目 - web/api.ts:添加 reportError 函数 + Axios 5xx 拦截器 - web/main.tsx:添加 window.onerror / onunhandledrejection 全局捕获 - deploy.yaml:CI/CD 流水线各步骤失败时上报(build/deploy) - 重写 integration_guide.md:按三类上报(runtime/cicd/deployment)重新组织 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
207 lines
6.0 KiB
TypeScript
207 lines
6.0 KiB
TypeScript
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<string, unknown>) {
|
|
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<string, any>;
|
|
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<string, number>;
|
|
source_distribution: Record<string, number>;
|
|
}
|
|
|
|
export interface PaginatedResponse<T> {
|
|
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<DashboardStats>('/api/v1/dashboard/stats');
|
|
|
|
export const getBugs = (params: {
|
|
page?: number;
|
|
page_size?: number;
|
|
status?: string;
|
|
project_id?: string;
|
|
source?: string;
|
|
}) => api.get<PaginatedResponse<ErrorLog>>('/api/v1/bugs', { params });
|
|
|
|
export const getBugDetail = (id: number) => api.get<ErrorLog>(`/api/v1/bugs/${id}`);
|
|
|
|
export const getProjects = () => api.get<{ projects: Project[] }>('/api/v1/projects');
|
|
|
|
export const getProjectDetail = (projectId: string) =>
|
|
api.get<Project>(`/api/v1/projects/${projectId}`);
|
|
|
|
export const updateProject = (projectId: string, data: Partial<Project>) =>
|
|
api.put<Project>(`/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<PaginatedResponse<RepairReport>>('/api/v1/repair/reports', { params });
|
|
|
|
export const getRepairReportDetail = (id: number) => api.get<RepairReport>(`/api/v1/repair/reports/${id}`);
|
|
|
|
export const getRepairReportsByBug = (errorLogId: number) =>
|
|
api.get<PaginatedResponse<RepairReport>>('/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;
|