feat: dispositivos e ajustes de csat e relatórios
This commit is contained in:
parent
25d2a9b062
commit
e0ef66555d
86 changed files with 5811 additions and 992 deletions
|
|
@ -8,6 +8,7 @@ import { randomBytes } from "@noble/hashes/utils"
|
|||
import type { Doc, Id } from "./_generated/dataModel"
|
||||
import type { MutationCtx, QueryCtx } from "./_generated/server"
|
||||
import { normalizeStatus } from "./tickets"
|
||||
import { requireAdmin } from "./rbac"
|
||||
|
||||
const DEFAULT_TENANT_ID = "tenant-atlas"
|
||||
const DEFAULT_TOKEN_TTL_MS = 1000 * 60 * 60 * 24 * 30 // 30 dias
|
||||
|
|
@ -65,6 +66,14 @@ function normalizeIdentifiers(macAddresses: string[], serialNumbers: string[]):
|
|||
return { macs, serials }
|
||||
}
|
||||
|
||||
function normalizeOptionalIdentifiers(macAddresses?: string[] | null, serialNumbers?: string[] | null): NormalizedIdentifiers {
|
||||
const normalizeMac = (value: string) => value.replace(/[^a-f0-9]/gi, "").toLowerCase()
|
||||
const normalizeSerial = (value: string) => value.trim().toLowerCase()
|
||||
const macs = Array.from(new Set((macAddresses ?? []).map(normalizeMac).filter(Boolean))).sort()
|
||||
const serials = Array.from(new Set((serialNumbers ?? []).map(normalizeSerial).filter(Boolean))).sort()
|
||||
return { macs, serials }
|
||||
}
|
||||
|
||||
async function findActiveMachineToken(ctx: QueryCtx, machineId: Id<"machines">, now: number) {
|
||||
const tokens = await ctx.db
|
||||
.query("machineTokens")
|
||||
|
|
@ -93,6 +102,51 @@ function computeFingerprint(tenantId: string, companySlug: string | undefined, h
|
|||
return toHex(sha256(payload))
|
||||
}
|
||||
|
||||
function generateManualFingerprint(tenantId: string, displayName: string) {
|
||||
const payload = JSON.stringify({
|
||||
tenantId,
|
||||
displayName: displayName.trim().toLowerCase(),
|
||||
nonce: toHex(randomBytes(16)),
|
||||
createdAt: Date.now(),
|
||||
})
|
||||
return toHex(sha256(payload))
|
||||
}
|
||||
|
||||
function formatDeviceCustomFieldDisplay(
|
||||
type: string,
|
||||
value: unknown,
|
||||
options?: Array<{ value: string; label: string }>
|
||||
): string | null {
|
||||
if (value === null || value === undefined) return null
|
||||
switch (type) {
|
||||
case "text":
|
||||
return String(value).trim()
|
||||
case "number": {
|
||||
const num = typeof value === "number" ? value : Number(value)
|
||||
if (!Number.isFinite(num)) return null
|
||||
return String(num)
|
||||
}
|
||||
case "boolean":
|
||||
return value ? "Sim" : "Não"
|
||||
case "date": {
|
||||
const date = value instanceof Date ? value : new Date(String(value))
|
||||
if (Number.isNaN(date.getTime())) return null
|
||||
return date.toISOString().slice(0, 10)
|
||||
}
|
||||
case "select": {
|
||||
const raw = String(value)
|
||||
const option = options?.find((opt) => opt.value === raw || opt.label === raw)
|
||||
return option?.label ?? raw
|
||||
}
|
||||
default:
|
||||
try {
|
||||
return JSON.stringify(value)
|
||||
} catch {
|
||||
return String(value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function extractCollaboratorEmail(metadata: unknown): string | null {
|
||||
if (!metadata || typeof metadata !== "object") return null
|
||||
const record = metadata as Record<string, unknown>
|
||||
|
|
@ -126,18 +180,18 @@ async function getActiveToken(
|
|||
.unique()
|
||||
|
||||
if (!token) {
|
||||
throw new ConvexError("Token de máquina inválido")
|
||||
throw new ConvexError("Token de dispositivo inválido")
|
||||
}
|
||||
if (token.revoked) {
|
||||
throw new ConvexError("Token de máquina revogado")
|
||||
throw new ConvexError("Token de dispositivo revogado")
|
||||
}
|
||||
if (token.expiresAt < Date.now()) {
|
||||
throw new ConvexError("Token de máquina expirado")
|
||||
throw new ConvexError("Token de dispositivo expirado")
|
||||
}
|
||||
|
||||
const machine = await ctx.db.get(token.machineId)
|
||||
if (!machine) {
|
||||
throw new ConvexError("Máquina não encontrada para o token fornecido")
|
||||
throw new ConvexError("Dispositivo não encontrada para o token fornecido")
|
||||
}
|
||||
|
||||
return { token, machine }
|
||||
|
|
@ -381,7 +435,7 @@ async function evaluatePostureAndMaybeRaise(
|
|||
if ((process.env["MACHINE_ALERTS_CREATE_TICKETS"] ?? "false").toLowerCase() !== "true") return
|
||||
if (lastAtPrev && now - lastAtPrev < 30 * 60 * 1000) return
|
||||
|
||||
const subject = `Alerta de máquina: ${machine.hostname}`
|
||||
const subject = `Alerta de dispositivo: ${machine.hostname}`
|
||||
const summary = findings.map((f) => `${f.severity.toUpperCase()}: ${f.message}`).join(" | ")
|
||||
await createTicketForAlert(ctx, machine.tenantId, machine.companyId, subject, summary)
|
||||
}
|
||||
|
|
@ -445,6 +499,7 @@ export const register = mutation({
|
|||
companyId: companyId ?? existing.companyId,
|
||||
companySlug: companySlug ?? existing.companySlug,
|
||||
hostname: args.hostname,
|
||||
displayName: existing.displayName ?? args.hostname,
|
||||
osName: args.os.name,
|
||||
osVersion: args.os.version,
|
||||
architecture: args.os.architecture,
|
||||
|
|
@ -457,6 +512,9 @@ export const register = mutation({
|
|||
status: "online",
|
||||
isActive: true,
|
||||
registeredBy: args.registeredBy ?? existing.registeredBy,
|
||||
deviceType: existing.deviceType ?? "desktop",
|
||||
devicePlatform: args.os.name ?? existing.devicePlatform,
|
||||
managementMode: existing.managementMode ?? "agent",
|
||||
persona: existing.persona,
|
||||
assignedUserId: existing.assignedUserId,
|
||||
assignedUserEmail: existing.assignedUserEmail,
|
||||
|
|
@ -470,6 +528,7 @@ export const register = mutation({
|
|||
companyId,
|
||||
companySlug,
|
||||
hostname: args.hostname,
|
||||
displayName: args.hostname,
|
||||
osName: args.os.name,
|
||||
osVersion: args.os.version,
|
||||
architecture: args.os.architecture,
|
||||
|
|
@ -483,6 +542,9 @@ export const register = mutation({
|
|||
createdAt: now,
|
||||
updatedAt: now,
|
||||
registeredBy: args.registeredBy,
|
||||
deviceType: "desktop",
|
||||
devicePlatform: args.os.name,
|
||||
managementMode: "agent",
|
||||
persona: undefined,
|
||||
assignedUserId: undefined,
|
||||
assignedUserEmail: undefined,
|
||||
|
|
@ -582,6 +644,7 @@ export const upsertInventory = mutation({
|
|||
companyId: companyId ?? existing.companyId,
|
||||
companySlug: companySlug ?? existing.companySlug,
|
||||
hostname: args.hostname,
|
||||
displayName: existing.displayName ?? args.hostname,
|
||||
osName: args.os.name,
|
||||
osVersion: args.os.version,
|
||||
architecture: args.os.architecture,
|
||||
|
|
@ -592,6 +655,9 @@ export const upsertInventory = mutation({
|
|||
updatedAt: now,
|
||||
status: args.metrics ? "online" : existing.status ?? "unknown",
|
||||
registeredBy: args.registeredBy ?? existing.registeredBy,
|
||||
deviceType: existing.deviceType ?? "desktop",
|
||||
devicePlatform: args.os.name ?? existing.devicePlatform,
|
||||
managementMode: existing.managementMode ?? "agent",
|
||||
persona: existing.persona,
|
||||
assignedUserId: existing.assignedUserId,
|
||||
assignedUserEmail: existing.assignedUserEmail,
|
||||
|
|
@ -605,6 +671,7 @@ export const upsertInventory = mutation({
|
|||
companyId,
|
||||
companySlug,
|
||||
hostname: args.hostname,
|
||||
displayName: args.hostname,
|
||||
osName: args.os.name,
|
||||
osVersion: args.os.version,
|
||||
architecture: args.os.architecture,
|
||||
|
|
@ -617,6 +684,9 @@ export const upsertInventory = mutation({
|
|||
createdAt: now,
|
||||
updatedAt: now,
|
||||
registeredBy: args.registeredBy,
|
||||
deviceType: "desktop",
|
||||
devicePlatform: args.os.name,
|
||||
managementMode: "agent",
|
||||
persona: undefined,
|
||||
assignedUserId: undefined,
|
||||
assignedUserEmail: undefined,
|
||||
|
|
@ -673,9 +743,13 @@ export const heartbeat = mutation({
|
|||
|
||||
await ctx.db.patch(machine._id, {
|
||||
hostname: args.hostname ?? machine.hostname,
|
||||
displayName: machine.displayName ?? args.hostname ?? machine.hostname,
|
||||
osName: args.os?.name ?? machine.osName,
|
||||
osVersion: args.os?.version ?? machine.osVersion,
|
||||
architecture: args.os?.architecture ?? machine.architecture,
|
||||
devicePlatform: args.os?.name ?? machine.devicePlatform,
|
||||
deviceType: machine.deviceType ?? "desktop",
|
||||
managementMode: machine.managementMode ?? "agent",
|
||||
lastHeartbeatAt: now,
|
||||
updatedAt: now,
|
||||
status: args.status ?? "online",
|
||||
|
|
@ -839,6 +913,11 @@ export const listByTenant = query({
|
|||
id: machine._id,
|
||||
tenantId: machine.tenantId,
|
||||
hostname: machine.hostname,
|
||||
displayName: machine.displayName ?? null,
|
||||
deviceType: machine.deviceType ?? "desktop",
|
||||
devicePlatform: machine.devicePlatform ?? null,
|
||||
deviceProfile: machine.deviceProfile ?? null,
|
||||
managementMode: machine.managementMode ?? "agent",
|
||||
companyId: machine.companyId ?? null,
|
||||
companySlug: machine.companySlug ?? companyFromId?.slug ?? companyFromSlug?.slug ?? null,
|
||||
companyName: resolvedCompany?.name ?? null,
|
||||
|
|
@ -873,6 +952,7 @@ export const listByTenant = query({
|
|||
inventory,
|
||||
postureAlerts,
|
||||
lastPostureAt,
|
||||
customFields: machine.customFields ?? [],
|
||||
}
|
||||
})
|
||||
)
|
||||
|
|
@ -957,6 +1037,11 @@ export async function getByIdHandler(
|
|||
id: machine._id,
|
||||
tenantId: machine.tenantId,
|
||||
hostname: machine.hostname,
|
||||
displayName: machine.displayName ?? null,
|
||||
deviceType: machine.deviceType ?? "desktop",
|
||||
devicePlatform: machine.devicePlatform ?? null,
|
||||
deviceProfile: machine.deviceProfile ?? null,
|
||||
managementMode: machine.managementMode ?? "agent",
|
||||
companyId: machine.companyId ?? null,
|
||||
companySlug: machine.companySlug ?? resolvedCompany?.slug ?? null,
|
||||
companyName: resolvedCompany?.name ?? null,
|
||||
|
|
@ -992,6 +1077,7 @@ export async function getByIdHandler(
|
|||
postureAlerts,
|
||||
lastPostureAt,
|
||||
remoteAccess: machine.remoteAccess ?? null,
|
||||
customFields: machine.customFields ?? [],
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1333,7 +1419,7 @@ export async function updatePersonaHandler(
|
|||
) {
|
||||
const machine = await ctx.db.get(args.machineId)
|
||||
if (!machine) {
|
||||
throw new ConvexError("Máquina não encontrada")
|
||||
throw new ConvexError("Dispositivo não encontrada")
|
||||
}
|
||||
|
||||
let nextPersona = machine.persona ?? undefined
|
||||
|
|
@ -1343,7 +1429,7 @@ export async function updatePersonaHandler(
|
|||
if (!trimmed) {
|
||||
nextPersona = undefined
|
||||
} else if (!ALLOWED_MACHINE_PERSONAS.has(trimmed)) {
|
||||
throw new ConvexError("Perfil inválido para a máquina")
|
||||
throw new ConvexError("Perfil inválido para a dispositivo")
|
||||
} else {
|
||||
nextPersona = trimmed
|
||||
}
|
||||
|
|
@ -1380,7 +1466,7 @@ export async function updatePersonaHandler(
|
|||
}
|
||||
|
||||
if (nextPersona && !nextAssignedUserId) {
|
||||
throw new ConvexError("Associe um usuário ao definir a persona da máquina")
|
||||
throw new ConvexError("Associe um usuário ao definir a persona da dispositivo")
|
||||
}
|
||||
|
||||
if (nextPersona && nextAssignedUserId) {
|
||||
|
|
@ -1435,6 +1521,196 @@ export async function updatePersonaHandler(
|
|||
return { ok: true, persona: nextPersona ?? null }
|
||||
}
|
||||
|
||||
export const saveDeviceCustomFields = mutation({
|
||||
args: {
|
||||
tenantId: v.string(),
|
||||
actorId: v.id("users"),
|
||||
machineId: v.id("machines"),
|
||||
fields: v.array(
|
||||
v.object({
|
||||
fieldId: v.id("deviceFields"),
|
||||
value: v.any(),
|
||||
})
|
||||
),
|
||||
},
|
||||
handler: async (ctx, args) => {
|
||||
await requireAdmin(ctx, args.actorId, args.tenantId)
|
||||
const machine = await ctx.db.get(args.machineId)
|
||||
if (!machine || machine.tenantId !== args.tenantId) {
|
||||
throw new ConvexError("Dispositivo não encontrado")
|
||||
}
|
||||
|
||||
const companyId = machine.companyId ?? null
|
||||
const deviceType = (machine.deviceType ?? "desktop").toLowerCase()
|
||||
|
||||
const entries = await Promise.all(
|
||||
args.fields.map(async ({ fieldId, value }) => {
|
||||
const definition = await ctx.db.get(fieldId)
|
||||
if (!definition || definition.tenantId !== args.tenantId) {
|
||||
return null
|
||||
}
|
||||
if (companyId && definition.companyId && definition.companyId !== companyId) {
|
||||
return null
|
||||
}
|
||||
if (!companyId && definition.companyId) {
|
||||
return null
|
||||
}
|
||||
const scope = (definition.scope ?? "all").toLowerCase()
|
||||
if (scope !== "all" && scope !== deviceType) {
|
||||
return null
|
||||
}
|
||||
const displayValue =
|
||||
value === null || value === undefined
|
||||
? null
|
||||
: formatDeviceCustomFieldDisplay(definition.type, value, definition.options ?? undefined)
|
||||
return {
|
||||
fieldId: definition._id,
|
||||
fieldKey: definition.key,
|
||||
label: definition.label,
|
||||
type: definition.type,
|
||||
value: value ?? null,
|
||||
displayValue: displayValue ?? undefined,
|
||||
}
|
||||
})
|
||||
)
|
||||
|
||||
const customFields = entries.filter(Boolean) as Array<{
|
||||
fieldId: Id<"deviceFields">
|
||||
fieldKey: string
|
||||
label: string
|
||||
type: string
|
||||
value: unknown
|
||||
displayValue?: string
|
||||
}>
|
||||
|
||||
await ctx.db.patch(args.machineId, {
|
||||
customFields,
|
||||
updatedAt: Date.now(),
|
||||
})
|
||||
},
|
||||
})
|
||||
|
||||
export const saveDeviceProfile = mutation({
|
||||
args: {
|
||||
tenantId: v.string(),
|
||||
actorId: v.id("users"),
|
||||
machineId: v.optional(v.id("machines")),
|
||||
companyId: v.optional(v.id("companies")),
|
||||
companySlug: v.optional(v.string()),
|
||||
displayName: v.string(),
|
||||
hostname: v.optional(v.string()),
|
||||
deviceType: v.string(),
|
||||
devicePlatform: v.optional(v.string()),
|
||||
osName: v.optional(v.string()),
|
||||
osVersion: v.optional(v.string()),
|
||||
macAddresses: v.optional(v.array(v.string())),
|
||||
serialNumbers: v.optional(v.array(v.string())),
|
||||
profile: v.optional(v.any()),
|
||||
status: v.optional(v.string()),
|
||||
isActive: v.optional(v.boolean()),
|
||||
},
|
||||
handler: async (ctx, args) => {
|
||||
await requireAdmin(ctx, args.actorId, args.tenantId)
|
||||
const displayName = args.displayName.trim()
|
||||
if (!displayName) {
|
||||
throw new ConvexError("Informe o nome do dispositivo")
|
||||
}
|
||||
const hostname = (args.hostname ?? displayName).trim()
|
||||
if (!hostname) {
|
||||
throw new ConvexError("Informe o identificador do dispositivo")
|
||||
}
|
||||
|
||||
const normalizedType = (() => {
|
||||
const candidate = args.deviceType.trim().toLowerCase()
|
||||
if (["desktop", "mobile", "tablet"].includes(candidate)) return candidate
|
||||
return "desktop"
|
||||
})()
|
||||
const normalizedPlatform = args.devicePlatform?.trim() || args.osName?.trim() || null
|
||||
const normalizedStatus = (args.status ?? "unknown").trim() || "unknown"
|
||||
const normalizedSlug = args.companySlug?.trim() || undefined
|
||||
const osNameInput = args.osName === undefined ? undefined : args.osName.trim()
|
||||
const osVersionInput = args.osVersion === undefined ? undefined : args.osVersion.trim()
|
||||
const now = Date.now()
|
||||
|
||||
if (args.machineId) {
|
||||
const machine = await ctx.db.get(args.machineId)
|
||||
if (!machine || machine.tenantId !== args.tenantId) {
|
||||
throw new ConvexError("Dispositivo não encontrado para atualização")
|
||||
}
|
||||
if (machine.managementMode && machine.managementMode !== "manual") {
|
||||
throw new ConvexError("Somente dispositivos manuais podem ser editados por esta ação")
|
||||
}
|
||||
|
||||
const normalizedIdentifiers = normalizeOptionalIdentifiers(args.macAddresses, args.serialNumbers)
|
||||
const macAddresses =
|
||||
args.macAddresses === undefined ? machine.macAddresses : normalizedIdentifiers.macs
|
||||
const serialNumbers =
|
||||
args.serialNumbers === undefined ? machine.serialNumbers : normalizedIdentifiers.serials
|
||||
const deviceProfilePatch = args.profile === undefined ? undefined : args.profile ?? null
|
||||
|
||||
const osNameValue = osNameInput === undefined ? machine.osName : osNameInput || machine.osName
|
||||
const osVersionValue =
|
||||
osVersionInput === undefined ? machine.osVersion ?? undefined : osVersionInput || undefined
|
||||
|
||||
await ctx.db.patch(args.machineId, {
|
||||
companyId: args.companyId ?? machine.companyId ?? undefined,
|
||||
companySlug: normalizedSlug ?? machine.companySlug ?? undefined,
|
||||
hostname,
|
||||
displayName,
|
||||
osName: osNameValue,
|
||||
osVersion: osVersionValue,
|
||||
macAddresses,
|
||||
serialNumbers,
|
||||
deviceType: normalizedType,
|
||||
devicePlatform: normalizedPlatform ?? machine.devicePlatform ?? undefined,
|
||||
deviceProfile: deviceProfilePatch,
|
||||
managementMode: "manual",
|
||||
status: normalizedStatus,
|
||||
isActive: args.isActive ?? machine.isActive ?? true,
|
||||
updatedAt: now,
|
||||
})
|
||||
|
||||
return { machineId: args.machineId }
|
||||
}
|
||||
|
||||
const normalizedIdentifiers = normalizeOptionalIdentifiers(args.macAddresses, args.serialNumbers)
|
||||
const fingerprint = generateManualFingerprint(args.tenantId, displayName)
|
||||
const deviceProfile = args.profile ?? undefined
|
||||
const osNameValue = osNameInput || normalizedPlatform || "Desconhecido"
|
||||
const osVersionValue = osVersionInput || undefined
|
||||
|
||||
const machineId = await ctx.db.insert("machines", {
|
||||
tenantId: args.tenantId,
|
||||
companyId: args.companyId ?? undefined,
|
||||
companySlug: normalizedSlug ?? undefined,
|
||||
hostname,
|
||||
displayName,
|
||||
osName: osNameValue,
|
||||
osVersion: osVersionValue,
|
||||
macAddresses: normalizedIdentifiers.macs,
|
||||
serialNumbers: normalizedIdentifiers.serials,
|
||||
fingerprint,
|
||||
metadata: undefined,
|
||||
deviceType: normalizedType,
|
||||
devicePlatform: normalizedPlatform ?? undefined,
|
||||
deviceProfile,
|
||||
managementMode: "manual",
|
||||
status: normalizedStatus,
|
||||
isActive: args.isActive ?? true,
|
||||
createdAt: now,
|
||||
updatedAt: now,
|
||||
registeredBy: "manual",
|
||||
persona: undefined,
|
||||
assignedUserId: undefined,
|
||||
assignedUserEmail: undefined,
|
||||
assignedUserName: undefined,
|
||||
assignedUserRole: undefined,
|
||||
})
|
||||
|
||||
return { machineId }
|
||||
},
|
||||
})
|
||||
|
||||
export const updatePersona = mutation({
|
||||
args: {
|
||||
machineId: v.id("machines"),
|
||||
|
|
@ -1454,7 +1730,7 @@ export const getContext = query({
|
|||
handler: async (ctx, args) => {
|
||||
const machine = await ctx.db.get(args.machineId)
|
||||
if (!machine) {
|
||||
throw new ConvexError("Máquina não encontrada")
|
||||
throw new ConvexError("Dispositivo não encontrada")
|
||||
}
|
||||
|
||||
const linkedUserIds = machine.linkedUserIds ?? []
|
||||
|
|
@ -1515,7 +1791,7 @@ export const linkAuthAccount = mutation({
|
|||
handler: async (ctx, args) => {
|
||||
const machine = await ctx.db.get(args.machineId)
|
||||
if (!machine) {
|
||||
throw new ConvexError("Máquina não encontrada")
|
||||
throw new ConvexError("Dispositivo não encontrada")
|
||||
}
|
||||
|
||||
await ctx.db.patch(machine._id, {
|
||||
|
|
@ -1535,7 +1811,7 @@ export const linkUser = mutation({
|
|||
},
|
||||
handler: async (ctx, { machineId, email }) => {
|
||||
const machine = await ctx.db.get(machineId)
|
||||
if (!machine) throw new ConvexError("Máquina não encontrada")
|
||||
if (!machine) throw new ConvexError("Dispositivo não encontrada")
|
||||
const tenantId = machine.tenantId
|
||||
const normalized = email.trim().toLowerCase()
|
||||
|
||||
|
|
@ -1546,7 +1822,7 @@ export const linkUser = mutation({
|
|||
if (!user) throw new ConvexError("Usuário não encontrado")
|
||||
const role = (user.role ?? "").toUpperCase()
|
||||
if (role === 'ADMIN' || role === 'AGENT') {
|
||||
throw new ConvexError('Usuários administrativos não podem ser vinculados a máquinas')
|
||||
throw new ConvexError('Usuários administrativos não podem ser vinculados a dispositivos')
|
||||
}
|
||||
|
||||
const current = new Set<Id<"users">>(machine.linkedUserIds ?? [])
|
||||
|
|
@ -1563,7 +1839,7 @@ export const unlinkUser = mutation({
|
|||
},
|
||||
handler: async (ctx, { machineId, userId }) => {
|
||||
const machine = await ctx.db.get(machineId)
|
||||
if (!machine) throw new ConvexError("Máquina não encontrada")
|
||||
if (!machine) throw new ConvexError("Dispositivo não encontrada")
|
||||
const next = (machine.linkedUserIds ?? []).filter((id) => id !== userId)
|
||||
await ctx.db.patch(machine._id, { linkedUserIds: next, updatedAt: Date.now() })
|
||||
return { ok: true }
|
||||
|
|
@ -1580,25 +1856,29 @@ export const rename = mutation({
|
|||
// Reutiliza requireStaff através de tickets.ts helpers
|
||||
const machine = await ctx.db.get(machineId)
|
||||
if (!machine) {
|
||||
throw new ConvexError("Máquina não encontrada")
|
||||
throw new ConvexError("Dispositivo não encontrada")
|
||||
}
|
||||
// Verifica permissão no tenant da máquina
|
||||
// Verifica permissão no tenant da dispositivo
|
||||
const viewer = await ctx.db.get(actorId)
|
||||
if (!viewer || viewer.tenantId !== machine.tenantId) {
|
||||
throw new ConvexError("Acesso negado ao tenant da máquina")
|
||||
throw new ConvexError("Acesso negado ao tenant da dispositivo")
|
||||
}
|
||||
const normalizedRole = (viewer.role ?? "AGENT").toUpperCase()
|
||||
const STAFF = new Set(["ADMIN", "MANAGER", "AGENT"])
|
||||
if (!STAFF.has(normalizedRole)) {
|
||||
throw new ConvexError("Apenas equipe interna pode renomear máquinas")
|
||||
throw new ConvexError("Apenas equipe interna pode renomear dispositivos")
|
||||
}
|
||||
|
||||
const nextName = hostname.trim()
|
||||
if (nextName.length < 2) {
|
||||
throw new ConvexError("Informe um nome válido para a máquina")
|
||||
throw new ConvexError("Informe um nome válido para a dispositivo")
|
||||
}
|
||||
|
||||
await ctx.db.patch(machineId, { hostname: nextName, updatedAt: Date.now() })
|
||||
await ctx.db.patch(machineId, {
|
||||
hostname: nextName,
|
||||
displayName: nextName,
|
||||
updatedAt: Date.now(),
|
||||
})
|
||||
return { ok: true }
|
||||
},
|
||||
})
|
||||
|
|
@ -1612,17 +1892,17 @@ export const toggleActive = mutation({
|
|||
handler: async (ctx, { machineId, actorId, active }) => {
|
||||
const machine = await ctx.db.get(machineId)
|
||||
if (!machine) {
|
||||
throw new ConvexError("Máquina não encontrada")
|
||||
throw new ConvexError("Dispositivo não encontrada")
|
||||
}
|
||||
|
||||
const actor = await ctx.db.get(actorId)
|
||||
if (!actor || actor.tenantId !== machine.tenantId) {
|
||||
throw new ConvexError("Acesso negado ao tenant da máquina")
|
||||
throw new ConvexError("Acesso negado ao tenant da dispositivo")
|
||||
}
|
||||
const normalizedRole = (actor.role ?? "AGENT").toUpperCase()
|
||||
const STAFF = new Set(["ADMIN", "MANAGER", "AGENT"])
|
||||
if (!STAFF.has(normalizedRole)) {
|
||||
throw new ConvexError("Apenas equipe interna pode atualizar o status da máquina")
|
||||
throw new ConvexError("Apenas equipe interna pode atualizar o status da dispositivo")
|
||||
}
|
||||
|
||||
await ctx.db.patch(machineId, {
|
||||
|
|
@ -1642,17 +1922,17 @@ export const resetAgent = mutation({
|
|||
handler: async (ctx, { machineId, actorId }) => {
|
||||
const machine = await ctx.db.get(machineId)
|
||||
if (!machine) {
|
||||
throw new ConvexError("Máquina não encontrada")
|
||||
throw new ConvexError("Dispositivo não encontrada")
|
||||
}
|
||||
|
||||
const actor = await ctx.db.get(actorId)
|
||||
if (!actor || actor.tenantId !== machine.tenantId) {
|
||||
throw new ConvexError("Acesso negado ao tenant da máquina")
|
||||
throw new ConvexError("Acesso negado ao tenant da dispositivo")
|
||||
}
|
||||
const normalizedRole = (actor.role ?? "AGENT").toUpperCase()
|
||||
const STAFF = new Set(["ADMIN", "MANAGER", "AGENT"])
|
||||
if (!STAFF.has(normalizedRole)) {
|
||||
throw new ConvexError("Apenas equipe interna pode resetar o agente da máquina")
|
||||
throw new ConvexError("Apenas equipe interna pode resetar o agente da dispositivo")
|
||||
}
|
||||
|
||||
const tokens = await ctx.db
|
||||
|
|
@ -1808,12 +2088,12 @@ export const updateRemoteAccess = mutation({
|
|||
handler: async (ctx, { machineId, actorId, provider, identifier, url, notes, action, entryId, clear }) => {
|
||||
const machine = await ctx.db.get(machineId)
|
||||
if (!machine) {
|
||||
throw new ConvexError("Máquina não encontrada")
|
||||
throw new ConvexError("Dispositivo não encontrada")
|
||||
}
|
||||
|
||||
const actor = await ctx.db.get(actorId)
|
||||
if (!actor || actor.tenantId !== machine.tenantId) {
|
||||
throw new ConvexError("Acesso negado ao tenant da máquina")
|
||||
throw new ConvexError("Acesso negado ao tenant da dispositivo")
|
||||
}
|
||||
|
||||
const normalizedRole = (actor.role ?? "AGENT").toUpperCase()
|
||||
|
|
@ -1937,17 +2217,17 @@ export const remove = mutation({
|
|||
handler: async (ctx, { machineId, actorId }) => {
|
||||
const machine = await ctx.db.get(machineId)
|
||||
if (!machine) {
|
||||
throw new ConvexError("Máquina não encontrada")
|
||||
throw new ConvexError("Dispositivo não encontrada")
|
||||
}
|
||||
|
||||
const actor = await ctx.db.get(actorId)
|
||||
if (!actor || actor.tenantId !== machine.tenantId) {
|
||||
throw new ConvexError("Acesso negado ao tenant da máquina")
|
||||
throw new ConvexError("Acesso negado ao tenant da dispositivo")
|
||||
}
|
||||
const role = (actor.role ?? "AGENT").toUpperCase()
|
||||
const STAFF = new Set(["ADMIN", "MANAGER", "AGENT"])
|
||||
if (!STAFF.has(role)) {
|
||||
throw new ConvexError("Apenas equipe interna pode excluir máquinas")
|
||||
throw new ConvexError("Apenas equipe interna pode excluir dispositivos")
|
||||
}
|
||||
|
||||
const tokens = await ctx.db
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue