Compare commits

..

6 Commits

Author SHA1 Message Date
9 Technology Group LTD
3f18074f01 Merge pull request #304 from PatchMon/feature/alpine
new binary for alpie apk support
2025-11-11 12:49:09 +00:00
Muhammad Ibrahim
ab700a3bc8 new binary for alpie apk support 2025-11-11 12:42:00 +00:00
9 Technology Group LTD
9857d7cdfc Merge pull request #302 from PatchMon/feature/api
added the migration file
2025-11-10 22:02:37 +00:00
9 Technology Group LTD
d7d47089b2 Merge pull request #301 from PatchMon/feature/api
Feature/api
2025-11-10 20:41:36 +00:00
9 Technology Group LTD
427743b81e Merge pull request #294 from PatchMon/feature/alpine
alpine support (apk) support agents
2025-11-08 21:26:04 +00:00
9 Technology Group LTD
a4922b4e54 Merge pull request #292 from PatchMon/feature/alpine
arm support
2025-11-08 12:24:42 +00:00
10 changed files with 124 additions and 396 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -1,31 +1,6 @@
#!/bin/sh
# PatchMon Agent Installation Script
# This script requires bash for full functionality
# Usage: curl -s {PATCHMON_URL}/api/v1/hosts/install -H "X-API-ID: {API_ID}" -H "X-API-KEY: {API_KEY}" | sh
# Check if bash is available, if not try to install it (for Alpine Linux)
if ! command -v bash >/dev/null 2>&1; then
if command -v apk >/dev/null 2>&1; then
echo "Installing bash for script compatibility..."
apk add --no-cache bash >/dev/null 2>&1 || true
fi
fi
# If bash is available and we're not already running in bash, switch to bash
# When piped, we can't re-execute easily, so we'll continue with sh
# but ensure bash is available for bash-specific features
if command -v bash >/dev/null 2>&1 && [ -z "${BASH_VERSION:-}" ]; then
# Check if we're being piped (stdin is not a terminal)
if [ -t 0 ]; then
# Direct execution, re-execute with bash
exec bash "$0" "$@"
exit $?
fi
# When piped, we continue with sh but bash is now available
# The script will use bash-specific features which should work if bash is installed
fi
# PatchMon Agent Installation Script
# POSIX-compliant shell script (works with dash, ash, bash, etc.)
# Usage: curl -s {PATCHMON_URL}/api/v1/hosts/install -H "X-API-ID: {API_ID}" -H "X-API-KEY: {API_KEY}" | sh
set -e
@@ -44,20 +19,20 @@ NC='\033[0m' # No Color
# Functions
error() {
echo -e "${RED}❌ ERROR: $1${NC}" >&2
printf "%b\n" "${RED}❌ ERROR: $1${NC}" >&2
exit 1
}
info() {
echo -e "${BLUE} $1${NC}"
printf "%b\n" "${BLUE} $1${NC}"
}
success() {
echo -e "${GREEN}$1${NC}"
printf "%b\n" "${GREEN}$1${NC}"
}
warning() {
echo -e "${YELLOW}⚠️ $1${NC}"
printf "%b\n" "${YELLOW}⚠️ $1${NC}"
}
# Check if running as root
@@ -75,7 +50,7 @@ verify_datetime() {
# Display current datetime info
echo ""
echo -e "${BLUE}📅 Current System Date/Time:${NC}"
printf "%b\n" "${BLUE}📅 Current System Date/Time:${NC}"
echo " • Date/Time: $system_time"
echo " • Timezone: $timezone"
echo ""
@@ -93,20 +68,20 @@ verify_datetime() {
;;
*)
echo ""
echo -e "${RED}❌ Date/time verification failed${NC}"
printf "%b\n" "${RED}❌ Date/time verification failed${NC}"
echo ""
echo -e "${YELLOW}💡 Please fix the date/time and re-run the installation script:${NC}"
printf "%b\n" "${YELLOW}💡 Please fix the date/time and re-run the installation script:${NC}"
echo " sudo timedatectl set-time 'YYYY-MM-DD HH:MM:SS'"
echo " sudo timedatectl set-timezone 'America/New_York' # or your timezone"
echo " sudo timedatectl list-timezones # to see available timezones"
echo ""
echo -e "${BLUE} After fixing the date/time, re-run this installation script.${NC}"
printf "%b\n" "${BLUE} After fixing the date/time, re-run this installation script.${NC}"
error "Installation cancelled - please fix date/time and re-run"
;;
esac
else
# Non-interactive (piped from curl) - show warning and continue
echo -e "${YELLOW}⚠️ Non-interactive installation detected${NC}"
printf "%b\n" "${YELLOW}⚠️ Non-interactive installation detected${NC}"
echo ""
echo "Please verify the date/time shown above is correct."
echo "If the date/time is incorrect, it may cause issues with:"
@@ -114,7 +89,7 @@ verify_datetime() {
echo " • Scheduled updates"
echo " • Data synchronization"
echo ""
echo -e "${GREEN}✅ Continuing with installation...${NC}"
printf "%b\n" "${GREEN}✅ Continuing with installation...${NC}"
success "✅ Date/time verification completed (assumed correct)"
echo ""
fi
@@ -211,17 +186,17 @@ export MACHINE_ID
info "🚀 Starting PatchMon Agent Installation..."
info "📋 Server: $PATCHMON_URL"
info "🔑 API ID: ${API_ID:0:16}..."
info "🆔 Machine ID: ${MACHINE_ID:0:16}..."
info "🔑 API ID: $(echo "$API_ID" | cut -c1-16)..."
info "🆔 Machine ID: $(echo "$MACHINE_ID" | cut -c1-16)..."
info "🏗️ Architecture: $ARCHITECTURE"
# Display diagnostic information
echo ""
echo -e "${BLUE}🔧 Installation Diagnostics:${NC}"
printf "%b\n" "${BLUE}🔧 Installation Diagnostics:${NC}"
echo " • URL: $PATCHMON_URL"
echo " • CURL FLAGS: $CURL_FLAGS"
echo " • API ID: ${API_ID:0:16}..."
echo " • API Key: ${API_KEY:0:16}..."
echo " • API ID: $(echo "$API_ID" | cut -c1-16)..."
echo " • API Key: $(echo "$API_KEY" | cut -c1-16)..."
echo " • Architecture: $ARCHITECTURE"
echo ""
@@ -236,52 +211,56 @@ command_exists() {
# Function to install packages with error handling
install_apt_packages() {
local packages=("$@")
local missing_packages=()
# Space-separated list of packages
_packages="$*"
_missing_packages=""
# Check which packages are missing
for pkg in "${packages[@]}"; do
for pkg in $_packages; do
if ! command_exists "$pkg"; then
missing_packages+=("$pkg")
_missing_packages="$_missing_packages $pkg"
fi
done
if [ ${#missing_packages[@]} -eq 0 ]; then
# Trim leading space
_missing_packages=$(echo "$_missing_packages" | sed 's/^ //')
if [ -z "$_missing_packages" ]; then
success "All required packages are already installed"
return 0
fi
info "Need to install: ${missing_packages[*]}"
info "Need to install: $_missing_packages"
# Build apt-get command based on force mode
local apt_cmd="apt-get install ${missing_packages[*]} -y"
_apt_cmd="apt-get install $_missing_packages -y"
if [ "$FORCE_INSTALL" = "true" ]; then
info "Using force mode - bypassing broken packages..."
apt_cmd="$apt_cmd -o APT::Get::Fix-Broken=false -o DPkg::Options::=\"--force-confold\" -o DPkg::Options::=\"--force-confdef\""
_apt_cmd="$_apt_cmd -o APT::Get::Fix-Broken=false -o DPkg::Options::=\"--force-confold\" -o DPkg::Options::=\"--force-confdef\""
fi
# Try to install packages
if eval "$apt_cmd" 2>&1 | tee /tmp/patchmon_apt_install.log; then
if eval "$_apt_cmd" 2>&1 | tee /tmp/patchmon_apt_install.log; then
success "Packages installed successfully"
return 0
else
warning "Package installation encountered issues, checking if required tools are available..."
# Verify critical dependencies are actually available
local all_ok=true
for pkg in "${packages[@]}"; do
_all_ok=true
for pkg in $_packages; do
if ! command_exists "$pkg"; then
if [ "$FORCE_INSTALL" = "true" ]; then
error "Critical dependency '$pkg' is not available even with --force. Please install manually."
else
error "Critical dependency '$pkg' is not available. Try again with --force flag or install manually: apt-get install $pkg"
fi
all_ok=false
_all_ok=false
fi
done
if $all_ok; then
if $_all_ok; then
success "All required tools are available despite installation warnings"
return 0
else
@@ -292,121 +271,133 @@ install_apt_packages() {
# Function to check and install packages for yum/dnf
install_yum_dnf_packages() {
local pkg_manager="$1"
_pkg_manager="$1"
shift
local packages=("$@")
local missing_packages=()
_packages="$*"
_missing_packages=""
# Check which packages are missing
for pkg in "${packages[@]}"; do
for pkg in $_packages; do
if ! command_exists "$pkg"; then
missing_packages+=("$pkg")
_missing_packages="$_missing_packages $pkg"
fi
done
if [ ${#missing_packages[@]} -eq 0 ]; then
# Trim leading space
_missing_packages=$(echo "$_missing_packages" | sed 's/^ //')
if [ -z "$_missing_packages" ]; then
success "All required packages are already installed"
return 0
fi
info "Need to install: ${missing_packages[*]}"
info "Need to install: $_missing_packages"
if [ "$pkg_manager" = "yum" ]; then
yum install -y "${missing_packages[@]}"
if [ "$_pkg_manager" = "yum" ]; then
yum install -y $_missing_packages
else
dnf install -y "${missing_packages[@]}"
dnf install -y $_missing_packages
fi
}
# Function to check and install packages for zypper
install_zypper_packages() {
local packages=("$@")
local missing_packages=()
_packages="$*"
_missing_packages=""
# Check which packages are missing
for pkg in "${packages[@]}"; do
for pkg in $_packages; do
if ! command_exists "$pkg"; then
missing_packages+=("$pkg")
_missing_packages="$_missing_packages $pkg"
fi
done
if [ ${#missing_packages[@]} -eq 0 ]; then
# Trim leading space
_missing_packages=$(echo "$_missing_packages" | sed 's/^ //')
if [ -z "$_missing_packages" ]; then
success "All required packages are already installed"
return 0
fi
info "Need to install: ${missing_packages[*]}"
zypper install -y "${missing_packages[@]}"
info "Need to install: $_missing_packages"
zypper install -y $_missing_packages
}
# Function to check and install packages for pacman
install_pacman_packages() {
local packages=("$@")
local missing_packages=()
_packages="$*"
_missing_packages=""
# Check which packages are missing
for pkg in "${packages[@]}"; do
for pkg in $_packages; do
if ! command_exists "$pkg"; then
missing_packages+=("$pkg")
_missing_packages="$_missing_packages $pkg"
fi
done
if [ ${#missing_packages[@]} -eq 0 ]; then
# Trim leading space
_missing_packages=$(echo "$_missing_packages" | sed 's/^ //')
if [ -z "$_missing_packages" ]; then
success "All required packages are already installed"
return 0
fi
info "Need to install: ${missing_packages[*]}"
pacman -S --noconfirm "${missing_packages[@]}"
info "Need to install: $_missing_packages"
pacman -S --noconfirm $_missing_packages
}
# Function to check and install packages for apk
install_apk_packages() {
local packages=("$@")
local missing_packages=()
_packages="$*"
_missing_packages=""
# Check which packages are missing
for pkg in "${packages[@]}"; do
for pkg in $_packages; do
if ! command_exists "$pkg"; then
missing_packages+=("$pkg")
_missing_packages="$_missing_packages $pkg"
fi
done
if [ ${#missing_packages[@]} -eq 0 ]; then
# Trim leading space
_missing_packages=$(echo "$_missing_packages" | sed 's/^ //')
if [ -z "$_missing_packages" ]; then
success "All required packages are already installed"
return 0
fi
info "Need to install: ${missing_packages[*]}"
info "Need to install: $_missing_packages"
# Update package index before installation
info "Updating package index..."
apk update -q || true
# Build apk command
local apk_cmd="apk add --no-cache ${missing_packages[*]}"
_apk_cmd="apk add --no-cache $_missing_packages"
# Try to install packages
if eval "$apk_cmd" 2>&1 | tee /tmp/patchmon_apk_install.log; then
if eval "$_apk_cmd" 2>&1 | tee /tmp/patchmon_apk_install.log; then
success "Packages installed successfully"
return 0
else
warning "Package installation encountered issues, checking if required tools are available..."
# Verify critical dependencies are actually available
local all_ok=true
for pkg in "${packages[@]}"; do
_all_ok=true
for pkg in $_packages; do
if ! command_exists "$pkg"; then
if [ "$FORCE_INSTALL" = "true" ]; then
error "Critical dependency '$pkg' is not available even with --force. Please install manually."
else
error "Critical dependency '$pkg' is not available. Try again with --force flag or install manually: apk add $pkg"
fi
all_ok=false
_all_ok=false
fi
done
if $all_ok; then
if $_all_ok; then
success "All required tools are available despite installation warnings"
return 0
else
@@ -790,7 +781,7 @@ fi
# Installation complete
success "🎉 PatchMon Agent installation completed successfully!"
echo ""
echo -e "${GREEN}📋 Installation Summary:${NC}"
printf "%b\n" "${GREEN}📋 Installation Summary:${NC}"
echo " • Configuration directory: /etc/patchmon"
echo " • Agent binary installed: /usr/local/bin/patchmon-agent"
echo " • Architecture: $ARCHITECTURE"
@@ -810,16 +801,16 @@ echo " • Logs directory: /etc/patchmon/logs"
MOVED_FILES=$(ls /etc/patchmon/credentials.yml.backup.* /etc/patchmon/config.yml.backup.* /usr/local/bin/patchmon-agent.backup.* /etc/patchmon/logs/patchmon-agent.log.old.* /usr/local/bin/patchmon-agent.sh.backup.* /etc/patchmon/credentials.backup.* 2>/dev/null || true)
if [ -n "$MOVED_FILES" ]; then
echo ""
echo -e "${YELLOW}📋 Files Moved for Fresh Installation:${NC}"
printf "%b\n" "${YELLOW}📋 Files Moved for Fresh Installation:${NC}"
echo "$MOVED_FILES" | while read -r moved_file; do
echo "$moved_file"
done
echo ""
echo -e "${BLUE}💡 Note: Old files are automatically cleaned up (keeping last 3)${NC}"
printf "%b\n" "${BLUE}💡 Note: Old files are automatically cleaned up (keeping last 3)${NC}"
fi
echo ""
echo -e "${BLUE}🔧 Management Commands:${NC}"
printf "%b\n" "${BLUE}🔧 Management Commands:${NC}"
echo " • Test connection: /usr/local/bin/patchmon-agent ping"
echo " • Manual report: /usr/local/bin/patchmon-agent report"
echo " • Check status: /usr/local/bin/patchmon-agent diagnostics"

View File

@@ -1,7 +1,8 @@
#!/bin/bash
#!/bin/sh
# PatchMon Agent Removal Script
# Usage: curl -s {PATCHMON_URL}/api/v1/hosts/remove | bash
# POSIX-compliant shell script (works with dash, ash, bash, etc.)
# Usage: curl -s {PATCHMON_URL}/api/v1/hosts/remove | sh
# This script completely removes PatchMon from the system
set -e
@@ -20,24 +21,24 @@ NC='\033[0m' # No Color
# Functions
error() {
echo -e "${RED}❌ ERROR: $1${NC}" >&2
printf "%b\n" "${RED}❌ ERROR: $1${NC}" >&2
exit 1
}
info() {
echo -e "${BLUE} $1${NC}"
printf "%b\n" "${BLUE} $1${NC}"
}
success() {
echo -e "${GREEN}$1${NC}"
printf "%b\n" "${GREEN}$1${NC}"
}
warning() {
echo -e "${YELLOW}⚠️ $1${NC}"
printf "%b\n" "${YELLOW}⚠️ $1${NC}"
}
# Check if running as root
if [[ $EUID -ne 0 ]]; then
if [ "$(id -u)" -ne 0 ]; then
error "This script must be run as root (use sudo)"
fi
@@ -67,7 +68,7 @@ fi
# Step 3: Remove agent script
info "📄 Removing agent script..."
if [[ -f "/usr/local/bin/patchmon-agent.sh" ]]; then
if [ -f "/usr/local/bin/patchmon-agent.sh" ]; then
warning "Removing agent script: /usr/local/bin/patchmon-agent.sh"
rm -f /usr/local/bin/patchmon-agent.sh
success "Agent script removed"
@@ -77,7 +78,7 @@ fi
# Step 4: Remove configuration directory and files
info "📁 Removing configuration files..."
if [[ -d "/etc/patchmon" ]]; then
if [ -d "/etc/patchmon" ]; then
warning "Removing configuration directory: /etc/patchmon"
# Show what's being removed
@@ -95,7 +96,7 @@ fi
# Step 5: Remove log files
info "📝 Removing log files..."
if [[ -f "/var/log/patchmon-agent.log" ]]; then
if [ -f "/var/log/patchmon-agent.log" ]; then
warning "Removing log file: /var/log/patchmon-agent.log"
rm -f /var/log/patchmon-agent.log
success "Log file removed"
@@ -109,29 +110,29 @@ BACKUP_COUNT=0
# Count credential backups
CRED_BACKUPS=$(ls /etc/patchmon/credentials.backup.* 2>/dev/null | wc -l || echo "0")
if [[ $CRED_BACKUPS -gt 0 ]]; then
if [ "$CRED_BACKUPS" -gt 0 ]; then
BACKUP_COUNT=$((BACKUP_COUNT + CRED_BACKUPS))
fi
# Count agent backups
AGENT_BACKUPS=$(ls /usr/local/bin/patchmon-agent.sh.backup.* 2>/dev/null | wc -l || echo "0")
if [[ $AGENT_BACKUPS -gt 0 ]]; then
if [ "$AGENT_BACKUPS" -gt 0 ]; then
BACKUP_COUNT=$((BACKUP_COUNT + AGENT_BACKUPS))
fi
# Count log backups
LOG_BACKUPS=$(ls /var/log/patchmon-agent.log.old.* 2>/dev/null | wc -l || echo "0")
if [[ $LOG_BACKUPS -gt 0 ]]; then
if [ "$LOG_BACKUPS" -gt 0 ]; then
BACKUP_COUNT=$((BACKUP_COUNT + LOG_BACKUPS))
fi
if [[ $BACKUP_COUNT -gt 0 ]]; then
if [ "$BACKUP_COUNT" -gt 0 ]; then
warning "Found $BACKUP_COUNT backup files"
echo ""
echo -e "${YELLOW}📋 Backup files found:${NC}"
printf "%b\n" "${YELLOW}📋 Backup files found:${NC}"
# Show credential backups
if [[ $CRED_BACKUPS -gt 0 ]]; then
if [ "$CRED_BACKUPS" -gt 0 ]; then
echo " Credential backups:"
ls /etc/patchmon/credentials.backup.* 2>/dev/null | while read -r file; do
echo "$file"
@@ -139,7 +140,7 @@ if [[ $BACKUP_COUNT -gt 0 ]]; then
fi
# Show agent backups
if [[ $AGENT_BACKUPS -gt 0 ]]; then
if [ "$AGENT_BACKUPS" -gt 0 ]; then
echo " Agent script backups:"
ls /usr/local/bin/patchmon-agent.sh.backup.* 2>/dev/null | while read -r file; do
echo "$file"
@@ -147,7 +148,7 @@ if [[ $BACKUP_COUNT -gt 0 ]]; then
fi
# Show log backups
if [[ $LOG_BACKUPS -gt 0 ]]; then
if [ "$LOG_BACKUPS" -gt 0 ]; then
echo " Log file backups:"
ls /var/log/patchmon-agent.log.old.* 2>/dev/null | while read -r file; do
echo "$file"
@@ -155,8 +156,8 @@ if [[ $BACKUP_COUNT -gt 0 ]]; then
fi
echo ""
echo -e "${BLUE}💡 Note: Backup files are preserved for safety${NC}"
echo -e "${BLUE}💡 You can remove them manually if not needed${NC}"
printf "%b\n" "${BLUE}💡 Note: Backup files are preserved for safety${NC}"
printf "%b\n" "${BLUE}💡 You can remove them manually if not needed${NC}"
else
info "No backup files found"
fi
@@ -165,16 +166,16 @@ fi
info "📦 Checking for PatchMon-specific dependencies..."
if command -v jq >/dev/null 2>&1; then
warning "jq is installed (used by PatchMon)"
echo -e "${BLUE}💡 Note: jq may be used by other applications${NC}"
echo -e "${BLUE}💡 Consider keeping it unless you're sure it's not needed${NC}"
printf "%b\n" "${BLUE}💡 Note: jq may be used by other applications${NC}"
printf "%b\n" "${BLUE}💡 Consider keeping it unless you're sure it's not needed${NC}"
else
info "jq not found"
fi
if command -v curl >/dev/null 2>&1; then
warning "curl is installed (used by PatchMon)"
echo -e "${BLUE}💡 Note: curl is commonly used by many applications${NC}"
echo -e "${BLUE}💡 Consider keeping it unless you're sure it's not needed${NC}"
printf "%b\n" "${BLUE}💡 Note: curl is commonly used by many applications${NC}"
printf "%b\n" "${BLUE}💡 Consider keeping it unless you're sure it's not needed${NC}"
else
info "curl not found"
fi
@@ -183,15 +184,15 @@ fi
info "🔍 Verifying removal..."
REMAINING_FILES=0
if [[ -f "/usr/local/bin/patchmon-agent.sh" ]]; then
if [ -f "/usr/local/bin/patchmon-agent.sh" ]; then
REMAINING_FILES=$((REMAINING_FILES + 1))
fi
if [[ -d "/etc/patchmon" ]]; then
if [ -d "/etc/patchmon" ]; then
REMAINING_FILES=$((REMAINING_FILES + 1))
fi
if [[ -f "/var/log/patchmon-agent.log" ]]; then
if [ -f "/var/log/patchmon-agent.log" ]; then
REMAINING_FILES=$((REMAINING_FILES + 1))
fi
@@ -199,15 +200,15 @@ if crontab -l 2>/dev/null | grep -q "patchmon-agent.sh"; then
REMAINING_FILES=$((REMAINING_FILES + 1))
fi
if [[ $REMAINING_FILES -eq 0 ]]; then
if [ "$REMAINING_FILES" -eq 0 ]; then
success "✅ PatchMon has been completely removed from the system!"
else
warning "⚠️ Some PatchMon files may still remain ($REMAINING_FILES items)"
echo -e "${BLUE}💡 You may need to remove them manually${NC}"
printf "%b\n" "${BLUE}💡 You may need to remove them manually${NC}"
fi
echo ""
echo -e "${GREEN}📋 Removal Summary:${NC}"
printf "%b\n" "${GREEN}📋 Removal Summary:${NC}"
echo " • Agent script: Removed"
echo " • Configuration files: Removed"
echo " • Log files: Removed"
@@ -215,7 +216,7 @@ echo " • Crontab entries: Removed"
echo " • Running processes: Stopped"
echo " • Backup files: Preserved (if any)"
echo ""
echo -e "${BLUE}🔧 Manual cleanup (if needed):${NC}"
printf "%b\n" "${BLUE}🔧 Manual cleanup (if needed):${NC}"
echo " • Remove backup files: rm /etc/patchmon/credentials.backup.* /usr/local/bin/patchmon-agent.sh.backup.* /var/log/patchmon-agent.log.old.*"
echo " • Remove dependencies: apt remove jq curl (if not needed by other apps)"
echo ""

View File

@@ -567,7 +567,7 @@ router.get("/proxmox-lxc", async (req, res) => {
const force_install = req.query.force === "true" || req.query.force === "1";
// Inject the token credentials, server URL, curl flags, and force flag into the script
const env_vars = `#!/bin/bash
const env_vars = `#!/bin/sh
# PatchMon Auto-Enrollment Configuration (Auto-generated)
export PATCHMON_URL="${server_url}"
export AUTO_ENROLLMENT_KEY="${token.token_key}"

View File

@@ -1682,7 +1682,7 @@ router.get("/install", async (req, res) => {
const archExport = architecture
? `export ARCHITECTURE="${architecture}"\n`
: "";
const envVars = `#!/bin/bash
const envVars = `#!/bin/sh
export PATCHMON_URL="${serverUrl}"
export API_ID="${host.api_id}"
export API_KEY="${host.api_key}"
@@ -1781,7 +1781,7 @@ router.get("/remove", async (_req, res) => {
} catch (_) {}
// Prepend environment for CURL_FLAGS so script can use it if needed
const envPrefix = `#!/bin/bash\nexport CURL_FLAGS="${curlFlags}"\n\n`;
const envPrefix = `#!/bin/sh\nexport CURL_FLAGS="${curlFlags}"\n\n`;
script = script.replace(/^#!/, "#");
script = envPrefix + script;

View File

@@ -1817,7 +1817,7 @@ const CredentialsModal = ({ host, isOpen, onClose }) => {
<div className="flex items-center gap-2">
<input
type="text"
value={`curl ${getCurlFlags()} ${getInstallUrl()} -H "X-API-ID: ${host.api_id}" -H "X-API-KEY: ${host.api_key}" | bash`}
value={`curl ${getCurlFlags()} ${getInstallUrl()} -H "X-API-ID: ${host.api_id}" -H "X-API-KEY: ${host.api_key}" | sh`}
readOnly
className="flex-1 px-3 py-2 border border-primary-300 dark:border-primary-600 rounded-md bg-white dark:bg-secondary-800 text-sm font-mono text-secondary-900 dark:text-white"
/>
@@ -1825,7 +1825,7 @@ const CredentialsModal = ({ host, isOpen, onClose }) => {
type="button"
onClick={() =>
copyToClipboard(
`curl ${getCurlFlags()} ${getInstallUrl()} -H "X-API-ID: ${host.api_id}" -H "X-API-KEY: ${host.api_key}" | bash`,
`curl ${getCurlFlags()} ${getInstallUrl()} -H "X-API-ID: ${host.api_id}" -H "X-API-KEY: ${host.api_key}" | sh`,
)
}
className="btn-primary flex items-center gap-1"
@@ -1835,270 +1835,6 @@ const CredentialsModal = ({ host, isOpen, onClose }) => {
</button>
</div>
</div>
<div className="bg-secondary-50 dark:bg-secondary-700 rounded-lg p-4">
<h4 className="text-sm font-medium text-secondary-900 dark:text-white mb-2">
Manual Installation
</h4>
<p className="text-sm text-secondary-600 dark:text-secondary-300 mb-3">
If you prefer to install manually, follow these steps:
</p>
<div className="space-y-3">
<div className="bg-white dark:bg-secondary-800 rounded-md p-3 border border-secondary-200 dark:border-secondary-600">
<h5 className="text-sm font-medium text-secondary-900 dark:text-white mb-2">
1. Create Configuration Directory
</h5>
<div className="flex items-center gap-2">
<input
type="text"
value="sudo mkdir -p /etc/patchmon"
readOnly
className="flex-1 px-3 py-2 border border-secondary-300 dark:border-secondary-600 rounded-md bg-white dark:bg-secondary-800 text-sm font-mono text-secondary-900 dark:text-white"
/>
<button
type="button"
onClick={() =>
copyToClipboard("sudo mkdir -p /etc/patchmon")
}
className="btn-secondary flex items-center gap-1"
>
<Copy className="h-4 w-4" />
Copy
</button>
</div>
</div>
<div className="bg-white dark:bg-secondary-800 rounded-md p-3 border border-secondary-200 dark:border-secondary-600">
<h5 className="text-sm font-medium text-secondary-900 dark:text-white mb-2">
2. Download and Install Agent Binary
</h5>
<div className="flex items-center gap-2">
<input
type="text"
value={`curl ${getCurlFlags()} -o /usr/local/bin/patchmon-agent ${serverUrl}/api/v1/hosts/agent/download?arch=${architecture} -H "X-API-ID: ${host.api_id}" -H "X-API-KEY: ${host.api_key}" && sudo chmod +x /usr/local/bin/patchmon-agent`}
readOnly
className="flex-1 px-3 py-2 border border-secondary-300 dark:border-secondary-600 rounded-md bg-white dark:bg-secondary-800 text-sm font-mono text-secondary-900 dark:text-white"
/>
<button
type="button"
onClick={() =>
copyToClipboard(
`curl ${getCurlFlags()} -o /usr/local/bin/patchmon-agent ${serverUrl}/api/v1/hosts/agent/download?arch=${architecture} -H "X-API-ID: ${host.api_id}" -H "X-API-KEY: ${host.api_key}" && sudo chmod +x /usr/local/bin/patchmon-agent`,
)
}
className="btn-secondary flex items-center gap-1"
>
<Copy className="h-4 w-4" />
Copy
</button>
</div>
</div>
<div className="bg-white dark:bg-secondary-800 rounded-md p-3 border border-secondary-200 dark:border-secondary-600">
<h5 className="text-sm font-medium text-secondary-900 dark:text-white mb-2">
3. Configure Credentials
</h5>
<div className="flex items-center gap-2">
<input
type="text"
value={`sudo /usr/local/bin/patchmon-agent config set-api "${host.api_id}" "${host.api_key}" "${serverUrl}"`}
readOnly
className="flex-1 px-3 py-2 border border-secondary-300 dark:border-secondary-600 rounded-md bg-white dark:bg-secondary-800 text-sm font-mono text-secondary-900 dark:text-white"
/>
<button
type="button"
onClick={() =>
copyToClipboard(
`sudo /usr/local/bin/patchmon-agent config set-api "${host.api_id}" "${host.api_key}" "${serverUrl}"`,
)
}
className="btn-secondary flex items-center gap-1"
>
<Copy className="h-4 w-4" />
Copy
</button>
</div>
</div>
<div className="bg-white dark:bg-secondary-800 rounded-md p-3 border border-secondary-200 dark:border-secondary-600">
<h5 className="text-sm font-medium text-secondary-900 dark:text-white mb-2">
4. Test Configuration
</h5>
<div className="flex items-center gap-2">
<input
type="text"
value="sudo /usr/local/bin/patchmon-agent ping"
readOnly
className="flex-1 px-3 py-2 border border-secondary-300 dark:border-secondary-600 rounded-md bg-white dark:bg-secondary-800 text-sm font-mono text-secondary-900 dark:text-white"
/>
<button
type="button"
onClick={() =>
copyToClipboard(
"sudo /usr/local/bin/patchmon-agent ping",
)
}
className="btn-secondary flex items-center gap-1"
>
<Copy className="h-4 w-4" />
Copy
</button>
</div>
</div>
<div className="bg-white dark:bg-secondary-800 rounded-md p-3 border border-secondary-200 dark:border-secondary-600">
<h5 className="text-sm font-medium text-secondary-900 dark:text-white mb-2">
5. Send Initial Data
</h5>
<div className="flex items-center gap-2">
<input
type="text"
value="sudo /usr/local/bin/patchmon-agent report"
readOnly
className="flex-1 px-3 py-2 border border-secondary-300 dark:border-secondary-600 rounded-md bg-white dark:bg-secondary-800 text-sm font-mono text-secondary-900 dark:text-white"
/>
<button
type="button"
onClick={() =>
copyToClipboard(
"sudo /usr/local/bin/patchmon-agent report",
)
}
className="btn-secondary flex items-center gap-1"
>
<Copy className="h-4 w-4" />
Copy
</button>
</div>
</div>
<div className="bg-white dark:bg-secondary-800 rounded-md p-3 border border-secondary-200 dark:border-secondary-600">
<h5 className="text-sm font-medium text-secondary-900 dark:text-white mb-2">
6. Create Systemd Service File
</h5>
<div className="flex items-center gap-2">
<input
type="text"
value={`sudo tee /etc/systemd/system/patchmon-agent.service > /dev/null << 'EOF'
[Unit]
Description=PatchMon Agent Service
After=network.target
Wants=network.target
[Service]
Type=simple
User=root
ExecStart=/usr/local/bin/patchmon-agent serve
Restart=always
RestartSec=10
WorkingDirectory=/etc/patchmon
# Logging
StandardOutput=journal
StandardError=journal
SyslogIdentifier=patchmon-agent
[Install]
WantedBy=multi-user.target
EOF`}
readOnly
className="flex-1 px-3 py-2 border border-secondary-300 dark:border-secondary-600 rounded-md bg-white dark:bg-secondary-800 text-sm font-mono text-secondary-900 dark:text-white"
/>
<button
type="button"
onClick={() =>
copyToClipboard(
`sudo tee /etc/systemd/system/patchmon-agent.service > /dev/null << 'EOF'
[Unit]
Description=PatchMon Agent Service
After=network.target
Wants=network.target
[Service]
Type=simple
User=root
ExecStart=/usr/local/bin/patchmon-agent serve
Restart=always
RestartSec=10
WorkingDirectory=/etc/patchmon
# Logging
StandardOutput=journal
StandardError=journal
SyslogIdentifier=patchmon-agent
[Install]
WantedBy=multi-user.target
EOF`,
)
}
className="btn-secondary flex items-center gap-1"
>
<Copy className="h-4 w-4" />
Copy
</button>
</div>
</div>
<div className="bg-white dark:bg-secondary-800 rounded-md p-3 border border-secondary-200 dark:border-secondary-600">
<h5 className="text-sm font-medium text-secondary-900 dark:text-white mb-2">
7. Enable and Start Service
</h5>
<div className="flex items-center gap-2">
<input
type="text"
value="sudo systemctl daemon-reload && sudo systemctl enable patchmon-agent && sudo systemctl start patchmon-agent"
readOnly
className="flex-1 px-3 py-2 border border-secondary-300 dark:border-secondary-600 rounded-md bg-white dark:bg-secondary-800 text-sm font-mono text-secondary-900 dark:text-white"
/>
<button
type="button"
onClick={() =>
copyToClipboard(
"sudo systemctl daemon-reload && sudo systemctl enable patchmon-agent && sudo systemctl start patchmon-agent",
)
}
className="btn-secondary flex items-center gap-1"
>
<Copy className="h-4 w-4" />
Copy
</button>
</div>
<p className="text-xs text-secondary-600 dark:text-secondary-400 mt-2">
This will start the agent service and establish WebSocket
connection for real-time communication
</p>
</div>
<div className="bg-white dark:bg-secondary-800 rounded-md p-3 border border-secondary-200 dark:border-secondary-600">
<h5 className="text-sm font-medium text-secondary-900 dark:text-white mb-2">
8. Verify Service Status
</h5>
<div className="flex items-center gap-2">
<input
type="text"
value="sudo systemctl status patchmon-agent"
readOnly
className="flex-1 px-3 py-2 border border-secondary-300 dark:border-secondary-600 rounded-md bg-white dark:bg-secondary-800 text-sm font-mono text-secondary-900 dark:text-white"
/>
<button
type="button"
onClick={() =>
copyToClipboard("sudo systemctl status patchmon-agent")
}
className="btn-secondary flex items-center gap-1"
>
<Copy className="h-4 w-4" />
Copy
</button>
</div>
<p className="text-xs text-secondary-600 dark:text-secondary-400 mt-2">
Check that the service is running and WebSocket connection
is established
</p>
</div>
</div>
</div>
</div>
)}

View File

@@ -1669,7 +1669,7 @@ const Integrations = () => {
<div className="flex items-center gap-2">
<input
type="text"
value={`curl -s "${getProxmoxUrl()}" | bash`}
value={`curl -s "${getProxmoxUrl()}" | sh`}
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"
/>
@@ -1677,7 +1677,7 @@ const Integrations = () => {
type="button"
onClick={() =>
copy_to_clipboard(
`curl -s "${getProxmoxUrl()}" | bash`,
`curl -s "${getProxmoxUrl()}" | sh`,
"curl-command",
)
}