import { z } from 'zod'; import { db, getClientById, getClientByIdCached, getId, getProjectByIdCached, getProjectsByOrganizationId, } from '@openpanel/db'; import { stripTrailingSlash } from '@openpanel/common'; import { zProject } from '@openpanel/validation'; import { addDays, addHours } from 'date-fns'; import { getProjectAccess } from '../access'; import { TRPCAccessError } from '../errors'; import { createTRPCRouter, protectedProcedure } from '../trpc'; export const projectRouter = createTRPCRouter({ list: protectedProcedure .input( z.object({ organizationId: z.string().nullable(), }), ) .query(async ({ input: { organizationId } }) => { if (organizationId === null) return []; return getProjectsByOrganizationId(organizationId); }), update: protectedProcedure .input(zProject.partial()) .mutation(async ({ input, ctx }) => { if (!input.id) { throw new Error('Project ID is required to update a project'); } const access = await getProjectAccess({ userId: ctx.session.userId, projectId: input.id, }); if (!access) { throw TRPCAccessError('You do not have access to this project'); } const res = await db.project.update({ where: { id: input.id, }, data: { name: input.name, crossDomain: input.crossDomain, filters: input.filters === undefined ? undefined : input.filters || [], domain: input.domain === undefined ? undefined : input.domain ? stripTrailingSlash(input.domain) : null, cors: input.cors === undefined ? undefined : input.cors.map((c) => stripTrailingSlash(c)) || [], }, include: { clients: { select: { id: true, }, }, }, }); await Promise.all([ getProjectByIdCached.clear(input.id), res.clients.map((client) => { getClientByIdCached.clear(client.id); }), ]); return res; }), create: protectedProcedure .input( zProject.omit({ id: true }).merge( z.object({ organizationId: z.string(), }), ), ) .mutation(async ({ input }) => { return db.project.create({ data: { id: await getId('project', input.name), organizationId: input.organizationId, name: input.name, domain: input.domain, cors: input.cors, crossDomain: input.crossDomain, filters: [], }, }); }), delete: protectedProcedure .input( z.object({ projectId: z.string(), }), ) .mutation(async ({ input, ctx }) => { const access = await getProjectAccess({ userId: ctx.session.userId, projectId: input.projectId, }); if (!access) { throw TRPCAccessError('You do not have access to this project'); } await db.project.update({ where: { id: input.projectId, }, data: { deleteAt: addHours(new Date(), 24), }, }); return true; }), cancelDeletion: protectedProcedure .input( z.object({ projectId: z.string(), }), ) .mutation(async ({ input, ctx }) => { const access = await getProjectAccess({ userId: ctx.session.userId, projectId: input.projectId, }); if (!access) { throw TRPCAccessError('You do not have access to this project'); } await db.project.update({ where: { id: input.projectId, }, data: { deleteAt: null, }, }); return true; }), });