Initial commit: Port Nimara CRM (Layers 0-4)
Full CRM rebuild with Next.js 15, TypeScript, Tailwind, Drizzle ORM, PostgreSQL, Redis, BullMQ, MinIO, and Socket.io. Includes 461 source files covering clients, berths, interests/pipeline, documents/EOI, expenses/invoices, email, notifications, dashboard, admin, and client portal. CI/CD via Gitea Actions with Docker builds. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,22 @@
|
||||
import { NextRequest, NextResponse } from 'next/server';
|
||||
|
||||
import { withAuth, withPermission } from '@/lib/api/helpers';
|
||||
import { parseQuery } from '@/lib/api/route-helpers';
|
||||
import { errorResponse } from '@/lib/errors';
|
||||
import { listDeliveriesSchema } from '@/lib/validators/webhooks';
|
||||
import { listDeliveries } from '@/lib/services/webhooks.service';
|
||||
|
||||
// ─── GET /api/v1/admin/webhooks/[webhookId]/deliveries ────────────────────────
|
||||
|
||||
export const GET = withAuth(
|
||||
withPermission('admin', 'manage_webhooks', async (req: NextRequest, ctx, params) => {
|
||||
try {
|
||||
const { webhookId } = params;
|
||||
const query = parseQuery(req, listDeliveriesSchema);
|
||||
const result = await listDeliveries(ctx.portId, webhookId!, query);
|
||||
return NextResponse.json(result);
|
||||
} catch (error) {
|
||||
return errorResponse(error);
|
||||
}
|
||||
}),
|
||||
);
|
||||
@@ -0,0 +1,24 @@
|
||||
import { NextRequest, NextResponse } from 'next/server';
|
||||
|
||||
import { withAuth, withPermission } from '@/lib/api/helpers';
|
||||
import { errorResponse } from '@/lib/errors';
|
||||
import { regenerateSecret } from '@/lib/services/webhooks.service';
|
||||
|
||||
// ─── POST /api/v1/admin/webhooks/[webhookId]/regenerate-secret ────────────────
|
||||
|
||||
export const POST = withAuth(
|
||||
withPermission('admin', 'manage_webhooks', async (_req: NextRequest, ctx, params) => {
|
||||
try {
|
||||
const { webhookId } = params;
|
||||
const data = await regenerateSecret(ctx.portId, webhookId!, {
|
||||
userId: ctx.userId,
|
||||
portId: ctx.portId,
|
||||
ipAddress: ctx.ipAddress,
|
||||
userAgent: ctx.userAgent,
|
||||
});
|
||||
return NextResponse.json({ data });
|
||||
} catch (error) {
|
||||
return errorResponse(error);
|
||||
}
|
||||
}),
|
||||
);
|
||||
64
src/app/api/v1/admin/webhooks/[webhookId]/route.ts
Normal file
64
src/app/api/v1/admin/webhooks/[webhookId]/route.ts
Normal file
@@ -0,0 +1,64 @@
|
||||
import { NextRequest, NextResponse } from 'next/server';
|
||||
|
||||
import { withAuth, withPermission } from '@/lib/api/helpers';
|
||||
import { parseBody } from '@/lib/api/route-helpers';
|
||||
import { errorResponse } from '@/lib/errors';
|
||||
import { updateWebhookSchema } from '@/lib/validators/webhooks';
|
||||
import {
|
||||
getWebhook,
|
||||
updateWebhook,
|
||||
deleteWebhook,
|
||||
} from '@/lib/services/webhooks.service';
|
||||
|
||||
// ─── GET /api/v1/admin/webhooks/[webhookId] ───────────────────────────────────
|
||||
|
||||
export const GET = withAuth(
|
||||
withPermission('admin', 'manage_webhooks', async (_req: NextRequest, ctx, params) => {
|
||||
try {
|
||||
const { webhookId } = params;
|
||||
const data = await getWebhook(ctx.portId, webhookId!);
|
||||
return NextResponse.json({ data });
|
||||
} catch (error) {
|
||||
return errorResponse(error);
|
||||
}
|
||||
}),
|
||||
);
|
||||
|
||||
// ─── PATCH /api/v1/admin/webhooks/[webhookId] ─────────────────────────────────
|
||||
|
||||
export const PATCH = withAuth(
|
||||
withPermission('admin', 'manage_webhooks', async (req: NextRequest, ctx, params) => {
|
||||
try {
|
||||
const { webhookId } = params;
|
||||
const body = await parseBody(req, updateWebhookSchema);
|
||||
const data = await updateWebhook(ctx.portId, webhookId!, body, {
|
||||
userId: ctx.userId,
|
||||
portId: ctx.portId,
|
||||
ipAddress: ctx.ipAddress,
|
||||
userAgent: ctx.userAgent,
|
||||
});
|
||||
return NextResponse.json({ data });
|
||||
} catch (error) {
|
||||
return errorResponse(error);
|
||||
}
|
||||
}),
|
||||
);
|
||||
|
||||
// ─── DELETE /api/v1/admin/webhooks/[webhookId] ────────────────────────────────
|
||||
|
||||
export const DELETE = withAuth(
|
||||
withPermission('admin', 'manage_webhooks', async (_req: NextRequest, ctx, params) => {
|
||||
try {
|
||||
const { webhookId } = params;
|
||||
await deleteWebhook(ctx.portId, webhookId!, {
|
||||
userId: ctx.userId,
|
||||
portId: ctx.portId,
|
||||
ipAddress: ctx.ipAddress,
|
||||
userAgent: ctx.userAgent,
|
||||
});
|
||||
return NextResponse.json({ success: true });
|
||||
} catch (error) {
|
||||
return errorResponse(error);
|
||||
}
|
||||
}),
|
||||
);
|
||||
27
src/app/api/v1/admin/webhooks/[webhookId]/test/route.ts
Normal file
27
src/app/api/v1/admin/webhooks/[webhookId]/test/route.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
import { NextRequest, NextResponse } from 'next/server';
|
||||
import { z } from 'zod';
|
||||
|
||||
import { withAuth, withPermission } from '@/lib/api/helpers';
|
||||
import { parseBody } from '@/lib/api/route-helpers';
|
||||
import { errorResponse } from '@/lib/errors';
|
||||
import { WEBHOOK_EVENTS } from '@/lib/services/webhook-event-map';
|
||||
import { sendTestWebhook } from '@/lib/services/webhooks.service';
|
||||
|
||||
const testWebhookSchema = z.object({
|
||||
eventType: z.enum(WEBHOOK_EVENTS).default('client.created'),
|
||||
});
|
||||
|
||||
// ─── POST /api/v1/admin/webhooks/[webhookId]/test ─────────────────────────────
|
||||
|
||||
export const POST = withAuth(
|
||||
withPermission('admin', 'manage_webhooks', async (req: NextRequest, ctx, params) => {
|
||||
try {
|
||||
const { webhookId } = params;
|
||||
const body = await parseBody(req, testWebhookSchema);
|
||||
const result = await sendTestWebhook(ctx.portId, webhookId!, body.eventType);
|
||||
return NextResponse.json({ data: result });
|
||||
} catch (error) {
|
||||
return errorResponse(error);
|
||||
}
|
||||
}),
|
||||
);
|
||||
39
src/app/api/v1/admin/webhooks/route.ts
Normal file
39
src/app/api/v1/admin/webhooks/route.ts
Normal file
@@ -0,0 +1,39 @@
|
||||
import { NextRequest, NextResponse } from 'next/server';
|
||||
|
||||
import { withAuth, withPermission } from '@/lib/api/helpers';
|
||||
import { parseBody } from '@/lib/api/route-helpers';
|
||||
import { errorResponse } from '@/lib/errors';
|
||||
import { createWebhookSchema } from '@/lib/validators/webhooks';
|
||||
import { listWebhooks, createWebhook } from '@/lib/services/webhooks.service';
|
||||
|
||||
// ─── GET /api/v1/admin/webhooks ───────────────────────────────────────────────
|
||||
|
||||
export const GET = withAuth(
|
||||
withPermission('admin', 'manage_webhooks', async (_req: NextRequest, ctx) => {
|
||||
try {
|
||||
const data = await listWebhooks(ctx.portId);
|
||||
return NextResponse.json({ data });
|
||||
} catch (error) {
|
||||
return errorResponse(error);
|
||||
}
|
||||
}),
|
||||
);
|
||||
|
||||
// ─── POST /api/v1/admin/webhooks ──────────────────────────────────────────────
|
||||
|
||||
export const POST = withAuth(
|
||||
withPermission('admin', 'manage_webhooks', async (req: NextRequest, ctx) => {
|
||||
try {
|
||||
const body = await parseBody(req, createWebhookSchema);
|
||||
const webhook = await createWebhook(ctx.portId, ctx.userId, body, {
|
||||
userId: ctx.userId,
|
||||
portId: ctx.portId,
|
||||
ipAddress: ctx.ipAddress,
|
||||
userAgent: ctx.userAgent,
|
||||
});
|
||||
return NextResponse.json({ data: webhook }, { status: 201 });
|
||||
} catch (error) {
|
||||
return errorResponse(error);
|
||||
}
|
||||
}),
|
||||
);
|
||||
Reference in New Issue
Block a user