From ed0cf79b53a0520c338eab137f31dca20ef67021 Mon Sep 17 00:00:00 2001
From: Muhammad Ibrahim
Date: Tue, 30 Sep 2025 19:48:28 +0100
Subject: [PATCH 01/35] Added settings pages to bring all the settings together
from patchmon options, profile page and server settings.
---
backend/src/routes/hostGroupRoutes.js | 8 +-
frontend/src/App.jsx | 156 ++-
frontend/src/components/Layout.jsx | 236 +++--
frontend/src/components/SettingsLayout.jsx | 248 +++++
.../settings/AgentManagementTab.jsx | 368 +++++++
.../components/settings/AgentUpdatesTab.jsx | 425 +++++++++
.../components/settings/ProtocolUrlTab.jsx | 305 ++++++
frontend/src/components/settings/RolesTab.jsx | 570 +++++++++++
frontend/src/components/settings/UsersTab.jsx | 897 ++++++++++++++++++
.../components/settings/VersionUpdateTab.jsx | 573 +++++++++++
frontend/src/pages/Hosts.jsx | 7 +
frontend/src/pages/Profile.jsx | 103 +-
.../pages/settings/SettingsAgentConfig.jsx | 89 ++
.../src/pages/settings/SettingsHostGroups.jsx | 599 ++++++++++++
.../pages/settings/SettingsServerConfig.jsx | 96 ++
frontend/src/pages/settings/SettingsUsers.jsx | 107 +++
16 files changed, 4638 insertions(+), 149 deletions(-)
create mode 100644 frontend/src/components/SettingsLayout.jsx
create mode 100644 frontend/src/components/settings/AgentManagementTab.jsx
create mode 100644 frontend/src/components/settings/AgentUpdatesTab.jsx
create mode 100644 frontend/src/components/settings/ProtocolUrlTab.jsx
create mode 100644 frontend/src/components/settings/RolesTab.jsx
create mode 100644 frontend/src/components/settings/UsersTab.jsx
create mode 100644 frontend/src/components/settings/VersionUpdateTab.jsx
create mode 100644 frontend/src/pages/settings/SettingsAgentConfig.jsx
create mode 100644 frontend/src/pages/settings/SettingsHostGroups.jsx
create mode 100644 frontend/src/pages/settings/SettingsServerConfig.jsx
create mode 100644 frontend/src/pages/settings/SettingsUsers.jsx
diff --git a/backend/src/routes/hostGroupRoutes.js b/backend/src/routes/hostGroupRoutes.js
index cc9b491..42300be 100644
--- a/backend/src/routes/hostGroupRoutes.js
+++ b/backend/src/routes/hostGroupRoutes.js
@@ -205,11 +205,11 @@ router.delete(
return res.status(404).json({ error: "Host group not found" });
}
- // Check if host group has hosts
+ // If host group has hosts, ungroup them first
if (existingGroup._count.hosts > 0) {
- return res.status(400).json({
- error:
- "Cannot delete host group that contains hosts. Please move or remove hosts first.",
+ await prisma.hosts.updateMany({
+ where: { host_group_id: id },
+ data: { host_group_id: null },
});
}
diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx
index e6a3efd..3ef31c7 100644
--- a/frontend/src/App.jsx
+++ b/frontend/src/App.jsx
@@ -2,6 +2,7 @@ import { Route, Routes } from "react-router-dom";
import FirstTimeAdminSetup from "./components/FirstTimeAdminSetup";
import Layout from "./components/Layout";
import ProtectedRoute from "./components/ProtectedRoute";
+import SettingsLayout from "./components/SettingsLayout";
import { isAuthPhase } from "./constants/authPhases";
import { AuthProvider, useAuth } from "./contexts/AuthContext";
import { ThemeProvider } from "./contexts/ThemeContext";
@@ -10,15 +11,16 @@ import Dashboard from "./pages/Dashboard";
import HostDetail from "./pages/HostDetail";
import Hosts from "./pages/Hosts";
import Login from "./pages/Login";
-import Options from "./pages/Options";
import PackageDetail from "./pages/PackageDetail";
import Packages from "./pages/Packages";
-import Permissions from "./pages/Permissions";
import Profile from "./pages/Profile";
import Repositories from "./pages/Repositories";
import RepositoryDetail from "./pages/RepositoryDetail";
import Settings from "./pages/Settings";
-import Users from "./pages/Users";
+import SettingsAgentConfig from "./pages/settings/SettingsAgentConfig";
+import SettingsHostGroups from "./pages/settings/SettingsHostGroups";
+import SettingsServerConfig from "./pages/settings/SettingsServerConfig";
+import SettingsUsers from "./pages/settings/SettingsUsers";
function AppRoutes() {
const { needsFirstTimeSetup, authPhase, isAuthenticated } = useAuth();
@@ -114,7 +116,7 @@ function AppRoutes() {
element={
-
+
}
@@ -124,13 +126,115 @@ function AppRoutes() {
element={
-
+
}
/>
+
+
+
+
+ }
+ />
+
+
+
+
+
+ }
+ />
+
+
+
+
+
+ }
+ />
+
+
+
+
+
+
+
+ }
+ />
+
+
+
+
+
+ }
+ />
+
+
+
+
+
+ }
+ />
+
+
+
+
+
+ }
+ />
+
+
+
+
+
+ }
+ />
+
+
+
+
+
+ }
+ />
+
+
+
+
+
+ }
+ />
+
@@ -139,22 +243,42 @@ function AppRoutes() {
}
/>
+
+
+
+
+
+ }
+ />
+
+
+
+
+
+ }
+ />
+
+
+
+
+
+ }
+ />
-
-
-
- }
- />
-
-
-
+
}
diff --git a/frontend/src/components/Layout.jsx b/frontend/src/components/Layout.jsx
index d3c735d..3c0de48 100644
--- a/frontend/src/components/Layout.jsx
+++ b/frontend/src/components/Layout.jsx
@@ -141,69 +141,41 @@ const Layout = ({ children }) => {
}
}
- // PatchMon Users section - only show if user can view/manage users
- if (canViewUsers() || canManageUsers()) {
- const userItems = [];
-
- if (canViewUsers()) {
- userItems.push({ name: "Users", href: "/users", icon: Users });
- }
-
- if (canManageSettings()) {
- userItems.push({
- name: "Permissions",
- href: "/permissions",
- icon: Shield,
- });
- }
-
- if (userItems.length > 0) {
- nav.push({
- section: "PatchMon Users",
- items: userItems,
- });
- }
- }
-
- // Settings section - only show if user has any settings permissions
- if (canManageSettings() || canViewReports() || canExportData()) {
- const settingsItems = [];
-
- if (canManageSettings()) {
- settingsItems.push({
- name: "PatchMon Options",
- href: "/options",
- icon: Settings,
- });
- settingsItems.push({
- name: "Server Config",
- href: "/settings",
- icon: Wrench,
- showUpgradeIcon: updateAvailable,
- });
- }
-
- if (canViewReports() || canExportData()) {
- settingsItems.push({
- name: "Audit Log",
- href: "/audit-log",
- icon: FileText,
- comingSoon: true,
- });
- }
-
- if (settingsItems.length > 0) {
- nav.push({
- section: "Settings",
- items: settingsItems,
- });
- }
- }
-
return nav;
};
+ // Build settings navigation separately (for bottom placement)
+ const buildSettingsNavigation = () => {
+ const settingsNav = [];
+
+ // Settings section - consolidated all settings into one page
+ if (
+ canManageSettings() ||
+ canViewUsers() ||
+ canManageUsers() ||
+ canViewReports() ||
+ canExportData()
+ ) {
+ const settingsItems = [];
+
+ settingsItems.push({
+ name: "Settings",
+ href: "/settings/users",
+ icon: Settings,
+ showUpgradeIcon: updateAvailable,
+ });
+
+ settingsNav.push({
+ section: "Settings",
+ items: settingsItems,
+ });
+ }
+
+ return settingsNav;
+ };
+
const navigation = buildNavigation();
+ const settingsNavigation = buildSettingsNavigation();
const isActive = (path) => location.pathname === path;
@@ -223,9 +195,10 @@ const Layout = ({ children }) => {
if (path === "/settings") return "Settings";
if (path === "/options") return "PatchMon Options";
if (path === "/audit-log") return "Audit Log";
- if (path === "/profile") return "My Profile";
+ if (path === "/settings/profile") return "My Profile";
if (path.startsWith("/hosts/")) return "Host Details";
if (path.startsWith("/packages/")) return "Package Details";
+ if (path.startsWith("/settings/")) return "Settings";
return "PatchMon";
};
@@ -342,7 +315,7 @@ const Layout = ({ children }) => {
-
-
- Add Rule
-
{/* Coming Soon Card */}
@@ -46,7 +42,7 @@ const Notifications = () => {
{/* Notification Settings Preview */}
-
+
{/* Package Updates */}
@@ -87,47 +83,6 @@ const Notifications = () => {
-
- {/* System Events */}
-
-
-
-
-
-
- System Events
-
-
-
- Monitor system health and performance events
-
-
-
-
- Host Offline
-
-
- Coming Soon
-
-
-
-
- High CPU Usage
-
-
- Coming Soon
-
-
-
-
- Disk Space Low
-
-
- Coming Soon
-
-
-
-
{/* Empty State */}
@@ -148,12 +103,6 @@ const Notifications = () => {
Get started by creating your first notification rule.
-
diff --git a/frontend/src/pages/settings/PatchManagement.jsx b/frontend/src/pages/settings/PatchManagement.jsx
new file mode 100644
index 0000000..582a23a
--- /dev/null
+++ b/frontend/src/pages/settings/PatchManagement.jsx
@@ -0,0 +1,98 @@
+import { Settings } from "lucide-react";
+import SettingsLayout from "../../components/SettingsLayout";
+
+const PatchManagement = () => {
+ return (
+
+
+ {/* Header */}
+
+
+
+ Patch Management
+
+
+ Define and enforce policies for applying and monitoring patches
+
+
+
+
+ {/* Coming Soon Card */}
+
+
+
+
+
+ Patch Management Coming Soon
+
+
+ We are designing rule sets, approval workflows, and automated
+ patch policies to give you granular control over updates.
+
+
+
+ In Development
+
+
+
+
+
+
+ {/* Patch Policy */}
+
+
+
+
+
+
+ Patch Policy
+
+
+
+ Configure rule sets for patch management and monitoring
+
+
+
+
+ Rule Sets
+
+
+ Coming Soon
+
+
+
+
+ Patch Approval Workflows
+
+
+ Coming Soon
+
+
+
+
+ Security Patch Priority
+
+
+ Coming Soon
+
+
+
+
+ Auto-Update Policies
+
+
+ Coming Soon
+
+
+
+
+
+
+ );
+};
+
+export default PatchManagement;
From 42f882c1c67745705fa5af271ab9e9909d23c33b Mon Sep 17 00:00:00 2001
From: Muhammad Ibrahim
Date: Wed, 1 Oct 2025 10:52:08 +0100
Subject: [PATCH 14/35] Made installation script output the install for jq and
curl if theres any issues with repos rathet than just exiting
---
agents/patchmon_install.sh | 24 ++++++++++++------------
1 file changed, 12 insertions(+), 12 deletions(-)
diff --git a/agents/patchmon_install.sh b/agents/patchmon_install.sh
index 6767c79..1d63ca4 100644
--- a/agents/patchmon_install.sh
+++ b/agents/patchmon_install.sh
@@ -133,23 +133,23 @@ info "📦 Installing required dependencies..."
# Detect package manager and install jq and curl
if command -v apt-get >/dev/null 2>&1; then
# Debian/Ubuntu
- apt-get update >/dev/null 2>&1
- apt-get install -y jq curl >/dev/null 2>&1
-elif command -v yum >/dev/null 2>&1; then
+ apt-get update
+ apt-get install jq curl -y
+elif command -v yum
# CentOS/RHEL 7
- yum install -y jq curl >/dev/null 2>&1
-elif command -v dnf >/dev/null 2>&1; then
+ yum install -y jq curl
+elif command -v dnf
# CentOS/RHEL 8+/Fedora
- dnf install -y jq curl >/dev/null 2>&1
-elif command -v zypper >/dev/null 2>&1; then
+ dnf install -y jq curl
+elif command -v zypper
# openSUSE
- zypper install -y jq curl >/dev/null 2>&1
-elif command -v pacman >/dev/null 2>&1; then
+ zypper install -y jq curl
+elif command -v pacman
# Arch Linux
- pacman -S --noconfirm jq curl >/dev/null 2>&1
-elif command -v apk >/dev/null 2>&1; then
+ pacman -S --noconfirm jq curl
+elif command -v apk
# Alpine Linux
- apk add --no-cache jq curl >/dev/null 2>&1
+ apk add --no-cache jq curl
else
warning "Could not detect package manager. Please ensure 'jq' and 'curl' are installed manually."
fi
From d13469ce33e5bbb4742e09a1b3c6add86da3e1b3 Mon Sep 17 00:00:00 2001
From: Muhammad Ibrahim
Date: Wed, 1 Oct 2025 12:50:55 +0100
Subject: [PATCH 15/35] Fixed dnf and issues for almalinux / rhel dervied
systems, added bc as a prerequisite
---
agents/patchmon-agent.sh | 31 +++++++++++++++++++++--
agents/patchmon_install.sh | 51 ++++++++++++++++++++++++++++----------
2 files changed, 67 insertions(+), 15 deletions(-)
diff --git a/agents/patchmon-agent.sh b/agents/patchmon-agent.sh
index 08fce7b..a760a7b 100755
--- a/agents/patchmon-agent.sh
+++ b/agents/patchmon-agent.sh
@@ -644,16 +644,31 @@ get_yum_packages() {
fi
# Get upgradable packages
- local upgradable=$($package_manager check-update 2>/dev/null | grep -v "^$" | grep -v "^Loaded" | grep -v "^Last metadata" | tail -n +2)
+ local upgradable=$($package_manager check-update 2>/dev/null | grep -v "^$" | grep -v "^Loaded" | grep -v "^Last metadata" | grep -v "^Security" | tail -n +2)
while IFS= read -r line; do
+ # Skip empty lines and lines with special characters
+ [[ -z "$line" ]] && continue
+ [[ "$line" =~ ^[[:space:]]*$ ]] && continue
+
if [[ "$line" =~ ^([^[:space:]]+)[[:space:]]+([^[:space:]]+)[[:space:]]+([^[:space:]]+) ]]; then
local package_name="${BASH_REMATCH[1]}"
local available_version="${BASH_REMATCH[2]}"
local repo="${BASH_REMATCH[3]}"
+ # Sanitize package name and versions (remove any control characters)
+ package_name=$(echo "$package_name" | tr -d '[:cntrl:]' | sed 's/[^a-zA-Z0-9._+-]//g')
+ available_version=$(echo "$available_version" | tr -d '[:cntrl:]' | sed 's/[^a-zA-Z0-9._+-]//g')
+ repo=$(echo "$repo" | tr -d '[:cntrl:]')
+
+ # Skip if package name is empty after sanitization
+ [[ -z "$package_name" ]] && continue
+
# Get current version
- local current_version=$($package_manager list installed "$package_name" 2>/dev/null | grep "^$package_name" | awk '{print $2}')
+ local current_version=$($package_manager list installed "$package_name" 2>/dev/null | grep "^$package_name" | awk '{print $2}' | tr -d '[:cntrl:]' | sed 's/[^a-zA-Z0-9._+-]//g')
+
+ # Skip if we couldn't get current version
+ [[ -z "$current_version" ]] && current_version="unknown"
local is_security_update=false
if echo "$repo" | grep -q "security"; then
@@ -674,10 +689,22 @@ get_yum_packages() {
local installed=$($package_manager list installed 2>/dev/null | grep -v "^Loaded" | grep -v "^Installed" | head -100)
while IFS= read -r line; do
+ # Skip empty lines
+ [[ -z "$line" ]] && continue
+ [[ "$line" =~ ^[[:space:]]*$ ]] && continue
+
if [[ "$line" =~ ^([^[:space:]]+)[[:space:]]+([^[:space:]]+) ]]; then
local package_name="${BASH_REMATCH[1]}"
local version="${BASH_REMATCH[2]}"
+ # Sanitize package name and version
+ package_name=$(echo "$package_name" | tr -d '[:cntrl:]' | sed 's/[^a-zA-Z0-9._+-]//g')
+ version=$(echo "$version" | tr -d '[:cntrl:]' | sed 's/[^a-zA-Z0-9._+-]//g')
+
+ # Skip if package name is empty after sanitization
+ [[ -z "$package_name" ]] && continue
+ [[ -z "$version" ]] && version="unknown"
+
# Check if this package is not in the upgrade list
if ! echo "$upgradable" | grep -q "^$package_name "; then
if [[ "$first_ref" == true ]]; then
diff --git a/agents/patchmon_install.sh b/agents/patchmon_install.sh
index 1d63ca4..e84b2bc 100644
--- a/agents/patchmon_install.sh
+++ b/agents/patchmon_install.sh
@@ -129,31 +129,56 @@ echo ""
# Install required dependencies
info "📦 Installing required dependencies..."
+echo ""
# Detect package manager and install jq and curl
if command -v apt-get >/dev/null 2>&1; then
# Debian/Ubuntu
+ info "Detected apt-get (Debian/Ubuntu)"
+ echo ""
+ info "Updating package lists..."
apt-get update
- apt-get install jq curl -y
-elif command -v yum
+ echo ""
+ info "Installing jq, curl, and bc..."
+ apt-get install jq curl bc -y
+elif command -v yum >/dev/null 2>&1; then
# CentOS/RHEL 7
- yum install -y jq curl
-elif command -v dnf
+ info "Detected yum (CentOS/RHEL 7)"
+ echo ""
+ info "Installing jq, curl, and bc..."
+ yum install -y jq curl bc
+elif command -v dnf >/dev/null 2>&1; then
# CentOS/RHEL 8+/Fedora
- dnf install -y jq curl
-elif command -v zypper
+ info "Detected dnf (CentOS/RHEL 8+/Fedora)"
+ echo ""
+ info "Installing jq, curl, and bc..."
+ dnf install -y jq curl bc
+elif command -v zypper >/dev/null 2>&1; then
# openSUSE
- zypper install -y jq curl
-elif command -v pacman
+ info "Detected zypper (openSUSE)"
+ echo ""
+ info "Installing jq, curl, and bc..."
+ zypper install -y jq curl bc
+elif command -v pacman >/dev/null 2>&1; then
# Arch Linux
- pacman -S --noconfirm jq curl
-elif command -v apk
+ info "Detected pacman (Arch Linux)"
+ echo ""
+ info "Installing jq, curl, and bc..."
+ pacman -S --noconfirm jq curl bc
+elif command -v apk >/dev/null 2>&1; then
# Alpine Linux
- apk add --no-cache jq curl
+ info "Detected apk (Alpine Linux)"
+ echo ""
+ info "Installing jq, curl, and bc..."
+ apk add --no-cache jq curl bc
else
- warning "Could not detect package manager. Please ensure 'jq' and 'curl' are installed manually."
+ warning "Could not detect package manager. Please ensure 'jq', 'curl', and 'bc' are installed manually."
fi
+echo ""
+success "Dependencies installation completed"
+echo ""
+
# Step 1: Handle existing configuration directory
info "📁 Setting up configuration directory..."
@@ -322,7 +347,7 @@ echo ""
echo -e "${GREEN}📋 Installation Summary:${NC}"
echo " • Configuration directory: /etc/patchmon"
echo " • Agent installed: /usr/local/bin/patchmon-agent.sh"
-echo " • Dependencies installed: jq, curl"
+echo " • Dependencies installed: jq, curl, bc"
echo " • Crontab configured for automatic updates"
echo " • API credentials configured and tested"
From 28124f5fba43f2c80d4466ab01f3378076045667 Mon Sep 17 00:00:00 2001
From: Muhammad Ibrahim
Date: Wed, 1 Oct 2025 13:13:39 +0100
Subject: [PATCH 16/35] Fixed selinux detection issue
---
agents/patchmon-agent.sh | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/agents/patchmon-agent.sh b/agents/patchmon-agent.sh
index a760a7b..843e21d 100755
--- a/agents/patchmon-agent.sh
+++ b/agents/patchmon-agent.sh
@@ -810,8 +810,16 @@ get_system_info() {
# SELinux Status
if command -v getenforce >/dev/null 2>&1; then
selinux_status=$(getenforce 2>/dev/null | tr '[:upper:]' '[:lower:]')
+ # Map "enforcing" to "enabled" for server validation
+ if [[ "$selinux_status" == "enforcing" ]]; then
+ selinux_status="enabled"
+ fi
elif [[ -f /etc/selinux/config ]]; then
selinux_status=$(grep "^SELINUX=" /etc/selinux/config | cut -d'=' -f2 | tr '[:upper:]' '[:lower:]')
+ # Map "enforcing" to "enabled" for server validation
+ if [[ "$selinux_status" == "enforcing" ]]; then
+ selinux_status="enabled"
+ fi
else
selinux_status="disabled"
fi
From fe448d0111cd0d41a9f42f79bb072806f4b2eede Mon Sep 17 00:00:00 2001
From: Muhammad Ibrahim
Date: Wed, 1 Oct 2025 20:43:37 +0100
Subject: [PATCH 17/35] Removed crontab insertion as the initial update by the
agent configures crontab anyway
---
agents/patchmon_install.sh | 71 +++-----------------------------------
1 file changed, 4 insertions(+), 67 deletions(-)
diff --git a/agents/patchmon_install.sh b/agents/patchmon_install.sh
index e84b2bc..0950e37 100644
--- a/agents/patchmon_install.sh
+++ b/agents/patchmon_install.sh
@@ -268,79 +268,15 @@ else
error "❌ Failed to validate API credentials or reach server"
fi
-# Step 5: Send initial data
+# Step 5: Send initial data and setup automated updates
info "📊 Sending initial package data to server..."
if /usr/local/bin/patchmon-agent.sh update; then
success "✅ UPDATE: Initial package data sent successfully"
+ info "✅ Automated updates configured by agent"
else
warning "⚠️ Failed to send initial data. You can retry later with: /usr/local/bin/patchmon-agent.sh update"
fi
-# Step 6: Get update interval policy from server and setup crontab
-info "⏰ Getting update interval policy from server..."
-UPDATE_INTERVAL=$(curl $CURL_FLAGS \
- -H "X-API-ID: $API_ID" \
- -H "X-API-KEY: $API_KEY" \
- "$PATCHMON_URL/api/v1/settings/update-interval" | \
- grep -o '"updateInterval":[0-9]*' | cut -d':' -f2 2>/dev/null || echo "60")
-
-info "📋 Update interval: $UPDATE_INTERVAL minutes"
-
-# Setup crontab (smart duplicate detection)
-info "📅 Setting up automated updates..."
-
-# Check if PatchMon cron entries already exist
-if crontab -l 2>/dev/null | grep -q "/usr/local/bin/patchmon-agent.sh update"; then
- warning "⚠️ Existing PatchMon cron entries found"
- warning "⚠️ These will be replaced with new schedule"
-fi
-
-# Function to setup crontab without duplicates
-setup_crontab() {
- local update_interval="$1"
- local patchmon_pattern="/usr/local/bin/patchmon-agent.sh update"
-
- # Normalize interval: min 5, max 1440
- if [[ -z "$update_interval" ]]; then update_interval=60; fi
- if [[ "$update_interval" -lt 5 ]]; then update_interval=5; fi
- if [[ "$update_interval" -gt 1440 ]]; then update_interval=1440; fi
-
- # Get current crontab, remove any existing patchmon entries
- local current_cron=$(crontab -l 2>/dev/null | grep -v "$patchmon_pattern" || true)
-
- # Determine new cron entry
- local new_entry
- if [[ "$update_interval" -lt 60 ]]; then
- # Every N minutes (5-59)
- new_entry="*/$update_interval * * * * $patchmon_pattern >/dev/null 2>&1"
- info "📋 Configuring updates every $update_interval minutes"
- else
- if [[ "$update_interval" -eq 60 ]]; then
- # Hourly updates - use current minute to spread load
- local current_minute=$(date +%M)
- new_entry="$current_minute * * * * $patchmon_pattern >/dev/null 2>&1"
- info "📋 Configuring hourly updates at minute $current_minute"
- else
- # For 120, 180, 360, 720, 1440 -> every H hours at minute 0
- local hours=$((update_interval / 60))
- new_entry="0 */$hours * * * $patchmon_pattern >/dev/null 2>&1"
- info "📋 Configuring updates every $hours hour(s)"
- fi
- fi
-
- # Combine existing cron (without patchmon entries) + new entry
- {
- if [[ -n "$current_cron" ]]; then
- echo "$current_cron"
- fi
- echo "$new_entry"
- } | crontab -
-
- success "✅ Crontab configured successfully (duplicates removed)"
-}
-
-setup_crontab "$UPDATE_INTERVAL"
-
# Installation complete
success "🎉 PatchMon Agent installation completed successfully!"
echo ""
@@ -348,8 +284,9 @@ echo -e "${GREEN}📋 Installation Summary:${NC}"
echo " • Configuration directory: /etc/patchmon"
echo " • Agent installed: /usr/local/bin/patchmon-agent.sh"
echo " • Dependencies installed: jq, curl, bc"
-echo " • Crontab configured for automatic updates"
+echo " • Automated updates configured via crontab"
echo " • API credentials configured and tested"
+echo " • Update schedule managed by agent"
# Check for moved files and show them
MOVED_FILES=$(ls /etc/patchmon/credentials.backup.* /usr/local/bin/patchmon-agent.sh.backup.* /var/log/patchmon-agent.log.old.* 2>/dev/null || true)
From de76836ba0c4f61627ac29a10c6055e8939ffede Mon Sep 17 00:00:00 2001
From: 9 Technology Group LTD
Date: Wed, 1 Oct 2025 21:09:10 +0100
Subject: [PATCH 18/35] Create app_build.yml
---
.github/workflows/app_build.yml | 17 +++++++++++++++++
1 file changed, 17 insertions(+)
create mode 100644 .github/workflows/app_build.yml
diff --git a/.github/workflows/app_build.yml b/.github/workflows/app_build.yml
new file mode 100644
index 0000000..70fc285
--- /dev/null
+++ b/.github/workflows/app_build.yml
@@ -0,0 +1,17 @@
+name: Build on Merge
+
+on:
+ push:
+ branches:
+ - main
+ - dev
+
+jobs:
+ deploy:
+ runs-on: self-hosted
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v5
+
+ - name: Run rebuild script
+ run: /root/patchmon/platform/scripts/app_build.sh ${{ github.ref_name }}
From db1f03b0e08b04378fd790e4e77075a52b858665 Mon Sep 17 00:00:00 2001
From: tigattack <10629864+tigattack@users.noreply.github.com>
Date: Thu, 2 Oct 2025 00:38:14 +0100
Subject: [PATCH 19/35] ci(docker): replace GHCR_PAT with GITHUB_TOKEN
---
.github/workflows/docker.yml | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)
diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml
index eeba32a..86e7fd0 100644
--- a/.github/workflows/docker.yml
+++ b/.github/workflows/docker.yml
@@ -42,9 +42,7 @@ jobs:
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.repository_owner }}
- # Using PAT as a hack due to issues with GITHUB_TOKEN and package permissions
- # This should be reverted to use GITHUB_TOKEN once a solution is discovered.
- password: ${{ secrets.GHCR_PAT }}
+ password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract metadata (tags, labels)
id: meta
From ac22adde6765a2d940d64a06e518ca4784b17917 Mon Sep 17 00:00:00 2001
From: tigattack <10629864+tigattack@users.noreply.github.com>
Date: Thu, 2 Oct 2025 00:39:00 +0100
Subject: [PATCH 20/35] ci(docker): simplify conditional for workflow_dispatch
input handling
Don't skip Docker login, doesn't really match the input option
---
.github/workflows/docker.yml | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml
index 86e7fd0..0fb9f95 100644
--- a/.github/workflows/docker.yml
+++ b/.github/workflows/docker.yml
@@ -37,7 +37,6 @@ jobs:
uses: docker/setup-buildx-action@v3
- name: Log in to Container Registry
- if: github.event_name != 'workflow_dispatch' || github.event_name == 'workflow_dispatch' && github.event.inputs.push == 'true'
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
@@ -57,7 +56,7 @@ jobs:
type=raw,value=latest,enable={{is_default_branch}}
- name: Build and push ${{ matrix.image }} image
- if: github.event_name != 'workflow_dispatch' || github.event_name == 'workflow_dispatch' && github.event.inputs.push == 'true'
+ if: github.event_name != 'workflow_dispatch' || inputs.push == 'true'
uses: docker/build-push-action@v6
with:
context: .
From 5a498a5f7ad963928865b9b15e0171b007ef993d Mon Sep 17 00:00:00 2001
From: tigattack <10629864+tigattack@users.noreply.github.com>
Date: Thu, 2 Oct 2025 13:21:12 +0100
Subject: [PATCH 21/35] ci(docker): login before buildx setup
---
.github/workflows/docker.yml | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml
index 0fb9f95..6294b87 100644
--- a/.github/workflows/docker.yml
+++ b/.github/workflows/docker.yml
@@ -33,16 +33,16 @@ jobs:
- name: Checkout repository
uses: actions/checkout@v5
- - name: Set up Docker Buildx
- uses: docker/setup-buildx-action@v3
-
- - name: Log in to Container Registry
+ - name: Log in to container registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
+ - name: Set up Docker Buildx
+ uses: docker/setup-buildx-action@v3
+
- name: Extract metadata (tags, labels)
id: meta
uses: docker/metadata-action@v5
From 3eb413086589659a1c815a79fd89b63ecc1e314a Mon Sep 17 00:00:00 2001
From: tigattack <10629864+tigattack@users.noreply.github.com>
Date: Thu, 2 Oct 2025 13:54:00 +0100
Subject: [PATCH 22/35] ci(docker): fix push condition for build step
---
.github/workflows/docker.yml | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml
index 6294b87..40c11eb 100644
--- a/.github/workflows/docker.yml
+++ b/.github/workflows/docker.yml
@@ -56,13 +56,12 @@ jobs:
type=raw,value=latest,enable={{is_default_branch}}
- name: Build and push ${{ matrix.image }} image
- if: github.event_name != 'workflow_dispatch' || inputs.push == 'true'
uses: docker/build-push-action@v6
with:
context: .
file: docker/${{ matrix.image }}.Dockerfile
platforms: linux/amd64,linux/arm64
- push: true
+ push: ${{ github.event_name != 'workflow_dispatch' || inputs.push == 'true' }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha,scope=${{ matrix.image }}
From 31a95ed94693014169dd438d8e3a06a78b4d0c7e Mon Sep 17 00:00:00 2001
From: tigattack <10629864+tigattack@users.noreply.github.com>
Date: Thu, 2 Oct 2025 13:54:30 +0100
Subject: [PATCH 23/35] ci(docker): simplify image name template
---
.github/workflows/docker.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml
index 40c11eb..815a668 100644
--- a/.github/workflows/docker.yml
+++ b/.github/workflows/docker.yml
@@ -47,7 +47,7 @@ jobs:
id: meta
uses: docker/metadata-action@v5
with:
- images: ${{ env.REGISTRY }}/${{ github.repository_owner }}/patchmon-${{ matrix.image }}
+ images: ${{ env.REGISTRY }}/${{ github.repository }}-${{ matrix.image }}
tags: |
type=ref,event=pr
type=semver,pattern={{version}}
From 7a17958ad84ecea7d9f71092b3c2ce5e4b7f1e80 Mon Sep 17 00:00:00 2001
From: tigattack <10629864+tigattack@users.noreply.github.com>
Date: Wed, 1 Oct 2025 23:54:46 +0100
Subject: [PATCH 24/35] feat(env): validate required env vars on start
---
backend/src/server.js | 36 ++++++++++++++++++++++++++++++++++++
1 file changed, 36 insertions(+)
diff --git a/backend/src/server.js b/backend/src/server.js
index 78fc180..394fe2a 100644
--- a/backend/src/server.js
+++ b/backend/src/server.js
@@ -1,4 +1,40 @@
require("dotenv").config();
+
+// Validate required environment variables on startup
+function validateEnvironmentVariables() {
+ const requiredVars = {
+ JWT_SECRET: "Required for secure authentication token generation",
+ DATABASE_URL: "Required for database connection",
+ };
+
+ const missing = [];
+
+ // Check required variables
+ for (const [varName, description] of Object.entries(requiredVars)) {
+ if (!process.env[varName]) {
+ missing.push(`${varName}: ${description}`);
+ }
+ }
+
+ // Fail if required variables are missing
+ if (missing.length > 0) {
+ console.error("❌ Missing required environment variables:");
+ for (const error of missing) {
+ console.error(` - ${error}`);
+ }
+ console.error("");
+ console.error(
+ "Please set these environment variables and restart the application.",
+ );
+ process.exit(1);
+ }
+
+ console.log("✅ Environment variable validation passed");
+}
+
+// Validate environment variables before importing any modules that depend on them
+validateEnvironmentVariables();
+
const express = require("express");
const cors = require("cors");
const helmet = require("helmet");
From 50b47bdd65b9fb9829b29408b73d24cd14ebe8f6 Mon Sep 17 00:00:00 2001
From: tigattack <10629864+tigattack@users.noreply.github.com>
Date: Thu, 2 Oct 2025 00:09:18 +0100
Subject: [PATCH 25/35] feat(docker): add JWT configs to backend image &
compose
---
docker/backend.Dockerfile | 5 ++++-
docker/docker-compose.dev.yml | 4 ++++
docker/docker-compose.yml | 4 ++++
3 files changed, 12 insertions(+), 1 deletion(-)
diff --git a/docker/backend.Dockerfile b/docker/backend.Dockerfile
index fc28021..3c095b8 100644
--- a/docker/backend.Dockerfile
+++ b/docker/backend.Dockerfile
@@ -59,7 +59,10 @@ ENV NODE_ENV=production \
ENABLE_LOGGING=true \
LOG_LEVEL=info \
PM_LOG_TO_CONSOLE=true \
- PORT=3001
+ PORT=3001 \
+ JWT_EXPIRES_IN=1h \
+ JWT_REFRESH_EXPIRES_IN=7d \
+ SESSION_INACTIVITY_TIMEOUT_MINUTES=30
RUN apk add --no-cache openssl tini curl
diff --git a/docker/docker-compose.dev.yml b/docker/docker-compose.dev.yml
index bf401ef..28f41b8 100644
--- a/docker/docker-compose.dev.yml
+++ b/docker/docker-compose.dev.yml
@@ -28,6 +28,10 @@ services:
DATABASE_URL: postgresql://patchmon_user:INSECURE_REPLACE_ME_PLEASE_INSECURE@database:5432/patchmon_db
PM_DB_CONN_MAX_ATTEMPTS: 30
PM_DB_CONN_WAIT_INTERVAL: 2
+ JWT_SECRET: INS3CURE_DEV_7WT_5ECR3T
+ JWT_EXPIRES_IN: 1h
+ JWT_REFRESH_EXPIRES_IN: 7d
+ SESSION_INACTIVITY_TIMEOUT_MINUTES: 30
SERVER_PROTOCOL: http
SERVER_HOST: localhost
SERVER_PORT: 3000
diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml
index 01e01d1..ded1de5 100644
--- a/docker/docker-compose.yml
+++ b/docker/docker-compose.yml
@@ -22,6 +22,10 @@ services:
DATABASE_URL: postgresql://patchmon_user:INSECURE_REPLACE_ME_PLEASE_INSECURE@database:5432/patchmon_db
PM_DB_CONN_MAX_ATTEMPTS: 30
PM_DB_CONN_WAIT_INTERVAL: 2
+ JWT_SECRET: # CREATE A STRONG SECRET AND PUT IT HERE - Generate with 'openssl rand -hex 64'
+ JWT_EXPIRES_IN: 1h
+ JWT_REFRESH_EXPIRES_IN: 7d
+ SESSION_INACTIVITY_TIMEOUT_MINUTES: 30
SERVER_PROTOCOL: http
SERVER_HOST: localhost
SERVER_PORT: 3000
From ab97e04cc108eb08d6a83f9a812ba69520833840 Mon Sep 17 00:00:00 2001
From: tigattack <10629864+tigattack@users.noreply.github.com>
Date: Thu, 2 Oct 2025 00:09:41 +0100
Subject: [PATCH 26/35] chore(docker): add service name to compose files
---
docker/docker-compose.dev.yml | 2 ++
docker/docker-compose.yml | 2 ++
2 files changed, 4 insertions(+)
diff --git a/docker/docker-compose.dev.yml b/docker/docker-compose.dev.yml
index 28f41b8..66bf85e 100644
--- a/docker/docker-compose.dev.yml
+++ b/docker/docker-compose.dev.yml
@@ -1,3 +1,5 @@
+name: patchmon-dev
+
services:
database:
image: postgres:17-alpine
diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml
index ded1de5..8046e32 100644
--- a/docker/docker-compose.yml
+++ b/docker/docker-compose.yml
@@ -1,3 +1,5 @@
+name: patchmon
+
services:
database:
image: postgres:17-alpine
From 01dac49c05050c89ae391a5e806124da5f8a38e6 Mon Sep 17 00:00:00 2001
From: tigattack <10629864+tigattack@users.noreply.github.com>
Date: Thu, 2 Oct 2025 00:10:22 +0100
Subject: [PATCH 27/35] refactor(docker): update PostgreSQL password
placeholder in compose files
---
docker/docker-compose.dev.yml | 4 ++--
docker/docker-compose.yml | 4 ++--
2 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/docker/docker-compose.dev.yml b/docker/docker-compose.dev.yml
index 66bf85e..49f60fa 100644
--- a/docker/docker-compose.dev.yml
+++ b/docker/docker-compose.dev.yml
@@ -7,7 +7,7 @@ services:
environment:
POSTGRES_DB: patchmon_db
POSTGRES_USER: patchmon_user
- POSTGRES_PASSWORD: INSECURE_REPLACE_ME_PLEASE_INSECURE
+ POSTGRES_PASSWORD: 1NS3CU6E_DEV_D8_PASSW0RD
ports:
- "5432:5432"
volumes:
@@ -27,7 +27,7 @@ services:
environment:
NODE_ENV: development
LOG_LEVEL: info
- DATABASE_URL: postgresql://patchmon_user:INSECURE_REPLACE_ME_PLEASE_INSECURE@database:5432/patchmon_db
+ DATABASE_URL: postgresql://patchmon_user:1NS3CU6E_DEV_D8_PASSW0RD@database:5432/patchmon_db
PM_DB_CONN_MAX_ATTEMPTS: 30
PM_DB_CONN_WAIT_INTERVAL: 2
JWT_SECRET: INS3CURE_DEV_7WT_5ECR3T
diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml
index 8046e32..ec5525c 100644
--- a/docker/docker-compose.yml
+++ b/docker/docker-compose.yml
@@ -7,7 +7,7 @@ services:
environment:
POSTGRES_DB: patchmon_db
POSTGRES_USER: patchmon_user
- POSTGRES_PASSWORD: INSECURE_REPLACE_ME_PLEASE_INSECURE
+ POSTGRES_PASSWORD: # CREATE A STRONG PASSWORD AND PUT IT HERE
volumes:
- postgres_data:/var/lib/postgresql/data
healthcheck:
@@ -21,7 +21,7 @@ services:
restart: unless-stopped
environment:
LOG_LEVEL: info
- DATABASE_URL: postgresql://patchmon_user:INSECURE_REPLACE_ME_PLEASE_INSECURE@database:5432/patchmon_db
+ DATABASE_URL: postgresql://patchmon_user:REPLACE_YOUR_POSTGRES_PASSWORD_HERE@database:5432/patchmon_db
PM_DB_CONN_MAX_ATTEMPTS: 30
PM_DB_CONN_WAIT_INTERVAL: 2
JWT_SECRET: # CREATE A STRONG SECRET AND PUT IT HERE - Generate with 'openssl rand -hex 64'
From b85eddf22a7c54a714c13b21946e89d2452f62bf Mon Sep 17 00:00:00 2001
From: tigattack <10629864+tigattack@users.noreply.github.com>
Date: Thu, 2 Oct 2025 00:10:39 +0100
Subject: [PATCH 28/35] feat(docker): add tags for dev images in compose file
---
docker/docker-compose.dev.yml | 2 ++
1 file changed, 2 insertions(+)
diff --git a/docker/docker-compose.dev.yml b/docker/docker-compose.dev.yml
index 49f60fa..8103ee9 100644
--- a/docker/docker-compose.dev.yml
+++ b/docker/docker-compose.dev.yml
@@ -23,6 +23,7 @@ services:
context: ..
dockerfile: docker/backend.Dockerfile
target: development
+ tags: [patchmon-backend:dev]
restart: unless-stopped
environment:
NODE_ENV: development
@@ -65,6 +66,7 @@ services:
context: ..
dockerfile: docker/frontend.Dockerfile
target: development
+ tags: [patchmon-frontend:dev]
restart: unless-stopped
environment:
BACKEND_HOST: backend
From f6d21e0ed588d5a7d5de26eecd554bf2bcdd4876 Mon Sep 17 00:00:00 2001
From: tigattack <10629864+tigattack@users.noreply.github.com>
Date: Thu, 2 Oct 2025 00:33:14 +0100
Subject: [PATCH 29/35] docs(docker): improve secrets instructions, add JWT
info
---
docker/README.md | 27 +++++++++++++++++++--------
1 file changed, 19 insertions(+), 8 deletions(-)
diff --git a/docker/README.md b/docker/README.md
index 5a45e7b..d67b8f0 100644
--- a/docker/README.md
+++ b/docker/README.md
@@ -20,26 +20,37 @@ Version tags are also available (e.g. `1.2.3`) for both of these images.
### Production Deployment
1. Download the [Docker Compose file](docker-compose.yml)
-2. Change the default database password in the file:
+2. Set a database password in the file where it says:
```yaml
environment:
- POSTGRES_PASSWORD: YOUR_SECURE_PASSWORD_HERE
+ POSTGRES_PASSWORD: # CREATE A STRONG PASSWORD AND PUT IT HERE
```
-3. Update the corresponding `DATABASE_URL` in the backend service:
+3. Update the corresponding `DATABASE_URL` with your password in the backend service where it says:
```yaml
environment:
- DATABASE_URL: postgresql://patchmon_user:YOUR_SECURE_PASSWORD_HERE@database:5432/patchmon_db
+ DATABASE_URL: postgresql://patchmon_user:REPLACE_YOUR_POSTGRES_PASSWORD_HERE@database:5432/patchmon_db
```
-4. Configure environment variables (see [Configuration](#configuration) section)
-5. Start the application:
+4. Generate a strong JWT secret. You can do this like so:
+ ```bash
+ openssl rand -hex 64
+ ```
+5. Set a JWT secret in the backend service where it says:
+ ```yaml
+ environment:
+ JWT_SECRET: # CREATE A STRONG SECRET AND PUT IT HERE
+ ```
+6. Configure environment variables (see [Configuration](#configuration) section)
+7. Start the application:
```bash
docker compose up -d
```
-6. Access the application at `http://localhost:3000`
+8. Access the application at `http://localhost:3000`
## Updating
-To update PatchMon to the latest version:
+By default, the compose file uses the `latest` tag for both backend and frontend images.
+
+This means you can update PatchMon to the latest version as easily as:
```bash
docker compose up -d --pull
From b3c1319df4817c71b580f4143f7ec2dcfd62af9b Mon Sep 17 00:00:00 2001
From: tigattack <10629864+tigattack@users.noreply.github.com>
Date: Thu, 2 Oct 2025 00:33:56 +0100
Subject: [PATCH 30/35] docs(docker): clarify instructions for version-specific
updates
Changes example version to 1.2.3 to hopefully make it clearer that this is JUST an example.
---
docker/README.md | 10 ++++++----
1 file changed, 6 insertions(+), 4 deletions(-)
diff --git a/docker/README.md b/docker/README.md
index d67b8f0..8d77dab 100644
--- a/docker/README.md
+++ b/docker/README.md
@@ -63,16 +63,18 @@ This command will:
### Version-Specific Updates
-If you're using specific version tags instead of `latest` in your compose file:
+If you'd like to pin your Docker deployment of PatchMon to a specific version, you can do this in the compose file.
-1. Update the image tags in your `docker-compose.yml`. For example:
+When you do this, updating to a new version requires manually updating the image tags in the compose file yourself:
+
+1. Update the image tags in `docker-compose.yml`. For example:
```yaml
services:
backend:
- image: ghcr.io/9technologygroup/patchmon-backend:1.2.7 # Update version here
+ image: ghcr.io/9technologygroup/patchmon-backend:1.2.3 # Update version here
...
frontend:
- image: ghcr.io/9technologygroup/patchmon-frontend:1.2.7 # Update version here
+ image: ghcr.io/9technologygroup/patchmon-frontend:1.2.3 # Update version here
...
```
From 258bc67efc72ef757f8b7d8022d25d47c832d1fe Mon Sep 17 00:00:00 2001
From: tigattack <10629864+tigattack@users.noreply.github.com>
Date: Thu, 2 Oct 2025 00:34:19 +0100
Subject: [PATCH 31/35] docs(docker): update repo links with new URL
---
docker/README.md | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/docker/README.md b/docker/README.md
index 8d77dab..0607bf7 100644
--- a/docker/README.md
+++ b/docker/README.md
@@ -84,7 +84,7 @@ When you do this, updating to a new version requires manually updating the image
```
> [!TIP]
-> Check the [releases page](https://github.com/9technologygroup/patchmon.net/releases) for version-specific changes and migration notes.
+> Check the [releases page](https://github.com/PatchMon/PatchMon/releases) for version-specific changes and migration notes.
## Configuration
@@ -142,7 +142,7 @@ For development with live reload and source code mounting:
1. Clone the repository:
```bash
- git clone https://github.com/9technologygroup/patchmon.net.git
+ git clone https://github.com/PatchMon/PatchMon.git
cd patchmon.net
```
@@ -216,7 +216,7 @@ The development setup exposes additional ports for debugging:
1. **Initial Setup**: Clone repository and start development environment
```bash
- git clone https://github.com/9technologygroup/patchmon.net.git
+ git clone https://github.com/PatchMon/PatchMon.git
cd patchmon.net
docker compose -f docker/docker-compose.dev.yml up -d --build
```
From 8464a3692dffe014292ecfee2a53a05700a4c308 Mon Sep 17 00:00:00 2001
From: tigattack <10629864+tigattack@users.noreply.github.com>
Date: Thu, 2 Oct 2025 02:40:14 +0100
Subject: [PATCH 32/35] docs(docker): restructure env var docs and add missing
vars
---
docker/README.md | 75 ++++++++++++++++++++++++++++++++++++------------
1 file changed, 56 insertions(+), 19 deletions(-)
diff --git a/docker/README.md b/docker/README.md
index 0607bf7..90d572a 100644
--- a/docker/README.md
+++ b/docker/README.md
@@ -92,31 +92,68 @@ When you do this, updating to a new version requires manually updating the image
#### Database Service
-- `POSTGRES_DB`: Database name (default: `patchmon_db`)
-- `POSTGRES_USER`: Database user (default: `patchmon_user`)
-- `POSTGRES_PASSWORD`: Database password - **MUST BE CHANGED!**
+| Variable | Description | Default |
+| ------------------- | ----------------- | ---------------- |
+| `POSTGRES_DB` | Database name | `patchmon_db` |
+| `POSTGRES_USER` | Database user | `patchmon_user` |
+| `POSTGRES_PASSWORD` | Database password | **MUST BE SET!** |
#### Backend Service
-- `LOG_LEVEL`: Logging level (`debug`, `info`, `warn`, `error`)
-- `DATABASE_URL`: PostgreSQL connection string
-- `PM_DB_CONN_MAX_ATTEMPTS`: Maximum database connection attempts (default: 30)
-- `PM_DB_CONN_WAIT_INTERVAL`: Wait interval between connection attempts in seconds (default: 2)
-- `SERVER_PROTOCOL`: Frontend server protocol (`http` or `https`)
-- `SERVER_HOST`: Frontend server host (default: `localhost`)
-- `SERVER_PORT`: Frontend server port (default: 3000)
-- `PORT`: Backend API port (default: 3001)
-- `API_VERSION`: API version (default: `v1`)
-- `CORS_ORIGIN`: CORS origin URL
-- `RATE_LIMIT_WINDOW_MS`: Rate limiting window in milliseconds (default: 900000)
-- `RATE_LIMIT_MAX`: Maximum requests per window (default: 100)
-- `ENABLE_HSTS`: Enable HTTP Strict Transport Security (default: true)
-- `TRUST_PROXY`: Trust proxy headers (default: true) - See [Express.js docs](https://expressjs.com/en/guide/behind-proxies.html) for usage.
+##### Database Configuration
+
+| Variable | Description | Default |
+| -------------------------- | ---------------------------------------------------- | ------------------------------------------------ |
+| `DATABASE_URL` | PostgreSQL connection string | **MUST BE UPDATED WITH YOUR POSTGRES_PASSWORD!** |
+| `PM_DB_CONN_MAX_ATTEMPTS` | Maximum database connection attempts | `30` |
+| `PM_DB_CONN_WAIT_INTERVAL` | Wait interval between connection attempts in seconds | `2` |
+
+##### Authentication & Security
+
+| Variable | Description | Default |
+| ------------------------------------ | --------------------------------------------------------- | ---------------- |
+| `JWT_SECRET` | JWT signing secret - Generate with `openssl rand -hex 64` | **MUST BE SET!** |
+| `JWT_EXPIRES_IN` | JWT token expiration time | `1h` |
+| `JWT_REFRESH_EXPIRES_IN` | JWT refresh token expiration time | `7d` |
+| `SESSION_INACTIVITY_TIMEOUT_MINUTES` | Session inactivity timeout in minutes | `30` |
+| `DEFAULT_USER_ROLE` | Default role for new users | `user` |
+
+##### Server & Network Configuration
+
+| Variable | Description | Default |
+| ----------------- | ----------------------------------------------------------------------------------------------- | ----------------------- |
+| `PORT` | Backend API port | `3001` |
+| `SERVER_PROTOCOL` | Frontend server protocol (`http` or `https`) | `http` |
+| `SERVER_HOST` | Frontend server host | `localhost` |
+| `SERVER_PORT` | Frontend server port | `3000` |
+| `CORS_ORIGIN` | CORS origin URL | `http://localhost:3000` |
+| `ENABLE_HSTS` | Enable HTTP Strict Transport Security | `true` |
+| `TRUST_PROXY` | Trust proxy headers - See [Express.js docs](https://expressjs.com/en/guide/behind-proxies.html) | `true` |
+
+##### Rate Limiting
+
+| Variable | Description | Default |
+| ---------------------------- | --------------------------------------------------- | -------- |
+| `RATE_LIMIT_WINDOW_MS` | Rate limiting window in milliseconds | `900000` |
+| `RATE_LIMIT_MAX` | Maximum requests per window | `5000` |
+| `AUTH_RATE_LIMIT_WINDOW_MS` | Authentication rate limiting window in milliseconds | `600000` |
+| `AUTH_RATE_LIMIT_MAX` | Maximum authentication requests per window | `500` |
+| `AGENT_RATE_LIMIT_WINDOW_MS` | Agent API rate limiting window in milliseconds | `60000` |
+| `AGENT_RATE_LIMIT_MAX` | Maximum agent requests per window | `1000` |
+
+##### Logging
+
+| Variable | Description | Default |
+| ---------------- | ------------------------------------------------ | ------- |
+| `LOG_LEVEL` | Logging level (`debug`, `info`, `warn`, `error`) | `info` |
+| `ENABLE_LOGGING` | Enable application logging | `true` |
#### Frontend Service
-- `BACKEND_HOST`: Backend service hostname (default: `backend`)
-- `BACKEND_PORT`: Backend service port (default: 3001)
+| Variable | Description | Default |
+| -------------- | ------------------------ | --------- |
+| `BACKEND_HOST` | Backend service hostname | `backend` |
+| `BACKEND_PORT` | Backend service port | `3001` |
### Volumes
From 841b97cb5d4af0e73663cb6cedbcfb727b1de63c Mon Sep 17 00:00:00 2001
From: tigattack <10629864+tigattack@users.noreply.github.com>
Date: Thu, 2 Oct 2025 02:41:01 +0100
Subject: [PATCH 33/35] chore(docker): remove optional env vars from compose
---
docker/docker-compose.dev.yml | 7 -------
docker/docker-compose.yml | 8 +-------
2 files changed, 1 insertion(+), 14 deletions(-)
diff --git a/docker/docker-compose.dev.yml b/docker/docker-compose.dev.yml
index 8103ee9..55346b5 100644
--- a/docker/docker-compose.dev.yml
+++ b/docker/docker-compose.dev.yml
@@ -29,18 +29,11 @@ services:
NODE_ENV: development
LOG_LEVEL: info
DATABASE_URL: postgresql://patchmon_user:1NS3CU6E_DEV_D8_PASSW0RD@database:5432/patchmon_db
- PM_DB_CONN_MAX_ATTEMPTS: 30
- PM_DB_CONN_WAIT_INTERVAL: 2
JWT_SECRET: INS3CURE_DEV_7WT_5ECR3T
- JWT_EXPIRES_IN: 1h
- JWT_REFRESH_EXPIRES_IN: 7d
- SESSION_INACTIVITY_TIMEOUT_MINUTES: 30
SERVER_PROTOCOL: http
SERVER_HOST: localhost
SERVER_PORT: 3000
CORS_ORIGIN: http://localhost:3000
- RATE_LIMIT_WINDOW_MS: 900000
- RATE_LIMIT_MAX: 100
ports:
- "3001:3001"
volumes:
diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml
index ec5525c..7222cde 100644
--- a/docker/docker-compose.yml
+++ b/docker/docker-compose.yml
@@ -19,21 +19,15 @@ services:
backend:
image: ghcr.io/9technologygroup/patchmon-backend:latest
restart: unless-stopped
+ # See PatchMon Docker README for additional environment variables and configuration instructions
environment:
LOG_LEVEL: info
DATABASE_URL: postgresql://patchmon_user:REPLACE_YOUR_POSTGRES_PASSWORD_HERE@database:5432/patchmon_db
- PM_DB_CONN_MAX_ATTEMPTS: 30
- PM_DB_CONN_WAIT_INTERVAL: 2
JWT_SECRET: # CREATE A STRONG SECRET AND PUT IT HERE - Generate with 'openssl rand -hex 64'
- JWT_EXPIRES_IN: 1h
- JWT_REFRESH_EXPIRES_IN: 7d
- SESSION_INACTIVITY_TIMEOUT_MINUTES: 30
SERVER_PROTOCOL: http
SERVER_HOST: localhost
SERVER_PORT: 3000
CORS_ORIGIN: http://localhost:3000
- RATE_LIMIT_WINDOW_MS: 900000
- RATE_LIMIT_MAX: 100
volumes:
- agent_files:/app/agents
depends_on:
From c004734a44fd21911c9a422b81dca21524212a38 Mon Sep 17 00:00:00 2001
From: tigattack <10629864+tigattack@users.noreply.github.com>
Date: Thu, 2 Oct 2025 15:52:12 +0100
Subject: [PATCH 34/35] fix(docker): update image references to use the correct
repository
---
docker/README.md | 8 ++++----
docker/docker-compose.yml | 4 ++--
2 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/docker/README.md b/docker/README.md
index 90d572a..059b9f8 100644
--- a/docker/README.md
+++ b/docker/README.md
@@ -10,8 +10,8 @@ PatchMon is a containerised application that monitors system patches and updates
## Images
-- **Backend**: [ghcr.io/9technologygroup/patchmon-backend:latest](https://github.com/9technologygroup/patchmon.net/pkgs/container/patchmon-backend)
-- **Frontend**: [ghcr.io/9technologygroup/patchmon-frontend:latest](https://github.com/9technologygroup/patchmon.net/pkgs/container/patchmon-frontend)
+- **Backend**: [ghcr.io/patchmon/patchmon-backend:latest](https://github.com/patchmon/patchmon.net/pkgs/container/patchmon-backend)
+- **Frontend**: [ghcr.io/patchmon/patchmon-frontend:latest](https://github.com/patchmon/patchmon.net/pkgs/container/patchmon-frontend)
Version tags are also available (e.g. `1.2.3`) for both of these images.
@@ -71,10 +71,10 @@ When you do this, updating to a new version requires manually updating the image
```yaml
services:
backend:
- image: ghcr.io/9technologygroup/patchmon-backend:1.2.3 # Update version here
+ image: ghcr.io/patchmon/patchmon-backend:1.2.3 # Update version here
...
frontend:
- image: ghcr.io/9technologygroup/patchmon-frontend:1.2.3 # Update version here
+ image: ghcr.io/patchmon/patchmon-frontend:1.2.3 # Update version here
...
```
diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml
index 7222cde..54ec143 100644
--- a/docker/docker-compose.yml
+++ b/docker/docker-compose.yml
@@ -17,7 +17,7 @@ services:
retries: 7
backend:
- image: ghcr.io/9technologygroup/patchmon-backend:latest
+ image: ghcr.io/patchmon/patchmon-backend:latest
restart: unless-stopped
# See PatchMon Docker README for additional environment variables and configuration instructions
environment:
@@ -35,7 +35,7 @@ services:
condition: service_healthy
frontend:
- image: ghcr.io/9technologygroup/patchmon-frontend:latest
+ image: ghcr.io/patchmon/patchmon-frontend:latest
restart: unless-stopped
ports:
- "3000:3000"
From 9ddc27e50cc3ba59eddb84cf91e63e2928b0feb5 Mon Sep 17 00:00:00 2001
From: tigattack <10629864+tigattack@users.noreply.github.com>
Date: Thu, 2 Oct 2025 18:05:30 +0100
Subject: [PATCH 35/35] ci(docker): add QEMU setup
---
.github/workflows/docker.yml | 3 +++
1 file changed, 3 insertions(+)
diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml
index 815a668..5e24f83 100644
--- a/.github/workflows/docker.yml
+++ b/.github/workflows/docker.yml
@@ -40,6 +40,9 @@ jobs:
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
+ - name: Set up QEMU
+ uses: docker/setup-qemu-action@v3
+
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3