mirror of
https://github.com/9technologygroup/patchmon.net.git
synced 2025-11-20 06:28:29 +00:00
Compare commits
5 Commits
v1.3.5
...
release/1-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6f7dbdb04b | ||
|
|
aaed443081 | ||
|
|
5b96b67db9 | ||
|
|
b55ee6a6e0 | ||
|
|
189de7a593 |
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -197,6 +197,40 @@ while IFS= read -r line; do
|
|||||||
continue
|
continue
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Check if agent is already installed and working BEFORE enrollment
|
||||||
|
info " Checking if agent is already configured..."
|
||||||
|
config_check=$(timeout 10 pct exec "$vmid" -- bash -c "
|
||||||
|
if [[ -f /etc/patchmon/config.yml ]] && [[ -f /etc/patchmon/credentials.yml ]]; then
|
||||||
|
if [[ -f /usr/local/bin/patchmon-agent ]]; then
|
||||||
|
# Try to ping using existing configuration
|
||||||
|
if /usr/local/bin/patchmon-agent ping >/dev/null 2>&1; then
|
||||||
|
echo 'ping_success'
|
||||||
|
else
|
||||||
|
echo 'ping_failed'
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo 'binary_missing'
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo 'not_configured'
|
||||||
|
fi
|
||||||
|
" 2>/dev/null </dev/null || echo "error")
|
||||||
|
|
||||||
|
if [[ "$config_check" == "ping_success" ]]; then
|
||||||
|
info " ✓ Host already enrolled and agent ping successful - skipping enrollment"
|
||||||
|
((skipped_count++)) || true
|
||||||
|
echo ""
|
||||||
|
continue
|
||||||
|
elif [[ "$config_check" == "ping_failed" ]]; then
|
||||||
|
warn " ⚠ Agent configuration exists but ping failed - will re-enroll and reinstall"
|
||||||
|
elif [[ "$config_check" == "binary_missing" ]]; then
|
||||||
|
warn " ⚠ Config exists but agent binary missing - will re-enroll and reinstall"
|
||||||
|
elif [[ "$config_check" == "not_configured" ]]; then
|
||||||
|
info " ℹ Agent not yet configured - proceeding with enrollment"
|
||||||
|
else
|
||||||
|
warn " ⚠ Could not check agent status - proceeding with enrollment"
|
||||||
|
fi
|
||||||
|
|
||||||
# Call PatchMon auto-enrollment API
|
# Call PatchMon auto-enrollment API
|
||||||
info " Enrolling $friendly_name in PatchMon..."
|
info " Enrolling $friendly_name in PatchMon..."
|
||||||
|
|
||||||
@@ -230,40 +264,6 @@ while IFS= read -r line; do
|
|||||||
|
|
||||||
info " ✓ Host enrolled successfully: $api_id"
|
info " ✓ Host enrolled successfully: $api_id"
|
||||||
|
|
||||||
# Check if agent is already installed and working
|
|
||||||
info " Checking if agent is already configured..."
|
|
||||||
config_check=$(timeout 10 pct exec "$vmid" -- bash -c "
|
|
||||||
if [[ -f /etc/patchmon/config.yml ]] && [[ -f /etc/patchmon/credentials.yml ]]; then
|
|
||||||
if [[ -f /usr/local/bin/patchmon-agent ]]; then
|
|
||||||
# Try to ping using existing configuration
|
|
||||||
if /usr/local/bin/patchmon-agent ping >/dev/null 2>&1; then
|
|
||||||
echo 'ping_success'
|
|
||||||
else
|
|
||||||
echo 'ping_failed'
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
echo 'binary_missing'
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
echo 'not_configured'
|
|
||||||
fi
|
|
||||||
" 2>/dev/null </dev/null || echo "error")
|
|
||||||
|
|
||||||
if [[ "$config_check" == "ping_success" ]]; then
|
|
||||||
info " ✓ Host already enrolled and agent ping successful - skipping"
|
|
||||||
((skipped_count++)) || true
|
|
||||||
echo ""
|
|
||||||
continue
|
|
||||||
elif [[ "$config_check" == "ping_failed" ]]; then
|
|
||||||
warn " ⚠ Agent configuration exists but ping failed - will reinstall"
|
|
||||||
elif [[ "$config_check" == "binary_missing" ]]; then
|
|
||||||
warn " ⚠ Config exists but agent binary missing - will reinstall"
|
|
||||||
elif [[ "$config_check" == "not_configured" ]]; then
|
|
||||||
info " ℹ Agent not yet configured - proceeding with installation"
|
|
||||||
else
|
|
||||||
warn " ⚠ Could not check agent status - proceeding with installation"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Ensure curl is installed in the container
|
# Ensure curl is installed in the container
|
||||||
info " Checking for curl in container..."
|
info " Checking for curl in container..."
|
||||||
curl_check=$(timeout 10 pct exec "$vmid" -- bash -c "command -v curl >/dev/null 2>&1 && echo 'installed' || echo 'missing'" 2>/dev/null </dev/null || echo "error")
|
curl_check=$(timeout 10 pct exec "$vmid" -- bash -c "command -v curl >/dev/null 2>&1 && echo 'installed' || echo 'missing'" 2>/dev/null </dev/null || echo "error")
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "patchmon-backend",
|
"name": "patchmon-backend",
|
||||||
"version": "1.3.5",
|
"version": "1.3.6",
|
||||||
"description": "Backend API for Linux Patch Monitoring System",
|
"description": "Backend API for Linux Patch Monitoring System",
|
||||||
"license": "AGPL-3.0",
|
"license": "AGPL-3.0",
|
||||||
"main": "src/server.js",
|
"main": "src/server.js",
|
||||||
|
|||||||
@@ -103,6 +103,7 @@ model hosts {
|
|||||||
gateway_ip String?
|
gateway_ip String?
|
||||||
hostname String?
|
hostname String?
|
||||||
kernel_version String?
|
kernel_version String?
|
||||||
|
installed_kernel_version String?
|
||||||
load_average Json?
|
load_average Json?
|
||||||
network_interfaces Json?
|
network_interfaces Json?
|
||||||
ram_installed Int?
|
ram_installed Int?
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ const {
|
|||||||
} = require("../middleware/permissions");
|
} = require("../middleware/permissions");
|
||||||
const { queueManager, QUEUE_NAMES } = require("../services/automation");
|
const { queueManager, QUEUE_NAMES } = require("../services/automation");
|
||||||
const { pushIntegrationToggle, isConnected } = require("../services/agentWs");
|
const { pushIntegrationToggle, isConnected } = require("../services/agentWs");
|
||||||
const agentVersionService = require("../services/agentVersionService");
|
|
||||||
const { compareVersions } = require("../services/automation/shared/utils");
|
const { compareVersions } = require("../services/automation/shared/utils");
|
||||||
|
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
@@ -507,6 +506,10 @@ router.post(
|
|||||||
.optional()
|
.optional()
|
||||||
.isString()
|
.isString()
|
||||||
.withMessage("Kernel version must be a string"),
|
.withMessage("Kernel version must be a string"),
|
||||||
|
body("installedKernelVersion")
|
||||||
|
.optional()
|
||||||
|
.isString()
|
||||||
|
.withMessage("Installed kernel version must be a string"),
|
||||||
body("selinuxStatus")
|
body("selinuxStatus")
|
||||||
.optional()
|
.optional()
|
||||||
.isIn(["enabled", "disabled", "permissive"])
|
.isIn(["enabled", "disabled", "permissive"])
|
||||||
@@ -588,6 +591,8 @@ router.post(
|
|||||||
// System Information
|
// System Information
|
||||||
if (req.body.kernelVersion)
|
if (req.body.kernelVersion)
|
||||||
updateData.kernel_version = req.body.kernelVersion;
|
updateData.kernel_version = req.body.kernelVersion;
|
||||||
|
if (req.body.installedKernelVersion)
|
||||||
|
updateData.installed_kernel_version = req.body.installedKernelVersion;
|
||||||
if (req.body.selinuxStatus)
|
if (req.body.selinuxStatus)
|
||||||
updateData.selinux_status = req.body.selinuxStatus;
|
updateData.selinux_status = req.body.selinuxStatus;
|
||||||
if (req.body.systemUptime)
|
if (req.body.systemUptime)
|
||||||
|
|||||||
@@ -6,5 +6,5 @@ VITE_API_URL=http://localhost:3001/api/v1
|
|||||||
|
|
||||||
# Application Metadata
|
# Application Metadata
|
||||||
VITE_APP_NAME=PatchMon
|
VITE_APP_NAME=PatchMon
|
||||||
VITE_APP_VERSION=1.3.5
|
VITE_APP_VERSION=1.3.6
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "patchmon-frontend",
|
"name": "patchmon-frontend",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "1.3.5",
|
"version": "1.3.6",
|
||||||
"license": "AGPL-3.0",
|
"license": "AGPL-3.0",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
@@ -1013,29 +1013,25 @@ const HostDetail = () => {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{(host.kernel_version ||
|
{host.kernel_version && (
|
||||||
host.installed_kernel_version) && (
|
<div>
|
||||||
<div className="flex flex-col gap-2">
|
<p className="text-xs text-secondary-500 dark:text-secondary-300">
|
||||||
{host.kernel_version && (
|
Running Kernel
|
||||||
<div>
|
</p>
|
||||||
<p className="text-xs text-secondary-500 dark:text-secondary-300 mb-1">
|
<p className="font-medium text-secondary-900 dark:text-white font-mono text-sm break-all">
|
||||||
Running Kernel
|
{host.kernel_version}
|
||||||
</p>
|
</p>
|
||||||
<p className="font-medium text-secondary-900 dark:text-white font-mono text-sm break-all">
|
</div>
|
||||||
{host.kernel_version}
|
)}
|
||||||
</p>
|
|
||||||
</div>
|
{host.installed_kernel_version && (
|
||||||
)}
|
<div>
|
||||||
{host.installed_kernel_version && (
|
<p className="text-xs text-secondary-500 dark:text-secondary-300">
|
||||||
<div>
|
Installed Kernel
|
||||||
<p className="text-xs text-secondary-500 dark:text-secondary-300 mb-1">
|
</p>
|
||||||
Installed Kernel
|
<p className="font-medium text-secondary-900 dark:text-white font-mono text-sm break-all">
|
||||||
</p>
|
{host.installed_kernel_version}
|
||||||
<p className="font-medium text-secondary-900 dark:text-white font-mono text-sm break-all">
|
</p>
|
||||||
{host.installed_kernel_version}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|||||||
@@ -248,7 +248,6 @@ const Hosts = () => {
|
|||||||
const showFiltersParam = searchParams.get("showFilters");
|
const showFiltersParam = searchParams.get("showFilters");
|
||||||
const osFilterParam = searchParams.get("osFilter");
|
const osFilterParam = searchParams.get("osFilter");
|
||||||
const groupParam = searchParams.get("group");
|
const groupParam = searchParams.get("group");
|
||||||
const rebootParam = searchParams.get("reboot");
|
|
||||||
|
|
||||||
if (filter === "needsUpdates") {
|
if (filter === "needsUpdates") {
|
||||||
setShowFilters(true);
|
setShowFilters(true);
|
||||||
@@ -650,11 +649,27 @@ const Hosts = () => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleSelectAll = () => {
|
const handleSelectAll = (hostsToSelect) => {
|
||||||
if (selectedHosts.length === hosts.length) {
|
const hostIdsToSelect = hostsToSelect.map((host) => host.id);
|
||||||
setSelectedHosts([]);
|
const allSelected = hostIdsToSelect.every((id) =>
|
||||||
|
selectedHosts.includes(id),
|
||||||
|
);
|
||||||
|
if (allSelected) {
|
||||||
|
// Deselect all hosts in this group
|
||||||
|
setSelectedHosts((prev) =>
|
||||||
|
prev.filter((id) => !hostIdsToSelect.includes(id)),
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
setSelectedHosts(hosts.map((host) => host.id));
|
// Select all hosts in this group (merge with existing selections)
|
||||||
|
setSelectedHosts((prev) => {
|
||||||
|
const newSelection = [...prev];
|
||||||
|
hostIdsToSelect.forEach((id) => {
|
||||||
|
if (!newSelection.includes(id)) {
|
||||||
|
newSelection.push(id);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return newSelection;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1219,7 +1234,7 @@ const Hosts = () => {
|
|||||||
navigate("/hosts", { replace: true });
|
navigate("/hosts", { replace: true });
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleUpToDateClick = () => {
|
const _handleUpToDateClick = () => {
|
||||||
// Filter to show only up-to-date hosts
|
// Filter to show only up-to-date hosts
|
||||||
setStatusFilter("active");
|
setStatusFilter("active");
|
||||||
setShowFilters(true);
|
setShowFilters(true);
|
||||||
@@ -1656,11 +1671,14 @@ const Hosts = () => {
|
|||||||
{column.id === "select" ? (
|
{column.id === "select" ? (
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={handleSelectAll}
|
onClick={() =>
|
||||||
|
handleSelectAll(groupHosts)
|
||||||
|
}
|
||||||
className="flex items-center gap-2 hover:text-secondary-700"
|
className="flex items-center gap-2 hover:text-secondary-700"
|
||||||
>
|
>
|
||||||
{selectedHosts.length ===
|
{groupHosts.every((host) =>
|
||||||
groupHosts.length ? (
|
selectedHosts.includes(host.id),
|
||||||
|
) ? (
|
||||||
<CheckSquare className="h-4 w-4" />
|
<CheckSquare className="h-4 w-4" />
|
||||||
) : (
|
) : (
|
||||||
<Square className="h-4 w-4" />
|
<Square className="h-4 w-4" />
|
||||||
|
|||||||
@@ -1586,7 +1586,7 @@ const Integrations = () => {
|
|||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
value={`curl ${curl_flags} "${getEnrollmentUrl()}" | sh`}
|
value={`curl ${curl_flags} "${getEnrollmentUrl()}" | ${selected_script_type === "proxmox-lxc" ? "bash" : "sh"}`}
|
||||||
readOnly
|
readOnly
|
||||||
className="flex-1 px-3 py-2 border border-secondary-300 dark:border-secondary-600 rounded-md bg-secondary-50 dark:bg-secondary-900 text-secondary-900 dark:text-white font-mono text-xs"
|
className="flex-1 px-3 py-2 border border-secondary-300 dark:border-secondary-600 rounded-md bg-secondary-50 dark:bg-secondary-900 text-secondary-900 dark:text-white font-mono text-xs"
|
||||||
/>
|
/>
|
||||||
@@ -1594,7 +1594,7 @@ const Integrations = () => {
|
|||||||
type="button"
|
type="button"
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
copy_to_clipboard(
|
copy_to_clipboard(
|
||||||
`curl ${curl_flags} "${getEnrollmentUrl()}" | sh`,
|
`curl ${curl_flags} "${getEnrollmentUrl()}" | ${selected_script_type === "proxmox-lxc" ? "bash" : "sh"}`,
|
||||||
"enrollment-command",
|
"enrollment-command",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "patchmon",
|
"name": "patchmon",
|
||||||
"version": "1.3.5",
|
"version": "1.3.6",
|
||||||
"description": "Linux Patch Monitoring System",
|
"description": "Linux Patch Monitoring System",
|
||||||
"license": "AGPL-3.0",
|
"license": "AGPL-3.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
|
|||||||
Reference in New Issue
Block a user