import { createClient } from "@supabase/supabase-js"; import type { Profile, Project, ProjectMember, AuditTask, AuditIssue, InstantAnalysis, CreateProjectForm, CreateAuditTaskForm, InstantAnalysisForm } from "@/types/types"; const supabaseUrl = import.meta.env.VITE_SUPABASE_URL; const supabaseAnonKey = import.meta.env.VITE_SUPABASE_ANON_KEY; const isValidUuid = (value?: string): boolean => { if (!value) return false; return /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(value); }; export const supabase = createClient(supabaseUrl, supabaseAnonKey, { global: { fetch: undefined }, auth: { storageKey: (import.meta.env.VITE_APP_ID || "sb") + "-auth-token" } }); // 用户相关API export const api = { // Profile相关 async getProfilesById(id: string): Promise { const { data, error } = await supabase .from('profiles') .select('*') .eq('id', id) .limit(1); if (error) throw error; return Array.isArray(data) && data.length > 0 ? data[0] : null; }, async getProfilesCount(): Promise { const { count, error } = await supabase .from('profiles') .select('*', { count: 'exact', head: true }); if (error) throw error; return count || 0; }, async createProfiles(profile: Partial): Promise { const { data, error } = await supabase .from('profiles') .insert([{ id: profile.id, phone: profile.phone || null, email: profile.email || null, full_name: profile.full_name || null, avatar_url: profile.avatar_url || null, role: profile.role || 'member', github_username: profile.github_username || null, gitlab_username: profile.gitlab_username || null }]) .select() .limit(1); if (error) throw error; return Array.isArray(data) && data.length > 0 ? data[0] : {} as Profile; }, async updateProfile(id: string, updates: Partial): Promise { const { data, error } = await supabase .from('profiles') .update(updates) .eq('id', id) .select() .limit(1); if (error) throw error; return Array.isArray(data) && data.length > 0 ? data[0] : {} as Profile; }, async getAllProfiles(): Promise { const { data, error } = await supabase .from('profiles') .select('*') .order('created_at', { ascending: false }); if (error) throw error; return Array.isArray(data) ? data : []; }, // Project相关 async getProjects(): Promise { const { data, error } = await supabase .from('projects') .select(` *, owner:profiles!projects_owner_id_fkey(*) `) .eq('is_active', true) .order('created_at', { ascending: false }); if (error) throw error; return Array.isArray(data) ? data : []; }, async getProjectById(id: string): Promise { const { data, error } = await supabase .from('projects') .select(` *, owner:profiles!projects_owner_id_fkey(*) `) .eq('id', id) .limit(1); if (error) throw error; return Array.isArray(data) && data.length > 0 ? data[0] : null; }, async createProject(project: CreateProjectForm & { owner_id?: string }): Promise { const { data, error } = await supabase .from('projects') .insert([{ name: project.name, description: project.description || null, repository_url: project.repository_url || null, repository_type: project.repository_type || 'other', default_branch: project.default_branch || 'main', programming_languages: JSON.stringify(project.programming_languages || []), owner_id: isValidUuid(project.owner_id) ? project.owner_id : null, is_active: true }]) .select(` *, owner:profiles!projects_owner_id_fkey(*) `) .limit(1); if (error) throw error; return Array.isArray(data) && data.length > 0 ? data[0] : {} as Project; }, async updateProject(id: string, updates: Partial): Promise { const updateData: any = { ...updates }; if (updates.programming_languages) { updateData.programming_languages = JSON.stringify(updates.programming_languages); } const { data, error } = await supabase .from('projects') .update(updateData) .eq('id', id) .select(` *, owner:profiles!projects_owner_id_fkey(*) `) .limit(1); if (error) throw error; return Array.isArray(data) && data.length > 0 ? data[0] : {} as Project; }, async deleteProject(id: string): Promise { const { error } = await supabase .from('projects') .update({ is_active: false }) .eq('id', id); if (error) throw error; }, // ProjectMember相关 async getProjectMembers(projectId: string): Promise { const { data, error } = await supabase .from('project_members') .select(` *, user:profiles!project_members_user_id_fkey(*), project:projects!project_members_project_id_fkey(*) `) .eq('project_id', projectId) .order('joined_at', { ascending: false }); if (error) throw error; return Array.isArray(data) ? data : []; }, async addProjectMember(projectId: string, userId: string, role: string = 'member'): Promise { const { data, error } = await supabase .from('project_members') .insert([{ project_id: projectId, user_id: userId, role: role, permissions: '{}' }]) .select(` *, user:profiles!project_members_user_id_fkey(*), project:projects!project_members_project_id_fkey(*) `) .limit(1); if (error) throw error; return Array.isArray(data) && data.length > 0 ? data[0] : {} as ProjectMember; }, // AuditTask相关 async getAuditTasks(projectId?: string): Promise { let query = supabase .from('audit_tasks') .select(` *, project:projects!audit_tasks_project_id_fkey(*), creator:profiles!audit_tasks_created_by_fkey(*) `); if (projectId) { query = query.eq('project_id', projectId); } const { data, error } = await query.order('created_at', { ascending: false }); if (error) throw error; return Array.isArray(data) ? data : []; }, async getAuditTaskById(id: string): Promise { const { data, error } = await supabase .from('audit_tasks') .select(` *, project:projects!audit_tasks_project_id_fkey(*), creator:profiles!audit_tasks_created_by_fkey(*) `) .eq('id', id) .limit(1); if (error) throw error; return Array.isArray(data) && data.length > 0 ? data[0] : null; }, async createAuditTask(task: CreateAuditTaskForm & { created_by: string }): Promise { const { data, error } = await supabase .from('audit_tasks') .insert([{ project_id: task.project_id, task_type: task.task_type, branch_name: task.branch_name || null, exclude_patterns: JSON.stringify(task.exclude_patterns || []), scan_config: JSON.stringify(task.scan_config || {}), created_by: task.created_by, status: 'pending' }]) .select(` *, project:projects!audit_tasks_project_id_fkey(*), creator:profiles!audit_tasks_created_by_fkey(*) `) .limit(1); if (error) throw error; return Array.isArray(data) && data.length > 0 ? data[0] : {} as AuditTask; }, async updateAuditTask(id: string, updates: Partial): Promise { const { data, error } = await supabase .from('audit_tasks') .update(updates) .eq('id', id) .select(` *, project:projects!audit_tasks_project_id_fkey(*), creator:profiles!audit_tasks_created_by_fkey(*) `) .limit(1); if (error) throw error; return Array.isArray(data) && data.length > 0 ? data[0] : {} as AuditTask; }, // AuditIssue相关 async getAuditIssues(taskId: string): Promise { const { data, error } = await supabase .from('audit_issues') .select(` *, task:audit_tasks!audit_issues_task_id_fkey(*), resolver:profiles!audit_issues_resolved_by_fkey(*) `) .eq('task_id', taskId) .order('severity', { ascending: false }) .order('created_at', { ascending: false }); if (error) throw error; return Array.isArray(data) ? data : []; }, async createAuditIssue(issue: Omit): Promise { const { data, error } = await supabase .from('audit_issues') .insert([issue]) .select(` *, task:audit_tasks!audit_issues_task_id_fkey(*), resolver:profiles!audit_issues_resolved_by_fkey(*) `) .limit(1); if (error) throw error; return Array.isArray(data) && data.length > 0 ? data[0] : {} as AuditIssue; }, async updateAuditIssue(id: string, updates: Partial): Promise { const { data, error } = await supabase .from('audit_issues') .update(updates) .eq('id', id) .select(` *, task:audit_tasks!audit_issues_task_id_fkey(*), resolver:profiles!audit_issues_resolved_by_fkey(*) `) .limit(1); if (error) throw error; return Array.isArray(data) && data.length > 0 ? data[0] : {} as AuditIssue; }, // InstantAnalysis相关 async getInstantAnalyses(userId?: string): Promise { let query = supabase .from('instant_analyses') .select(` *, user:profiles!instant_analyses_user_id_fkey(*) `); if (userId) { query = query.eq('user_id', userId); } const { data, error } = await query.order('created_at', { ascending: false }); if (error) throw error; return Array.isArray(data) ? data : []; }, async createInstantAnalysis(analysis: InstantAnalysisForm & { user_id: string; analysis_result?: string; issues_count?: number; quality_score?: number; analysis_time?: number; }): Promise { const { data, error } = await supabase .from('instant_analyses') .insert([{ user_id: analysis.user_id, language: analysis.language, // 遵循安全要求:不持久化用户代码内容 code_content: '', analysis_result: analysis.analysis_result || '{}', issues_count: analysis.issues_count || 0, quality_score: analysis.quality_score || 0, analysis_time: analysis.analysis_time || 0 }]) .select(` *, user:profiles!instant_analyses_user_id_fkey(*) `) .limit(1); if (error) throw error; return Array.isArray(data) && data.length > 0 ? data[0] : {} as InstantAnalysis; }, // 统计相关 async getProjectStats(): Promise { const [projectsResult, tasksResult, issuesResult] = await Promise.all([ supabase.from('projects').select('id, is_active', { count: 'exact' }), supabase.from('audit_tasks').select('id, status', { count: 'exact' }), supabase.from('audit_issues').select('id, status', { count: 'exact' }) ]); return { total_projects: projectsResult.count || 0, active_projects: projectsResult.data?.filter(p => p.is_active).length || 0, total_tasks: tasksResult.count || 0, completed_tasks: tasksResult.data?.filter(t => t.status === 'completed').length || 0, total_issues: issuesResult.count || 0, resolved_issues: issuesResult.data?.filter(i => i.status === 'resolved').length || 0 }; } };