feat(proxies): CM-9 backend — polymorphic point-of-contact + resolver
- proxies table (migration 0095, port_id cascade), one per client/interest/yacht - service: get/set(upsert)/clear + resolveEffectiveProxy (yacht → interest → client precedence), port-scoped with entity-in-port guard - per-entity sub-resource routes (/clients|interests|yachts/[id]/proxy) reusing each entity's existing view/edit permission (no new permission resource) - 3 integration tests (CRUD/upsert, tenant guard, resolution precedence) Backend only — ProxyCard UI on the 3 detail pages to follow. tsc clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
56
src/lib/api/proxy-route-handlers.ts
Normal file
56
src/lib/api/proxy-route-handlers.ts
Normal file
@@ -0,0 +1,56 @@
|
||||
/**
|
||||
* CM-9: shared GET/PUT/DELETE handlers for the per-entity proxy sub-resource
|
||||
* (`/api/v1/{clients|interests|yachts}/[id]/proxy`). Each entity's route.ts
|
||||
* binds these with its own permission resource so we reuse existing
|
||||
* clients/interests/yachts gating instead of a new permission.
|
||||
*/
|
||||
|
||||
import { NextResponse } from 'next/server';
|
||||
|
||||
import { type RouteHandler } from '@/lib/api/helpers';
|
||||
import { parseBody } from '@/lib/api/route-helpers';
|
||||
import { errorResponse } from '@/lib/errors';
|
||||
import { clearProxy, getProxy, setProxy } from '@/lib/services/proxies.service';
|
||||
import { setProxySchema, type ProxyEntityType } from '@/lib/validators/proxies';
|
||||
|
||||
export function makeProxyHandlers(entityType: ProxyEntityType) {
|
||||
const getHandler: RouteHandler = async (req, ctx, params) => {
|
||||
try {
|
||||
const proxy = await getProxy(ctx.portId, entityType, params.id!);
|
||||
return NextResponse.json({ data: proxy });
|
||||
} catch (error) {
|
||||
return errorResponse(error);
|
||||
}
|
||||
};
|
||||
|
||||
const putHandler: RouteHandler = async (req, ctx, params) => {
|
||||
try {
|
||||
const body = await parseBody(req, setProxySchema);
|
||||
const proxy = await setProxy(ctx.portId, entityType, params.id!, body, {
|
||||
userId: ctx.userId,
|
||||
portId: ctx.portId,
|
||||
ipAddress: ctx.ipAddress,
|
||||
userAgent: ctx.userAgent,
|
||||
});
|
||||
return NextResponse.json({ data: proxy });
|
||||
} catch (error) {
|
||||
return errorResponse(error);
|
||||
}
|
||||
};
|
||||
|
||||
const deleteHandler: RouteHandler = async (req, ctx, params) => {
|
||||
try {
|
||||
await clearProxy(ctx.portId, entityType, params.id!, {
|
||||
userId: ctx.userId,
|
||||
portId: ctx.portId,
|
||||
ipAddress: ctx.ipAddress,
|
||||
userAgent: ctx.userAgent,
|
||||
});
|
||||
return new NextResponse(null, { status: 204 });
|
||||
} catch (error) {
|
||||
return errorResponse(error);
|
||||
}
|
||||
};
|
||||
|
||||
return { getHandler, putHandler, deleteHandler };
|
||||
}
|
||||
Reference in New Issue
Block a user