mirror of
https://github.com/9technologygroup/patchmon.net.git
synced 2025-11-02 21:13:45 +00:00
Merge pull request #242 from PatchMon/release/1-3-1
Agent update initiation from server
This commit is contained in:
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1430,6 +1430,69 @@ router.patch(
|
||||
},
|
||||
);
|
||||
|
||||
// Force agent update for specific host
|
||||
router.post(
|
||||
"/:hostId/force-agent-update",
|
||||
authenticateToken,
|
||||
requireManageHosts,
|
||||
async (req, res) => {
|
||||
try {
|
||||
const { hostId } = req.params;
|
||||
|
||||
// Get host to verify it exists
|
||||
const host = await prisma.hosts.findUnique({
|
||||
where: { id: hostId },
|
||||
});
|
||||
|
||||
if (!host) {
|
||||
return res.status(404).json({ error: "Host not found" });
|
||||
}
|
||||
|
||||
// Get queue manager
|
||||
const { QUEUE_NAMES } = require("../services/automation");
|
||||
const queueManager = req.app.locals.queueManager;
|
||||
|
||||
if (!queueManager) {
|
||||
return res.status(500).json({
|
||||
error: "Queue manager not available",
|
||||
});
|
||||
}
|
||||
|
||||
// Get the agent-commands queue
|
||||
const queue = queueManager.queues[QUEUE_NAMES.AGENT_COMMANDS];
|
||||
|
||||
// Add job to queue
|
||||
await queue.add(
|
||||
"update_agent",
|
||||
{
|
||||
api_id: host.api_id,
|
||||
type: "update_agent",
|
||||
},
|
||||
{
|
||||
attempts: 3,
|
||||
backoff: {
|
||||
type: "exponential",
|
||||
delay: 2000,
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
message: "Agent update queued successfully",
|
||||
host: {
|
||||
id: host.id,
|
||||
friendlyName: host.friendly_name,
|
||||
apiId: host.api_id,
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("Force agent update error:", error);
|
||||
res.status(500).json({ error: "Failed to force agent update" });
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
// Serve the installation script (requires API authentication)
|
||||
router.get("/install", async (req, res) => {
|
||||
try {
|
||||
|
||||
@@ -176,6 +176,15 @@ function pushSettingsUpdate(apiId, newInterval) {
|
||||
);
|
||||
}
|
||||
|
||||
function pushUpdateAgent(apiId) {
|
||||
const ws = apiIdToSocket.get(apiId);
|
||||
safeSend(ws, JSON.stringify({ type: "update_agent" }));
|
||||
}
|
||||
|
||||
function getConnectionByApiId(apiId) {
|
||||
return apiIdToSocket.get(apiId);
|
||||
}
|
||||
|
||||
function pushUpdateNotification(apiId, updateInfo) {
|
||||
const ws = apiIdToSocket.get(apiId);
|
||||
if (ws && ws.readyState === WebSocket.OPEN) {
|
||||
@@ -330,10 +339,12 @@ module.exports = {
|
||||
broadcastSettingsUpdate,
|
||||
pushReportNow,
|
||||
pushSettingsUpdate,
|
||||
pushUpdateAgent,
|
||||
pushUpdateNotification,
|
||||
pushUpdateNotificationToAll,
|
||||
// Expose read-only view of connected agents
|
||||
getConnectedApiIds: () => Array.from(apiIdToSocket.keys()),
|
||||
getConnectionByApiId,
|
||||
isConnected: (apiId) => {
|
||||
const ws = apiIdToSocket.get(apiId);
|
||||
return !!ws && ws.readyState === WebSocket.OPEN;
|
||||
|
||||
@@ -190,6 +190,19 @@ class QueueManager {
|
||||
// For settings update, we need additional data
|
||||
const { update_interval } = job.data;
|
||||
agentWs.pushSettingsUpdate(api_id, update_interval);
|
||||
} else if (type === "update_agent") {
|
||||
// Force agent to update by sending WebSocket command
|
||||
const ws = agentWs.getConnectionByApiId(api_id);
|
||||
if (ws && ws.readyState === 1) {
|
||||
// WebSocket.OPEN
|
||||
agentWs.pushUpdateAgent(api_id);
|
||||
console.log(`✅ Update command sent to agent ${api_id}`);
|
||||
} else {
|
||||
console.error(`❌ Agent ${api_id} is not connected`);
|
||||
throw new Error(
|
||||
`Agent ${api_id} is not connected. Cannot send update command.`,
|
||||
);
|
||||
}
|
||||
} else {
|
||||
console.error(`Unknown agent command type: ${type}`);
|
||||
}
|
||||
|
||||
@@ -187,6 +187,16 @@ const HostDetail = () => {
|
||||
},
|
||||
});
|
||||
|
||||
// Force agent update mutation
|
||||
const forceAgentUpdateMutation = useMutation({
|
||||
mutationFn: () =>
|
||||
adminHostsAPI.forceAgentUpdate(hostId).then((res) => res.data),
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries(["host", hostId]);
|
||||
queryClient.invalidateQueries(["hosts"]);
|
||||
},
|
||||
});
|
||||
|
||||
const updateFriendlyNameMutation = useMutation({
|
||||
mutationFn: (friendlyName) =>
|
||||
adminHostsAPI
|
||||
@@ -703,6 +713,29 @@ const HostDetail = () => {
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<p className="text-xs text-secondary-500 dark:text-secondary-300 mb-1.5">
|
||||
Force Update
|
||||
</p>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => forceAgentUpdateMutation.mutate()}
|
||||
disabled={forceAgentUpdateMutation.isPending}
|
||||
className="flex items-center gap-1.5 px-3 py-1.5 text-xs font-medium text-primary-600 dark:text-primary-400 bg-primary-50 dark:bg-primary-900/20 border border-primary-200 dark:border-primary-800 rounded-md hover:bg-primary-100 dark:hover:bg-primary-900/40 transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
|
||||
>
|
||||
<RefreshCw
|
||||
className={`h-3 w-3 ${
|
||||
forceAgentUpdateMutation.isPending
|
||||
? "animate-spin"
|
||||
: ""
|
||||
}`}
|
||||
/>
|
||||
{forceAgentUpdateMutation.isPending
|
||||
? "Updating..."
|
||||
: "Update Now"}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -95,6 +95,7 @@ export const adminHostsAPI = {
|
||||
api.put("/hosts/bulk/groups", { hostIds, groupIds }),
|
||||
toggleAutoUpdate: (hostId, autoUpdate) =>
|
||||
api.patch(`/hosts/${hostId}/auto-update`, { auto_update: autoUpdate }),
|
||||
forceAgentUpdate: (hostId) => api.post(`/hosts/${hostId}/force-agent-update`),
|
||||
updateFriendlyName: (hostId, friendlyName) =>
|
||||
api.patch(`/hosts/${hostId}/friendly-name`, {
|
||||
friendly_name: friendlyName,
|
||||
|
||||
54
setup.sh
54
setup.sh
@@ -1797,7 +1797,12 @@ create_agent_version() {
|
||||
cp "$APP_DIR/agents/patchmon-agent.sh" "$APP_DIR/backend/"
|
||||
|
||||
print_status "Agent version management removed - using file-based approach"
|
||||
# Ensure we close the conditional and the function properly
|
||||
fi
|
||||
|
||||
# Make agent binaries executable
|
||||
if [ -d "$APP_DIR/agents" ]; then
|
||||
chmod +x "$APP_DIR/agents/patchmon-agent-linux-"* 2>/dev/null || true
|
||||
print_status "Agent binaries made executable"
|
||||
fi
|
||||
|
||||
return 0
|
||||
@@ -2703,6 +2708,13 @@ update_env_file() {
|
||||
: ${TFA_MAX_REMEMBER_SESSIONS:=5}
|
||||
: ${TFA_SUSPICIOUS_ACTIVITY_THRESHOLD:=3}
|
||||
|
||||
# Prisma Connection Pool
|
||||
: ${DB_CONNECTION_LIMIT:=30}
|
||||
: ${DB_POOL_TIMEOUT:=20}
|
||||
: ${DB_CONNECT_TIMEOUT:=10}
|
||||
: ${DB_IDLE_TIMEOUT:=300}
|
||||
: ${DB_MAX_LIFETIME:=1800}
|
||||
|
||||
# Track which variables were added
|
||||
local added_vars=()
|
||||
|
||||
@@ -2764,6 +2776,21 @@ update_env_file() {
|
||||
if ! grep -q "^TFA_SUSPICIOUS_ACTIVITY_THRESHOLD=" "$env_file"; then
|
||||
added_vars+=("TFA_SUSPICIOUS_ACTIVITY_THRESHOLD")
|
||||
fi
|
||||
if ! grep -q "^DB_CONNECTION_LIMIT=" "$env_file"; then
|
||||
added_vars+=("DB_CONNECTION_LIMIT")
|
||||
fi
|
||||
if ! grep -q "^DB_POOL_TIMEOUT=" "$env_file"; then
|
||||
added_vars+=("DB_POOL_TIMEOUT")
|
||||
fi
|
||||
if ! grep -q "^DB_CONNECT_TIMEOUT=" "$env_file"; then
|
||||
added_vars+=("DB_CONNECT_TIMEOUT")
|
||||
fi
|
||||
if ! grep -q "^DB_IDLE_TIMEOUT=" "$env_file"; then
|
||||
added_vars+=("DB_IDLE_TIMEOUT")
|
||||
fi
|
||||
if ! grep -q "^DB_MAX_LIFETIME=" "$env_file"; then
|
||||
added_vars+=("DB_MAX_LIFETIME")
|
||||
fi
|
||||
|
||||
# If there are missing variables, add them
|
||||
if [ ${#added_vars[@]} -gt 0 ]; then
|
||||
@@ -2849,6 +2876,25 @@ EOF
|
||||
echo "TFA_SUSPICIOUS_ACTIVITY_THRESHOLD=$TFA_SUSPICIOUS_ACTIVITY_THRESHOLD" >> "$env_file"
|
||||
fi
|
||||
|
||||
# Add Prisma connection pool config if missing
|
||||
if printf '%s\n' "${added_vars[@]}" | grep -q "DB_CONNECTION_LIMIT"; then
|
||||
echo "" >> "$env_file"
|
||||
echo "# Database Connection Pool Configuration (Prisma)" >> "$env_file"
|
||||
echo "DB_CONNECTION_LIMIT=$DB_CONNECTION_LIMIT" >> "$env_file"
|
||||
fi
|
||||
if printf '%s\n' "${added_vars[@]}" | grep -q "DB_POOL_TIMEOUT"; then
|
||||
echo "DB_POOL_TIMEOUT=$DB_POOL_TIMEOUT" >> "$env_file"
|
||||
fi
|
||||
if printf '%s\n' "${added_vars[@]}" | grep -q "DB_CONNECT_TIMEOUT"; then
|
||||
echo "DB_CONNECT_TIMEOUT=$DB_CONNECT_TIMEOUT" >> "$env_file"
|
||||
fi
|
||||
if printf '%s\n' "${added_vars[@]}" | grep -q "DB_IDLE_TIMEOUT"; then
|
||||
echo "DB_IDLE_TIMEOUT=$DB_IDLE_TIMEOUT" >> "$env_file"
|
||||
fi
|
||||
if printf '%s\n' "${added_vars[@]}" | grep -q "DB_MAX_LIFETIME"; then
|
||||
echo "DB_MAX_LIFETIME=$DB_MAX_LIFETIME" >> "$env_file"
|
||||
fi
|
||||
|
||||
print_status ".env file updated with ${#added_vars[@]} new variable(s)"
|
||||
print_info "Added variables: ${added_vars[*]}"
|
||||
else
|
||||
@@ -3054,6 +3100,12 @@ update_installation() {
|
||||
print_info "Building frontend..."
|
||||
npm run build
|
||||
|
||||
# Make agent binaries executable
|
||||
if [ -d "$instance_dir/agents" ]; then
|
||||
chmod +x "$instance_dir/agents/patchmon-agent-linux-"* 2>/dev/null || true
|
||||
print_status "Agent binaries made executable"
|
||||
fi
|
||||
|
||||
# Run database migrations with self-healing
|
||||
print_info "Running database migrations..."
|
||||
cd "$instance_dir/backend"
|
||||
|
||||
Reference in New Issue
Block a user