feat: dashboard v2, esm, upgrades (#211)

* esm

* wip

* wip

* wip

* wip

* wip

* wip

* subscription notice

* wip

* wip

* wip

* fix envs

* fix: update docker build

* fix

* esm/types

* delete dashboard :D

* add patches to dockerfiles

* update packages + catalogs + ts

* wip

* remove native libs

* ts

* improvements

* fix redirects and fetching session

* try fix favicon

* fixes

* fix

* order and resize reportds within a dashboard

* improvements

* wip

* added userjot to dashboard

* fix

* add op

* wip

* different cache key

* improve date picker

* fix table

* event details loading

* redo onboarding completely

* fix login

* fix

* fix

* extend session, billing and improve bars

* fix

* reduce price on 10M
This commit is contained in:
Carl-Gerhard Lindesvärd
2025-10-16 12:27:44 +02:00
committed by GitHub
parent 436e81ecc9
commit 81a7e5d62e
741 changed files with 32695 additions and 16996 deletions

View File

@@ -0,0 +1,143 @@
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;
}>([
'origin',
'path',
'COUNT(*) as count',
'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;
}>([
'referrer_name',
'COUNT(*) as count',
'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;
}>([
'country',
'city',
'COUNT(*) as count',
'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;
}),
});