// server/supabase/functions/admin-audit-log/index.ts // 감사로그 조회 — manager 이상 import { corsHeaders, handleCorsPreflightRequest } from '../_shared/cors.ts' import { authErrorResponse, type AuthError } from '../_shared/auth.ts' import { requireManager } from '../_shared/admin-auth.ts' import { createServiceRoleClient } from '../_shared/quota.ts' function jsonResponse(body: Record | Record[], status = 200): Response { return new Response(JSON.stringify(body), { status, headers: { ...corsHeaders, 'Content-Type': 'application/json' }, }) } // @ts-expect-error — Deno 런타임 전역 Deno.serve(async (req: Request) => { const preflight = handleCorsPreflightRequest(req) if (preflight) return preflight if (req.method !== 'GET') { return jsonResponse({ error: 'Method not allowed' }, 405) } try { await requireManager(req) const url = new URL(req.url) const serviceClient = createServiceRoleClient() // 단건 상세 const logId = url.searchParams.get('id') if (logId) { const { data: log, error } = await serviceClient .from('audit_log') .select('*') .eq('id', parseInt(logId, 10)) .maybeSingle() if (error) return jsonResponse({ error: error.message }, 500) if (!log) return jsonResponse({ error: 'Audit log not found' }, 404) // admin 프로필 정보 함께 const { data: adminProfile } = await serviceClient .from('profiles') .select('id, name, avatar_url') .eq('id', (log as Record).admin_id) .maybeSingle() return jsonResponse({ log: log as unknown as Record, admin: adminProfile as unknown as Record, }) } // 목록 조회 const page = parseInt(url.searchParams.get('page') ?? '1', 10) const limit = parseInt(url.searchParams.get('limit') ?? '20', 10) const targetType = url.searchParams.get('target_type') ?? '' const adminId = url.searchParams.get('admin_id') ?? '' const fromDate = url.searchParams.get('from') ?? '' const toDate = url.searchParams.get('to') ?? '' const targetId = url.searchParams.get('target_id') ?? '' const rangeFrom = (page - 1) * limit const rangeTo = rangeFrom + limit - 1 let query = serviceClient .from('audit_log') .select('*', { count: 'exact' }) if (targetType) query = query.eq('target_type', targetType) if (adminId) query = query.eq('admin_id', adminId) if (targetId) query = query.eq('target_id', targetId) if (fromDate) query = query.gte('created_at', `${fromDate}T00:00:00Z`) if (toDate) query = query.lte('created_at', `${toDate}T23:59:59Z`) const { data, count, error } = await query .order('created_at', { ascending: false }) .range(rangeFrom, rangeTo) if (error) return jsonResponse({ error: error.message }, 500) // admin 이름 매핑 const logs = (data ?? []) as unknown as Record[] const adminIds = [...new Set(logs.map(l => l.admin_id as string))] let adminMap: Record = {} if (adminIds.length > 0) { const { data: admins } = await serviceClient .from('profiles') .select('id, name') .in('id', adminIds) if (admins) { adminMap = Object.fromEntries( (admins as Array<{ id: string; name: string | null }>).map(a => [a.id, a.name ?? 'Unknown']) ) } } const enrichedLogs = logs.map(log => ({ ...log, admin_name: adminMap[log.admin_id as string] ?? 'Unknown', })) return jsonResponse({ logs: enrichedLogs, total: count ?? 0, page, limit }) } catch (err) { if (err && typeof err === 'object' && 'status' in err && 'message' in err) { return authErrorResponse(err as AuthError, corsHeaders) } const message = err instanceof Error ? err.message : 'Unknown error' return jsonResponse({ error: message }, 500) } })