feat: insights
* fix: migration for newly created self-hosting instances * fix: build script * wip * wip * wip * fix: tailwind css
This commit is contained in:
committed by
GitHub
parent
1e4f02fb5e
commit
5f38560373
@@ -5,6 +5,7 @@ import { clientRouter } from './routers/client';
|
||||
import { dashboardRouter } from './routers/dashboard';
|
||||
import { eventRouter } from './routers/event';
|
||||
import { importRouter } from './routers/import';
|
||||
import { insightRouter } from './routers/insight';
|
||||
import { integrationRouter } from './routers/integration';
|
||||
import { notificationRouter } from './routers/notification';
|
||||
import { onboardingRouter } from './routers/onboarding';
|
||||
@@ -47,6 +48,7 @@ export const appRouter = createTRPCRouter({
|
||||
overview: overviewRouter,
|
||||
realtime: realtimeRouter,
|
||||
chat: chatRouter,
|
||||
insight: insightRouter,
|
||||
});
|
||||
|
||||
// export type definition of API
|
||||
|
||||
102
packages/trpc/src/routers/insight.ts
Normal file
102
packages/trpc/src/routers/insight.ts
Normal file
@@ -0,0 +1,102 @@
|
||||
import { db } from '@openpanel/db';
|
||||
import { z } from 'zod';
|
||||
import { getProjectAccess } from '../access';
|
||||
import { TRPCAccessError } from '../errors';
|
||||
import { createTRPCRouter, protectedProcedure } from '../trpc';
|
||||
|
||||
export const insightRouter = createTRPCRouter({
|
||||
list: protectedProcedure
|
||||
.input(
|
||||
z.object({
|
||||
projectId: z.string(),
|
||||
limit: z.number().min(1).max(100).optional().default(50),
|
||||
}),
|
||||
)
|
||||
.query(async ({ input: { projectId, limit }, ctx }) => {
|
||||
const access = await getProjectAccess({
|
||||
userId: ctx.session.userId,
|
||||
projectId,
|
||||
});
|
||||
|
||||
if (!access) {
|
||||
throw TRPCAccessError('You do not have access to this project');
|
||||
}
|
||||
|
||||
// Fetch more insights than needed to account for deduplication
|
||||
const allInsights = await db.projectInsight.findMany({
|
||||
where: {
|
||||
projectId,
|
||||
state: 'active',
|
||||
moduleKey: {
|
||||
notIn: ['page-trends', 'entry-pages'],
|
||||
},
|
||||
},
|
||||
orderBy: {
|
||||
impactScore: 'desc',
|
||||
},
|
||||
take: limit * 3, // Fetch 3x to account for deduplication
|
||||
});
|
||||
|
||||
// WindowKind priority: yesterday (1) > rolling_7d (2) > rolling_30d (3)
|
||||
const windowKindPriority: Record<string, number> = {
|
||||
yesterday: 1,
|
||||
rolling_7d: 2,
|
||||
rolling_30d: 3,
|
||||
};
|
||||
|
||||
// Group by moduleKey + dimensionKey, keep only highest priority windowKind
|
||||
const deduplicated = new Map<string, (typeof allInsights)[0]>();
|
||||
for (const insight of allInsights) {
|
||||
const key = `${insight.moduleKey}:${insight.dimensionKey}`;
|
||||
const existing = deduplicated.get(key);
|
||||
const currentPriority = windowKindPriority[insight.windowKind] ?? 999;
|
||||
const existingPriority = existing
|
||||
? (windowKindPriority[existing.windowKind] ?? 999)
|
||||
: 999;
|
||||
|
||||
// Keep if no existing, or if current has higher priority (lower number)
|
||||
if (!existing || currentPriority < existingPriority) {
|
||||
deduplicated.set(key, insight);
|
||||
}
|
||||
}
|
||||
|
||||
// Convert back to array, sort by impactScore, and limit
|
||||
const insights = Array.from(deduplicated.values())
|
||||
.sort((a, b) => (b.impactScore ?? 0) - (a.impactScore ?? 0))
|
||||
.slice(0, limit)
|
||||
.map(({ impactScore, ...rest }) => rest); // Remove impactScore from response
|
||||
|
||||
return insights;
|
||||
}),
|
||||
|
||||
listAll: protectedProcedure
|
||||
.input(
|
||||
z.object({
|
||||
projectId: z.string(),
|
||||
limit: z.number().min(1).max(500).optional().default(200),
|
||||
}),
|
||||
)
|
||||
.query(async ({ input: { projectId, limit }, ctx }) => {
|
||||
const access = await getProjectAccess({
|
||||
userId: ctx.session.userId,
|
||||
projectId,
|
||||
});
|
||||
|
||||
if (!access) {
|
||||
throw TRPCAccessError('You do not have access to this project');
|
||||
}
|
||||
|
||||
const insights = await db.projectInsight.findMany({
|
||||
where: {
|
||||
projectId,
|
||||
state: 'active',
|
||||
},
|
||||
orderBy: {
|
||||
impactScore: 'desc',
|
||||
},
|
||||
take: limit,
|
||||
});
|
||||
|
||||
return insights;
|
||||
}),
|
||||
});
|
||||
Reference in New Issue
Block a user