fix(api): add Zod bounds on financial fields, type vacation router, type scenarioData

- dailyCostCents, hoursPerDay, percentage now validated at API boundary
- vacation router no longer uses ctx.db as any
- scenarioData reads through typed Zod schema

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-09 14:08:16 +02:00
parent d3bfa8ca98
commit 9a42615a21
5 changed files with 158 additions and 11 deletions
@@ -1,5 +1,9 @@
import { PermissionKey } from "@capakraken/shared";
import { z } from "zod";
import {
parseScenarioBaselineResult,
parseScenarioSimulationResult,
} from "../lib/scenario-schema.js";
import type { TRPCContext } from "../trpc.js";
import { requirePermission } from "../trpc.js";
import { applyProjectScenario } from "./scenario-apply.js";
@@ -34,14 +38,16 @@ export async function getProjectScenarioBaseline(
input: z.infer<typeof ScenarioProjectIdInputSchema>,
) {
requirePermission(ctx, PermissionKey.VIEW_COSTS);
return readProjectScenarioBaseline(ctx.db, input.projectId);
const result = await readProjectScenarioBaseline(ctx.db, input.projectId);
return parseScenarioBaselineResult(result);
}
export async function simulateScenario(
ctx: Pick<TRPCContext, "db">,
input: z.infer<typeof ScenarioSimulationInputSchema>,
) {
return simulateProjectScenario(ctx.db, input);
const result = await simulateProjectScenario(ctx.db, input);
return parseScenarioSimulationResult(result);
}
export async function applyScenario(
@@ -5,7 +5,7 @@ import { z } from "zod";
import { findUniqueOrThrow } from "../db/helpers.js";
import { RESOURCE_BRIEF_SELECT } from "../db/selects.js";
import { createAuditEntry } from "../lib/audit.js";
import { checkBatchVacationConflicts, checkVacationConflicts } from "../lib/vacation-conflicts.js";
import { checkBatchVacationConflicts, checkVacationConflicts, type DbClient as VacationConflictDbClient } from "../lib/vacation-conflicts.js";
import { emitVacationUpdated } from "../sse/event-bus.js";
import { adminProcedure, managerProcedure, protectedProcedure } from "../trpc.js";
import {
@@ -73,8 +73,7 @@ export const vacationManagementProcedures = {
const userRecord = await findVacationActor(ctx.db, ctx.session.user?.email);
const conflictResult = await checkVacationConflicts(
// eslint-disable-next-line @typescript-eslint/no-explicit-any
ctx.db as any,
ctx.db as unknown as VacationConflictDbClient,
input.id,
userRecord?.id,
);
@@ -186,8 +185,7 @@ export const vacationManagementProcedures = {
}
const conflictMap = await checkBatchVacationConflicts(
// eslint-disable-next-line @typescript-eslint/no-explicit-any
ctx.db as any,
ctx.db as unknown as VacationConflictDbClient,
vacations.map((vacation) => vacation.id),
userRecord?.id,
);