import { and, eq } from "drizzle-orm" import type { FastifyInstance } from "fastify" import { authProfiles } from "../../db/schema" import { createScannerInboxService, SCANNER_DAV_PATH, SCANNER_UPLOAD_LIMIT, type ScannerInboxService } from "../modules/scanner-inbox.service" import { assignmentTargets, type FileAssignments } from "../modules/scanner-inbox-assignments" export default async function documentInboxRoutes(server: FastifyInstance, options: { service?: ScannerInboxService }) { const service = options.service || createScannerInboxService(server.db) server.addHook("preHandler", async (req, reply) => { if (!req.user?.tenant_id) return reply.code(400).send({ error: "Bitte einen Mandanten auswählen." }) const [profile] = await server.db.select({ portal: authProfiles.customer_for_portal }).from(authProfiles) .where(and(eq(authProfiles.tenant_id, req.user.tenant_id), eq(authProfiles.user_id, req.user.user_id))).limit(1) if (profile?.portal) return reply.code(403).send({ error: "Die Dokumentenablage ist nur intern verfügbar." }) if (req.routeOptions.url?.includes("/scanner") && !req.user.is_admin) { return reply.code(403).send({ error: "Scanner-Zugänge können nur Administratoren verwalten." }) } reply.header("Cache-Control", "no-store") }) server.get("/document-inbox", async (req, reply) => { const query = req.query as { limit?: string; offset?: string; search?: string } const limit = Number(query.limit ?? 50) const offset = Number(query.offset ?? 0) if (!Number.isSafeInteger(limit) || limit < 1 || limit > 100 || !Number.isSafeInteger(offset) || offset < 0 || (query.search?.length || 0) > 200) { return reply.code(400).send({ error: "Ungültige Such- oder Seiteneinstellungen." }) } return service.list(req.user.tenant_id!, limit, offset, query.search?.trim() || "") }) server.get("/document-inbox/filing-options", async req => service.filingOptions(req.user.tenant_id!)) server.get("/document-inbox/count", async req => service.openCount(req.user.tenant_id!)) server.get<{ Querystring: { kind: string; search?: string } }>("/document-inbox/assignment-options", { schema: { querystring: { type: "object", required: ["kind"], properties: { kind: { type: "string", enum: Object.keys(assignmentTargets) }, search: { type: "string", maxLength: 200 }, } } }, }, async req => service.assignmentOptions(req.user.tenant_id!, req.query.kind, req.query.search?.trim() || "")) server.get<{ Params: { id: string } }>("/document-inbox/:id/assignments", { schema: { params: { type: "object", required: ["id"], properties: { id: { type: "string", format: "uuid" } } } }, }, async req => service.getAssignments(req.user.tenant_id!, req.params.id)) server.get<{ Params: { id: string } }>("/document-inbox/:id/suggestion", { schema: { params: { type: "object", required: ["id"], properties: { id: { type: "string", format: "uuid" } } } }, }, async req => service.getSuggestion(req.user.tenant_id!, req.params.id)) server.post<{ Params: { id: string } }>("/document-inbox/:id/suggestion", { schema: { params: { type: "object", required: ["id"], properties: { id: { type: "string", format: "uuid" } } } }, }, async (req, reply) => reply.code(202).send(await service.queueSuggestion(req.user.tenant_id!, req.params.id))) server.post<{ Params: { id: string } }>("/document-inbox/:id/archive", { schema: { params: { type: "object", required: ["id"], properties: { id: { type: "string", format: "uuid" } } } }, }, async req => service.archiveDocument(req.user.tenant_id!, req.user.user_id, req.params.id)) server.post<{ Params: { id: string }; Body: { folder: string | null; type: string; assignments?: FileAssignments } }>("/document-inbox/:id/file", { schema: { params: { type: "object", required: ["id"], properties: { id: { type: "string", format: "uuid" } } }, body: { type: "object", required: ["folder", "type"], additionalProperties: false, properties: { folder: { anyOf: [{ type: "string", format: "uuid" }, { type: "null" }] }, type: { type: "string", format: "uuid" }, assignments: { type: "object", additionalProperties: false, properties: Object.fromEntries(Object.keys(assignmentTargets).map(kind => [kind, { anyOf: [{ type: "null" }, { type: "integer", minimum: 1, maximum: Number.MAX_SAFE_INTEGER }, { type: "string", minLength: 1, maxLength: 36 }] }])) }, } }, }, }, async req => service.fileDocument(req.user.tenant_id!, req.user.user_id, req.params.id, req.body.folder, req.body.type, req.body.assignments)) server.get("/document-inbox/scanner", async req => ({ connection: await service.connection(req.user.tenant_id!), path: SCANNER_DAV_PATH, maxFileSize: SCANNER_UPLOAD_LIMIT, })) server.post("/document-inbox/scanner/credentials", async req => ({ connection: await service.credentials(req.user.tenant_id!), path: SCANNER_DAV_PATH, })) server.delete("/document-inbox/scanner", async (_req, reply) => { await service.disable(_req.user.tenant_id!) return reply.code(204).send() }) }