-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathanalytics.ts
64 lines (54 loc) · 1.53 KB
/
analytics.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
import { JsonStore } from './store/json-store'
interface UrlInfo {
hits: number
}
const ignoreRoutes = ['/data/flushall']
export class Analytics {
tempKeyCount = 0
temp: { [key: string]: UrlInfo | undefined } = {}
storing = false
recordUrlVisit(url: string) {
if (ignoreRoutes.find((r) => url.startsWith(r))) {
return // ignore this route
}
if (this.temp[url] === undefined) {
this.tempKeyCount++
if (this.tempKeyCount > 1_000) {
// this might likely be an attack, so stop analytics
return
}
this.temp[url] = { hits: 0 }
}
this.temp[url]!.hits += 1
}
async storeTemp() {
if (this.storing) return
this.storing = true
const store = new JsonStore(`data/_analytics/${date()}.json`, true)
const entries = Object.entries(this.temp)
this.temp = {}
const resultArray: (UrlInfo | undefined)[] = await store._getMultiple<UrlInfo>(
entries.map((e) => e[0])
)
for (let i = 0; i < resultArray.length; i++) {
const result = resultArray[i]
const key = entries[i][0]
const value = entries[i][1]!
if (result === undefined) {
resultArray[i] = value
await store.set(key, value)
} else {
// merge data
result.hits += value.hits
}
}
await store._setMultiple(
entries.map((entry, i) => ({ key: entry[0], value: resultArray[i] }))
)
this.storing = false
}
}
export function date() {
const d = new Date()
return `${d.getFullYear()}-${d.getMonth()}-${d.getDate()}`
}