v1.3.3: 자체 최소 통계 및 스플래시 localStorage 정리
- 일별 익명 방문자 해시·사이트/게시물 통계(030 마이그레이션) - POST /api/analytics/pageview, 관리자 analytics API, 클라이언트 트래커 - 관리자 대시보드 통계 카드·인기 게시물 Top 5 - 스플래시: SITE_BRAND_LOGO_TEXT localStorage 제거
This commit is contained in:
68
server/utils/analytics-pageview-input.js
Normal file
68
server/utils/analytics-pageview-input.js
Normal file
@@ -0,0 +1,68 @@
|
||||
import { z } from 'zod'
|
||||
import {
|
||||
isBotUserAgent,
|
||||
isTrackableAnalyticsPath,
|
||||
normalizePostSlugForAnalytics
|
||||
} from '../../lib/analytics.js'
|
||||
import { getPostBySlug } from '../repositories/content-repository.js'
|
||||
import {
|
||||
createVisitorHashFromEvent,
|
||||
recordAnalyticsPageview
|
||||
} from '../repositories/analytics-repository.js'
|
||||
|
||||
const pageviewInputSchema = z.object({
|
||||
path: z.string().trim().min(1).max(500),
|
||||
postSlug: z.string().trim().max(200).optional().default(''),
|
||||
read: z.boolean().optional().default(false)
|
||||
})
|
||||
|
||||
/**
|
||||
* 페이지뷰 추적 요청을 처리한다.
|
||||
* @param {import('h3').H3Event} event - H3 이벤트
|
||||
* @returns {Promise<{ ok: true }>}
|
||||
*/
|
||||
export const handleAnalyticsPageview = async (event) => {
|
||||
const parsedBody = pageviewInputSchema.safeParse(await readBody(event))
|
||||
|
||||
if (!parsedBody.success) {
|
||||
throw createError({
|
||||
statusCode: 400,
|
||||
message: '통계 요청 형식이 올바르지 않습니다.'
|
||||
})
|
||||
}
|
||||
|
||||
const body = parsedBody.data
|
||||
const userAgent = String(getRequestHeader(event, 'user-agent') || '')
|
||||
|
||||
if (isBotUserAgent(userAgent)) {
|
||||
return { ok: true }
|
||||
}
|
||||
|
||||
if (!isTrackableAnalyticsPath(body.path)) {
|
||||
return { ok: true }
|
||||
}
|
||||
|
||||
const postSlug = normalizePostSlugForAnalytics(body.postSlug)
|
||||
let postId = null
|
||||
|
||||
if (postSlug) {
|
||||
const post = await getPostBySlug(postSlug)
|
||||
if (!post) {
|
||||
return { ok: true }
|
||||
}
|
||||
postId = post.id
|
||||
}
|
||||
|
||||
const visitorHash = createVisitorHashFromEvent(event)
|
||||
const isReadEvent = Boolean(body.read)
|
||||
|
||||
await recordAnalyticsPageview({
|
||||
visitorHash,
|
||||
postId,
|
||||
recordSite: !isReadEvent,
|
||||
recordView: Boolean(postId) && !isReadEvent,
|
||||
recordRead: Boolean(postId) && isReadEvent
|
||||
})
|
||||
|
||||
return { ok: true }
|
||||
}
|
||||
Reference in New Issue
Block a user