- src/lib/email.ts: SMTP client with pre-built templates: - sendAgencyApprovedEmail (reseller approval + discount) - sendAgencyRejectedEmail (reseller rejection + reason) - sendLicenseKeyEmail (license key delivery) - sendTicketCreatedEmail (ticket confirmation) - Agency approve route: sends approval email after DB update - Agency reject route: sends rejection email with reason - License creation route: sends license key email (fire-and-forget) - Graceful degradation: if SMTP not configured, logs warning + continues - nodemailer + @types/nodemailer installed
79 lines
2.3 KiB
TypeScript
79 lines
2.3 KiB
TypeScript
import { NextResponse } from 'next/server'
|
|
import { db } from '@/lib/db'
|
|
import { z } from 'zod'
|
|
|
|
export const dynamic = 'force-dynamic'
|
|
|
|
const schema = z.object({
|
|
reason: z.string().min(1).max(500),
|
|
reviewedBy: z.string().optional(),
|
|
})
|
|
|
|
// PATCH /api/aos/agency/[id]/reject
|
|
// Admin rejects an agency application. Sets Company.agencyStatus = "rejected".
|
|
export async function PATCH(req: Request, { params }: { params: Promise<{ id: string }> }) {
|
|
try {
|
|
const { id } = await params
|
|
const parsed = schema.safeParse(await req.json())
|
|
if (!parsed.success) {
|
|
return NextResponse.json({ error: 'reason required', details: parsed.error.flatten() }, { status: 400 })
|
|
}
|
|
const { reason, reviewedBy } = parsed.data
|
|
|
|
const application = await db.agencyApplication.findUnique({
|
|
where: { id },
|
|
include: { company: true },
|
|
})
|
|
if (!application) {
|
|
return NextResponse.json({ error: 'application not found' }, { status: 404 })
|
|
}
|
|
|
|
const now = new Date()
|
|
await db.$transaction([
|
|
db.agencyApplication.update({
|
|
where: { id },
|
|
data: {
|
|
status: 'rejected',
|
|
rejectReason: reason,
|
|
reviewedBy: reviewedBy ?? 'admin:ui',
|
|
reviewedAt: now,
|
|
},
|
|
}),
|
|
db.company.update({
|
|
where: { id: application.companyId },
|
|
data: {
|
|
agencyStatus: 'rejected',
|
|
agencyRejectedAt: now,
|
|
agencyRejectReason: reason,
|
|
},
|
|
}),
|
|
])
|
|
|
|
await db.auditLog.create({
|
|
data: {
|
|
actor: reviewedBy ?? 'admin:ui',
|
|
action: 'agency.reject',
|
|
entity: 'AgencyApplication',
|
|
entityId: id,
|
|
after: JSON.stringify({ company: application.company.name, reason }),
|
|
hash: 'sha256:' + Math.random().toString(36).slice(2),
|
|
},
|
|
})
|
|
|
|
// Send rejection email
|
|
const { sendAgencyRejectedEmail } = await import('@/lib/email')
|
|
if (application.company.email) {
|
|
await sendAgencyRejectedEmail({
|
|
to: application.company.email,
|
|
companyName: application.company.name,
|
|
reason,
|
|
})
|
|
}
|
|
|
|
return NextResponse.json({ ok: true, status: 'rejected', reason })
|
|
} catch (err) {
|
|
const msg = err instanceof Error ? err.message : 'internal server error'
|
|
return NextResponse.json({ error: msg }, { status: 500 })
|
|
}
|
|
}
|