refactor: route/schema consistency (parseId, broad invalidation, Czech messages, Zod 4 strictObject)

- OfferDetail: invalidate broad ["offers"] not ["offers","list"] (CLAUDE.md convention)
- trips.ts/sessions.ts: use parseId helper (validated 400) instead of raw parseInt for route ids
- audit-log.ts: z.object().strict() -> z.strictObject() (Zod 4 idiom)
- attendance.ts, orders/offers schemas: English user-facing strings -> Czech
- project-files.ts: unify 'not found' phrasing to the codebase-dominant 'nenalezen' form

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
BOHA
2026-06-06 02:29:04 +02:00
parent 522afefc80
commit 9ae49c0d6a
8 changed files with 43 additions and 45 deletions

View File

@@ -185,7 +185,7 @@ export default async function attendanceRoutes(
return error(reply, "Nedostatečná oprávnění", 403);
}
const attendanceId = Number(query.attendance_id);
if (!attendanceId) return error(reply, "Missing attendance_id", 400);
if (!attendanceId) return error(reply, "Chybí attendance_id", 400);
const data = await attendanceService.getProjectLogs(attendanceId);
return reply.send({ success: true, data });
}
@@ -193,7 +193,7 @@ export default async function attendanceRoutes(
// --- action=location: single record with GPS data ---
if (action === "location") {
const id = Number(query.id);
if (!id) return error(reply, "Missing id", 400);
if (!id) return error(reply, "Chybí id záznamu", 400);
const record = await attendanceService.getLocationRecord(id);
if (!record) return error(reply, "Záznam nenalezen", 404);
const isAdmin = authData.permissions.includes("attendance.manage");

View File

@@ -12,12 +12,10 @@ import { parseBody } from "../../schemas/common";
// forensic history.
const DELETE_ALL_CONFIRM = "DELETE_ALL_AUDIT" as const;
const AuditCleanupSchema = z
.object({
days: z.number().int().nonnegative().optional(),
confirm: z.string().optional(),
})
.strict();
const AuditCleanupSchema = z.strictObject({
days: z.number().int().nonnegative().optional(),
confirm: z.string().optional(),
});
export default async function auditLogRoutes(
fastify: FastifyInstance,

View File

@@ -58,7 +58,7 @@ export default async function projectFilesRoutes(
const { project_id: projectIdStr, path: subPath = "" } = parsedQuery.data;
const projectId = Number(projectIdStr);
const project = await getProjectForFiles(projectId);
if (!project) return error(reply, "Projekt nebyl nalezen", 404);
if (!project) return error(reply, "Projekt nenalezen", 404);
if (!fm.isConfigured()) {
return error(reply, "Souborový systém není nakonfigurován", 500);
@@ -70,7 +70,7 @@ export default async function projectFilesRoutes(
return error(reply, "Projekt nemá číslo projektu");
const result = fm.downloadFile(project.project_number, subPath);
if (!result) return error(reply, "Soubor nebyl nalezen", 404);
if (!result) return error(reply, "Soubor nenalezen", 404);
const stream = fs.createReadStream(result.filePath);
const encodedName = encodeURIComponent(result.fileName);
@@ -89,7 +89,7 @@ export default async function projectFilesRoutes(
const result = fm.listFiles(project.project_number, subPath);
if (result === null) {
return error(reply, "Složka nebyla nalezena", 404);
return error(reply, "Složka nenalezena", 404);
}
return success(reply, {
@@ -109,7 +109,7 @@ export default async function projectFilesRoutes(
if ("error" in parsedQuery) return error(reply, parsedQuery.error, 400);
const projectId = Number(parsedQuery.data.project_id);
const project = await getProjectForFiles(projectId);
if (!project) return error(reply, "Projekt nebyl nalezen", 404);
if (!project) return error(reply, "Projekt nenalezen", 404);
if (!project.project_number)
return error(reply, "Projekt nemá číslo projektu");
@@ -162,7 +162,7 @@ export default async function projectFilesRoutes(
if ("error" in parsedQuery) return error(reply, parsedQuery.error, 400);
const projectId = Number(parsedQuery.data.project_id);
const project = await getProjectForFiles(projectId);
if (!project) return error(reply, "Projekt nebyl nalezen", 404);
if (!project) return error(reply, "Projekt nenalezen", 404);
if (!project.project_number)
return error(reply, "Projekt nemá číslo projektu");
@@ -225,7 +225,7 @@ export default async function projectFilesRoutes(
if ("error" in parsedQuery) return error(reply, parsedQuery.error, 400);
const projectId = Number(parsedQuery.data.project_id);
const project = await getProjectForFiles(projectId);
if (!project) return error(reply, "Projekt nebyl nalezen", 404);
if (!project) return error(reply, "Projekt nenalezen", 404);
if (!project.project_number)
return error(reply, "Projekt nemá číslo projektu");
@@ -267,7 +267,7 @@ export default async function projectFilesRoutes(
if ("error" in parsedQuery) return error(reply, parsedQuery.error, 400);
const projectId = Number(parsedQuery.data.project_id);
const project = await getProjectForFiles(projectId);
if (!project) return error(reply, "Projekt nebyl nalezen", 404);
if (!project) return error(reply, "Projekt nenalezen", 404);
if (!project.project_number)
return error(reply, "Projekt nemá číslo projektu");

View File

@@ -1,7 +1,7 @@
import { FastifyInstance } from "fastify";
import prisma from "../../config/database";
import { requireAuth } from "../../middleware/auth";
import { success, error } from "../../utils/response";
import { success, error, parseId } from "../../utils/response";
import { hashToken } from "../../services/auth";
/** Parse user-agent string into browser, OS, and device icon */
@@ -81,8 +81,8 @@ export default async function sessionsRoutes(
"/:id",
{ preHandler: requireAuth },
async (request, reply) => {
const id = parseInt(request.params.id, 10);
if (Number.isNaN(id)) return error(reply, "Neplatné ID relace", 400);
const id = parseId((request.params as any).id, reply);
if (id === null) return;
const authData = request.authData!;
const session = await prisma.refresh_tokens.findFirst({

View File

@@ -2,7 +2,7 @@ import { FastifyInstance } from "fastify";
import prisma from "../../config/database";
import { requireAuth, requirePermission } from "../../middleware/auth";
import { logAudit } from "../../services/audit";
import { success, error } from "../../utils/response";
import { success, error, parseId } from "../../utils/response";
import { parsePagination, buildPaginationMeta } from "../../utils/pagination";
import { parseBody } from "../../schemas/common";
import { CreateTripSchema, UpdateTripSchema } from "../../schemas/trips.schema";
@@ -189,8 +189,8 @@ export default async function tripsRoutes(
"/last-km/:vehicleId",
{ preHandler: requirePermission("trips.manage") },
async (request, reply) => {
const vehicleId = parseInt(request.params.vehicleId, 10);
if (isNaN(vehicleId)) return error(reply, "Neplatné ID vozidla", 400);
const vehicleId = parseId((request.params as any).vehicleId, reply);
if (vehicleId === null) return;
const [lastTrip, vehicle] = await Promise.all([
prisma.trips.findFirst({
@@ -261,8 +261,8 @@ export default async function tripsRoutes(
"/:id",
{ preHandler: requireAuth },
async (request, reply) => {
const id = parseInt(request.params.id, 10);
if (isNaN(id)) return error(reply, "Neplatné ID", 400);
const id = parseId((request.params as any).id, reply);
if (id === null) return;
const parsed = parseBody(UpdateTripSchema, request.body);
if ("error" in parsed) return error(reply, parsed.error, 400);
const body = parsed.data;
@@ -335,8 +335,8 @@ export default async function tripsRoutes(
"/:id",
{ preHandler: requireAuth },
async (request, reply) => {
const id = parseInt(request.params.id, 10);
if (isNaN(id)) return error(reply, "Neplatné ID", 400);
const id = parseId((request.params as any).id, reply);
if (id === null) return;
const authData = request.authData!;
const existing = await prisma.trips.findUnique({ where: { id } });
if (!existing) return error(reply, "Jízda nenalezena", 404);