import { eq, and, desc, isNull, isNotNull, inArray } from "drizzle-orm"; import type { Database } from "../client.js"; import { hosts } from "../schema/index.js"; import { DEFAULT_TENANT_ID } from "@opentrust/shared"; export function hostQueries(db: Database) { return { async upsert(data: { hostname: string; os?: string; arch?: string; ip?: string; cliVersion?: string; projectMode?: string; projectRoot?: string; services?: Record; metadata?: Record; tenantId?: string; }) { const tenantId = data.tenantId ?? DEFAULT_TENANT_ID; const now = new Date().toISOString(); const existing = await db .select() .from(hosts) .where(and(eq(hosts.tenantId, tenantId), eq(hosts.hostname, data.hostname))) .limit(1); if (existing.length > 0) { await db .update(hosts) .set({ os: data.os, arch: data.arch, ip: data.ip, cliVersion: data.cliVersion, projectMode: data.projectMode, projectRoot: data.projectRoot, services: data.services ?? null, metadata: data.metadata ?? null, status: "online", lastSeenAt: now, }) .where(eq(hosts.id, existing[0].id)); return existing[0].id; } const id = crypto.randomUUID(); await db.insert(hosts).values({ id, tenantId, hostname: data.hostname, os: data.os, arch: data.arch, ip: data.ip, cliVersion: data.cliVersion, projectMode: data.projectMode, projectRoot: data.projectRoot, services: data.services ?? null, metadata: data.metadata ?? null, status: "online", lastSeenAt: now, createdAt: now, }); return id; }, async heartbeat( id: string, data: { services?: Record; metadata?: Record }, ) { await db .update(hosts) .set({ services: data.services ?? undefined, metadata: data.metadata ?? undefined, status: "online", lastSeenAt: new Date().toISOString(), }) .where(eq(hosts.id, id)); }, async findAll(tenantId: string = DEFAULT_TENANT_ID, includeArchived = false) { const conditions = [eq(hosts.tenantId, tenantId)]; if (!includeArchived) conditions.push(isNull(hosts.archivedAt)); return db .select() .from(hosts) .where(and(...conditions)) .orderBy(desc(hosts.lastSeenAt)); }, async findById(id: string) { const rows = await db.select().from(hosts).where(eq(hosts.id, id)).limit(1); return rows[0] ?? null; }, async remove(id: string) { await db.delete(hosts).where(eq(hosts.id, id)); }, async setOffline(id: string) { await db .update(hosts) .set({ status: "offline", lastSeenAt: new Date().toISOString() }) .where(eq(hosts.id, id)); }, async archive(ids: string[]) { if (ids.length === 0) return; await db .update(hosts) .set({ archivedAt: new Date().toISOString() }) .where(inArray(hosts.id, ids)); }, async unarchive(ids: string[]) { if (ids.length === 0) return; await db .update(hosts) .set({ archivedAt: null }) .where(inArray(hosts.id, ids)); }, async findArchived(tenantId: string = DEFAULT_TENANT_ID) { return db .select() .from(hosts) .where(and(eq(hosts.tenantId, tenantId), isNotNull(hosts.archivedAt))) .orderBy(desc(hosts.lastSeenAt)); }, }; }