feat: improve ticket export and navigation

This commit is contained in:
Esdras Renan 2025-10-13 00:08:18 -03:00
parent 0731c5d1ea
commit 7d6f3bea01
28 changed files with 1612 additions and 609 deletions

View file

@ -65,6 +65,24 @@ function computeFingerprint(tenantId: string, companySlug: string | undefined, h
return toHex(sha256(payload))
}
function extractCollaboratorEmail(metadata: unknown): string | null {
if (!metadata || typeof metadata !== "object") return null
const record = metadata as Record<string, unknown>
const collaborator = record["collaborator"]
if (!collaborator || typeof collaborator !== "object") return null
const email = (collaborator as { email?: unknown }).email
if (typeof email !== "string") return null
const trimmed = email.trim().toLowerCase()
return trimmed || null
}
function matchesExistingHardware(existing: Doc<"machines">, identifiers: NormalizedIdentifiers, hostname: string): boolean {
const intersectsMac = existing.macAddresses.some((mac) => identifiers.macs.includes(mac))
const intersectsSerial = existing.serialNumbers.some((serial) => identifiers.serials.includes(serial))
const sameHostname = existing.hostname.trim().toLowerCase() === hostname.trim().toLowerCase()
return intersectsMac || intersectsSerial || sameHostname
}
function hashToken(token: string) {
return toHex(sha256(token))
}
@ -305,11 +323,24 @@ export const register = mutation({
const now = Date.now()
const metadataPatch = args.metadata && typeof args.metadata === "object" ? (args.metadata as Record<string, unknown>) : undefined
const existing = await ctx.db
let existing = await ctx.db
.query("machines")
.withIndex("by_tenant_fingerprint", (q) => q.eq("tenantId", tenantId).eq("fingerprint", fingerprint))
.first()
if (!existing) {
const collaboratorEmail = extractCollaboratorEmail(metadataPatch ?? args.metadata)
if (collaboratorEmail) {
const candidate = await ctx.db
.query("machines")
.withIndex("by_tenant_assigned_email", (q) => q.eq("tenantId", tenantId).eq("assignedUserEmail", collaboratorEmail))
.first()
if (candidate && matchesExistingHardware(candidate, identifiers, args.hostname)) {
existing = candidate
}
}
}
let machineId: Id<"machines">
if (existing) {
@ -323,6 +354,7 @@ export const register = mutation({
architecture: args.os.architecture,
macAddresses: identifiers.macs,
serialNumbers: identifiers.serials,
fingerprint,
metadata: metadataPatch ? mergeMetadata(existing.metadata, metadataPatch) : existing.metadata,
lastHeartbeatAt: now,
updatedAt: now,
@ -834,6 +866,28 @@ export const getContext = query({
},
})
export const findByAuthEmail = query({
args: {
authEmail: v.string(),
},
handler: async (ctx, args) => {
const normalizedEmail = args.authEmail.trim().toLowerCase()
const machine = await ctx.db
.query("machines")
.withIndex("by_auth_email", (q) => q.eq("authEmail", normalizedEmail))
.first()
if (!machine) {
return null
}
return {
id: machine._id,
}
},
})
export const linkAuthAccount = mutation({
args: {
machineId: v.id("machines"),