feat(agent): v0.3.9 sync, UI details, self-update, centralized version
- Add agent/server startup sync (sync/sync_response) - Centralize agent version in agent/VERSION + expose /api/agent/version - Display agent version, nodeId and server version in local UI - Add agent self-update detection/download/restart via helper scripts - Run start/stop/delete/reset handlers in goroutines to avoid WebSocket blocking - Update dashboard download links and SUIVI_VPN_ONDEMAND.md - Document Podman stays installer-managed, not agent-updated
This commit is contained in:
+60
-1
@@ -3,6 +3,7 @@ import { randomBytes } from "crypto";
|
||||
import type { IncomingMessage } from "http";
|
||||
import { prisma } from "./prisma";
|
||||
import { createEphemeralPreAuthKey, getHeadscaleUserId } from "./headscale";
|
||||
import { getAgentVersion } from "./agent-version";
|
||||
|
||||
interface NodeMessage {
|
||||
action: string;
|
||||
@@ -12,10 +13,16 @@ interface NodeMessage {
|
||||
type?: string;
|
||||
port?: number;
|
||||
composeConfig?: string;
|
||||
initScript?: string;
|
||||
studentName?: string;
|
||||
error?: string;
|
||||
tailscaleIp?: string;
|
||||
token?: string;
|
||||
serverVersion?: string;
|
||||
instances?: Array<{ id: string; status: string; port: number; templateName?: string }>;
|
||||
toStart?: Array<{ id: string; type: string; port: number; composeConfig?: string; initScript?: string }>;
|
||||
toDelete?: string[];
|
||||
toStop?: string[];
|
||||
}
|
||||
|
||||
const nodes = new Map<string, WebSocket>();
|
||||
@@ -126,7 +133,7 @@ export function initWebSocketServer(wss: WebSocketServer) {
|
||||
create: { id, status: "online", lastSeen: new Date() },
|
||||
});
|
||||
}
|
||||
ws.send(JSON.stringify({ action: "registered" }));
|
||||
ws.send(JSON.stringify({ action: "registered", serverVersion: getAgentVersion() }));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -263,6 +270,58 @@ export function initWebSocketServer(wss: WebSocketServer) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (msg.action === "sync" && msg.instances) {
|
||||
const serverInstances = await prisma.instance.findMany({
|
||||
where: { nodeId },
|
||||
include: { template: true },
|
||||
});
|
||||
|
||||
const localIds = new Set(msg.instances.map((i) => i.id));
|
||||
const serverIds = new Set(serverInstances.map((i) => i.id));
|
||||
|
||||
const toDelete = msg.instances
|
||||
.filter((i) => !serverIds.has(i.id))
|
||||
.map((i) => i.id);
|
||||
|
||||
const toStop = msg.instances
|
||||
.filter((i) => {
|
||||
const server = serverInstances.find((s) => s.id === i.id);
|
||||
return server && server.status === "stopped" && i.status === "running";
|
||||
})
|
||||
.map((i) => i.id);
|
||||
|
||||
const toStart = serverInstances
|
||||
.filter((s) => !localIds.has(s.id))
|
||||
.map((s) => ({
|
||||
id: s.id,
|
||||
type: s.template.type,
|
||||
port: s.port,
|
||||
composeConfig: s.template.composeConfig,
|
||||
initScript: s.template.initScript ?? undefined,
|
||||
}));
|
||||
|
||||
console.log(
|
||||
"[WS] Sync for",
|
||||
nodeId,
|
||||
"- toStart:",
|
||||
toStart.length,
|
||||
"toDelete:",
|
||||
toDelete.length,
|
||||
"toStop:",
|
||||
toStop.length
|
||||
);
|
||||
|
||||
ws.send(
|
||||
JSON.stringify({
|
||||
action: "sync_response",
|
||||
toStart,
|
||||
toDelete,
|
||||
toStop,
|
||||
})
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if (msg.action === "instance_started" && msg.instanceId) {
|
||||
const { count } = await prisma.instance.updateMany({
|
||||
where: { id: msg.instanceId },
|
||||
|
||||
Reference in New Issue
Block a user