summaryrefslogtreecommitdiff
path: root/db/schema/history.ts
diff options
context:
space:
mode:
authordujinkim <dujin.kim@dtsolution.co.kr>2025-07-07 01:44:45 +0000
committerdujinkim <dujin.kim@dtsolution.co.kr>2025-07-07 01:44:45 +0000
commit90f79a7a691943a496f67f01c1e493256070e4de (patch)
tree37275fde3ae08c2bca384fbbc8eb378de7e39230 /db/schema/history.ts
parentfbb3b7f05737f9571b04b0a8f4f15c0928de8545 (diff)
(대표님) 변경사항 20250707 10시 43분 - unstaged 변경사항 추가
Diffstat (limited to 'db/schema/history.ts')
-rw-r--r--db/schema/history.ts113
1 files changed, 113 insertions, 0 deletions
diff --git a/db/schema/history.ts b/db/schema/history.ts
new file mode 100644
index 00000000..13b00196
--- /dev/null
+++ b/db/schema/history.ts
@@ -0,0 +1,113 @@
+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; \ No newline at end of file