diff options
| author | dujinkim <dujin.kim@dtsolution.co.kr> | 2025-07-07 01:44:45 +0000 |
|---|---|---|
| committer | dujinkim <dujin.kim@dtsolution.co.kr> | 2025-07-07 01:44:45 +0000 |
| commit | 90f79a7a691943a496f67f01c1e493256070e4de (patch) | |
| tree | 37275fde3ae08c2bca384fbbc8eb378de7e39230 /lib/users/middleware/page-tracking.ts | |
| parent | fbb3b7f05737f9571b04b0a8f4f15c0928de8545 (diff) | |
(대표님) 변경사항 20250707 10시 43분 - unstaged 변경사항 추가
Diffstat (limited to 'lib/users/middleware/page-tracking.ts')
| -rw-r--r-- | lib/users/middleware/page-tracking.ts | 98 |
1 files changed, 98 insertions, 0 deletions
diff --git a/lib/users/middleware/page-tracking.ts b/lib/users/middleware/page-tracking.ts new file mode 100644 index 00000000..bd93fb82 --- /dev/null +++ b/lib/users/middleware/page-tracking.ts @@ -0,0 +1,98 @@ + +// lib/middleware/page-tracking.ts - 페이지 방문 추적 미들웨어 +import { NextRequest, NextResponse } from 'next/server' +import { getToken } from 'next-auth/jwt' +import { UAParser } from 'ua-parser-js'; +import { SessionRepository } from '../session/repository' + +export async function trackPageVisit(request: NextRequest) { + try { + const token = await getToken({ req: request }) + const url = new URL(request.url) + + // API 경로나 정적 파일은 추적하지 않음 + if (url.pathname.startsWith('/api') || + url.pathname.startsWith('/_next') || + url.pathname.includes('.')) { + return + } + + const userAgent = request.headers.get('user-agent') || '' + const parser = new UAParser(userAgent) + const result = parser.getResult() + + // 활성 세션 조회 + let sessionId = null + if (token?.id) { + const activeSession = await SessionRepository.getActiveSessionByUserId(token.id) + if (activeSession) { + sessionId = activeSession.id + // 세션 활동 시간 업데이트 + await SessionRepository.updateLoginSession(activeSession.id, { + lastActivityAt: new Date() + }) + } + } + + // 페이지 방문 기록 + await SessionRepository.recordPageVisit({ + userId: token?.id || undefined, + sessionId, + route: url.pathname, + pageTitle: extractPageTitle(url.pathname), // 구현 필요 + referrer: request.headers.get('referer') || undefined, + ipAddress: getClientIP(request), + userAgent, + queryParams: url.search ? url.search.substring(1) : undefined, + deviceType: getDeviceType(result.device.type), + browserName: result.browser.name, + osName: result.os.name, + }) + + } catch (error) { + console.error('Failed to track page visit:', error) + } +} + +function getClientIP(request: NextRequest): string { + const forwarded = request.headers.get('x-forwarded-for'); + const realIP = request.headers.get('x-real-ip'); + const cfConnectingIP = request.headers.get('cf-connecting-ip'); // Cloudflare + + if (cfConnectingIP) { + return cfConnectingIP; + } + + if (forwarded) { + return forwarded.split(',')[0].trim(); + } + + if (realIP) { + return realIP; + } + + // NextRequest에는 ip 프로퍼티가 없으므로 기본값 반환 + return '127.0.0.1'; +} + + +function getDeviceType(deviceType?: string): string { + if (!deviceType) return 'desktop' + if (deviceType === 'mobile') return 'mobile' + if (deviceType === 'tablet') return 'tablet' + return 'desktop' +} + +function extractPageTitle(pathname: string): string { + // 라우트 기반 페이지 제목 매핑 + const titleMap: Record<string, string> = { + '/': 'Home', + '/dashboard': 'Dashboard', + '/profile': 'Profile', + '/settings': 'Settings', + // 추가 필요 + } + + return titleMap[pathname] || pathname +} + |
