Files
pn-new-crm/src/app/api/v1/berths/[id]/route.ts

55 lines
1.6 KiB
TypeScript
Raw Normal View History

import { NextResponse } from 'next/server';
import { withAuth, withPermission } from '@/lib/api/helpers';
import { parseBody } from '@/lib/api/route-helpers';
import { updateBerthSchema } from '@/lib/validators/berths';
import { getBerthById, updateBerth, deleteBerth } from '@/lib/services/berths.service';
import { errorResponse } from '@/lib/errors';
// GET /api/v1/berths/[id]
export const GET = withAuth(
withPermission('berths', 'view', async (req, ctx, params) => {
try {
const berth = await getBerthById(params.id!, ctx.portId);
return NextResponse.json({ data: berth });
} catch (error) {
return errorResponse(error);
}
}),
);
// PATCH /api/v1/berths/[id]
export const PATCH = withAuth(
withPermission('berths', 'edit', async (req, ctx, params) => {
try {
const body = await parseBody(req, updateBerthSchema);
const updated = await updateBerth(params.id!, ctx.portId, body, {
userId: ctx.userId,
portId: ctx.portId,
ipAddress: ctx.ipAddress,
userAgent: ctx.userAgent,
});
return NextResponse.json({ data: updated });
} catch (error) {
return errorResponse(error);
}
}),
);
// DELETE /api/v1/berths/[id]
export const DELETE = withAuth(
withPermission('berths', 'edit', async (_req, ctx, params) => {
try {
await deleteBerth(params.id!, ctx.portId, {
userId: ctx.userId,
portId: ctx.portId,
ipAddress: ctx.ipAddress,
userAgent: ctx.userAgent,
});
fix(audit): non-Documenso backlog sweep — port-binding, NULLS NOT DISTINCT, custom merge tokens, company docs Wave through the remaining audit-final-deferred items that aren't blocked on the back-burnered Documenso work. Multi-tenant isolation: - Storage proxy ProxyTokenPayload gains optional `p` (port slug) claim; verifier asserts `key.startsWith(${p}/)`. Defense-in-depth against a buggy issuer in some future code path that mixes port scopes — every storage key generated by generateStorageKey() already prefixes the slug. document-sends opts in for 24h emailed download links; other callers continue working unchanged via the optional field. DB schema reconciliation: - Migration 0047 rebuilds system_settings unique index with NULLS NOT DISTINCT (Postgres 15+) so global settings (port_id IS NULL) are uniquely keyed by `key` alone. Surfaced + dedupe'd 65 duplicate (storage_backend, NULL) rows that had accumulated from race-prone delete-then-insert patterns in ocr-config / settings / residential- stages / ai-budget services. All four services converted to true onConflictDoUpdate upserts so the race window is closed. API uniformity: - Response shape standardization: 16 routes converted from `{ success: true }` to 204 No Content. CLAUDE.md documents the convention (`{ data: <T> }` for content, 204 for empty mutations, portal-auth retains `{ success: true }` for the frontend's auth chain). - req.json() → parseBody() migration across 9 admin/CRM routes (custom-fields, expenses/export ×3, currency convert, search/recently-viewed, admin/duplicates, berths/pdf-{upload-url, versions, parse-results}). Uniform 400 error shapes for ZodError-flagged bodies. Custom-fields merge tokens (shipped end-to-end): - merge-fields.ts gains CUSTOM_MERGE_TOKEN_RE + helpers for the `{{custom.<fieldName>}}` shape. - document-templates validator accepts the dynamic shape alongside the static catalog tokens. - document-sends.service mergeCustomFieldValues resolver fetches per-port custom_field_definitions for client/interest/berth contexts and substitutes stored values keyed by `{{custom.fieldName}}`. - custom-fields-manager amber banner updated to reflect that merge tokens now expand (search index + entity-diff remain documented design limitations). /api/v1/files cross-entity filtering: - Validator + listFiles + uploadFile accept companyId AND yachtId alongside clientId. file-upload-zone propagates both. - New CompanyFilesTab component mirrors ClientFilesTab; restored as a visible Documents tab in company-tabs.tsx (was a hidden stub). Inline TODOs: - Reviewed remaining two TODOs (per-user reminder schedule, import worker handlers). Both are placeholders for future feature surfaces, not bugs — per-port digest works for every customer; nothing currently enqueues import jobs (verified). Annotated in BACKLOG. BACKLOG.md updated to reflect what landed and what's still pending (Documenso-related items still bundled with the back-burnered phases). Tests: 1185/1185 vitest, tsc clean.
2026-05-08 02:20:27 +02:00
return new NextResponse(null, { status: 204 });
} catch (error) {
return errorResponse(error);
}
}),
);