'use server' import db from '@/db/db' import { policyVersions, userConsents, consentLogs } from '@/db/schema' import { eq, desc, and } from 'drizzle-orm' import { revalidatePath } from 'next/cache' // 정책 버전 생성 export async function createPolicyVersion(data: { policyType: 'privacy_policy' | 'terms_of_service' version: string content: string effectiveDate: Date }) { try { // 트랜잭션으로 처리 const result = await db.transaction(async (tx) => { // 기존의 현재 정책들을 비활성화 await tx .update(policyVersions) .set({ isCurrent: false }) .where(eq(policyVersions.policyType, data.policyType)) // 새 정책 버전 생성 const [newPolicy] = await tx .insert(policyVersions) .values({ policyType: data.policyType, version: data.version, content: data.content, effectiveDate: data.effectiveDate, isCurrent: true, }) .returning() return newPolicy }) revalidatePath('/evcp/policies') return { success: true, policy: result } } catch (error) { console.error('Error creating policy version:', error) return { success: false, error: error instanceof Error ? error.message : '정책 생성에 실패했습니다.' } } } // 정책 버전 활성화 export async function activatePolicyVersion(policyId: number) { try { // 먼저 해당 정책의 타입을 조회 const targetPolicy = await db .select() .from(policyVersions) .where(eq(policyVersions.id, policyId)) .limit(1) if (targetPolicy.length === 0) { return { success: false, error: '정책을 찾을 수 없습니다.' } } // 트랜잭션으로 처리 await db.transaction(async (tx) => { // 해당 정책 타입의 모든 버전을 비활성화 await tx .update(policyVersions) .set({ isCurrent: false }) .where(eq(policyVersions.policyType, targetPolicy[0].policyType)) // 선택한 버전만 활성화 await tx .update(policyVersions) .set({ isCurrent: true }) .where(eq(policyVersions.id, policyId)) }) revalidatePath('/admin/policies') return { success: true } } catch (error) { console.error('Error activating policy version:', error) return { success: false, error: error instanceof Error ? error.message : '정책 활성화에 실패했습니다.' } } } // 정책 히스토리 조회 export async function getPolicyHistory(policyType: 'privacy_policy' | 'terms_of_service') { try { const history = await db .select() .from(policyVersions) .where(eq(policyVersions.policyType, policyType)) .orderBy(desc(policyVersions.createdAt)) return { success: true, data: history } } catch (error) { console.error('Error fetching policy history:', error) return { success: false, error: error instanceof Error ? error.message : '히스토리 조회에 실패했습니다.' } } } // 현재 활성 정책들 조회 export async function getCurrentPolicies() { try { const currentPolicies = await db .select() .from(policyVersions) .where(eq(policyVersions.isCurrent, true)) // 정책 타입별로 그룹화 const grouped = currentPolicies.reduce((acc, policy) => { acc[policy.policyType] = policy return acc }, {} as Record) return { success: true, data: grouped } } catch (error) { console.error('Error fetching current policies:', error) return { success: false, error: error instanceof Error ? error.message : '정책 조회에 실패했습니다.' } } } // 모든 정책 조회 (관리자용) export async function getAllPolicies() { try { const allPolicies = await db .select() .from(policyVersions) .orderBy(desc(policyVersions.createdAt)) // 정책 타입별로 그룹화 const grouped = allPolicies.reduce((acc, policy) => { if (!acc[policy.policyType]) { acc[policy.policyType] = [] } acc[policy.policyType].push(policy) return acc }, {} as Record) return { success: true, data: grouped } } catch (error) { console.error('Error fetching all policies:', error) return { success: false, error: error instanceof Error ? error.message : '정책 조회에 실패했습니다.' } } } // 정책 통계 조회 export async function getPolicyStats() { try { // 각 정책별 버전 수와 최신 업데이트 날짜 const stats = await db .select({ policyType: policyVersions.policyType, totalVersions: 'COUNT(*)', latestUpdate: 'MAX(created_at)', currentVersion: policyVersions.version, }) .from(policyVersions) .where(eq(policyVersions.isCurrent, true)) .groupBy(policyVersions.policyType, policyVersions.version) return { success: true, data: stats } } catch (error) { console.error('Error fetching policy stats:', error) return { success: false, error: error instanceof Error ? error.message : '통계 조회에 실패했습니다.' } } } // 사용자 동의 기록 export async function recordUserConsent(data: { userId: number consents: Array<{ consentType: 'privacy_policy' | 'terms_of_service' | 'marketing' | 'optional' consentStatus: boolean policyVersion: string }> ipAddress?: string userAgent?: string }) { try { const result = await db.transaction(async (tx) => { const consentRecords = [] const logRecords = [] for (const consent of data.consents) { // 사용자 동의 기록 const [consentRecord] = await tx .insert(userConsents) .values({ userId: data.userId, consentType: consent.consentType, consentStatus: consent.consentStatus, policyVersion: consent.policyVersion, ipAddress: data.ipAddress, userAgent: data.userAgent, }) .returning() consentRecords.push(consentRecord) // 동의 로그 기록 const [logRecord] = await tx .insert(consentLogs) .values({ userId: data.userId, consentType: consent.consentType, action: 'consent', oldStatus: null, newStatus: consent.consentStatus, policyVersion: consent.policyVersion, ipAddress: data.ipAddress, userAgent: data.userAgent, }) .returning() logRecords.push(logRecord) } return { consents: consentRecords, logs: logRecords } }) return { success: true, data: result } } catch (error) { console.error('Error recording user consent:', error) return { success: false, error: error instanceof Error ? error.message : '동의 기록에 실패했습니다.' } } } // 사용자 동의 상태 조회 export async function getUserConsentStatus(userId: number) { try { const consents = await db .select() .from(userConsents) .where(eq(userConsents.userId, userId)) .orderBy(desc(userConsents.consentedAt)) // 각 동의 타입별 최신 상태만 반환 const latestConsents = consents.reduce((acc, consent) => { if (!acc[consent.consentType] || new Date(consent.consentedAt) > new Date(acc[consent.consentType].consentedAt)) { acc[consent.consentType] = consent } return acc }, {} as Record) return { success: true, data: latestConsents } } catch (error) { console.error('Error fetching user consent status:', error) return { success: false, error: error instanceof Error ? error.message : '동의 상태 조회에 실패했습니다.' } } } // 사용자 동의 철회 export async function revokeUserConsent(data: { userId: number consentType: 'privacy_policy' | 'terms_of_service' | 'marketing' | 'optional' revokeReason?: string ipAddress?: string userAgent?: string }) { try { // 현재 동의 상태 조회 const currentConsent = await db .select() .from(userConsents) .where( and( eq(userConsents.userId, data.userId), eq(userConsents.consentType, data.consentType) ) ) .orderBy(desc(userConsents.consentedAt)) .limit(1) if (currentConsent.length === 0) { return { success: false, error: '동의 기록을 찾을 수 없습니다.' } } const result = await db.transaction(async (tx) => { // 동의 철회 기록 const [updatedConsent] = await tx .update(userConsents) .set({ consentStatus: false, revokedAt: new Date(), revokeReason: data.revokeReason, }) .where(eq(userConsents.id, currentConsent[0].id)) .returning() // 철회 로그 기록 const [logRecord] = await tx .insert(consentLogs) .values({ userId: data.userId, consentType: data.consentType, action: 'revoke', oldStatus: currentConsent[0].consentStatus, newStatus: false, policyVersion: currentConsent[0].policyVersion, ipAddress: data.ipAddress, userAgent: data.userAgent, additionalData: data.revokeReason ? { revokeReason: data.revokeReason } : null, }) .returning() return { consent: updatedConsent, log: logRecord } }) return { success: true, data: result } } catch (error) { console.error('Error revoking user consent:', error) return { success: false, error: error instanceof Error ? error.message : '동의 철회에 실패했습니다.' } } }