import { TRPCError } from "@trpc/server"; import type { TRPCContext } from "../trpc.js"; export type OwnedResourceReadContext = Pick; export function canManageOwnedResourceReads(ctx: { dbUser: { systemRole: string } | null }): boolean { const role = ctx.dbUser?.systemRole; return role === "ADMIN" || role === "MANAGER"; } export async function findOwnedReadResourceId( ctx: OwnedResourceReadContext, ): Promise { if (!ctx.dbUser?.id) { return null; } if (!ctx.db.resource || typeof ctx.db.resource.findFirst !== "function") { return null; } const resource = await ctx.db.resource.findFirst({ where: { userId: ctx.dbUser.id }, select: { id: true }, }); return resource?.id ?? null; } export async function assertCanReadOwnedResource( ctx: OwnedResourceReadContext, resourceId: string, forbiddenMessage: string, ): Promise { if (canManageOwnedResourceReads(ctx)) { return; } const ownedResourceId = await findOwnedReadResourceId(ctx); if (!ownedResourceId || ownedResourceId !== resourceId) { throw new TRPCError({ code: "FORBIDDEN", message: forbiddenMessage, }); } } export async function resolveOwnedResourceReadFilter( ctx: OwnedResourceReadContext, requestedResourceId: string | undefined, forbiddenMessage: string, ): Promise { if (canManageOwnedResourceReads(ctx)) { return requestedResourceId; } const ownedResourceId = await findOwnedReadResourceId(ctx); if (requestedResourceId && requestedResourceId !== ownedResourceId) { throw new TRPCError({ code: "FORBIDDEN", message: forbiddenMessage, }); } return ownedResourceId; }