import { NextRequest, NextResponse } from 'next/server' import crypto from 'crypto' import { requireStaffPermission } from '@/lib/auth-helpers' import { prisma } from '@/lib/prisma' import { StaffRole } from '@prisma/client' import { getAssignableRoles } from '@/lib/services/permission-service' import { emailService } from '@/lib/services/email-service' interface InviteStaffRequest { email: string role: StaffRole } /** * POST /api/v1/admin/staff/invite * Send a staff invitation * Requires: staff:invite permission */ export async function POST(request: NextRequest) { try { const session = await requireStaffPermission('staff:invite') const body: InviteStaffRequest = await request.json() // Validate required fields if (!body.email) { return NextResponse.json( { error: 'Email is required' }, { status: 400 } ) } if (!body.role) { return NextResponse.json( { error: 'Role is required' }, { status: 400 } ) } // Validate email format const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/ if (!emailRegex.test(body.email)) { return NextResponse.json( { error: 'Invalid email format' }, { status: 400 } ) } // Validate role can be assigned const assignableRoles = getAssignableRoles(session.user.role) if (!assignableRoles.includes(body.role)) { return NextResponse.json( { error: 'Cannot invite staff with this role' }, { status: 403 } ) } // Check if email already exists as staff const existingStaff = await prisma.staff.findUnique({ where: { email: body.email }, }) if (existingStaff) { return NextResponse.json( { error: 'A staff member with this email already exists' }, { status: 409 } ) } // Check if pending invitation exists const existingInvite = await prisma.staffInvitation.findUnique({ where: { email: body.email }, }) if (existingInvite) { return NextResponse.json( { error: 'A pending invitation for this email already exists' }, { status: 409 } ) } // Generate secure token const token = crypto.randomBytes(32).toString('hex') // Create invitation with 7-day expiry const expiresAt = new Date() expiresAt.setDate(expiresAt.getDate() + 7) const invitation = await prisma.staffInvitation.create({ data: { email: body.email, role: body.role, token, expiresAt, invitedBy: session.user.id, }, select: { id: true, email: true, role: true, expiresAt: true, createdAt: true, }, }) // Get the inviter's name for the response const inviter = await prisma.staff.findUnique({ where: { id: session.user.id }, select: { name: true, email: true }, }) const baseUrl = process.env.NEXTAUTH_URL || 'http://localhost:3000' const inviteUrl = `${baseUrl}/invite/${token}` // Send invitation email let emailSent = false let emailError: string | undefined const isEmailConfigured = await emailService.isConfigured() if (isEmailConfigured) { const inviterName = inviter?.name || inviter?.email || 'A team member' const roleDisplay = body.role.charAt(0) + body.role.slice(1).toLowerCase() const emailResult = await emailService.sendEmail({ to: body.email, subject: `You've been invited to join LetsBe Hub`, html: `
${inviterName} has invited you to join LetsBe Hub as a ${roleDisplay}.
Click the button below to create your account and get started.
| Accept Invitation |
Or copy and paste this link into your browser:
This invitation expires in 7 days. If you didn't expect this invitation, you can safely ignore this email.
LetsBe Hub - Infrastructure Management Platform