feat: melhorar inventário e gestão de máquinas
This commit is contained in:
parent
b1d334045d
commit
3f0702d80b
5 changed files with 584 additions and 59 deletions
|
|
@ -112,9 +112,39 @@ async function getActiveToken(
|
|||
return { token, machine }
|
||||
}
|
||||
|
||||
function isObject(value: unknown): value is Record<string, unknown> {
|
||||
return Boolean(value) && typeof value === "object" && !Array.isArray(value)
|
||||
}
|
||||
|
||||
function mergeInventory(current: unknown, patch: unknown): unknown {
|
||||
if (!isObject(patch)) {
|
||||
return patch
|
||||
}
|
||||
const base: Record<string, unknown> = isObject(current) ? { ...(current as Record<string, unknown>) } : {}
|
||||
for (const [key, value] of Object.entries(patch)) {
|
||||
if (value === undefined) continue
|
||||
if (isObject(value) && isObject(base[key])) {
|
||||
base[key] = mergeInventory(base[key], value)
|
||||
} else {
|
||||
base[key] = value
|
||||
}
|
||||
}
|
||||
return base
|
||||
}
|
||||
|
||||
function mergeMetadata(current: unknown, patch: Record<string, unknown>) {
|
||||
if (!current || typeof current !== "object") return patch
|
||||
return { ...(current as Record<string, unknown>), ...patch }
|
||||
const base: Record<string, unknown> = isObject(current) ? { ...(current as Record<string, unknown>) } : {}
|
||||
for (const [key, value] of Object.entries(patch)) {
|
||||
if (value === undefined) continue
|
||||
if (key === "inventory") {
|
||||
base[key] = mergeInventory(base[key], value)
|
||||
} else if (isObject(value) && isObject(base[key])) {
|
||||
base[key] = mergeInventory(base[key], value)
|
||||
} else {
|
||||
base[key] = value
|
||||
}
|
||||
}
|
||||
return base
|
||||
}
|
||||
|
||||
type PostureFinding = {
|
||||
|
|
@ -272,6 +302,7 @@ export const register = mutation({
|
|||
const fingerprint = computeFingerprint(tenantId, args.companySlug, args.hostname, identifiers)
|
||||
const { companyId, companySlug } = await ensureCompany(ctx, tenantId, args.companySlug)
|
||||
const now = Date.now()
|
||||
const metadataPatch = args.metadata && typeof args.metadata === "object" ? (args.metadata as Record<string, unknown>) : undefined
|
||||
|
||||
const existing = await ctx.db
|
||||
.query("machines")
|
||||
|
|
@ -291,7 +322,7 @@ export const register = mutation({
|
|||
architecture: args.os.architecture,
|
||||
macAddresses: identifiers.macs,
|
||||
serialNumbers: identifiers.serials,
|
||||
metadata: args.metadata ? mergeMetadata(existing.metadata, { inventory: args.metadata }) : existing.metadata,
|
||||
metadata: metadataPatch ? mergeMetadata(existing.metadata, metadataPatch) : existing.metadata,
|
||||
lastHeartbeatAt: now,
|
||||
updatedAt: now,
|
||||
status: "online",
|
||||
|
|
@ -310,7 +341,7 @@ export const register = mutation({
|
|||
macAddresses: identifiers.macs,
|
||||
serialNumbers: identifiers.serials,
|
||||
fingerprint,
|
||||
metadata: args.metadata ? { inventory: args.metadata } : undefined,
|
||||
metadata: metadataPatch ? mergeMetadata(undefined, metadataPatch) : undefined,
|
||||
lastHeartbeatAt: now,
|
||||
status: "online",
|
||||
createdAt: now,
|
||||
|
|
@ -385,10 +416,13 @@ export const upsertInventory = mutation({
|
|||
const { companyId, companySlug } = await ensureCompany(ctx, tenantId, args.companySlug)
|
||||
const now = Date.now()
|
||||
|
||||
const metadataPatch = mergeMetadata({}, {
|
||||
...(args.inventory ? { inventory: args.inventory } : {}),
|
||||
...(args.metrics ? { metrics: args.metrics } : {}),
|
||||
})
|
||||
const metadataPatch: Record<string, unknown> = {}
|
||||
if (args.inventory && typeof args.inventory === "object") {
|
||||
metadataPatch.inventory = args.inventory as Record<string, unknown>
|
||||
}
|
||||
if (args.metrics && typeof args.metrics === "object") {
|
||||
metadataPatch.metrics = args.metrics as Record<string, unknown>
|
||||
}
|
||||
|
||||
const existing = await ctx.db
|
||||
.query("machines")
|
||||
|
|
@ -408,7 +442,7 @@ export const upsertInventory = mutation({
|
|||
architecture: args.os.architecture,
|
||||
macAddresses: identifiers.macs,
|
||||
serialNumbers: identifiers.serials,
|
||||
metadata: mergeMetadata(existing.metadata, metadataPatch),
|
||||
metadata: Object.keys(metadataPatch).length ? mergeMetadata(existing.metadata, metadataPatch) : existing.metadata,
|
||||
lastHeartbeatAt: now,
|
||||
updatedAt: now,
|
||||
status: args.metrics ? "online" : existing.status ?? "unknown",
|
||||
|
|
@ -427,7 +461,7 @@ export const upsertInventory = mutation({
|
|||
macAddresses: identifiers.macs,
|
||||
serialNumbers: identifiers.serials,
|
||||
fingerprint,
|
||||
metadata: metadataPatch,
|
||||
metadata: Object.keys(metadataPatch).length ? mergeMetadata(undefined, metadataPatch) : undefined,
|
||||
lastHeartbeatAt: now,
|
||||
status: args.metrics ? "online" : "unknown",
|
||||
createdAt: now,
|
||||
|
|
@ -470,11 +504,17 @@ export const heartbeat = mutation({
|
|||
const { machine, token } = await getActiveToken(ctx, args.machineToken)
|
||||
const now = Date.now()
|
||||
|
||||
const mergedMetadata = mergeMetadata(machine.metadata, {
|
||||
...(args.metadata ?? {}),
|
||||
...(args.metrics ? { metrics: args.metrics } : {}),
|
||||
...(args.inventory ? { inventory: args.inventory } : {}),
|
||||
})
|
||||
const metadataPatch: Record<string, unknown> = {}
|
||||
if (args.metadata && typeof args.metadata === "object") {
|
||||
Object.assign(metadataPatch, args.metadata as Record<string, unknown>)
|
||||
}
|
||||
if (args.inventory && typeof args.inventory === "object") {
|
||||
metadataPatch.inventory = mergeInventory(metadataPatch.inventory, args.inventory as Record<string, unknown>)
|
||||
}
|
||||
if (args.metrics && typeof args.metrics === "object") {
|
||||
metadataPatch.metrics = args.metrics as Record<string, unknown>
|
||||
}
|
||||
const mergedMetadata = Object.keys(metadataPatch).length ? mergeMetadata(machine.metadata, metadataPatch) : machine.metadata
|
||||
|
||||
await ctx.db.patch(machine._id, {
|
||||
hostname: args.hostname ?? machine.hostname,
|
||||
|
|
@ -684,3 +724,35 @@ export const rename = mutation({
|
|||
return { ok: true }
|
||||
},
|
||||
})
|
||||
|
||||
export const remove = mutation({
|
||||
args: {
|
||||
machineId: v.id("machines"),
|
||||
actorId: v.id("users"),
|
||||
},
|
||||
handler: async (ctx, { machineId, actorId }) => {
|
||||
const machine = await ctx.db.get(machineId)
|
||||
if (!machine) {
|
||||
throw new ConvexError("Máquina 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")
|
||||
}
|
||||
const role = (actor.role ?? "AGENT").toUpperCase()
|
||||
const STAFF = new Set(["ADMIN", "MANAGER", "AGENT", "COLLABORATOR"])
|
||||
if (!STAFF.has(role)) {
|
||||
throw new ConvexError("Apenas equipe interna pode excluir máquinas")
|
||||
}
|
||||
|
||||
const tokens = await ctx.db
|
||||
.query("machineTokens")
|
||||
.withIndex("by_machine", (q) => q.eq("machineId", machineId))
|
||||
.collect()
|
||||
|
||||
await Promise.all(tokens.map((token) => ctx.db.delete(token._id)))
|
||||
await ctx.db.delete(machineId)
|
||||
return { ok: true }
|
||||
},
|
||||
})
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue