Files
stats/packages/trpc/src/routers/dashboard.ts
Carl-Gerhard Lindesvärd 81a7e5d62e 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
2025-10-16 12:27:44 +02:00

169 lines
4.1 KiB
TypeScript

import { PrismaError } from 'prisma-error-enum';
import { z } from 'zod';
import {
db,
getDashboardById,
getDashboardsByProjectId,
getId,
getProjectById,
} from '@openpanel/db';
import type { Prisma } from '@openpanel/db';
import { getProjectAccess } from '../access';
import { TRPCAccessError, TRPCNotFoundError } from '../errors';
import { createTRPCRouter, protectedProcedure } from '../trpc';
export const dashboardRouter = createTRPCRouter({
list: protectedProcedure
.input(
z.object({
projectId: z.string(),
}),
)
.query(({ input }) => {
return getDashboardsByProjectId(input.projectId);
}),
byId: protectedProcedure
.input(
z.object({
id: z.string(),
projectId: z.string(),
}),
)
.query(async ({ input, ctx }) => {
const access = await getProjectAccess({
projectId: input.projectId,
userId: ctx.session.userId,
});
if (!access) {
throw TRPCAccessError('You do not have access to this project');
}
const dashboard = await getDashboardById(input.id, input.projectId);
if (!dashboard) {
throw TRPCNotFoundError('Dashboard not found');
}
return dashboard;
}),
create: protectedProcedure
.input(
z.object({
name: z.string(),
projectId: z.string(),
}),
)
.mutation(async ({ input, ctx }) => {
const access = await getProjectAccess({
projectId: input.projectId,
userId: ctx.session.userId,
});
if (!access) {
throw TRPCAccessError('You do not have access to this project');
}
const project = await getProjectById(input.projectId);
if (!project) {
throw TRPCNotFoundError('Project not found');
}
return db.dashboard.create({
data: {
id: await getId('dashboard', input.name),
projectId: input.projectId,
organizationId: project.organizationId,
name: input.name,
},
});
}),
update: protectedProcedure
.input(
z.object({
id: z.string(),
name: z.string(),
}),
)
.mutation(async ({ input, ctx }) => {
const dashboard = await db.dashboard.findUniqueOrThrow({
where: {
id: input.id,
},
});
const access = await getProjectAccess({
projectId: dashboard.projectId,
userId: ctx.session.userId,
});
if (!access) {
throw TRPCAccessError('You do not have access to this dashboard');
}
return db.dashboard.update({
where: {
id: input.id,
},
data: {
name: input.name,
},
});
}),
delete: protectedProcedure
.input(
z.object({
id: z.string(),
forceDelete: z.boolean().optional(),
}),
)
.mutation(async ({ input, ctx }) => {
const dashboard = await db.dashboard.findUniqueOrThrow({
where: {
id: input.id,
},
});
const access = await getProjectAccess({
projectId: dashboard.projectId,
userId: ctx.session.userId,
});
if (!access) {
throw TRPCAccessError('You do not have access to this dashboard');
}
try {
if (input.forceDelete) {
await db.report.deleteMany({
where: {
dashboardId: input.id,
},
});
}
await db.dashboard.delete({
where: {
id: input.id,
},
});
} catch (e) {
// Below does not work...
// error instanceof Prisma.PrismaClientKnownRequestError
if (typeof e === 'object' && e && 'code' in e) {
const error = e as Prisma.PrismaClientKnownRequestError;
switch (error.code) {
case PrismaError.ForeignConstraintViolation:
throw new Error(
'Cannot delete dashboard with associated reports',
);
default:
throw new Error('Unknown error deleting dashboard');
}
}
}
}),
});