Files
sori.studio/lib/analytics.js
zenn 3623305119 v1.3.3: 자체 최소 통계 및 스플래시 localStorage 정리
- 일별 익명 방문자 해시·사이트/게시물 통계(030 마이그레이션)
- POST /api/analytics/pageview, 관리자 analytics API, 클라이언트 트래커
- 관리자 대시보드 통계 카드·인기 게시물 Top 5
- 스플래시: SITE_BRAND_LOGO_TEXT localStorage 제거
2026-05-20 12:15:13 +09:00

66 lines
1.8 KiB
JavaScript

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()