d3ro-voice/server/supabase/functions/_shared/audit.ts
윤찬 dca1b90faa feat(admin): Phase V2-6 Admin CRM 고도화 — CRUD + 차트 + 결제 + 감사
- DB: audit_log 테이블(diff 포함) + subscriptions.admin_note + super_admin role
- Edge Functions 4개: admin-users, admin-subscriptions, admin-payments, admin-audit-log
- 공유 유틸: admin-auth.ts(권한 검증), audit.ts(감사로그 기록)
- Swagger UI: 독립 정적 페이지 + OpenAPI 3.0 spec
- CRUD 페이지: 구독 생성/수정/삭제, role 변경, 감사로그 목록/상세
- recharts: feature별 StackedBar + DAU Line + Top Users HorizontalBar
- 결제 이력: DB + Payple API 병행 조회
- 권한: super_admin만 위험 작업, admin은 조회 전용
- RLS: admin/super_admin IN 정책 + super_admin 쓰기 정책
- SQL RPC: admin_usage_by_feature, admin_top_users, admin_dau
2026-04-12 21:32:47 +09:00

37 lines
1.1 KiB
TypeScript

// server/supabase/functions/_shared/audit.ts
// 감사로그 기록 유틸리티
// @ts-expect-error — Deno 런타임 import
import type { SupabaseClient } from 'https://esm.sh/@supabase/supabase-js@2.39.7'
export interface AuditLogEntry {
adminId: string
action: string // 'subscription.create', 'subscription.update', 'subscription.delete', 'user.role_change'
targetType: string // 'subscription', 'profile'
targetId: string
beforeData: Record<string, unknown> | null
afterData: Record<string, unknown> | null
memo: string
}
/**
* audit_log 테이블에 감사 기록을 삽입한다.
* service_role 클라이언트를 사용해야 RLS를 우회한다.
*/
export async function writeAuditLog(
supabase: SupabaseClient,
entry: AuditLogEntry
): Promise<void> {
const { error } = await supabase.from('audit_log').insert({
admin_id: entry.adminId,
action: entry.action,
target_type: entry.targetType,
target_id: entry.targetId,
before_data: entry.beforeData,
after_data: entry.afterData,
memo: entry.memo,
})
if (error) {
throw new Error(`Failed to write audit log: ${error.message}`)
}
}