// schema/consent.ts import { pgTable, integer, varchar, boolean, timestamp, text, json, pgEnum, uniqueIndex, index, } from "drizzle-orm/pg-core"; import { users } from "./users"; // 동의 타입 enum export const consentTypeEnum = pgEnum("consent_type", [ "privacy_policy", "terms_of_service", "marketing", "optional" ]); // 동의 액션 enum export const consentActionEnum = pgEnum("consent_action", [ "consent", "revoke", "update" ]); // 정책 타입 enum export const policyTypeEnum = pgEnum("policy_type", [ "privacy_policy", "terms_of_service" ]); // 1. 정책 버전 관리 테이블 export const policyVersions = pgTable("policy_versions", { id: integer("id").primaryKey().generatedAlwaysAsIdentity(), policyType: policyTypeEnum("policy_type").notNull(), version: varchar("version", { length: 20 }).notNull(), content: text("content").notNull(), effectiveDate: timestamp("effective_date", { withTimezone: true }).notNull(), isCurrent: boolean("is_current").default(false).notNull(), createdAt: timestamp("created_at", { withTimezone: true }) .defaultNow() .notNull(), }, (table) => { return { // 정책 타입과 버전의 유니크 조합 policyVersionUniqueIdx: uniqueIndex("policy_versions_type_version_idx") .on(table.policyType, table.version), // 현재 버전 조회용 인덱스 currentPolicyIdx: index("policy_versions_current_idx") .on(table.isCurrent), // 효력 발생일 인덱스 effectiveDateIdx: index("policy_versions_effective_date_idx") .on(table.effectiveDate), }; }); // 2. 사용자 동의 내역 테이블 export const userConsents = pgTable("user_consents", { id: integer("id").primaryKey().generatedAlwaysAsIdentity(), userId: integer("user_id") .references(() => users.id, { onDelete: "cascade" }) .notNull(), consentType: consentTypeEnum("consent_type").notNull(), consentStatus: boolean("consent_status").default(false).notNull(), policyVersion: varchar("policy_version", { length: 20 }).notNull(), consentedAt: timestamp("consented_at", { withTimezone: true }) .defaultNow() .notNull(), ipAddress: varchar("ip_address", { length: 45 }), // IPv6 지원 userAgent: text("user_agent"), revokedAt: timestamp("revoked_at", { withTimezone: true }), revokeReason: text("revoke_reason"), createdAt: timestamp("created_at", { withTimezone: true }) .defaultNow() .notNull(), updatedAt: timestamp("updated_at", { withTimezone: true }) .defaultNow() .notNull(), }, (table) => { return { // 사용자별 동의 타입 조회용 인덱스 userConsentTypeIdx: index("user_consents_user_type_idx") .on(table.userId, table.consentType), // 동의 시점 인덱스 consentedAtIdx: index("user_consents_consented_at_idx") .on(table.consentedAt), // 정책 버전별 인덱스 policyVersionIdx: index("user_consents_policy_version_idx") .on(table.policyVersion), }; }); // 3. 동의 로그 테이블 (모든 동의/철회 이력 보관) export const consentLogs = pgTable("consent_logs", { id: integer("id").primaryKey().generatedAlwaysAsIdentity(), userId: integer("user_id") .references(() => users.id, { onDelete: "cascade" }) .notNull(), consentType: consentTypeEnum("consent_type").notNull(), action: consentActionEnum("action").notNull(), oldStatus: boolean("old_status"), newStatus: boolean("new_status").notNull(), policyVersion: varchar("policy_version", { length: 20 }).notNull(), ipAddress: varchar("ip_address", { length: 45 }), userAgent: text("user_agent"), actionTimestamp: timestamp("action_timestamp", { withTimezone: true }) .defaultNow() .notNull(), additionalData: json("additional_data"), // 추가 메타데이터 }, (table) => { return { // 사용자별 액션 시간 순 조회용 인덱스 userActionTimestampIdx: index("consent_logs_user_action_timestamp_idx") .on(table.userId, table.actionTimestamp), // 동의 타입별 인덱스 consentTypeIdx: index("consent_logs_consent_type_idx") .on(table.consentType), // 액션 타입별 인덱스 actionIdx: index("consent_logs_action_idx") .on(table.action), }; }); export type PolicyVersion = typeof policyVersions.$inferSelect; export type NewPolicyVersion = typeof policyVersions.$inferInsert; export type UserConsent = typeof userConsents.$inferSelect; export type NewUserConsent = typeof userConsents.$inferInsert; export type ConsentLog = typeof consentLogs.$inferSelect; export type NewConsentLog = typeof consentLogs.$inferInsert;