import { NextRequest, NextResponse } from 'next/server'; import { z } from 'zod'; import { auditCreate } from '@/features/foundation/audit-log/service'; import { auditCrmSecurityEvent, buildCrmSecurityContext } from '@/features/crm/security/server/service'; import { createCustomer, listCustomers } from '@/features/crm/customers/server/service'; import { customerSchema } from '@/features/crm/customers/schemas/customer.schema'; import { PERMISSIONS } from '@/lib/auth/rbac'; import { AuthError, requireOrganizationAccess } from '@/lib/auth/session'; const customerRequestSchema = customerSchema.extend({ branchId: z.string().nullable().optional(), leadChannel: z.string().nullable().optional(), customerGroup: z.string().nullable().optional(), customerSubGroup: z.string().nullable().optional() }); export async function GET(request: NextRequest) { try { const { organization, session, membership } = await requireOrganizationAccess({ permission: PERMISSIONS.crmCustomerRead }); const { searchParams } = request.nextUrl; const page = Number(searchParams.get('page') ?? 1); const limit = Number(searchParams.get('limit') ?? 10); const search = searchParams.get('search') ?? undefined; const customerStatus = searchParams.get('customerStatus') ?? undefined; const customerType = searchParams.get('customerType') ?? undefined; const branch = searchParams.get('branch') ?? undefined; const sort = searchParams.get('sort') ?? undefined; const accessContext = buildCrmSecurityContext({ organizationId: organization.id, userId: session.user.id, membership }); const result = await listCustomers(organization.id, { page, limit, search, customerStatus, customerType, branch, sort }, accessContext); const offset = (page - 1) * limit; return NextResponse.json({ success: true, time: new Date().toISOString(), message: 'Customers loaded successfully', totalItems: result.totalItems, offset, limit, items: result.items }); } catch (error) { if (error instanceof AuthError) { if (error.status === 403) { const access = await requireOrganizationAccess({ permission: PERMISSIONS.crmCustomerRead }).catch(() => null); if (access) { await auditCrmSecurityEvent({ organizationId: access.organization.id, userId: access.session.user.id, action: 'customer_scope_violation', entityType: 'crm_customer', entityId: 'list', reason: 'Customer list visibility denied' }); } } return NextResponse.json({ message: error.message }, { status: error.status }); } console.error('GET /api/crm/customers failed', error); return NextResponse.json({ message: 'Unable to load customers' }, { status: 500 }); } } export async function POST(request: NextRequest) { try { const { organization, session } = await requireOrganizationAccess({ permission: PERMISSIONS.crmCustomerCreate }); const payload = customerRequestSchema.parse(await request.json()); const created = await createCustomer(organization.id, session.user.id, payload); await auditCreate({ organizationId: organization.id, branchId: created.branchId, userId: session.user.id, entityType: 'crm_customer', entityId: created.id, afterData: created }); return NextResponse.json( { success: true, message: 'Customer created successfully', customer: created }, { status: 201 } ); } catch (error) { if (error instanceof AuthError) { return NextResponse.json({ message: error.message }, { status: error.status }); } if (error instanceof z.ZodError) { return NextResponse.json( { message: error.issues[0]?.message ?? 'Invalid payload' }, { status: 400 } ); } console.error('POST /api/crm/customers failed', error); return NextResponse.json({ message: 'Unable to create customer' }, { status: 500 }); } }