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; }), });