/** biome-ignore-all lint/suspicious/noAssignInExpressions: it's fine :) */ import type { PreinitializedMapStore } from "nanostores" import { pb, verifyAuth } from "@/lib/api" import { $allSystemsById, $allSystemsByName, $downSystems, $longestSystemName, $pausedSystems, $upSystems, } from "@/lib/stores" import { isVisuallyLonger, updateFavicon } from "@/lib/utils" import type { SystemRecord } from "@/types" import { SystemStatus } from "./enums" const COLLECTION = pb.collection("systems") const FIELDS_DEFAULT = "id,name,host,port,info,status" let initialized = false // biome-ignore lint/suspicious/noConfusingVoidType: typescript rocks let unsub: (() => void) | undefined | void /** Initialize the systems manager and set up listeners */ export function init() { if (initialized) { return } initialized = true // sync system stores on change $allSystemsById.listen((newSystems, oldSystems, changedKey) => { const oldSystem = oldSystems[changedKey] const newSystem = newSystems[changedKey] // if system is undefined (deleted), remove it from the stores if (oldSystem && !newSystem?.id) { removeFromStore(oldSystem, $upSystems) removeFromStore(oldSystem, $downSystems) removeFromStore(oldSystem, $pausedSystems) removeFromStore(oldSystem, $allSystemsById) } if (!newSystem) { onSystemsChanged(newSystems, newSystem, oldSystem) return } const newStatus = newSystem.status if (newStatus === SystemStatus.Up) { $upSystems.setKey(newSystem.id, newSystem) removeFromStore(newSystem, $downSystems) removeFromStore(newSystem, $pausedSystems) } else if (newStatus === SystemStatus.Down) { $downSystems.setKey(newSystem.id, newSystem) removeFromStore(newSystem, $upSystems) removeFromStore(newSystem, $pausedSystems) } else if (newStatus === SystemStatus.Paused) { $pausedSystems.setKey(newSystem.id, newSystem) removeFromStore(newSystem, $upSystems) removeFromStore(newSystem, $downSystems) } else if (newStatus === SystemStatus.Pending) { removeFromStore(newSystem, $upSystems) removeFromStore(newSystem, $downSystems) removeFromStore(newSystem, $pausedSystems) } // run things that need to be done when systems change onSystemsChanged(newSystems, newSystem, oldSystem) }) } /** Update the longest system name string and favicon based on system status */ function onSystemsChanged(systems: Record, newSystem?: SystemRecord, oldSystem?: SystemRecord) { const downSystemsStore = $downSystems.get() const downSystems = Object.values(downSystemsStore) // if the old system's old name was the longest, we need to find the new longest name // otherwise, if the changed system's new name is longer than the current longest, update it const longestName = $longestSystemName.get() if (oldSystem?.name === longestName && oldSystem.name !== newSystem?.name) { let newLongest = "" for (const id in systems) { if (isVisuallyLonger(systems[id].name, newLongest)) { newLongest = systems[id].name } } $longestSystemName.set(newLongest) } else if (newSystem && newSystem.name !== longestName && isVisuallyLonger(newSystem.name, longestName)) { $longestSystemName.set(newSystem.name) } updateFavicon(downSystems.length) } /** Fetch systems from collection */ async function fetchSystems(): Promise { try { return await COLLECTION.getFullList({ sort: "+name", fields: FIELDS_DEFAULT }) } catch (error) { console.error("Failed to fetch systems:", error) return [] } } /** Makes sure the system has valid info object and throws if not */ function validateSystemInfo(system: SystemRecord) { if (!("cpu" in system.info)) { throw new Error(`${system.name} has no CPU info`) } } /** Add system to both name and ID stores */ export function add(system: SystemRecord) { try { validateSystemInfo(system) $allSystemsByName.setKey(system.name, system) $allSystemsById.setKey(system.id, system) } catch (error) { console.error(error) } } /** Update system in stores */ export function update(system: SystemRecord) { try { validateSystemInfo(system) // if name changed, make sure old name is removed from the name store const oldName = $allSystemsById.get()[system.id]?.name if (oldName !== system.name) { $allSystemsByName.setKey(oldName, undefined as unknown as SystemRecord) } add(system) } catch (error) { console.error(error) } } /** Remove system from stores */ export function remove(system: SystemRecord) { removeFromStore(system, $allSystemsByName) removeFromStore(system, $allSystemsById) removeFromStore(system, $upSystems) removeFromStore(system, $downSystems) removeFromStore(system, $pausedSystems) } /** Remove system from specific store */ function removeFromStore(system: SystemRecord, store: PreinitializedMapStore>) { const key = store === $allSystemsByName ? system.name : system.id store.setKey(key, undefined as unknown as SystemRecord) } /** Action functions for subscription */ const actionFns: Record void> = { create: add, update: update, delete: remove, } /** Subscribe to real-time system updates from the collection */ export async function subscribe() { try { unsub = await COLLECTION.subscribe("*", ({ action, record }) => actionFns[action]?.(record), { fields: FIELDS_DEFAULT, }) } catch (error) { console.error("Failed to subscribe to systems collection:", error) } } /** Refresh all systems with latest data from the hub */ export async function refresh() { try { const records = await fetchSystems() if (!records.length) { // No systems found, verify authentication verifyAuth() return } for (const record of records) { add(record) } } catch (error) { console.error("Failed to refresh systems:", error) } } /** Unsubscribe from real-time system updates */ export const unsubscribe = () => (unsub = unsub?.())