mirror of
https://github.com/9technologygroup/patchmon.net.git
synced 2025-10-23 16:13:57 +00:00
420 lines
11 KiB
JavaScript
420 lines
11 KiB
JavaScript
const express = require("express");
|
|
const router = express.Router();
|
|
const agentVersionService = require("../services/agentVersionService");
|
|
const { authenticateToken } = require("../middleware/auth");
|
|
const { requirePermission } = require("../middleware/permissions");
|
|
|
|
// Test GitHub API connectivity
|
|
router.get(
|
|
"/test-github",
|
|
authenticateToken,
|
|
requirePermission("can_manage_settings"),
|
|
async (_req, res) => {
|
|
try {
|
|
const axios = require("axios");
|
|
const response = await axios.get(
|
|
"https://api.github.com/repos/PatchMon/PatchMon-agent/releases",
|
|
{
|
|
timeout: 10000,
|
|
headers: {
|
|
"User-Agent": "PatchMon-Server/1.0",
|
|
Accept: "application/vnd.github.v3+json",
|
|
},
|
|
},
|
|
);
|
|
|
|
res.json({
|
|
success: true,
|
|
status: response.status,
|
|
releasesFound: response.data.length,
|
|
latestRelease: response.data[0]?.tag_name || "No releases",
|
|
rateLimitRemaining: response.headers["x-ratelimit-remaining"],
|
|
rateLimitLimit: response.headers["x-ratelimit-limit"],
|
|
});
|
|
} catch (error) {
|
|
console.error("❌ GitHub API test failed:", error.message);
|
|
res.status(500).json({
|
|
success: false,
|
|
error: error.message,
|
|
status: error.response?.status,
|
|
statusText: error.response?.statusText,
|
|
rateLimitRemaining: error.response?.headers["x-ratelimit-remaining"],
|
|
rateLimitLimit: error.response?.headers["x-ratelimit-limit"],
|
|
});
|
|
}
|
|
},
|
|
);
|
|
|
|
// Get current version information
|
|
router.get("/version", authenticateToken, async (_req, res) => {
|
|
try {
|
|
const versionInfo = await agentVersionService.getVersionInfo();
|
|
console.log(
|
|
"📊 Version info response:",
|
|
JSON.stringify(versionInfo, null, 2),
|
|
);
|
|
res.json(versionInfo);
|
|
} catch (error) {
|
|
console.error("❌ Failed to get version info:", error.message);
|
|
res.status(500).json({
|
|
error: "Failed to get version information",
|
|
details: error.message,
|
|
status: "error",
|
|
});
|
|
}
|
|
});
|
|
|
|
// Refresh current version by executing agent binary
|
|
router.post(
|
|
"/version/refresh",
|
|
authenticateToken,
|
|
requirePermission("can_manage_settings"),
|
|
async (_req, res) => {
|
|
try {
|
|
console.log("🔄 Refreshing current agent version...");
|
|
const currentVersion = await agentVersionService.refreshCurrentVersion();
|
|
console.log("📊 Refreshed current version:", currentVersion);
|
|
res.json({
|
|
success: true,
|
|
currentVersion: currentVersion,
|
|
message: currentVersion
|
|
? `Current version refreshed: ${currentVersion}`
|
|
: "No agent binary found",
|
|
});
|
|
} catch (error) {
|
|
console.error("❌ Failed to refresh current version:", error.message);
|
|
res.status(500).json({
|
|
success: false,
|
|
error: "Failed to refresh current version",
|
|
details: error.message,
|
|
});
|
|
}
|
|
},
|
|
);
|
|
|
|
// Download latest update
|
|
router.post(
|
|
"/version/download",
|
|
authenticateToken,
|
|
requirePermission("can_manage_settings"),
|
|
async (_req, res) => {
|
|
try {
|
|
console.log("🔄 Downloading latest agent update...");
|
|
const downloadResult = await agentVersionService.downloadLatestUpdate();
|
|
console.log(
|
|
"📊 Download result:",
|
|
JSON.stringify(downloadResult, null, 2),
|
|
);
|
|
res.json(downloadResult);
|
|
} catch (error) {
|
|
console.error("❌ Failed to download latest update:", error.message);
|
|
res.status(500).json({
|
|
success: false,
|
|
error: "Failed to download latest update",
|
|
details: error.message,
|
|
});
|
|
}
|
|
},
|
|
);
|
|
|
|
// Check for updates
|
|
router.post(
|
|
"/version/check",
|
|
authenticateToken,
|
|
requirePermission("can_manage_settings"),
|
|
async (_req, res) => {
|
|
try {
|
|
console.log("🔄 Manual update check triggered");
|
|
const updateInfo = await agentVersionService.checkForUpdates();
|
|
console.log(
|
|
"📊 Update check result:",
|
|
JSON.stringify(updateInfo, null, 2),
|
|
);
|
|
res.json(updateInfo);
|
|
} catch (error) {
|
|
console.error("❌ Failed to check for updates:", error.message);
|
|
res.status(500).json({ error: "Failed to check for updates" });
|
|
}
|
|
},
|
|
);
|
|
|
|
// Get available versions
|
|
router.get("/versions", authenticateToken, async (_req, res) => {
|
|
try {
|
|
const versions = await agentVersionService.getAvailableVersions();
|
|
console.log(
|
|
"📦 Available versions response:",
|
|
JSON.stringify(versions, null, 2),
|
|
);
|
|
res.json({ versions });
|
|
} catch (error) {
|
|
console.error("❌ Failed to get available versions:", error.message);
|
|
res.status(500).json({ error: "Failed to get available versions" });
|
|
}
|
|
});
|
|
|
|
// Get binary information
|
|
router.get(
|
|
"/binary/:version/:architecture",
|
|
authenticateToken,
|
|
async (_req, res) => {
|
|
try {
|
|
const { version, architecture } = req.params;
|
|
const binaryInfo = await agentVersionService.getBinaryInfo(
|
|
version,
|
|
architecture,
|
|
);
|
|
res.json(binaryInfo);
|
|
} catch (error) {
|
|
console.error("❌ Failed to get binary info:", error.message);
|
|
res.status(404).json({ error: error.message });
|
|
}
|
|
},
|
|
);
|
|
|
|
// Download agent binary
|
|
router.get(
|
|
"/download/:version/:architecture",
|
|
authenticateToken,
|
|
async (_req, res) => {
|
|
try {
|
|
const { version, architecture } = req.params;
|
|
|
|
// Validate architecture
|
|
if (!agentVersionService.supportedArchitectures.includes(architecture)) {
|
|
return res.status(400).json({ error: "Unsupported architecture" });
|
|
}
|
|
|
|
await agentVersionService.serveBinary(version, architecture, res);
|
|
} catch (error) {
|
|
console.error("❌ Failed to serve binary:", error.message);
|
|
res.status(500).json({ error: "Failed to serve binary" });
|
|
}
|
|
},
|
|
);
|
|
|
|
// Get latest binary for architecture (for agents to query)
|
|
router.get("/latest/:architecture", async (req, res) => {
|
|
try {
|
|
const { architecture } = req.params;
|
|
|
|
// Validate architecture
|
|
if (!agentVersionService.supportedArchitectures.includes(architecture)) {
|
|
return res.status(400).json({ error: "Unsupported architecture" });
|
|
}
|
|
|
|
const versionInfo = await agentVersionService.getVersionInfo();
|
|
|
|
if (!versionInfo.latestVersion) {
|
|
return res.status(404).json({ error: "No latest version available" });
|
|
}
|
|
|
|
const binaryInfo = await agentVersionService.getBinaryInfo(
|
|
versionInfo.latestVersion,
|
|
architecture,
|
|
);
|
|
|
|
res.json({
|
|
version: binaryInfo.version,
|
|
architecture: binaryInfo.architecture,
|
|
size: binaryInfo.size,
|
|
hash: binaryInfo.hash,
|
|
downloadUrl: `/api/v1/agent/download/${binaryInfo.version}/${binaryInfo.architecture}`,
|
|
});
|
|
} catch (error) {
|
|
console.error("❌ Failed to get latest binary info:", error.message);
|
|
res.status(500).json({ error: "Failed to get latest binary information" });
|
|
}
|
|
});
|
|
|
|
// Push update notification to specific agent
|
|
router.post(
|
|
"/notify-update/:apiId",
|
|
authenticateToken,
|
|
requirePermission("admin"),
|
|
async (_req, res) => {
|
|
try {
|
|
const { apiId } = req.params;
|
|
const { version, force = false } = req.body;
|
|
|
|
const versionInfo = await agentVersionService.getVersionInfo();
|
|
const targetVersion = version || versionInfo.latestVersion;
|
|
|
|
if (!targetVersion) {
|
|
return res
|
|
.status(400)
|
|
.json({ error: "No version specified or available" });
|
|
}
|
|
|
|
// Import WebSocket service
|
|
const { pushUpdateNotification } = require("../services/agentWs");
|
|
|
|
// Push update notification via WebSocket
|
|
pushUpdateNotification(apiId, {
|
|
version: targetVersion,
|
|
force,
|
|
downloadUrl: `/api/v1/agent/latest/${req.body.architecture || "linux-amd64"}`,
|
|
message: `Update available: ${targetVersion}`,
|
|
});
|
|
|
|
res.json({
|
|
success: true,
|
|
message: `Update notification sent to agent ${apiId}`,
|
|
version: targetVersion,
|
|
});
|
|
} catch (error) {
|
|
console.error("❌ Failed to notify agent update:", error.message);
|
|
res.status(500).json({ error: "Failed to notify agent update" });
|
|
}
|
|
},
|
|
);
|
|
|
|
// Push update notification to all agents
|
|
router.post(
|
|
"/notify-update-all",
|
|
authenticateToken,
|
|
requirePermission("admin"),
|
|
async (_req, res) => {
|
|
try {
|
|
const { version, force = false } = req.body;
|
|
|
|
const versionInfo = await agentVersionService.getVersionInfo();
|
|
const targetVersion = version || versionInfo.latestVersion;
|
|
|
|
if (!targetVersion) {
|
|
return res
|
|
.status(400)
|
|
.json({ error: "No version specified or available" });
|
|
}
|
|
|
|
// Import WebSocket service
|
|
const { pushUpdateNotificationToAll } = require("../services/agentWs");
|
|
|
|
// Push update notification to all connected agents
|
|
const result = await pushUpdateNotificationToAll({
|
|
version: targetVersion,
|
|
force,
|
|
message: `Update available: ${targetVersion}`,
|
|
});
|
|
|
|
res.json({
|
|
success: true,
|
|
message: `Update notification sent to ${result.notifiedCount} agents`,
|
|
version: targetVersion,
|
|
notifiedCount: result.notifiedCount,
|
|
failedCount: result.failedCount,
|
|
});
|
|
} catch (error) {
|
|
console.error("❌ Failed to notify all agents update:", error.message);
|
|
res.status(500).json({ error: "Failed to notify all agents update" });
|
|
}
|
|
},
|
|
);
|
|
|
|
// Check if specific agent needs update and push notification
|
|
router.post(
|
|
"/check-update/:apiId",
|
|
authenticateToken,
|
|
requirePermission("can_manage_settings"),
|
|
async (_req, res) => {
|
|
try {
|
|
const { apiId } = req.params;
|
|
const { version, force = false } = req.body;
|
|
|
|
if (!version) {
|
|
return res.status(400).json({
|
|
success: false,
|
|
error: "Agent version is required",
|
|
});
|
|
}
|
|
|
|
console.log(
|
|
`🔍 Checking update for agent ${apiId} (version: ${version})`,
|
|
);
|
|
const result = await agentVersionService.checkAndPushAgentUpdate(
|
|
apiId,
|
|
version,
|
|
force,
|
|
);
|
|
console.log(
|
|
"📊 Agent update check result:",
|
|
JSON.stringify(result, null, 2),
|
|
);
|
|
|
|
res.json({
|
|
success: true,
|
|
...result,
|
|
});
|
|
} catch (error) {
|
|
console.error("❌ Failed to check agent update:", error.message);
|
|
res.status(500).json({
|
|
success: false,
|
|
error: "Failed to check agent update",
|
|
details: error.message,
|
|
});
|
|
}
|
|
},
|
|
);
|
|
|
|
// Push updates to all connected agents
|
|
router.post(
|
|
"/push-updates-all",
|
|
authenticateToken,
|
|
requirePermission("can_manage_settings"),
|
|
async (_req, res) => {
|
|
try {
|
|
const { force = false } = req.body;
|
|
|
|
console.log(`🔄 Pushing updates to all agents (force: ${force})`);
|
|
const result = await agentVersionService.checkAndPushUpdatesToAll(force);
|
|
console.log("📊 Bulk update result:", JSON.stringify(result, null, 2));
|
|
|
|
res.json(result);
|
|
} catch (error) {
|
|
console.error("❌ Failed to push updates to all agents:", error.message);
|
|
res.status(500).json({
|
|
success: false,
|
|
error: "Failed to push updates to all agents",
|
|
details: error.message,
|
|
});
|
|
}
|
|
},
|
|
);
|
|
|
|
// Agent reports its version (for automatic update checking)
|
|
router.post("/report-version", authenticateToken, async (req, res) => {
|
|
try {
|
|
const { apiId, version } = req.body;
|
|
|
|
if (!apiId || !version) {
|
|
return res.status(400).json({
|
|
success: false,
|
|
error: "API ID and version are required",
|
|
});
|
|
}
|
|
|
|
console.log(`📊 Agent ${apiId} reported version: ${version}`);
|
|
|
|
// Check if agent needs update and push notification if needed
|
|
const updateResult = await agentVersionService.checkAndPushAgentUpdate(
|
|
apiId,
|
|
version,
|
|
);
|
|
|
|
res.json({
|
|
success: true,
|
|
message: "Version reported successfully",
|
|
updateCheck: updateResult,
|
|
});
|
|
} catch (error) {
|
|
console.error("❌ Failed to process agent version report:", error.message);
|
|
res.status(500).json({
|
|
success: false,
|
|
error: "Failed to process version report",
|
|
details: error.message,
|
|
});
|
|
}
|
|
});
|
|
|
|
module.exports = router;
|