Files
CapaKraken/packages/api/src/router/blueprint-support.ts
T
Hartmut e3551fb78f fix(api): validate rolePresets with RolePresetsSchema before DB cast
Replace z.array(z.unknown()) with RolePresetsSchema for blueprint
role presets mutation input, ensuring structural validation before
Prisma JSON cast. Also adds SECURITY.md for vulnerability disclosure.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-11 08:35:02 +02:00

112 lines
3.2 KiB
TypeScript

import type { Prisma, PrismaClient } from "@capakraken/db";
import {
CreateBlueprintSchema,
UpdateBlueprintSchema,
type BlueprintFieldDefinition,
} from "@capakraken/shared";
import { TRPCError } from "@trpc/server";
import { z } from "zod";
type BlueprintIdentifierDb = Pick<PrismaClient, "blueprint">;
type BlueprintGlobalFieldDefinition = BlueprintFieldDefinition & {
blueprintId: string;
blueprintName: string;
};
type BlueprintFieldDefinitionSource = {
id: string;
name: string;
fieldDefs: unknown;
};
type CreateBlueprintInput = z.infer<typeof CreateBlueprintSchema>;
type UpdateBlueprintInput = z.infer<typeof UpdateBlueprintSchema>;
export async function findBlueprintByIdentifier<TBlueprint>(
db: BlueprintIdentifierDb,
identifier: string,
extraArgs: Record<string, unknown>,
): Promise<TBlueprint> {
const normalizedIdentifier = identifier.trim();
let blueprint = (await db.blueprint.findUnique({
where: { id: normalizedIdentifier },
...extraArgs,
})) as TBlueprint | null;
if (!blueprint) {
blueprint = (await db.blueprint.findFirst({
where: { name: { equals: normalizedIdentifier, mode: "insensitive" } },
...extraArgs,
})) as TBlueprint | null;
}
if (!blueprint) {
blueprint = (await db.blueprint.findFirst({
where: { name: { contains: normalizedIdentifier, mode: "insensitive" } },
...extraArgs,
})) as TBlueprint | null;
}
if (!blueprint) {
throw new TRPCError({
code: "NOT_FOUND",
message: `Blueprint not found: ${normalizedIdentifier}`,
});
}
return blueprint;
}
export function buildBlueprintCreateData(
input: CreateBlueprintInput,
): Prisma.BlueprintUncheckedCreateInput {
return {
name: input.name,
target: input.target,
description: input.description ?? null,
fieldDefs: input.fieldDefs as unknown as Prisma.InputJsonValue,
defaults: input.defaults as unknown as Prisma.InputJsonValue,
validationRules: input.validationRules as unknown as Prisma.InputJsonValue,
};
}
export function buildBlueprintUpdateData(
input: UpdateBlueprintInput,
): Prisma.BlueprintUncheckedUpdateInput {
return {
...(input.name !== undefined ? { name: input.name } : {}),
...(input.description !== undefined ? { description: input.description } : {}),
...(input.fieldDefs !== undefined
? { fieldDefs: input.fieldDefs as unknown as Prisma.InputJsonValue }
: {}),
...(input.defaults !== undefined
? { defaults: input.defaults as unknown as Prisma.InputJsonValue }
: {}),
...(input.validationRules !== undefined
? { validationRules: input.validationRules as unknown as Prisma.InputJsonValue }
: {}),
};
}
export function buildBlueprintRolePresetsUpdateData(
rolePresets: readonly Record<string, unknown>[],
): Prisma.BlueprintUncheckedUpdateInput {
return {
rolePresets: rolePresets as unknown as Prisma.InputJsonValue,
};
}
export function expandGlobalBlueprintFieldDefs(
blueprints: BlueprintFieldDefinitionSource[],
): BlueprintGlobalFieldDefinition[] {
return blueprints.flatMap((blueprint) =>
(blueprint.fieldDefs as BlueprintFieldDefinition[]).map((fieldDef) => ({
...fieldDef,
blueprintId: blueprint.id,
blueprintName: blueprint.name,
})),
);
}