Relaid out settings page and configured agent and other communication to use curl flags which can be configured to ignore ssl cert if self-hosted.

This commit is contained in:
Muhammad Ibrahim
2025-09-30 21:38:13 +01:00
parent ed0cf79b53
commit 8be25283dc
19 changed files with 214 additions and 63 deletions

View File

@@ -11,6 +11,11 @@ CONFIG_FILE="/etc/patchmon/agent.conf"
CREDENTIALS_FILE="/etc/patchmon/credentials"
LOG_FILE="/var/log/patchmon-agent.log"
# This placeholder will be dynamically replaced by the server when serving this
# script based on the "ignore SSL self-signed" setting. If set to -k, curl will
# ignore certificate validation. Otherwise, it will be empty for secure default.
CURL_FLAGS=""
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
@@ -144,7 +149,7 @@ EOF
test_credentials() {
load_credentials
local response=$(curl -ks -X POST \
local response=$(curl $CURL_FLAGS -X POST \
-H "Content-Type: application/json" \
-H "X-API-ID: $API_ID" \
-H "X-API-KEY: $API_KEY" \
@@ -809,7 +814,7 @@ EOF
local payload=$(echo "$base_payload $merged_json" | jq -s '.[0] * .[1]')
local response=$(curl -ks -X POST \
local response=$(curl $CURL_FLAGS -X POST \
-H "Content-Type: application/json" \
-H "X-API-ID: $API_ID" \
-H "X-API-KEY: $API_KEY" \
@@ -870,7 +875,7 @@ EOF
ping_server() {
load_credentials
local response=$(curl -ks -X POST \
local response=$(curl $CURL_FLAGS -X POST \
-H "Content-Type: application/json" \
-H "X-API-ID: $API_ID" \
-H "X-API-KEY: $API_KEY" \
@@ -913,7 +918,7 @@ check_version() {
info "Checking for agent updates..."
local response=$(curl -ks -H "X-API-ID: $API_ID" -H "X-API-KEY: $API_KEY" -X GET "$PATCHMON_SERVER/api/$API_VERSION/hosts/agent/version")
local response=$(curl $CURL_FLAGS -H "X-API-ID: $API_ID" -H "X-API-KEY: $API_KEY" -X GET "$PATCHMON_SERVER/api/$API_VERSION/hosts/agent/version")
if [[ $? -eq 0 ]]; then
local current_version=$(echo "$response" | grep -o '"currentVersion":"[^"]*' | cut -d'"' -f4)
@@ -945,7 +950,7 @@ check_version() {
# Check if auto-update is enabled (both globally and for this host)
check_auto_update_enabled() {
# Get settings from server using API credentials
local response=$(curl -ks -H "X-API-ID: $API_ID" -H "X-API-KEY: $API_KEY" -X GET "$PATCHMON_SERVER/api/$API_VERSION/hosts/settings" 2>/dev/null)
local response=$(curl $CURL_FLAGS -H "X-API-ID: $API_ID" -H "X-API-KEY: $API_KEY" -X GET "$PATCHMON_SERVER/api/$API_VERSION/hosts/settings" 2>/dev/null)
if [[ $? -ne 0 ]]; then
return 1
fi
@@ -970,7 +975,7 @@ check_agent_update_needed() {
fi
# Get server agent info using API credentials
local response=$(curl -ks -H "X-API-ID: $API_ID" -H "X-API-KEY: $API_KEY" -X GET "$PATCHMON_SERVER/api/$API_VERSION/hosts/agent/timestamp" 2>/dev/null)
local response=$(curl $CURL_FLAGS -H "X-API-ID: $API_ID" -H "X-API-KEY: $API_KEY" -X GET "$PATCHMON_SERVER/api/$API_VERSION/hosts/agent/timestamp" 2>/dev/null)
if [[ $? -eq 0 ]]; then
local server_version=$(echo "$response" | grep -o '"version":"[^"]*' | cut -d'"' -f4)
@@ -1007,7 +1012,7 @@ check_agent_update() {
fi
# Get server agent info using API credentials
local response=$(curl -ks -H "X-API-ID: $API_ID" -H "X-API-KEY: $API_KEY" -X GET "$PATCHMON_SERVER/api/$API_VERSION/hosts/agent/timestamp")
local response=$(curl $CURL_FLAGS -H "X-API-ID: $API_ID" -H "X-API-KEY: $API_KEY" -X GET "$PATCHMON_SERVER/api/$API_VERSION/hosts/agent/timestamp")
if [[ $? -eq 0 ]]; then
local server_version=$(echo "$response" | grep -o '"version":"[^"]*' | cut -d'"' -f4)
@@ -1057,7 +1062,7 @@ update_agent() {
cp "$0" "$backup_file"
# Download new version using API credentials
if curl -ks -H "X-API-ID: $API_ID" -H "X-API-KEY: $API_KEY" -o "/tmp/patchmon-agent-new.sh" "$download_url"; then
if curl $CURL_FLAGS -H "X-API-ID: $API_ID" -H "X-API-KEY: $API_KEY" -o "/tmp/patchmon-agent-new.sh" "$download_url"; then
# Verify the downloaded script is valid
if bash -n "/tmp/patchmon-agent-new.sh" 2>/dev/null; then
# Replace current script
@@ -1090,7 +1095,7 @@ update_agent() {
update_crontab() {
load_credentials
info "Updating crontab with current policy..."
local response=$(curl -ks -H "X-API-ID: $API_ID" -H "X-API-KEY: $API_KEY" -X GET "$PATCHMON_SERVER/api/$API_VERSION/settings/update-interval")
local response=$(curl $CURL_FLAGS -H "X-API-ID: $API_ID" -H "X-API-KEY: $API_KEY" -X GET "$PATCHMON_SERVER/api/$API_VERSION/settings/update-interval")
if [[ $? -eq 0 ]]; then
local update_interval=$(echo "$response" | grep -o '"updateInterval":[0-9]*' | cut -d':' -f2)
# Fallback if not found

View File

@@ -1,10 +1,15 @@
#!/bin/bash
# PatchMon Agent Installation Script
# Usage: curl -ks {PATCHMON_URL}/api/v1/hosts/install -H "X-API-ID: {API_ID}" -H "X-API-KEY: {API_KEY}" | bash
# Usage: curl -s {PATCHMON_URL}/api/v1/hosts/install -H "X-API-ID: {API_ID}" -H "X-API-KEY: {API_KEY}" | bash
set -e
# This placeholder will be dynamically replaced by the server when serving this
# script based on the "ignore SSL self-signed" setting. If set to -k, curl will
# ignore certificate validation. Otherwise, it will be empty for secure default.
CURL_FLAGS=""
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
@@ -145,7 +150,7 @@ if [[ -f "/usr/local/bin/patchmon-agent.sh" ]]; then
info "📋 Moved existing agent to: /usr/local/bin/patchmon-agent.sh.backup.$(date +%Y%m%d_%H%M%S)"
fi
curl -ks \
curl $CURL_FLAGS \
-H "X-API-ID: $API_ID" \
-H "X-API-KEY: $API_KEY" \
"$PATCHMON_URL/api/v1/hosts/agent/download" \
@@ -185,7 +190,7 @@ fi
# Step 6: Get update interval policy from server and setup crontab
info "⏰ Getting update interval policy from server..."
UPDATE_INTERVAL=$(curl -ks \
UPDATE_INTERVAL=$(curl $CURL_FLAGS \
-H "X-API-ID: $API_ID" \
-H "X-API-KEY: $API_KEY" \
"$PATCHMON_URL/api/v1/settings/update-interval" | \

View File

@@ -1,11 +1,16 @@
#!/bin/bash
# PatchMon Agent Removal Script
# Usage: curl -ks {PATCHMON_URL}/api/v1/hosts/remove | bash
# Usage: curl -s {PATCHMON_URL}/api/v1/hosts/remove | bash
# This script completely removes PatchMon from the system
set -e
# This placeholder will be dynamically replaced by the server when serving this
# script based on the "ignore SSL self-signed" setting for any curl calls in
# future (left for consistency with install script).
CURL_FLAGS=""
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'

View File

@@ -0,0 +1,10 @@
-- Fix dashboard preferences unique constraint
-- This migration fixes the unique constraint on dashboard_preferences table
-- Drop existing indexes if they exist
DROP INDEX IF EXISTS "dashboard_preferences_card_id_key";
DROP INDEX IF EXISTS "dashboard_preferences_user_id_card_id_key";
DROP INDEX IF EXISTS "dashboard_preferences_user_id_key";
-- Add the correct unique constraint
ALTER TABLE "dashboard_preferences" ADD CONSTRAINT "dashboard_preferences_user_id_card_id_key" UNIQUE ("user_id", "card_id");

View File

@@ -0,0 +1,4 @@
-- Add ignore_ssl_self_signed column to settings table
-- This allows users to configure whether curl commands should ignore SSL certificate validation
ALTER TABLE "settings" ADD COLUMN "ignore_ssl_self_signed" BOOLEAN NOT NULL DEFAULT false;

View File

@@ -7,7 +7,6 @@ datasource db {
url = env("DATABASE_URL")
}
model dashboard_preferences {
id String @id
user_id String
@@ -157,6 +156,7 @@ model settings {
update_available Boolean @default(false)
signup_enabled Boolean @default(false)
default_user_role String @default("user")
ignore_ssl_self_signed Boolean @default(false)
}
model update_history {
@@ -175,8 +175,6 @@ model users {
username String @unique
email String @unique
password_hash String
first_name String?
last_name String?
role String @default("admin")
is_active Boolean @default(true)
last_login DateTime?
@@ -185,5 +183,7 @@ model users {
tfa_backup_codes String?
tfa_enabled Boolean @default(false)
tfa_secret String?
first_name String?
last_name String?
dashboard_preferences dashboard_preferences[]
}

View File

@@ -45,11 +45,26 @@ router.get("/agent/download", async (req, res) => {
}
// Read file and convert line endings
const scriptContent = fs
let scriptContent = fs
.readFileSync(agentPath, "utf8")
.replace(/\r\n/g, "\n")
.replace(/\r/g, "\n");
// Determine curl flags dynamically from settings for consistency
let curlFlags = "-s";
try {
const settings = await prisma.settings.findFirst();
if (settings && settings.ignore_ssl_self_signed === true) {
curlFlags = "-sk";
}
} catch (_) {}
// Inject the curl flags into the script
scriptContent = scriptContent.replace(
'CURL_FLAGS=""',
`CURL_FLAGS="${curlFlags}"`,
);
res.setHeader("Content-Type", "application/x-shellscript");
res.setHeader(
"Content-Disposition",
@@ -1101,11 +1116,21 @@ router.get("/install", async (req, res) => {
);
}
// Inject the API credentials and server URL into the script as environment variables
// Determine curl flags dynamically from settings (ignore self-signed)
let curlFlags = "-s";
try {
const settings = await prisma.settings.findFirst();
if (settings && settings.ignore_ssl_self_signed === true) {
curlFlags = "-sk";
}
} catch (_) {}
// Inject the API credentials, server URL, and curl flags into the script
const envVars = `#!/bin/bash
export PATCHMON_URL="${serverUrl}"
export API_ID="${host.api_id}"
export API_KEY="${host.api_key}"
export CURL_FLAGS="${curlFlags}"
`;
@@ -1141,7 +1166,24 @@ router.get("/remove", async (_req, res) => {
}
// Read the script content
const script = fs.readFileSync(scriptPath, "utf8");
let script = fs.readFileSync(scriptPath, "utf8");
// Convert line endings
script = script.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
// Determine curl flags dynamically from settings for consistency
let curlFlags = "-s";
try {
const settings = await prisma.settings.findFirst();
if (settings && settings.ignore_ssl_self_signed === true) {
curlFlags = "-sk";
}
} catch (_) {}
// Prepend environment for CURL_FLAGS so script can use it if needed
const envPrefix = `#!/bin/bash\nexport CURL_FLAGS="${curlFlags}"\n\n`;
script = script.replace(/^#!/, "#");
script = envPrefix + script;
// Set appropriate headers for script download
res.setHeader("Content-Type", "text/plain");

View File

@@ -165,19 +165,31 @@ router.put(
requireManageSettings,
[
body("serverProtocol")
.optional()
.isIn(["http", "https"])
.withMessage("Protocol must be http or https"),
body("serverHost")
.optional()
.isLength({ min: 1 })
.withMessage("Server host is required"),
body("serverPort")
.optional()
.isInt({ min: 1, max: 65535 })
.withMessage("Port must be between 1 and 65535"),
body("updateInterval")
.optional()
.isInt({ min: 5, max: 1440 })
.withMessage("Update interval must be between 5 and 1440 minutes"),
body("autoUpdate").isBoolean().withMessage("Auto update must be a boolean"),
body("autoUpdate")
.optional()
.isBoolean()
.withMessage("Auto update must be a boolean"),
body("ignoreSslSelfSigned")
.optional()
.isBoolean()
.withMessage("Ignore SSL self-signed must be a boolean"),
body("signupEnabled")
.optional()
.isBoolean()
.withMessage("Signup enabled must be a boolean"),
body("defaultUserRole")
@@ -218,6 +230,7 @@ router.put(
serverPort,
updateInterval,
autoUpdate,
ignoreSslSelfSigned,
signupEnabled,
defaultUserRole,
githubRepoUrl,
@@ -229,32 +242,43 @@ router.put(
const currentSettings = await getSettings();
const oldUpdateInterval = currentSettings.update_interval;
// Update settings using the service
const normalizedInterval = normalizeUpdateInterval(updateInterval || 60);
// Build update object with only provided fields
const updateData = {};
const updatedSettings = await updateSettings(currentSettings.id, {
server_protocol: serverProtocol,
server_host: serverHost,
server_port: serverPort,
update_interval: normalizedInterval,
auto_update: autoUpdate || false,
signup_enabled: signupEnabled || false,
default_user_role:
defaultUserRole || process.env.DEFAULT_USER_ROLE || "user",
github_repo_url:
githubRepoUrl !== undefined
? githubRepoUrl
: "git@github.com:9technologygroup/patchmon.net.git",
repository_type: repositoryType || "public",
ssh_key_path: sshKeyPath || null,
});
if (serverProtocol !== undefined)
updateData.server_protocol = serverProtocol;
if (serverHost !== undefined) updateData.server_host = serverHost;
if (serverPort !== undefined) updateData.server_port = serverPort;
if (updateInterval !== undefined) {
updateData.update_interval = normalizeUpdateInterval(updateInterval);
}
if (autoUpdate !== undefined) updateData.auto_update = autoUpdate;
if (ignoreSslSelfSigned !== undefined)
updateData.ignore_ssl_self_signed = ignoreSslSelfSigned;
if (signupEnabled !== undefined)
updateData.signup_enabled = signupEnabled;
if (defaultUserRole !== undefined)
updateData.default_user_role = defaultUserRole;
if (githubRepoUrl !== undefined)
updateData.github_repo_url = githubRepoUrl;
if (repositoryType !== undefined)
updateData.repository_type = repositoryType;
if (sshKeyPath !== undefined) updateData.ssh_key_path = sshKeyPath;
const updatedSettings = await updateSettings(
currentSettings.id,
updateData,
);
console.log("Settings updated successfully:", updatedSettings);
// If update interval changed, trigger crontab updates on all hosts with auto-update enabled
if (oldUpdateInterval !== normalizedInterval) {
if (
updateInterval !== undefined &&
oldUpdateInterval !== updateData.update_interval
) {
console.log(
`Update interval changed from ${oldUpdateInterval} to ${normalizedInterval} minutes. Triggering crontab updates...`,
`Update interval changed from ${oldUpdateInterval} to ${updateData.update_interval} minutes. Triggering crontab updates...`,
);
await triggerCrontabUpdates();
}

View File

@@ -43,6 +43,7 @@ async function createSettingsFromEnvironment() {
update_interval: 60,
auto_update: false,
signup_enabled: false,
ignore_ssl_self_signed: false,
updated_at: new Date(),
},
});

View File

@@ -6,7 +6,6 @@ import {
ChevronRight,
Clock,
Container,
FileText,
GitBranch,
Github,
Globe,
@@ -23,8 +22,6 @@ import {
Shield,
Star,
UserCircle,
Users,
Wrench,
X,
} from "lucide-react";
import { useCallback, useEffect, useRef, useState } from "react";

View File

@@ -1,13 +1,10 @@
import {
AlertTriangle,
ArrowLeft,
Bell,
ChevronLeft,
ChevronRight,
Code,
Folder,
RefreshCw,
Server,
Settings,
Shield,
UserCircle,
@@ -121,7 +118,7 @@ const SettingsLayout = ({ children }) => {
const isActive = (path) => location.pathname === path;
const getPageTitle = () => {
const _getPageTitle = () => {
const path = location.pathname;
if (path.startsWith("/settings/users")) return "Users";

View File

@@ -1,7 +1,7 @@
import { useMutation, useQuery } from "@tanstack/react-query";
import { AlertCircle, Code, Download, Plus, Shield, X } from "lucide-react";
import { useId, useState } from "react";
import { agentFileAPI } from "../../utils/api";
import { agentFileAPI, settingsAPI } from "../../utils/api";
const AgentManagementTab = () => {
const scriptFileId = useId();
@@ -19,6 +19,17 @@ const AgentManagementTab = () => {
queryFn: () => agentFileAPI.getInfo().then((res) => res.data),
});
// Fetch settings for dynamic curl flags
const { data: settings } = useQuery({
queryKey: ["settings"],
queryFn: () => settingsAPI.get().then((res) => res.data),
});
// Helper function to get curl flags based on settings
const getCurlFlags = () => {
return settings?.ignore_ssl_self_signed ? "-sk" : "-s";
};
const uploadAgentMutation = useMutation({
mutationFn: (scriptContent) =>
agentFileAPI.upload(scriptContent).then((res) => res.data),
@@ -171,13 +182,13 @@ const AgentManagementTab = () => {
</p>
<div className="flex items-center gap-2">
<div className="bg-red-100 dark:bg-red-800 rounded p-2 font-mono text-xs flex-1">
curl -ks {window.location.origin}
curl {getCurlFlags()} {window.location.origin}
/api/v1/hosts/remove | sudo bash
</div>
<button
type="button"
onClick={() => {
const command = `curl -ks ${window.location.origin}/api/v1/hosts/remove | sudo bash`;
const command = `curl ${getCurlFlags()} ${window.location.origin}/api/v1/hosts/remove | sudo bash`;
navigator.clipboard.writeText(command);
// You could add a toast notification here
}}

View File

@@ -8,11 +8,13 @@ const AgentUpdatesTab = () => {
const autoUpdateId = useId();
const signupEnabledId = useId();
const defaultRoleId = useId();
const ignoreSslId = useId();
const [formData, setFormData] = useState({
updateInterval: 60,
autoUpdate: false,
signupEnabled: false,
defaultUserRole: "user",
ignoreSslSelfSigned: false,
});
const [errors, setErrors] = useState({});
const [isDirty, setIsDirty] = useState(false);
@@ -41,8 +43,9 @@ const AgentUpdatesTab = () => {
const newFormData = {
updateInterval: settings.update_interval || 60,
autoUpdate: settings.auto_update || false,
signupEnabled: settings.signup_enabled === true ? true : false,
signupEnabled: settings.signup_enabled === true,
defaultUserRole: settings.default_user_role || "user",
ignoreSslSelfSigned: settings.ignore_ssl_self_signed === true,
};
setFormData(newFormData);
setIsDirty(false);
@@ -300,6 +303,31 @@ const AgentUpdatesTab = () => {
</p>
</div>
{/* SSL Certificate Setting */}
<div>
<label className="block text-sm font-medium text-secondary-700 dark:text-secondary-200 mb-2">
<div className="flex items-center gap-2">
<input
id={ignoreSslId}
type="checkbox"
checked={formData.ignoreSslSelfSigned}
onChange={(e) =>
handleInputChange("ignoreSslSelfSigned", e.target.checked)
}
className="rounded border-secondary-300 text-primary-600 shadow-sm focus:border-primary-300 focus:ring focus:ring-primary-200 focus:ring-opacity-50"
/>
<label htmlFor={ignoreSslId}>
Ignore SSL Self-Signed Certificates
</label>
</div>
</label>
<p className="mt-1 text-sm text-secondary-500 dark:text-secondary-400">
When enabled, curl commands in agent scripts will use the -k flag to
ignore SSL certificate validation errors. Use with caution on
production systems as this reduces security.
</p>
</div>
{/* User Signup Setting */}
<div>
<label className="block text-sm font-medium text-secondary-700 dark:text-secondary-200 mb-2">

View File

@@ -6,8 +6,6 @@ import {
Download,
Edit,
Package,
Plus,
RefreshCw,
Save,
Server,
Settings,

View File

@@ -5,7 +5,6 @@ import {
Edit,
Key,
Mail,
Plus,
Shield,
Trash2,
User,

View File

@@ -59,6 +59,17 @@ const HostDetail = () => {
refetchOnWindowFocus: false, // Don't refetch when window regains focus
});
// Fetch settings for dynamic curl flags
const { data: settings } = useQuery({
queryKey: ["settings"],
queryFn: () => settingsAPI.get().then((res) => res.data),
});
// Helper function to get curl flags based on settings
const getCurlFlags = () => {
return settings?.ignore_ssl_self_signed ? "-sk" : "-s";
};
// Tab change handler
const handleTabChange = (tabName) => {
setActiveTab(tabName);
@@ -419,7 +430,7 @@ const HostDetail = () => {
<div>
<p className="text-xs text-secondary-500 dark:text-secondary-300 mb-1.5">
Auto-update
Agent Auto-update
</p>
<button
type="button"
@@ -993,6 +1004,16 @@ const CredentialsModal = ({ host, isOpen, onClose }) => {
const serverUrl = serverUrlData?.server_url || "http://localhost:3001";
// Fetch settings for dynamic curl flags (local to modal)
const { data: settings } = useQuery({
queryKey: ["settings"],
queryFn: () => settingsAPI.get().then((res) => res.data),
});
const getCurlFlags = () => {
return settings?.ignore_ssl_self_signed ? "-ks" : "-s";
};
const copyToClipboard = async (text) => {
try {
// Try modern clipboard API first
@@ -1089,7 +1110,7 @@ const CredentialsModal = ({ host, isOpen, onClose }) => {
<div className="flex items-center gap-2">
<input
type="text"
value={`curl -ks ${serverUrl}/api/v1/hosts/install -H "X-API-ID: ${host.api_id}" -H "X-API-KEY: ${host.api_key}" | bash`}
value={`curl ${getCurlFlags()} ${serverUrl}/api/v1/hosts/install -H "X-API-ID: ${host.api_id}" -H "X-API-KEY: ${host.api_key}" | bash`}
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"
/>
@@ -1097,7 +1118,7 @@ const CredentialsModal = ({ host, isOpen, onClose }) => {
type="button"
onClick={() =>
copyToClipboard(
`curl -ks ${serverUrl}/api/v1/hosts/install -H "X-API-ID: ${host.api_id}" -H "X-API-KEY: ${host.api_key}" | bash`,
`curl ${getCurlFlags()} ${serverUrl}/api/v1/hosts/install -H "X-API-ID: ${host.api_id}" -H "X-API-KEY: ${host.api_key}" | bash`,
)
}
className="btn-primary flex items-center gap-1"
@@ -1147,7 +1168,7 @@ const CredentialsModal = ({ host, isOpen, onClose }) => {
<div className="flex items-center gap-2">
<input
type="text"
value={`curl -ko /usr/local/bin/patchmon-agent.sh ${serverUrl}/api/v1/hosts/agent/download -H "X-API-ID: ${host.api_id}" -H "X-API-KEY: ${host.api_key}" && sudo chmod +x /usr/local/bin/patchmon-agent.sh`}
value={`curl ${getCurlFlags()} -o /usr/local/bin/patchmon-agent.sh ${serverUrl}/api/v1/hosts/agent/download -H "X-API-ID: ${host.api_id}" -H "X-API-KEY: ${host.api_key}" && sudo chmod +x /usr/local/bin/patchmon-agent.sh`}
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"
/>
@@ -1155,7 +1176,7 @@ const CredentialsModal = ({ host, isOpen, onClose }) => {
type="button"
onClick={() =>
copyToClipboard(
`curl -ko /usr/local/bin/patchmon-agent.sh ${serverUrl}/api/v1/hosts/agent/download -H "X-API-ID: ${host.api_id}" -H "X-API-KEY: ${host.api_key}" && sudo chmod +x /usr/local/bin/patchmon-agent.sh`,
`curl ${getCurlFlags()} -o /usr/local/bin/patchmon-agent.sh ${serverUrl}/api/v1/hosts/agent/download -H "X-API-ID: ${host.api_id}" -H "X-API-KEY: ${host.api_key}" && sudo chmod +x /usr/local/bin/patchmon-agent.sh`,
)
}
className="btn-secondary flex items-center gap-1"

View File

@@ -11,7 +11,6 @@ import {
Moon,
RefreshCw,
Save,
Settings,
Shield,
Smartphone,
Sun,

View File

@@ -108,6 +108,11 @@ const Settings = () => {
queryFn: () => settingsAPI.get().then((res) => res.data),
});
// Helper function to get curl flags based on settings
const getCurlFlags = () => {
return settings?.ignore_ssl_self_signed ? "-sk" : "-s";
};
// Fetch available roles for default user role dropdown
const { data: roles, isLoading: rolesLoading } = useQuery({
queryKey: ["rolePermissions"],
@@ -938,13 +943,13 @@ const Settings = () => {
</p>
<div className="flex items-center gap-2">
<div className="bg-red-100 dark:bg-red-800 rounded p-2 font-mono text-xs flex-1">
curl -ks {window.location.origin}
curl {getCurlFlags()} {window.location.origin}
/api/v1/hosts/remove | sudo bash
</div>
<button
type="button"
onClick={() => {
const command = `curl -ks ${window.location.origin}/api/v1/hosts/remove | sudo bash`;
const command = `curl ${getCurlFlags()} ${window.location.origin}/api/v1/hosts/remove | sudo bash`;
navigator.clipboard.writeText(command);
// You could add a toast notification here
}}

View File

@@ -76,7 +76,7 @@ const SettingsHostGroups = () => {
updateMutation.mutate({ id: selectedGroup.id, data });
};
const handleDeleteClick = (group) => {
const _handleDeleteClick = (group) => {
setGroupToDelete(group);
setShowDeleteModal(true);
};