fix(build): extract route.ts handlers to handlers.ts (CLAUDE.md convention)
8 API route files were exporting handler functions directly from route.ts, which Next.js 15 rejects with "$NAME is not a valid Route export field". Per CLAUDE.md convention, service-tested handler functions live in sibling handlers.ts files and route.ts only re-exports the GET/POST/etc. wrapped in withAuth(withPermission(...)). Discovered during the mobile-foundation Task 24 build validation; the route files predate this branch but the build was never re-run on data-model. Files: - berth-reservations/[id], companies/autocomplete, companies/[id]/members + nested mid/set-primary, yachts/autocomplete, yachts/[id]/transfer, yachts/[id]/ownership-history - Integration tests updated to import from handlers.ts (companies, memberships, reservations, yachts-detail) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
107
src/app/api/v1/berth-reservations/[id]/handlers.ts
Normal file
107
src/app/api/v1/berth-reservations/[id]/handlers.ts
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
import { NextResponse } from 'next/server';
|
||||||
|
import { z } from 'zod';
|
||||||
|
|
||||||
|
import { type RouteHandler } from '@/lib/api/helpers';
|
||||||
|
import { parseBody } from '@/lib/api/route-helpers';
|
||||||
|
import { requirePermission } from '@/lib/auth/permissions';
|
||||||
|
import { errorResponse } from '@/lib/errors';
|
||||||
|
import {
|
||||||
|
activate,
|
||||||
|
cancel,
|
||||||
|
endReservation,
|
||||||
|
getById,
|
||||||
|
} from '@/lib/services/berth-reservations.service';
|
||||||
|
|
||||||
|
// ─── PATCH body schema (action-based discriminated union) ────────────────────
|
||||||
|
|
||||||
|
const patchBodySchema = z.discriminatedUnion('action', [
|
||||||
|
z.object({
|
||||||
|
action: z.literal('activate'),
|
||||||
|
contractFileId: z.string().optional(),
|
||||||
|
effectiveDate: z.coerce.date().optional(),
|
||||||
|
}),
|
||||||
|
z.object({
|
||||||
|
action: z.literal('end'),
|
||||||
|
endDate: z.coerce.date(),
|
||||||
|
notes: z.string().optional(),
|
||||||
|
}),
|
||||||
|
z.object({
|
||||||
|
action: z.literal('cancel'),
|
||||||
|
reason: z.string().optional(),
|
||||||
|
}),
|
||||||
|
]);
|
||||||
|
|
||||||
|
// ─── Handlers ────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
export const getHandler: RouteHandler = async (_req, ctx, params) => {
|
||||||
|
try {
|
||||||
|
const reservation = await getById(params.id!, ctx.portId);
|
||||||
|
return NextResponse.json({ data: reservation });
|
||||||
|
} catch (error) {
|
||||||
|
return errorResponse(error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const patchHandler: RouteHandler = async (req, ctx, params) => {
|
||||||
|
try {
|
||||||
|
const body = await parseBody(req, patchBodySchema);
|
||||||
|
const meta = {
|
||||||
|
userId: ctx.userId,
|
||||||
|
portId: ctx.portId,
|
||||||
|
ipAddress: ctx.ipAddress,
|
||||||
|
userAgent: ctx.userAgent,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (body.action === 'activate') {
|
||||||
|
requirePermission(ctx, 'reservations', 'activate');
|
||||||
|
const result = await activate(
|
||||||
|
params.id!,
|
||||||
|
ctx.portId,
|
||||||
|
{
|
||||||
|
contractFileId: body.contractFileId,
|
||||||
|
effectiveDate: body.effectiveDate,
|
||||||
|
},
|
||||||
|
meta,
|
||||||
|
);
|
||||||
|
return NextResponse.json({ data: result });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (body.action === 'end') {
|
||||||
|
// `end` is lifecycle progression; same privilege as activate.
|
||||||
|
requirePermission(ctx, 'reservations', 'activate');
|
||||||
|
const result = await endReservation(
|
||||||
|
params.id!,
|
||||||
|
ctx.portId,
|
||||||
|
{ endDate: body.endDate, notes: body.notes },
|
||||||
|
meta,
|
||||||
|
);
|
||||||
|
return NextResponse.json({ data: result });
|
||||||
|
}
|
||||||
|
|
||||||
|
// action === 'cancel'
|
||||||
|
requirePermission(ctx, 'reservations', 'cancel');
|
||||||
|
const result = await cancel(params.id!, ctx.portId, { reason: body.reason }, meta);
|
||||||
|
return NextResponse.json({ data: result });
|
||||||
|
} catch (error) {
|
||||||
|
return errorResponse(error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const deleteHandler: RouteHandler = async (_req, ctx, params) => {
|
||||||
|
try {
|
||||||
|
await cancel(
|
||||||
|
params.id!,
|
||||||
|
ctx.portId,
|
||||||
|
{},
|
||||||
|
{
|
||||||
|
userId: ctx.userId,
|
||||||
|
portId: ctx.portId,
|
||||||
|
ipAddress: ctx.ipAddress,
|
||||||
|
userAgent: ctx.userAgent,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
return new NextResponse(null, { status: 204 });
|
||||||
|
} catch (error) {
|
||||||
|
return errorResponse(error);
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -1,110 +1,6 @@
|
|||||||
import { NextResponse } from 'next/server';
|
import { withAuth, withPermission } from '@/lib/api/helpers';
|
||||||
import { z } from 'zod';
|
|
||||||
|
|
||||||
import { withAuth, withPermission, type RouteHandler } from '@/lib/api/helpers';
|
import { getHandler, patchHandler, deleteHandler } from './handlers';
|
||||||
import { parseBody } from '@/lib/api/route-helpers';
|
|
||||||
import { requirePermission } from '@/lib/auth/permissions';
|
|
||||||
import { errorResponse } from '@/lib/errors';
|
|
||||||
import {
|
|
||||||
activate,
|
|
||||||
cancel,
|
|
||||||
endReservation,
|
|
||||||
getById,
|
|
||||||
} from '@/lib/services/berth-reservations.service';
|
|
||||||
|
|
||||||
// ─── PATCH body schema (action-based discriminated union) ────────────────────
|
|
||||||
|
|
||||||
const patchBodySchema = z.discriminatedUnion('action', [
|
|
||||||
z.object({
|
|
||||||
action: z.literal('activate'),
|
|
||||||
contractFileId: z.string().optional(),
|
|
||||||
effectiveDate: z.coerce.date().optional(),
|
|
||||||
}),
|
|
||||||
z.object({
|
|
||||||
action: z.literal('end'),
|
|
||||||
endDate: z.coerce.date(),
|
|
||||||
notes: z.string().optional(),
|
|
||||||
}),
|
|
||||||
z.object({
|
|
||||||
action: z.literal('cancel'),
|
|
||||||
reason: z.string().optional(),
|
|
||||||
}),
|
|
||||||
]);
|
|
||||||
|
|
||||||
// ─── Handlers ────────────────────────────────────────────────────────────────
|
|
||||||
|
|
||||||
export const getHandler: RouteHandler = async (_req, ctx, params) => {
|
|
||||||
try {
|
|
||||||
const reservation = await getById(params.id!, ctx.portId);
|
|
||||||
return NextResponse.json({ data: reservation });
|
|
||||||
} catch (error) {
|
|
||||||
return errorResponse(error);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export const patchHandler: RouteHandler = async (req, ctx, params) => {
|
|
||||||
try {
|
|
||||||
const body = await parseBody(req, patchBodySchema);
|
|
||||||
const meta = {
|
|
||||||
userId: ctx.userId,
|
|
||||||
portId: ctx.portId,
|
|
||||||
ipAddress: ctx.ipAddress,
|
|
||||||
userAgent: ctx.userAgent,
|
|
||||||
};
|
|
||||||
|
|
||||||
if (body.action === 'activate') {
|
|
||||||
requirePermission(ctx, 'reservations', 'activate');
|
|
||||||
const result = await activate(
|
|
||||||
params.id!,
|
|
||||||
ctx.portId,
|
|
||||||
{
|
|
||||||
contractFileId: body.contractFileId,
|
|
||||||
effectiveDate: body.effectiveDate,
|
|
||||||
},
|
|
||||||
meta,
|
|
||||||
);
|
|
||||||
return NextResponse.json({ data: result });
|
|
||||||
}
|
|
||||||
|
|
||||||
if (body.action === 'end') {
|
|
||||||
// `end` is lifecycle progression; same privilege as activate.
|
|
||||||
requirePermission(ctx, 'reservations', 'activate');
|
|
||||||
const result = await endReservation(
|
|
||||||
params.id!,
|
|
||||||
ctx.portId,
|
|
||||||
{ endDate: body.endDate, notes: body.notes },
|
|
||||||
meta,
|
|
||||||
);
|
|
||||||
return NextResponse.json({ data: result });
|
|
||||||
}
|
|
||||||
|
|
||||||
// action === 'cancel'
|
|
||||||
requirePermission(ctx, 'reservations', 'cancel');
|
|
||||||
const result = await cancel(params.id!, ctx.portId, { reason: body.reason }, meta);
|
|
||||||
return NextResponse.json({ data: result });
|
|
||||||
} catch (error) {
|
|
||||||
return errorResponse(error);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export const deleteHandler: RouteHandler = async (_req, ctx, params) => {
|
|
||||||
try {
|
|
||||||
await cancel(
|
|
||||||
params.id!,
|
|
||||||
ctx.portId,
|
|
||||||
{},
|
|
||||||
{
|
|
||||||
userId: ctx.userId,
|
|
||||||
portId: ctx.portId,
|
|
||||||
ipAddress: ctx.ipAddress,
|
|
||||||
userAgent: ctx.userAgent,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
return new NextResponse(null, { status: 204 });
|
|
||||||
} catch (error) {
|
|
||||||
return errorResponse(error);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export const GET = withAuth(withPermission('reservations', 'view', getHandler));
|
export const GET = withAuth(withPermission('reservations', 'view', getHandler));
|
||||||
// PATCH cannot use `withPermission` wrapper — the required permission depends
|
// PATCH cannot use `withPermission` wrapper — the required permission depends
|
||||||
|
|||||||
47
src/app/api/v1/companies/[id]/members/[mid]/handlers.ts
Normal file
47
src/app/api/v1/companies/[id]/members/[mid]/handlers.ts
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
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 { endMembership, updateMembership } from '@/lib/services/company-memberships.service';
|
||||||
|
import { endMembershipSchema, updateMembershipSchema } from '@/lib/validators/company-memberships';
|
||||||
|
|
||||||
|
export const patchHandler: RouteHandler = async (req, ctx, params) => {
|
||||||
|
try {
|
||||||
|
const body = await parseBody(req, updateMembershipSchema);
|
||||||
|
const updated = await updateMembership(params.mid!, 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);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const deleteHandler: RouteHandler = async (req, ctx, params) => {
|
||||||
|
try {
|
||||||
|
let endDate = new Date();
|
||||||
|
const text = await req.text();
|
||||||
|
if (text.length > 0) {
|
||||||
|
const parsed = endMembershipSchema.parse(JSON.parse(text));
|
||||||
|
endDate = parsed.endDate;
|
||||||
|
}
|
||||||
|
await endMembership(
|
||||||
|
params.mid!,
|
||||||
|
ctx.portId,
|
||||||
|
{ endDate },
|
||||||
|
{
|
||||||
|
userId: ctx.userId,
|
||||||
|
portId: ctx.portId,
|
||||||
|
ipAddress: ctx.ipAddress,
|
||||||
|
userAgent: ctx.userAgent,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
return new NextResponse(null, { status: 204 });
|
||||||
|
} catch (error) {
|
||||||
|
return errorResponse(error);
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -1,50 +1,6 @@
|
|||||||
import { NextResponse } from 'next/server';
|
import { withAuth, withPermission } from '@/lib/api/helpers';
|
||||||
|
|
||||||
import { withAuth, withPermission, type RouteHandler } from '@/lib/api/helpers';
|
import { patchHandler, deleteHandler } from './handlers';
|
||||||
import { parseBody } from '@/lib/api/route-helpers';
|
|
||||||
import { errorResponse } from '@/lib/errors';
|
|
||||||
import { endMembership, updateMembership } from '@/lib/services/company-memberships.service';
|
|
||||||
import { endMembershipSchema, updateMembershipSchema } from '@/lib/validators/company-memberships';
|
|
||||||
|
|
||||||
export const patchHandler: RouteHandler = async (req, ctx, params) => {
|
|
||||||
try {
|
|
||||||
const body = await parseBody(req, updateMembershipSchema);
|
|
||||||
const updated = await updateMembership(params.mid!, 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);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export const deleteHandler: RouteHandler = async (req, ctx, params) => {
|
|
||||||
try {
|
|
||||||
let endDate = new Date();
|
|
||||||
const text = await req.text();
|
|
||||||
if (text.length > 0) {
|
|
||||||
const parsed = endMembershipSchema.parse(JSON.parse(text));
|
|
||||||
endDate = parsed.endDate;
|
|
||||||
}
|
|
||||||
await endMembership(
|
|
||||||
params.mid!,
|
|
||||||
ctx.portId,
|
|
||||||
{ endDate },
|
|
||||||
{
|
|
||||||
userId: ctx.userId,
|
|
||||||
portId: ctx.portId,
|
|
||||||
ipAddress: ctx.ipAddress,
|
|
||||||
userAgent: ctx.userAgent,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
return new NextResponse(null, { status: 204 });
|
|
||||||
} catch (error) {
|
|
||||||
return errorResponse(error);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export const PATCH = withAuth(withPermission('memberships', 'manage', patchHandler));
|
export const PATCH = withAuth(withPermission('memberships', 'manage', patchHandler));
|
||||||
export const DELETE = withAuth(withPermission('memberships', 'manage', deleteHandler));
|
export const DELETE = withAuth(withPermission('memberships', 'manage', deleteHandler));
|
||||||
|
|||||||
@@ -0,0 +1,19 @@
|
|||||||
|
import { NextResponse } from 'next/server';
|
||||||
|
|
||||||
|
import { type RouteHandler } from '@/lib/api/helpers';
|
||||||
|
import { errorResponse } from '@/lib/errors';
|
||||||
|
import { setPrimary } from '@/lib/services/company-memberships.service';
|
||||||
|
|
||||||
|
export const setPrimaryHandler: RouteHandler = async (_req, ctx, params) => {
|
||||||
|
try {
|
||||||
|
const membership = await setPrimary(params.mid!, ctx.portId, {
|
||||||
|
userId: ctx.userId,
|
||||||
|
portId: ctx.portId,
|
||||||
|
ipAddress: ctx.ipAddress,
|
||||||
|
userAgent: ctx.userAgent,
|
||||||
|
});
|
||||||
|
return NextResponse.json({ data: membership });
|
||||||
|
} catch (error) {
|
||||||
|
return errorResponse(error);
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -1,21 +1,5 @@
|
|||||||
import { NextResponse } from 'next/server';
|
import { withAuth, withPermission } from '@/lib/api/helpers';
|
||||||
|
|
||||||
import { withAuth, withPermission, type RouteHandler } from '@/lib/api/helpers';
|
import { setPrimaryHandler } from './handlers';
|
||||||
import { errorResponse } from '@/lib/errors';
|
|
||||||
import { setPrimary } from '@/lib/services/company-memberships.service';
|
|
||||||
|
|
||||||
export const setPrimaryHandler: RouteHandler = async (_req, ctx, params) => {
|
|
||||||
try {
|
|
||||||
const membership = await setPrimary(params.mid!, ctx.portId, {
|
|
||||||
userId: ctx.userId,
|
|
||||||
portId: ctx.portId,
|
|
||||||
ipAddress: ctx.ipAddress,
|
|
||||||
userAgent: ctx.userAgent,
|
|
||||||
});
|
|
||||||
return NextResponse.json({ data: membership });
|
|
||||||
} catch (error) {
|
|
||||||
return errorResponse(error);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export const POST = withAuth(withPermission('memberships', 'manage', setPrimaryHandler));
|
export const POST = withAuth(withPermission('memberships', 'manage', setPrimaryHandler));
|
||||||
|
|||||||
40
src/app/api/v1/companies/[id]/members/handlers.ts
Normal file
40
src/app/api/v1/companies/[id]/members/handlers.ts
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
import { NextResponse } from 'next/server';
|
||||||
|
import { z } from 'zod';
|
||||||
|
|
||||||
|
import { type RouteHandler } from '@/lib/api/helpers';
|
||||||
|
import { parseBody, parseQuery } from '@/lib/api/route-helpers';
|
||||||
|
import { errorResponse } from '@/lib/errors';
|
||||||
|
import { addMembership, listByCompany } from '@/lib/services/company-memberships.service';
|
||||||
|
import { addMembershipSchema } from '@/lib/validators/company-memberships';
|
||||||
|
|
||||||
|
const listQuerySchema = z.object({
|
||||||
|
activeOnly: z
|
||||||
|
.enum(['true', 'false'])
|
||||||
|
.transform((v) => v === 'true')
|
||||||
|
.default('true'),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const listHandler: RouteHandler = async (req, ctx, params) => {
|
||||||
|
try {
|
||||||
|
const { activeOnly } = parseQuery(req, listQuerySchema);
|
||||||
|
const memberships = await listByCompany(params.id!, ctx.portId, { activeOnly });
|
||||||
|
return NextResponse.json({ data: memberships });
|
||||||
|
} catch (error) {
|
||||||
|
return errorResponse(error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const createHandler: RouteHandler = async (req, ctx, params) => {
|
||||||
|
try {
|
||||||
|
const body = await parseBody(req, addMembershipSchema);
|
||||||
|
const membership = await addMembership(params.id!, ctx.portId, body, {
|
||||||
|
userId: ctx.userId,
|
||||||
|
portId: ctx.portId,
|
||||||
|
ipAddress: ctx.ipAddress,
|
||||||
|
userAgent: ctx.userAgent,
|
||||||
|
});
|
||||||
|
return NextResponse.json({ data: membership }, { status: 201 });
|
||||||
|
} catch (error) {
|
||||||
|
return errorResponse(error);
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -1,43 +1,6 @@
|
|||||||
import { NextResponse } from 'next/server';
|
import { withAuth, withPermission } from '@/lib/api/helpers';
|
||||||
import { z } from 'zod';
|
|
||||||
|
|
||||||
import { withAuth, withPermission, type RouteHandler } from '@/lib/api/helpers';
|
import { listHandler, createHandler } from './handlers';
|
||||||
import { parseBody, parseQuery } from '@/lib/api/route-helpers';
|
|
||||||
import { errorResponse } from '@/lib/errors';
|
|
||||||
import { addMembership, listByCompany } from '@/lib/services/company-memberships.service';
|
|
||||||
import { addMembershipSchema } from '@/lib/validators/company-memberships';
|
|
||||||
|
|
||||||
const listQuerySchema = z.object({
|
|
||||||
activeOnly: z
|
|
||||||
.enum(['true', 'false'])
|
|
||||||
.transform((v) => v === 'true')
|
|
||||||
.default('true'),
|
|
||||||
});
|
|
||||||
|
|
||||||
export const listHandler: RouteHandler = async (req, ctx, params) => {
|
|
||||||
try {
|
|
||||||
const { activeOnly } = parseQuery(req, listQuerySchema);
|
|
||||||
const memberships = await listByCompany(params.id!, ctx.portId, { activeOnly });
|
|
||||||
return NextResponse.json({ data: memberships });
|
|
||||||
} catch (error) {
|
|
||||||
return errorResponse(error);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export const createHandler: RouteHandler = async (req, ctx, params) => {
|
|
||||||
try {
|
|
||||||
const body = await parseBody(req, addMembershipSchema);
|
|
||||||
const membership = await addMembership(params.id!, ctx.portId, body, {
|
|
||||||
userId: ctx.userId,
|
|
||||||
portId: ctx.portId,
|
|
||||||
ipAddress: ctx.ipAddress,
|
|
||||||
userAgent: ctx.userAgent,
|
|
||||||
});
|
|
||||||
return NextResponse.json({ data: membership }, { status: 201 });
|
|
||||||
} catch (error) {
|
|
||||||
return errorResponse(error);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export const GET = withAuth(withPermission('memberships', 'view', listHandler));
|
export const GET = withAuth(withPermission('memberships', 'view', listHandler));
|
||||||
export const POST = withAuth(withPermission('memberships', 'manage', createHandler));
|
export const POST = withAuth(withPermission('memberships', 'manage', createHandler));
|
||||||
|
|||||||
18
src/app/api/v1/companies/autocomplete/handlers.ts
Normal file
18
src/app/api/v1/companies/autocomplete/handlers.ts
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
import { NextResponse } from 'next/server';
|
||||||
|
|
||||||
|
import { type RouteHandler } from '@/lib/api/helpers';
|
||||||
|
import { errorResponse } from '@/lib/errors';
|
||||||
|
import { autocomplete } from '@/lib/services/companies.service';
|
||||||
|
|
||||||
|
export const autocompleteHandler: RouteHandler = async (req, ctx) => {
|
||||||
|
try {
|
||||||
|
const q = req.nextUrl.searchParams.get('q');
|
||||||
|
if (!q) {
|
||||||
|
return NextResponse.json({ data: [] });
|
||||||
|
}
|
||||||
|
const companies = await autocomplete(ctx.portId, q);
|
||||||
|
return NextResponse.json({ data: companies });
|
||||||
|
} catch (error) {
|
||||||
|
return errorResponse(error);
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -1,20 +1,5 @@
|
|||||||
import { NextResponse } from 'next/server';
|
import { withAuth, withPermission } from '@/lib/api/helpers';
|
||||||
|
|
||||||
import { withAuth, withPermission, type RouteHandler } from '@/lib/api/helpers';
|
import { autocompleteHandler } from './handlers';
|
||||||
import { errorResponse } from '@/lib/errors';
|
|
||||||
import { autocomplete } from '@/lib/services/companies.service';
|
|
||||||
|
|
||||||
export const autocompleteHandler: RouteHandler = async (req, ctx) => {
|
|
||||||
try {
|
|
||||||
const q = req.nextUrl.searchParams.get('q');
|
|
||||||
if (!q) {
|
|
||||||
return NextResponse.json({ data: [] });
|
|
||||||
}
|
|
||||||
const companies = await autocomplete(ctx.portId, q);
|
|
||||||
return NextResponse.json({ data: companies });
|
|
||||||
} catch (error) {
|
|
||||||
return errorResponse(error);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export const GET = withAuth(withPermission('companies', 'view', autocompleteHandler));
|
export const GET = withAuth(withPermission('companies', 'view', autocompleteHandler));
|
||||||
|
|||||||
14
src/app/api/v1/yachts/[id]/ownership-history/handlers.ts
Normal file
14
src/app/api/v1/yachts/[id]/ownership-history/handlers.ts
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
import { NextResponse } from 'next/server';
|
||||||
|
|
||||||
|
import { type RouteHandler } from '@/lib/api/helpers';
|
||||||
|
import { errorResponse } from '@/lib/errors';
|
||||||
|
import { listOwnershipHistory } from '@/lib/services/yachts.service';
|
||||||
|
|
||||||
|
export const historyHandler: RouteHandler = async (_req, ctx, params) => {
|
||||||
|
try {
|
||||||
|
const history = await listOwnershipHistory(params.id!, ctx.portId);
|
||||||
|
return NextResponse.json({ data: history });
|
||||||
|
} catch (error) {
|
||||||
|
return errorResponse(error);
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -1,16 +1,5 @@
|
|||||||
import { NextResponse } from 'next/server';
|
import { withAuth, withPermission } from '@/lib/api/helpers';
|
||||||
|
|
||||||
import { withAuth, withPermission, type RouteHandler } from '@/lib/api/helpers';
|
import { historyHandler } from './handlers';
|
||||||
import { errorResponse } from '@/lib/errors';
|
|
||||||
import { listOwnershipHistory } from '@/lib/services/yachts.service';
|
|
||||||
|
|
||||||
export const historyHandler: RouteHandler = async (req, ctx, params) => {
|
|
||||||
try {
|
|
||||||
const history = await listOwnershipHistory(params.id!, ctx.portId);
|
|
||||||
return NextResponse.json({ data: history });
|
|
||||||
} catch (error) {
|
|
||||||
return errorResponse(error);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export const GET = withAuth(withPermission('yachts', 'view', historyHandler));
|
export const GET = withAuth(withPermission('yachts', 'view', historyHandler));
|
||||||
|
|||||||
22
src/app/api/v1/yachts/[id]/transfer/handlers.ts
Normal file
22
src/app/api/v1/yachts/[id]/transfer/handlers.ts
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
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 { transferOwnership } from '@/lib/services/yachts.service';
|
||||||
|
import { transferOwnershipSchema } from '@/lib/validators/yachts';
|
||||||
|
|
||||||
|
export const transferHandler: RouteHandler = async (req, ctx, params) => {
|
||||||
|
try {
|
||||||
|
const body = await parseBody(req, transferOwnershipSchema);
|
||||||
|
const yacht = await transferOwnership(params.id!, ctx.portId, body, {
|
||||||
|
userId: ctx.userId,
|
||||||
|
portId: ctx.portId,
|
||||||
|
ipAddress: ctx.ipAddress,
|
||||||
|
userAgent: ctx.userAgent,
|
||||||
|
});
|
||||||
|
return NextResponse.json({ data: yacht });
|
||||||
|
} catch (error) {
|
||||||
|
return errorResponse(error);
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -1,24 +1,5 @@
|
|||||||
import { NextResponse } from 'next/server';
|
import { withAuth, withPermission } from '@/lib/api/helpers';
|
||||||
|
|
||||||
import { withAuth, withPermission, type RouteHandler } from '@/lib/api/helpers';
|
import { transferHandler } from './handlers';
|
||||||
import { parseBody } from '@/lib/api/route-helpers';
|
|
||||||
import { errorResponse } from '@/lib/errors';
|
|
||||||
import { transferOwnership } from '@/lib/services/yachts.service';
|
|
||||||
import { transferOwnershipSchema } from '@/lib/validators/yachts';
|
|
||||||
|
|
||||||
export const transferHandler: RouteHandler = async (req, ctx, params) => {
|
|
||||||
try {
|
|
||||||
const body = await parseBody(req, transferOwnershipSchema);
|
|
||||||
const yacht = await transferOwnership(params.id!, ctx.portId, body, {
|
|
||||||
userId: ctx.userId,
|
|
||||||
portId: ctx.portId,
|
|
||||||
ipAddress: ctx.ipAddress,
|
|
||||||
userAgent: ctx.userAgent,
|
|
||||||
});
|
|
||||||
return NextResponse.json({ data: yacht });
|
|
||||||
} catch (error) {
|
|
||||||
return errorResponse(error);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export const POST = withAuth(withPermission('yachts', 'transfer', transferHandler));
|
export const POST = withAuth(withPermission('yachts', 'transfer', transferHandler));
|
||||||
|
|||||||
18
src/app/api/v1/yachts/autocomplete/handlers.ts
Normal file
18
src/app/api/v1/yachts/autocomplete/handlers.ts
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
import { NextResponse } from 'next/server';
|
||||||
|
|
||||||
|
import { type RouteHandler } from '@/lib/api/helpers';
|
||||||
|
import { errorResponse } from '@/lib/errors';
|
||||||
|
import { autocomplete } from '@/lib/services/yachts.service';
|
||||||
|
|
||||||
|
export const autocompleteHandler: RouteHandler = async (req, ctx) => {
|
||||||
|
try {
|
||||||
|
const q = req.nextUrl.searchParams.get('q');
|
||||||
|
if (!q) {
|
||||||
|
return NextResponse.json({ data: [] });
|
||||||
|
}
|
||||||
|
const yachts = await autocomplete(ctx.portId, q);
|
||||||
|
return NextResponse.json({ data: yachts });
|
||||||
|
} catch (error) {
|
||||||
|
return errorResponse(error);
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -1,20 +1,5 @@
|
|||||||
import { NextResponse } from 'next/server';
|
import { withAuth, withPermission } from '@/lib/api/helpers';
|
||||||
|
|
||||||
import { withAuth, withPermission, type RouteHandler } from '@/lib/api/helpers';
|
import { autocompleteHandler } from './handlers';
|
||||||
import { errorResponse } from '@/lib/errors';
|
|
||||||
import { autocomplete } from '@/lib/services/yachts.service';
|
|
||||||
|
|
||||||
export const autocompleteHandler: RouteHandler = async (req, ctx) => {
|
|
||||||
try {
|
|
||||||
const q = req.nextUrl.searchParams.get('q');
|
|
||||||
if (!q) {
|
|
||||||
return NextResponse.json({ data: [] });
|
|
||||||
}
|
|
||||||
const yachts = await autocomplete(ctx.portId, q);
|
|
||||||
return NextResponse.json({ data: yachts });
|
|
||||||
} catch (error) {
|
|
||||||
return errorResponse(error);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export const GET = withAuth(withPermission('yachts', 'view', autocompleteHandler));
|
export const GET = withAuth(withPermission('yachts', 'view', autocompleteHandler));
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { describe, it, expect } from 'vitest';
|
|||||||
|
|
||||||
import { listHandler, createHandler } from '@/app/api/v1/companies/handlers';
|
import { listHandler, createHandler } from '@/app/api/v1/companies/handlers';
|
||||||
import { getHandler, patchHandler, deleteHandler } from '@/app/api/v1/companies/[id]/handlers';
|
import { getHandler, patchHandler, deleteHandler } from '@/app/api/v1/companies/[id]/handlers';
|
||||||
import { autocompleteHandler } from '@/app/api/v1/companies/autocomplete/route';
|
import { autocompleteHandler } from '@/app/api/v1/companies/autocomplete/handlers';
|
||||||
import { db } from '@/lib/db';
|
import { db } from '@/lib/db';
|
||||||
import { companies } from '@/lib/db/schema';
|
import { companies } from '@/lib/db/schema';
|
||||||
import { eq } from 'drizzle-orm';
|
import { eq } from 'drizzle-orm';
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import { describe, it, expect } from 'vitest';
|
import { describe, it, expect } from 'vitest';
|
||||||
import { eq } from 'drizzle-orm';
|
import { eq } from 'drizzle-orm';
|
||||||
|
|
||||||
import { listHandler, createHandler } from '@/app/api/v1/companies/[id]/members/route';
|
import { listHandler, createHandler } from '@/app/api/v1/companies/[id]/members/handlers';
|
||||||
import { patchHandler, deleteHandler } from '@/app/api/v1/companies/[id]/members/[mid]/route';
|
import { patchHandler, deleteHandler } from '@/app/api/v1/companies/[id]/members/[mid]/handlers';
|
||||||
import { setPrimaryHandler } from '@/app/api/v1/companies/[id]/members/[mid]/set-primary/route';
|
import { setPrimaryHandler } from '@/app/api/v1/companies/[id]/members/[mid]/set-primary/handlers';
|
||||||
import { db } from '@/lib/db';
|
import { db } from '@/lib/db';
|
||||||
import { companyMemberships } from '@/lib/db/schema';
|
import { companyMemberships } from '@/lib/db/schema';
|
||||||
import { makeMockCtx, makeMockRequest } from '../../helpers/route-tester';
|
import { makeMockCtx, makeMockRequest } from '../../helpers/route-tester';
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import {
|
|||||||
getHandler as getReservationHandler,
|
getHandler as getReservationHandler,
|
||||||
patchHandler as patchReservationHandler,
|
patchHandler as patchReservationHandler,
|
||||||
deleteHandler as deleteReservationHandler,
|
deleteHandler as deleteReservationHandler,
|
||||||
} from '@/app/api/v1/berth-reservations/[id]/route';
|
} from '@/app/api/v1/berth-reservations/[id]/handlers';
|
||||||
import { db } from '@/lib/db';
|
import { db } from '@/lib/db';
|
||||||
import { berthReservations } from '@/lib/db/schema/reservations';
|
import { berthReservations } from '@/lib/db/schema/reservations';
|
||||||
import { makeMockCtx, makeMockRequest } from '../../helpers/route-tester';
|
import { makeMockCtx, makeMockRequest } from '../../helpers/route-tester';
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import { describe, it, expect } from 'vitest';
|
import { describe, it, expect } from 'vitest';
|
||||||
|
|
||||||
import { getHandler, patchHandler, deleteHandler } from '@/app/api/v1/yachts/[id]/handlers';
|
import { getHandler, patchHandler, deleteHandler } from '@/app/api/v1/yachts/[id]/handlers';
|
||||||
import { transferHandler } from '@/app/api/v1/yachts/[id]/transfer/route';
|
import { transferHandler } from '@/app/api/v1/yachts/[id]/transfer/handlers';
|
||||||
import { historyHandler } from '@/app/api/v1/yachts/[id]/ownership-history/route';
|
import { historyHandler } from '@/app/api/v1/yachts/[id]/ownership-history/handlers';
|
||||||
import { autocompleteHandler } from '@/app/api/v1/yachts/autocomplete/route';
|
import { autocompleteHandler } from '@/app/api/v1/yachts/autocomplete/handlers';
|
||||||
import { withPermission } from '@/lib/api/helpers';
|
import { withPermission } from '@/lib/api/helpers';
|
||||||
import { db } from '@/lib/db';
|
import { db } from '@/lib/db';
|
||||||
import { yachts } from '@/lib/db/schema';
|
import { yachts } from '@/lib/db/schema';
|
||||||
|
|||||||
Reference in New Issue
Block a user