-
-
Notifications
You must be signed in to change notification settings - Fork 2.6k
/
Copy pathaccess-log.ts
123 lines (107 loc) · 3.72 KB
/
access-log.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
import type { H3Event } from 'h3'
import { getFlag } from '@/utils/flag'
import { parseAcceptLanguage } from 'intl-parse-accept-language'
import { UAParser } from 'ua-parser-js'
import {
CLIs,
Crawlers,
Emails,
ExtraDevices,
Fetchers,
InApps,
MediaPlayers,
Vehicles,
} from 'ua-parser-js/extensions'
import { parseURL } from 'ufo'
function toBlobNumber(blob: string) {
return +blob.replace(/\D/g, '')
}
export const blobsMap = {
blob1: 'slug',
blob2: 'url',
blob3: 'ua',
blob4: 'ip',
blob5: 'referer',
blob6: 'country',
blob7: 'region',
blob8: 'city',
blob9: 'timezone',
blob10: 'language',
blob11: 'os',
blob12: 'browser',
blob13: 'browserType',
blob14: 'device',
blob15: 'deviceType',
} as const
export type BlobsMap = typeof blobsMap
export type BlobsKey = keyof BlobsMap
export type LogsKey = BlobsMap[BlobsKey]
export type LogsMap = { [key in LogsKey]: string | undefined }
export const logsMap: LogsMap = Object.entries(blobsMap).reduce((acc, [k, v]) => ({ ...acc, [v]: k }), {}) as LogsMap
function logs2blobs(logs: LogsMap) {
// @ts-expect-error todo
return Object.keys(blobsMap).sort((a, b) => toBlobNumber(a) - toBlobNumber(b)).map(key => logs[blobsMap[key]] || '')
}
function blobs2logs(blobs: string[]) {
const logsList = Object.keys(blobsMap)
// @ts-expect-error todo
return blobs.reduce((logs: LogsMap, blob, i) => {
// @ts-expect-error todo
logs[blobsMap[logsList[i]]] = blob
return logs
}, {})
}
export function useAccessLog(event: H3Event) {
const ip = getHeader(event, 'x-real-ip') || getRequestIP(event, { xForwardedFor: true })
const { host: referer } = parseURL(getHeader(event, 'referer'))
const acceptLanguage = getHeader(event, 'accept-language') || ''
const language = (parseAcceptLanguage(acceptLanguage) || [])[0]
const userAgent = getHeader(event, 'user-agent') || ''
const uaInfo = (new UAParser(userAgent, {
// eslint-disable-next-line ts/ban-ts-comment
// @ts-expect-error
browser: [Crawlers.browser || [], CLIs.browser || [], Emails.browser || [], Fetchers.browser || [], InApps.browser || [], MediaPlayers.browser || [], Vehicles.browser || []].flat(),
// eslint-disable-next-line ts/ban-ts-comment
// @ts-expect-error
device: [ExtraDevices.device || []].flat(),
})).getResult()
const { request: { cf } } = event.context.cloudflare
const link = event.context.link || {}
const isBot = cf?.botManagement?.verifiedBot
|| ['crawler', 'fetcher'].includes(uaInfo?.browser?.type || '')
|| ['spider', 'bot'].includes(uaInfo?.browser?.name?.toLowerCase() || '')
const { disableBotAccessLog } = useRuntimeConfig(event)
if (isBot && disableBotAccessLog) {
console.log('bot access log disabled:', userAgent)
return Promise.resolve()
}
const regionNames = new Intl.DisplayNames(['en'], { type: 'region' })
const countryName = regionNames.of(cf?.country || 'WD') // fallback to "Worldwide"
const accessLogs = {
url: link.url,
slug: link.slug,
ua: userAgent,
ip,
referer,
country: cf?.country,
region: `${getFlag(cf?.country)} ${[cf?.region, countryName].filter(Boolean).join(',')}`,
city: `${getFlag(cf?.country)} ${[cf?.city, countryName].filter(Boolean).join(',')}`,
timezone: cf?.timezone,
language,
os: uaInfo?.os?.name,
browser: uaInfo?.browser?.name,
browserType: uaInfo?.browser?.type,
device: uaInfo?.device?.model,
deviceType: uaInfo?.device?.type,
}
if (process.env.NODE_ENV === 'production') {
return hubAnalytics().put({
indexes: [link.id], // only one index
blobs: logs2blobs(accessLogs),
})
}
else {
console.log('access logs:', logs2blobs(accessLogs), blobs2logs(logs2blobs(accessLogs)))
return Promise.resolve()
}
}