import { pgTable, uuid, varchar, timestamp, text, boolean, integer, inet } from 'drizzle-orm/pg-core'; import { relations } from 'drizzle-orm'; import { users } from './users'; // 로그인 세션 테이블 (로그인/로그아웃 이력) export const loginSessions = pgTable('login_sessions', { id: uuid('id').primaryKey().defaultRandom(), userId: integer('user_id').references(() => users.id, { onDelete: 'cascade' }).notNull(), loginAt: timestamp('login_at').defaultNow().notNull(), logoutAt: timestamp('logout_at'), // 로그아웃 시간 (nullable) ipAddress: inet('ip_address').notNull(), userAgent: text('user_agent'), sessionToken: varchar('session_token', { length: 255 }).unique(), // NextAuth JWT token ID nextAuthSessionId: varchar('nextauth_session_id', { length: 255 }).unique(), // NextAuth 세션 ID authMethod: varchar('auth_method', { length: 50 }).notNull(), // 'otp', 'email', 'sgips', 'saml' isActive: boolean('is_active').default(true).notNull(), lastActivityAt: timestamp('last_activity_at').defaultNow().notNull(), // 마지막 활동 시간 sessionExpiredAt: timestamp('session_expired_at'), // 세션 만료 시간 createdAt: timestamp('created_at').defaultNow().notNull(), updatedAt: timestamp('updated_at').defaultNow().notNull(), }); // 페이지 방문 이력 테이블 (라우트별 접속 이력) export const pageVisits = pgTable('page_visits', { id: uuid('id').primaryKey().defaultRandom(), userId: integer('user_id').references(() => users.id, { onDelete: 'cascade' }), // nullable (비로그인 사용자 추적 가능) sessionId: uuid('session_id').references(() => loginSessions.id, { onDelete: 'set null' }), // nullable route: varchar('route', { length: 500 }).notNull(), // 방문한 라우트 pageTitle: varchar('page_title', { length: 200 }), // 페이지 제목 referrer: text('referrer'), // 이전 페이지 URL ipAddress: inet('ip_address').notNull(), userAgent: text('user_agent'), visitedAt: timestamp('visited_at').defaultNow().notNull(), duration: integer('duration'), // 페이지 체류 시간 (초 단위, nullable) // 추가 메타데이터 queryParams: text('query_params'), // URL 쿼리 파라미터 deviceType: varchar('device_type', { length: 50 }), // mobile, desktop, tablet browserName: varchar('browser_name', { length: 50 }), osName: varchar('os_name', { length: 50 }), }); // 일별 접속 통계 테이블 (선택사항 - 성능 최적화용) export const dailyAccessStats = pgTable('daily_access_stats', { id: uuid('id').primaryKey().defaultRandom(), date: timestamp('date').notNull(), totalVisits: integer('total_visits').default(0).notNull(), uniqueUsers: integer('unique_users').default(0).notNull(), totalSessions: integer('total_sessions').default(0).notNull(), avgSessionDuration: integer('avg_session_duration'), // 평균 세션 지속 시간 (초) createdAt: timestamp('created_at').defaultNow().notNull(), }); // Relations 정의 export const usersRelationsLogin = relations(users, ({ many }) => ({ loginSessions: many(loginSessions), pageVisits: many(pageVisits), })); export const loginSessionsRelations = relations(loginSessions, ({ one, many }) => ({ user: one(users, { fields: [loginSessions.userId], references: [users.id], }), pageVisits: many(pageVisits), })); export const pageVisitsRelations = relations(pageVisits, ({ one }) => ({ user: one(users, { fields: [pageVisits.userId], references: [users.id], }), session: one(loginSessions, { fields: [pageVisits.sessionId], references: [loginSessions.id], }), })); // NextAuth 연동을 위한 추가 필드 export const tempAuthSessions = pgTable('temp_auth_sessions', { id: uuid('id').primaryKey().defaultRandom(), tempAuthKey: varchar('temp_auth_key', { length: 255 }).unique().notNull(), userId: integer('user_id').references(() => users.id, { onDelete: 'cascade' }).notNull(), email: varchar('email', { length: 255 }).notNull(), authMethod: varchar('auth_method', { length: 50 }).notNull(), // 'otp', 'email', 'sgips', 'saml' expiresAt: timestamp('expires_at').notNull(), isUsed: boolean('is_used').default(false).notNull(), createdAt: timestamp('created_at').defaultNow().notNull(), }); export type TempAuthSession = typeof tempAuthSessions.$inferSelect; export type NewTempAuthSession = typeof tempAuthSessions.$inferInsert; export type LoginSession = typeof loginSessions.$inferSelect; export type NewLoginSession = typeof loginSessions.$inferInsert; export type PageVisit = typeof pageVisits.$inferSelect; export type NewPageVisit = typeof pageVisits.$inferInsert; export type DailyAccessStats = typeof dailyAccessStats.$inferSelect; export type NewDailyAccessStats = typeof dailyAccessStats.$inferInsert;