Files
Nexus/packages/api/src/router/resource-owned-read-access.ts
T
Hartmut 1df208dbcc feat(timeline): add pulse animation for in-flight drag mutations
Allocation bars that have active optimistic overrides (post-drag,
awaiting server confirmation) now pulse subtly via animate-pulse.
The pending set is derived from the existing optimisticAllocations
map keys, requiring no additional state.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-09 13:28:46 +02:00

67 lines
1.7 KiB
TypeScript

import { TRPCError } from "@trpc/server";
import { isAdminOrManager } from "@capakraken/shared";
import type { TRPCContext } from "../trpc.js";
export type OwnedResourceReadContext = Pick<TRPCContext, "db" | "dbUser">;
export function canManageOwnedResourceReads(ctx: { dbUser: { systemRole: string } | null }): boolean {
return isAdminOrManager(ctx.dbUser?.systemRole);
}
export async function findOwnedReadResourceId(
ctx: OwnedResourceReadContext,
): Promise<string | null> {
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<void> {
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<string | null | undefined> {
if (canManageOwnedResourceReads(ctx)) {
return requestedResourceId;
}
const ownedResourceId = await findOwnedReadResourceId(ctx);
if (requestedResourceId && requestedResourceId !== ownedResourceId) {
throw new TRPCError({
code: "FORBIDDEN",
message: forbiddenMessage,
});
}
return ownedResourceId;
}