L10b Betrieb & Aufräumen: /api/v1 über gemeinsamen Adapter, einheitliches Fehlerformat, Rate Limiting
Aufräumpunkt a: Die lane-lokalen API-Kontexte (imports/_context.ts, sync/api-context.ts,
reports/http.ts, work-orders/_http.ts mit moduleGuard) sind entfernt. Alle v1-Routen laufen über
requireApiContext (DB-autoritative Rechte, 401/403) und withApi/toErrorResponse (respond.ts):
- Fehlerformat überall { error: { code, message, details? } }; invalid und blocked → 422,
conflict → 409, payload_too_large → 413, rate_limited → 429 + Retry-After.
- Same-Origin-Prüfung in withApi für jede Mutation vor der Anmeldung (vorher fehlte sie bei
imports, reports und work-orders).
- Rate Limiting je Nutzer mit rate-limit.ts: api (API_RATE_LIMIT_PER_MINUTE, 300/min) und
apiField für sync/uploads/field (API_FIELD_RATE_LIMIT_PER_MINUTE, 1200/min).
- Clients angepasst: Import-Uploader liest das neue Fehlerformat, Upload/Outbox werten 422 als
endgültig ungültig (429 bleibt transient mit Backoff).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,11 +1,12 @@
|
||||
import { requireApiContext } from "@/server/api/context";
|
||||
import { json, withApi } from "@/server/api/respond";
|
||||
import { approveReport } from "@/server/services/reports/approve";
|
||||
import { reportDto, withReportsApi } from "@/server/services/reports/http";
|
||||
import { reportDto } from "@/server/services/reports/dto";
|
||||
|
||||
/** POST /api/v1/reports/:id/approve — team lead → team_approved, backoffice → approved (+ PDF job). */
|
||||
export async function POST(_req: Request, { params }: { params: Promise<{ id: string }> }) {
|
||||
/** POST /api/v1/reports/:id/approve — team lead → team_approved, backoffice → approved (+ PDF job). Approval rights are checked in the service. */
|
||||
export const POST = withApi(async (_req: Request, { params }: { params: Promise<{ id: string }> }) => {
|
||||
const ctx = await requireApiContext("reports", "report:read");
|
||||
const { id } = await params;
|
||||
return withReportsApi(["report:read"], async (ctx) => {
|
||||
const report = await approveReport(ctx, { reportId: id });
|
||||
return Response.json({ report: reportDto(report) });
|
||||
});
|
||||
}
|
||||
const report = await approveReport(ctx, { reportId: id });
|
||||
return json({ report: reportDto(report) });
|
||||
});
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
import { requireApiContext } from "@/server/api/context";
|
||||
import { withApi } from "@/server/api/respond";
|
||||
import { fileResponse, openReportFile } from "@/server/services/reports/files";
|
||||
import { withReportsApi } from "@/server/services/reports/http";
|
||||
|
||||
/** GET /api/v1/reports/:id/files/:documentId — photo/signature/logo referenced by the report snapshot. */
|
||||
export async function GET(req: Request, { params }: { params: Promise<{ id: string; documentId: string }> }) {
|
||||
export const GET = withApi(async (req: Request, { params }: { params: Promise<{ id: string; documentId: string }> }) => {
|
||||
const ctx = await requireApiContext("reports", "report:read");
|
||||
const { id, documentId } = await params;
|
||||
const download = new URL(req.url).searchParams.get("download") === "1";
|
||||
return withReportsApi(["report:read"], async (ctx) => fileResponse(await openReportFile(ctx, id, documentId), { download }));
|
||||
}
|
||||
return fileResponse(await openReportFile(ctx, id, documentId), { download });
|
||||
});
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
import { requireApiContext } from "@/server/api/context";
|
||||
import { withApi } from "@/server/api/respond";
|
||||
import { fileResponse, openReportFile } from "@/server/services/reports/files";
|
||||
import { withReportsApi } from "@/server/services/reports/http";
|
||||
|
||||
/** GET /api/v1/reports/:id/pdf — the immutable PDF of an approved report (?download=1 for attachment). */
|
||||
export async function GET(req: Request, { params }: { params: Promise<{ id: string }> }) {
|
||||
export const GET = withApi(async (req: Request, { params }: { params: Promise<{ id: string }> }) => {
|
||||
const ctx = await requireApiContext("reports", "report:read");
|
||||
const { id } = await params;
|
||||
const download = new URL(req.url).searchParams.get("download") === "1";
|
||||
return withReportsApi(["report:read"], async (ctx) => fileResponse(await openReportFile(ctx, id, "pdf"), { download }));
|
||||
}
|
||||
return fileResponse(await openReportFile(ctx, id, "pdf"), { download });
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user