fix: resolver avisos de build e tipagem

This commit is contained in:
Esdras Renan 2025-11-04 21:02:53 -03:00
parent 741f1d7f9c
commit fa9efdb5af
7 changed files with 154 additions and 59 deletions

View file

@ -1,7 +1,8 @@
import { v } from "convex/values"
import type { Id } from "./_generated/dataModel"
import type { Doc, Id } from "./_generated/dataModel"
import { query } from "./_generated/server"
import type { QueryCtx } from "./_generated/server"
import {
OPEN_STATUSES,
ONE_DAY_MS,
@ -26,7 +27,7 @@ type MetricRunPayload = {
data: unknown
}
type MetricResolver = (ctx: Parameters<typeof query>[0], input: MetricResolverInput) => Promise<MetricRunPayload>
type MetricResolver = (ctx: QueryCtx, input: MetricResolverInput) => Promise<MetricRunPayload>
function parseRange(params?: Record<string, unknown>): number {
const value = params?.range
@ -380,10 +381,12 @@ const metricResolvers: Record<string, MetricResolver> = {
const lastHeartbeatAt = machine.lastHeartbeatAt ?? null
const minutesSinceHeartbeat = lastHeartbeatAt ? Math.round((now - lastHeartbeatAt) / 60000) : null
const status = deriveMachineStatus(machine, now)
const cpu = clampPercent(machine.cpuUsagePercent)
const memory = clampPercent(machine.memoryUsedPercent)
const disk = clampPercent(machine.diskUsedPercent ?? machine.diskUsagePercent)
const alerts = Array.isArray(machine.postureAlerts) ? machine.postureAlerts.length : machine.postureAlertsCount ?? 0
const cpu = clampPercent(pickMachineMetric(machine, ["cpuUsagePercent", "cpu_usage_percent"]))
const memory = clampPercent(pickMachineMetric(machine, ["memoryUsedPercent", "memory_usage_percent"]))
const disk = clampPercent(pickMachineMetric(machine, ["diskUsedPercent", "diskUsagePercent", "storageUsedPercent"]))
const alerts = readMachineAlertsCount(machine)
const fallbackHostname = readString((machine as unknown as Record<string, unknown>)["computerName"])
const hostname = machine.hostname ?? fallbackHostname ?? "Dispositivo sem nome"
const attention =
(cpu ?? 0) > 85 ||
(memory ?? 0) > 90 ||
@ -392,7 +395,7 @@ const metricResolvers: Record<string, MetricResolver> = {
alerts > 0
return {
id: machine._id,
hostname: machine.hostname ?? machine.computerName ?? "Dispositivo sem nome",
hostname,
status,
cpuUsagePercent: cpu,
memoryUsedPercent: memory,
@ -467,3 +470,72 @@ function clampPercent(value: unknown) {
if (value > 100) return 100
return Math.round(value * 10) / 10
}
function readNumeric(value: unknown): number | null {
if (typeof value === "number" && Number.isFinite(value)) {
return value
}
if (typeof value === "string") {
const parsed = Number(value)
if (Number.isFinite(parsed)) {
return parsed
}
}
return null
}
function pickMachineMetric(machine: Doc<"machines">, keys: string[]): number | null {
const record = machine as unknown as Record<string, unknown>
for (const key of keys) {
const direct = readNumeric(record[key])
if (direct !== null) {
return direct
}
}
const metadata = record["metadata"]
if (metadata && typeof metadata === "object" && !Array.isArray(metadata)) {
const metrics = (metadata as Record<string, unknown>)["metrics"]
if (metrics && typeof metrics === "object" && !Array.isArray(metrics)) {
const metricsRecord = metrics as Record<string, unknown>
for (const key of keys) {
const value = readNumeric(metricsRecord[key])
if (value !== null) {
return value
}
}
}
}
return null
}
function readMachineAlertsCount(machine: Doc<"machines">): number {
const record = machine as unknown as Record<string, unknown>
const directCount = readNumeric(record["postureAlertsCount"])
if (directCount !== null) {
return directCount
}
const directArray = record["postureAlerts"]
if (Array.isArray(directArray)) {
return directArray.length
}
const metadata = record["metadata"]
if (metadata && typeof metadata === "object" && !Array.isArray(metadata)) {
const metadataRecord = metadata as Record<string, unknown>
const metaCount = readNumeric(metadataRecord["postureAlertsCount"])
if (metaCount !== null) {
return metaCount
}
const metaAlerts = metadataRecord["postureAlerts"]
if (Array.isArray(metaAlerts)) {
return metaAlerts.length
}
}
return 0
}
function readString(value: unknown): string | null {
if (typeof value === "string" && value.trim().length > 0) {
return value
}
return null
}

View file

@ -1898,12 +1898,15 @@ export const addComment = mutation({
try {
const snapshotEmail = (ticketDoc.requesterSnapshot as { email?: string } | undefined)?.email
if (requestedVisibility === "PUBLIC" && snapshotEmail && String(ticketDoc.requesterId) !== String(args.authorId)) {
await ctx.scheduler.runAfter(0, api.ticketNotifications.sendPublicCommentEmail, {
to: snapshotEmail,
ticketId: String(ticketDoc._id),
reference: ticketDoc.reference ?? 0,
subject: ticketDoc.subject ?? "",
})
const schedulerRunAfter = ctx.scheduler?.runAfter
if (typeof schedulerRunAfter === "function") {
await schedulerRunAfter(0, api.ticketNotifications.sendPublicCommentEmail, {
to: snapshotEmail,
ticketId: String(ticketDoc._id),
reference: ticketDoc.reference ?? 0,
subject: ticketDoc.subject ?? "",
})
}
}
} catch (e) {
console.warn("[tickets] Falha ao agendar e-mail de comentário", e)
@ -2148,12 +2151,15 @@ export async function resolveTicketHandler(
const requesterDoc = await ctx.db.get(ticketDoc.requesterId)
const email = (requesterDoc as Doc<"users"> | null)?.email || (ticketDoc.requesterSnapshot as { email?: string } | undefined)?.email || null
if (email) {
await ctx.scheduler.runAfter(0, api.ticketNotifications.sendResolvedEmail, {
to: email,
ticketId: String(ticketId),
reference: ticketDoc.reference ?? 0,
subject: ticketDoc.subject ?? "",
})
const schedulerRunAfter = ctx.scheduler?.runAfter
if (typeof schedulerRunAfter === "function") {
await schedulerRunAfter(0, api.ticketNotifications.sendResolvedEmail, {
to: email,
ticketId: String(ticketId),
reference: ticketDoc.reference ?? 0,
subject: ticketDoc.subject ?? "",
})
}
}
} catch (e) {
console.warn("[tickets] Falha ao agendar e-mail de encerramento", e)