mirror of
https://github.com/tale/headplane.git
synced 2026-08-04 03:07:43 +00:00
25dc09e025
Previously we would use naive revalidators which would invalidate EVERY SINGLE action loader every 3 seconds, resulting in several fetches. It would also bubble fetches across the layout actions into the individual pages. This new approach selectively has live stores of resources which then poll for changes on the server side and then dispatches updates to the client via a new /events/live SSE endpoint.
54 lines
1.6 KiB
TypeScript
54 lines
1.6 KiB
TypeScript
import { eq, isNotNull } from "drizzle-orm";
|
|
|
|
import { nodesResource } from "~/server/headscale/live-store";
|
|
import log from "~/utils/log";
|
|
|
|
import type { Route } from "../../layout/+types/app";
|
|
import { ephemeralNodes } from "./schema";
|
|
|
|
export async function pruneEphemeralNodes({ context, request }: Route.LoaderArgs) {
|
|
const principal = await context.auth.require(request);
|
|
const ephemerals = await context.db
|
|
.select()
|
|
.from(ephemeralNodes)
|
|
.where(isNotNull(ephemeralNodes.node_key));
|
|
|
|
if (ephemerals.length === 0) {
|
|
log.debug("api", "No ephemeral nodes to prune");
|
|
return;
|
|
}
|
|
|
|
const apiKey = context.auth.getHeadscaleApiKey(principal, context.oidc?.apiKey);
|
|
const api = context.hsApi.getRuntimeClient(apiKey);
|
|
const nodes = await api.getNodes();
|
|
const toPrune = nodes.filter((node) => {
|
|
if (node.online) {
|
|
return false;
|
|
}
|
|
|
|
return ephemerals.some((ephemeral) => node.nodeKey === ephemeral.node_key);
|
|
});
|
|
|
|
if (toPrune.length === 0) {
|
|
log.debug("api", "No SSH nodes to prune");
|
|
return;
|
|
}
|
|
|
|
// Delete from the Headscale nodes list and then from the database
|
|
const promises = toPrune.map((node) => {
|
|
return async () => {
|
|
log.debug("api", `Pruning node ${node.name}`);
|
|
await api.deleteNode(node.id);
|
|
|
|
await context.db.delete(ephemeralNodes).where(eq(ephemeralNodes.node_key, node.nodeKey));
|
|
log.debug("api", `Node ${node.name} pruned successfully`);
|
|
};
|
|
});
|
|
|
|
await Promise.all(promises.map((p) => p()));
|
|
|
|
if (toPrune.length > 0) {
|
|
await context.hsLive.refresh(nodesResource, api);
|
|
}
|
|
}
|