import { NextRequest, NextResponse } from 'next/server' import { randomBytes } from 'crypto' import { auth } from '@/lib/auth' import { prisma } from '@/lib/prisma' import { OrderStatus, SubscriptionTier, AutomationMode, Prisma } from '@prisma/client' /** * Generate a customer ID slug from company/user name * Only lowercase letters allowed (env_setup.sh requires ^[a-z]+$) */ function slugifyCustomer(name: string): string { return name .toLowerCase() .replace(/[^a-z]/g, '') .substring(0, 32) } /** * Generate a unique license key for the order */ function generateLicenseKey(): string { const hex = randomBytes(16).toString('hex') return `lb_inst_${hex}` } /** * GET /api/v1/admin/orders * List all orders with optional filters */ export async function GET(request: NextRequest) { try { const session = await auth() // Check authentication and authorization if (!session || session.user.userType !== 'staff') { return NextResponse.json({ error: 'Unauthorized' }, { status: 401 }) } // Parse query parameters const searchParams = request.nextUrl.searchParams const status = searchParams.get('status') as OrderStatus | null const tier = searchParams.get('tier') const search = searchParams.get('search') const page = parseInt(searchParams.get('page') || '1') const limit = parseInt(searchParams.get('limit') || '50') // Build where clause const where: Prisma.OrderWhereInput = {} if (status) { where.status = status } if (tier && Object.values(SubscriptionTier).includes(tier as SubscriptionTier)) { where.tier = tier as SubscriptionTier } if (search) { where.OR = [ { domain: { contains: search, mode: 'insensitive' } }, { user: { email: { contains: search, mode: 'insensitive' } } }, ] } // Get orders with pagination const [orders, total] = await Promise.all([ prisma.order.findMany({ where, include: { user: { select: { id: true, name: true, email: true, company: true, }, }, _count: { select: { provisioningLogs: true }, }, }, orderBy: { createdAt: 'desc' }, skip: (page - 1) * limit, take: limit, }), prisma.order.count({ where }), ]) return NextResponse.json({ orders, pagination: { page, limit, total, totalPages: Math.ceil(total / limit), }, }) } catch (error) { console.error('Error listing orders:', error) return NextResponse.json( { error: 'Failed to list orders' }, { status: 500 } ) } } /** * POST /api/v1/admin/orders * Create a new order (admin-initiated) */ export async function POST(request: NextRequest) { try { const session = await auth() if (!session || session.user.userType !== 'staff') { return NextResponse.json({ error: 'Unauthorized' }, { status: 401 }) } const body = await request.json() const { userId, domain, tier, tools } = body if (!userId || !domain || !tier || !tools) { return NextResponse.json( { error: 'userId, domain, tier, and tools are required' }, { status: 400 } ) } // Verify user exists const user = await prisma.user.findUnique({ where: { id: userId } }) if (!user) { return NextResponse.json({ error: 'User not found' }, { status: 404 }) } // Auto-generate provisioning config from user's company/name const displayName = user.company || user.name || 'customer' const customer = slugifyCustomer(displayName) || 'customer' const companyName = displayName const licenseKey = generateLicenseKey() // Create order with MANUAL automation mode (staff-created) const order = await prisma.order.create({ data: { userId, domain, tier, tools, status: OrderStatus.PAYMENT_CONFIRMED, configJson: { tools, tier, domain }, automationMode: AutomationMode.MANUAL, source: 'staff', // Auto-generated provisioning config customer, companyName, licenseKey, }, include: { user: { select: { id: true, name: true, email: true, }, }, }, }) return NextResponse.json(order, { status: 201 }) } catch (error) { console.error('Error creating order:', error) return NextResponse.json( { error: 'Failed to create order' }, { status: 500 } ) } }