diff options
Diffstat (limited to 'lib/polices')
| -rw-r--r-- | lib/polices/service.ts | 341 |
1 files changed, 341 insertions, 0 deletions
diff --git a/lib/polices/service.ts b/lib/polices/service.ts new file mode 100644 index 00000000..33cd592c --- /dev/null +++ b/lib/polices/service.ts @@ -0,0 +1,341 @@ +'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<string, any>) + + 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<string, any[]>) + + 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<string, any>) + + 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 : '동의 철회에 실패했습니다.' + } + } +}
\ No newline at end of file |
