Files
stats/packages/trpc/src/routers/realtime.ts
Carl-Gerhard Lindesvärd da59622dce fix: overall perf improvements
* fix: ignore private ips

* fix: performance related fixes

* fix: simply event buffer

* fix: default to 1 events queue shard

* add: cleanup scripts

* fix: comments

* fix comments

* fix

* fix: groupmq

* wip

* fix: sync cachable

* remove cluster names and add it behind env flag (if someone want to scale)

* fix

* wip

* better logger

* remove reqid and user agent

* fix lock

* remove wait_for_async_insert
2025-11-15 22:13:59 +01:00

150 lines
4.0 KiB
TypeScript

import { z } from 'zod';
import {
type EventMeta,
TABLE_NAMES,
ch,
chQuery,
clix,
db,
formatClickhouseDate,
getEventList,
} from '@openpanel/db';
import { subMinutes } from 'date-fns';
import sqlstring from 'sqlstring';
import { createTRPCRouter, protectedProcedure } from '../trpc';
export const realtimeRouter = createTRPCRouter({
coordinates: protectedProcedure
.input(z.object({ projectId: z.string() }))
.query(async ({ input }) => {
const res = await chQuery<{
city: string;
country: string;
long: number;
lat: number;
}>(
`SELECT DISTINCT country, city, longitude as long, latitude as lat FROM ${TABLE_NAMES.events} WHERE project_id = ${sqlstring.escape(input.projectId)} AND created_at >= '${formatClickhouseDate(subMinutes(new Date(), 30))}' ORDER BY created_at DESC`,
);
return res;
}),
activeSessions: protectedProcedure
.input(z.object({ projectId: z.string() }))
.query(async ({ input }) => {
return getEventList({
projectId: input.projectId,
take: 30,
select: {
name: true,
path: true,
origin: true,
referrer: true,
referrerName: true,
referrerType: true,
country: true,
device: true,
os: true,
browser: true,
createdAt: true,
profile: true,
meta: true,
},
});
}),
paths: protectedProcedure
.input(z.object({ projectId: z.string() }))
.query(async ({ input }) => {
const res = await clix(ch)
.select<{
origin: string;
path: string;
count: number;
avg_duration: number;
unique_sessions: number;
}>([
'origin',
'path',
'COUNT(*) as count',
'COUNT(DISTINCT session_id) as unique_sessions',
'round(avg(duration)/1000, 2) as avg_duration',
])
.from(TABLE_NAMES.events)
.where('project_id', '=', input.projectId)
.where('path', '!=', '')
.where(
'created_at',
'>=',
formatClickhouseDate(subMinutes(new Date(), 30)),
)
.groupBy(['path', 'origin'])
.orderBy('count', 'DESC')
.limit(100)
.execute();
return res;
}),
referrals: protectedProcedure
.input(z.object({ projectId: z.string() }))
.query(async ({ input }) => {
const res = await clix(ch)
.select<{
referrer_name: string;
count: number;
avg_duration: number;
unique_sessions: number;
}>([
'referrer_name',
'COUNT(*) as count',
'COUNT(DISTINCT session_id) as unique_sessions',
'round(avg(duration)/1000, 2) as avg_duration',
])
.from(TABLE_NAMES.events)
.where('project_id', '=', input.projectId)
.where('referrer_name', 'IS NOT NULL')
.where(
'created_at',
'>=',
formatClickhouseDate(subMinutes(new Date(), 30)),
)
.groupBy(['referrer_name'])
.orderBy('count', 'DESC')
.limit(100)
.execute();
return res;
}),
geo: protectedProcedure
.input(z.object({ projectId: z.string() }))
.query(async ({ input }) => {
const res = await clix(ch)
.select<{
country: string;
city: string;
count: number;
avg_duration: number;
unique_sessions: number;
}>([
'country',
'city',
'COUNT(*) as count',
'COUNT(DISTINCT session_id) as unique_sessions',
'round(avg(duration)/1000, 2) as avg_duration',
])
.from(TABLE_NAMES.events)
.where('project_id', '=', input.projectId)
.where(
'created_at',
'>=',
formatClickhouseDate(subMinutes(new Date(), 30)),
)
.groupBy(['country', 'city'])
.orderBy('count', 'DESC')
.limit(100)
.execute();
return res;
}),
});