import { createHash } from 'node:crypto' /** @type {RegExp} 추적 제외 경로 */ const EXCLUDED_PATH_PATTERN = /^\/(admin|signin|signup|forgot-password|settings)(\/|$)/ /** @type {RegExp} 봇 User-Agent 패턴 */ const BOT_USER_AGENT_PATTERN = /bot|crawl|spider|slurp|preview|headless|lighthouse|bytespider|facebookexternalhit/i /** * 오늘 날짜(UTC)를 YYYY-MM-DD로 반환한다. * @returns {string} 날짜 문자열 */ export const getAnalyticsDayKey = () => { const now = new Date() return now.toISOString().slice(0, 10) } /** * 통계 추적 대상 경로인지 확인한다. * @param {string} path - 요청 경로 * @returns {boolean} 추적 가능 여부 */ export const isTrackableAnalyticsPath = (path) => { const normalized = (path || '').trim() if (!normalized.startsWith('/')) { return false } if (EXCLUDED_PATH_PATTERN.test(normalized)) { return false } return true } /** * 봇 User-Agent 여부 * @param {string} userAgent - User-Agent * @returns {boolean} 봇 여부 */ export const isBotUserAgent = (userAgent) => { const value = (userAgent || '').trim() if (!value) { return false } return BOT_USER_AGENT_PATTERN.test(value) } /** * 일 단위 익명 방문자 해시를 생성한다. 원문 IP·UA는 저장하지 않는다. * @param {{ day: string, ip: string, userAgent: string, secret: string }} input - 해시 입력 * @returns {string} visitor hash */ export const createDailyVisitorHash = ({ day, ip, userAgent, secret }) => { const payload = `${day}|${ip}|${userAgent}|${secret}` return createHash('sha256').update(payload).digest('hex') } /** * 게시물 slug 정규화 * @param {string} slug - slug * @returns {string} 정규화된 slug */ export const normalizePostSlugForAnalytics = (slug) => (slug || '').trim()