sistema-de-chamados/src/app/api/tickets/mentions/route.ts

108 lines
3.7 KiB
TypeScript

import { NextResponse } from "next/server"
import { ConvexHttpClient } from "convex/browser"
import { api } from "@/convex/_generated/api"
import type { Id } from "@/convex/_generated/dataModel"
import { assertAuthenticatedSession } from "@/lib/auth-server"
import { DEFAULT_TENANT_ID } from "@/lib/constants"
import { requireConvexUrl } from "@/server/convex-client"
export const runtime = "nodejs"
const MAX_RESULTS = 10
type TicketRecord = {
id: string | number
reference: string | number
subject?: string | null
status?: string | null
priority?: string | null
requester?: { name?: string | null } | null
assignee?: { name?: string | null } | null
company?: { name?: string | null } | null
updatedAt?: number | null
}
function isTicketRecord(value: unknown): value is TicketRecord {
if (!value || typeof value !== "object") {
return false
}
const record = value as Record<string, unknown>
const hasValidId = typeof record.id === "string" || typeof record.id === "number"
const hasValidReference = typeof record.reference === "string" || typeof record.reference === "number"
return hasValidId && hasValidReference
}
function normalizeRole(role?: string | null) {
return (role ?? "").toLowerCase()
}
export async function GET(request: Request) {
const session = await assertAuthenticatedSession()
if (!session) {
return NextResponse.json({ items: [] }, { status: 401 })
}
const normalizedRole = normalizeRole(session.user.role)
const isAgentOrAdmin = normalizedRole === "admin" || normalizedRole === "agent"
const canLinkOwnTickets = normalizedRole === "collaborator"
if (!isAgentOrAdmin && !canLinkOwnTickets) {
return NextResponse.json({ items: [] }, { status: 403 })
}
const tenantId = session.user.tenantId ?? DEFAULT_TENANT_ID
const url = new URL(request.url)
const rawQuery = url.searchParams.get("q") ?? ""
const query = rawQuery.trim()
const client = new ConvexHttpClient(requireConvexUrl())
// Garantir que o usuário exista no Convex para obter viewerId
let viewerId: string | 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
} catch (error) {
console.error("[mentions] ensureUser failed", error)
return NextResponse.json({ items: [] }, { status: 500 })
}
if (!viewerId) {
return NextResponse.json({ items: [] }, { status: 403 })
}
// Pesquisar pelos tickets visíveis ao viewer (assunto, resumo ou #referência)
let tickets: TicketRecord[] = []
try {
const res = await client.query(api.tickets.list, {
tenantId,
viewerId: viewerId as unknown as Id<"users">,
search: query,
limit: 40,
})
tickets = Array.isArray(res) ? res.filter(isTicketRecord) : []
} catch (error) {
console.error("[mentions] tickets.list failed", error)
return NextResponse.json({ items: [] }, { status: 500 })
}
const basePath = isAgentOrAdmin ? "/tickets" : "/portal/tickets"
const items = tickets.slice(0, MAX_RESULTS).map((t) => ({
id: String(t.id),
reference: Number(t.reference),
subject: String(t.subject ?? ""),
status: String(t.status ?? "PENDING"),
priority: String(t.priority ?? "MEDIUM"),
requesterName: t.requester?.name ?? null,
assigneeName: t.assignee?.name ?? null,
companyName: t.company?.name ?? null,
url: `${basePath}/${String(t.id)}`,
updatedAt: t.updatedAt ? new Date(t.updatedAt).toISOString() : new Date().toISOString(),
}))
return NextResponse.json({ items })
}