import { NextResponse } from "next/server" import { ConvexHttpClient } from "convex/browser" import { api } from "@/convex/_generated/api" import type { Id } from "@/convex/_generated/dataModel" import { assertAdminSession } from "@/lib/auth-server" import { env } from "@/lib/env" import { prisma } from "@/lib/prisma" import { sendSmtpMail } from "@/server/email-smtp" export const runtime = "nodejs" function fmtHours(ms: number) { return (ms / 3600000).toFixed(2) } export async function POST(request: Request) { const session = await assertAdminSession() if (!session) return NextResponse.json({ error: "Não autorizado" }, { status: 401 }) const convexUrl = process.env.NEXT_PUBLIC_CONVEX_URL if (!convexUrl) return NextResponse.json({ error: "Convex não configurado" }, { status: 500 }) if (!env.SMTP) return NextResponse.json({ error: "SMTP não configurado" }, { status: 500 }) const { searchParams } = new URL(request.url) const range = searchParams.get("range") ?? "30d" const threshold = Number(searchParams.get("threshold") ?? 90) const client = new ConvexHttpClient(convexUrl) const tenantId = session.user.tenantId ?? "tenant-atlas" // Ensure user exists in Convex to obtain a typed viewerId let viewerId: Id<"users"> | null = null try { const ensured = await client.mutation(api.users.ensureUser, { tenantId, name: session.user.name ?? session.user.email, email: session.user.email, avatarUrl: session.user.avatarUrl ?? undefined, role: session.user.role.toUpperCase(), }) viewerId = (ensured?._id ?? null) as Id<"users"> | null } catch (error) { console.error("Failed to synchronize user with Convex for alerts", error) return NextResponse.json({ error: "Falha ao sincronizar usuário com Convex" }, { status: 500 }) } if (!viewerId) return NextResponse.json({ error: "Usuário não encontrado no Convex" }, { status: 403 }) const report = await client.query(api.reports.hoursByClient, { tenantId, viewerId, range, }) type HoursByClientItem = { companyId: Id<"companies"> name: string internalMs: number externalMs: number totalMs: number contractedHoursPerMonth: number | null } const items = (report.items ?? []) as HoursByClientItem[] const alerts = items.filter((i) => i.contractedHoursPerMonth != null && (i.totalMs / 3600000) / (i.contractedHoursPerMonth || 1) * 100 >= threshold) for (const item of alerts) { // Find managers of the company in Prisma const managers = await prisma.user.findMany({ where: { tenantId, companyId: item.companyId, role: "MANAGER", }, select: { email: true, name: true }, }) if (managers.length === 0) continue const subject = `Alerta: uso de horas em ${item.name} acima de ${threshold}%` const body = `

Olá,

O uso de horas contratadas para ${item.name} atingiu ${(((item.totalMs/3600000)/(item.contractedHoursPerMonth || 1))*100).toFixed(1)}%.

Reveja a alocação da equipe e, se necessário, ajuste o atendimento.

` let delivered = 0 for (const m of managers) { try { await sendSmtpMail( { host: env.SMTP!.host, port: env.SMTP!.port, username: env.SMTP!.username, password: env.SMTP!.password, from: env.SMTP!.from!, }, m.email, subject, body ) delivered += 1 } catch (error) { console.error("Failed to send alert to", m.email, error) } } try { await client.mutation(api.alerts.log, { tenantId, companyId: item.companyId, companyName: item.name, usagePct: (((item.totalMs/3600000)/(item.contractedHoursPerMonth || 1))*100), threshold, range, recipients: managers.map((m) => m.email), deliveredCount: delivered, }) } catch (error) { console.error("Failed to log alert in Convex", error) } } return NextResponse.json({ sent: alerts.length }) }