Compare commits

...

5 Commits

Author SHA1 Message Date
Muhammad Ibrahim
6f7dbdb04b put bash in for proxmox 2025-11-18 21:46:37 +00:00
Muhammad Ibrahim
aaed443081 new binary 2025-11-18 20:13:12 +00:00
Muhammad Ibrahim
5b96b67db9 Fixed some prisma issue on kernel displaying
Fixed multi host selection button to not select all
updating to 1.3.6
2025-11-18 20:08:34 +00:00
Muhammad Ibrahim
b55ee6a6e0 Fixing proxmox auto-enrolment script 2025-11-18 18:10:38 +00:00
Muhammad Ibrahim
189de7a593 fixed linting 2025-11-17 21:59:19 +00:00
14 changed files with 93 additions and 73 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -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")

View File

@@ -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",

View File

@@ -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?

View File

@@ -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)

View File

@@ -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

View File

@@ -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": {

View File

@@ -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>
)} )}

View File

@@ -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" />

View File

@@ -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",
) )
} }

View File

@@ -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,