Skip to content

Commit 28807f4

Browse files
committed
feat(dashboard): Added simple polling based KPI collection service(whole table sanner)
1 parent 4b6e89b commit 28807f4

1 file changed

Lines changed: 134 additions & 0 deletions

File tree

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
import { DashboardMetrics, EventsByKindCount, TopTalker } from '../types'
2+
import { createLogger } from '../../factories/logger-factory'
3+
import { DatabaseClient } from '../../@types/base'
4+
5+
const debug = createLogger('dashboard-service:kpi-collector')
6+
7+
const DEFAULT_TRACKED_KINDS = [7, 1, 6, 1984, 4, 3, 9735]
8+
9+
const toNumber = (value: unknown): number => {
10+
if (typeof value === 'number') {
11+
return value
12+
}
13+
14+
if (typeof value === 'string' && value !== '') {
15+
return Number(value)
16+
}
17+
18+
return 0
19+
}
20+
21+
export class KPICollectorService {
22+
public constructor(
23+
private readonly dbClient: DatabaseClient,
24+
private readonly trackedKinds: number[] = DEFAULT_TRACKED_KINDS,
25+
private readonly topTalkersLimit = 10,
26+
private readonly recentDays = 3,
27+
) { }
28+
29+
public async collectMetrics(): Promise<DashboardMetrics> {
30+
debug('collecting dashboard metrics')
31+
32+
const [
33+
eventsByKind,
34+
admittedUsers,
35+
satsPaid,
36+
allTimeTopTalkers,
37+
recentTopTalkers,
38+
] = await Promise.all([
39+
this.getEventsByKind(),
40+
this.getAdmittedUsersCount(),
41+
this.getSatsPaidCount(),
42+
this.getTopTalkersAllTime(),
43+
this.getTopTalkersRecent(),
44+
])
45+
46+
return {
47+
eventsByKind,
48+
admittedUsers,
49+
satsPaid,
50+
topTalkers: {
51+
allTime: allTimeTopTalkers,
52+
recent: recentTopTalkers,
53+
},
54+
}
55+
}
56+
57+
private async getEventsByKind(): Promise<EventsByKindCount[]> {
58+
const rows = await this.dbClient('events')
59+
.select('event_kind')
60+
.count('id as count')
61+
.whereIn('event_kind', this.trackedKinds)
62+
.groupBy('event_kind')
63+
.orderBy('count', 'desc') as Array<{ event_kind: number, count: string }>
64+
65+
const other = await this.dbClient('events')
66+
.whereNotIn('event_kind', this.trackedKinds)
67+
.count<{ count: string }>('id as count')
68+
.first()
69+
70+
const eventsByKind = rows.map((row) => {
71+
return {
72+
kind: String(row.event_kind),
73+
count: toNumber(row.count),
74+
}
75+
})
76+
77+
eventsByKind.push({
78+
kind: 'other',
79+
count: toNumber(other?.count),
80+
})
81+
82+
return eventsByKind
83+
}
84+
85+
private async getAdmittedUsersCount(): Promise<number> {
86+
const result = await this.dbClient('users')
87+
.where('is_admitted', true)
88+
.count<{ count: string }>('pubkey as count')
89+
.first()
90+
91+
return toNumber(result?.count)
92+
}
93+
94+
private async getSatsPaidCount(): Promise<number> {
95+
const result = await this.dbClient('users')
96+
.where('is_admitted', true)
97+
.sum<{ total: string | null }>('balance as total')
98+
.first()
99+
100+
const millisats = toNumber(result?.total)
101+
return millisats / 1000
102+
}
103+
104+
private async getTopTalkersAllTime(): Promise<TopTalker[]> {
105+
const rows = await this.dbClient('events')
106+
.select(this.dbClient.raw("encode(event_pubkey, 'hex') as pubkey"))
107+
.count('id as count')
108+
.groupBy('event_pubkey')
109+
.orderBy('count', 'desc')
110+
.limit(this.topTalkersLimit) as unknown as Array<{ pubkey: string | Buffer, count: string | number }>
111+
112+
return rows.map((row) => ({
113+
pubkey: String(row.pubkey),
114+
count: toNumber(row.count),
115+
}))
116+
}
117+
118+
private async getTopTalkersRecent(): Promise<TopTalker[]> {
119+
const since = new Date(Date.now() - this.recentDays * 24 * 60 * 60 * 1000)
120+
121+
const rows = await this.dbClient('events')
122+
.select(this.dbClient.raw("encode(event_pubkey, 'hex') as pubkey"))
123+
.count('id as count')
124+
.where('first_seen', '>=', since)
125+
.groupBy('event_pubkey')
126+
.orderBy('count', 'desc')
127+
.limit(this.topTalkersLimit) as unknown as Array<{ pubkey: string | Buffer, count: string | number }>
128+
129+
return rows.map((row) => ({
130+
pubkey: String(row.pubkey),
131+
count: toNumber(row.count),
132+
}))
133+
}
134+
}

0 commit comments

Comments
 (0)