fix(frontend): variable unused

This commit is contained in:
tigattack
2025-09-25 01:07:11 +01:00
parent 0414ea39d0
commit 42f6971da7

View File

@@ -9,11 +9,8 @@ import {
ChevronDown, ChevronDown,
Clock, Clock,
Columns, Columns,
Copy,
ExternalLink, ExternalLink,
Eye,
Eye as EyeIcon, Eye as EyeIcon,
EyeOff,
EyeOff as EyeOffIcon, EyeOff as EyeOffIcon,
Filter, Filter,
GripVertical, GripVertical,
@@ -35,7 +32,6 @@ import {
dashboardAPI, dashboardAPI,
formatRelativeTime, formatRelativeTime,
hostGroupsAPI, hostGroupsAPI,
settingsAPI,
} from "../utils/api"; } from "../utils/api";
import { OSIcon } from "../utils/osIcons.jsx"; import { OSIcon } from "../utils/osIcons.jsx";
@@ -232,565 +228,6 @@ const AddHostModal = ({ isOpen, onClose, onSuccess }) => {
); );
}; };
// Credentials Modal Component
const CredentialsModal = ({ host, isOpen, onClose }) => {
const apiIdId = useId();
const apiKeyId = useId();
const [showApiKey, setShowApiKey] = useState(false);
const [activeTab, setActiveTab] = useState(
host?.isNewHost ? "quick" : "credentials",
);
// Update active tab when host changes
React.useEffect(() => {
if (host?.isNewHost) {
setActiveTab("quick");
} else {
setActiveTab("credentials");
}
}, [host?.isNewHost]);
const copyToClipboard = async (text, label) => {
try {
// Try modern clipboard API first
if (navigator.clipboard && window.isSecureContext) {
await navigator.clipboard.writeText(text);
alert(`${label} copied to clipboard!`);
return;
}
// Fallback for older browsers or non-secure contexts
const textArea = document.createElement("textarea");
textArea.value = text;
textArea.style.position = "fixed";
textArea.style.left = "-999999px";
textArea.style.top = "-999999px";
document.body.appendChild(textArea);
textArea.focus();
textArea.select();
try {
const successful = document.execCommand("copy");
if (successful) {
alert(`${label} copied to clipboard!`);
} else {
throw new Error("Copy command failed");
}
} catch {
// If all else fails, show the text in a prompt
prompt(`Copy this ${label.toLowerCase()}:`, text);
} finally {
document.body.removeChild(textArea);
}
} catch (err) {
console.error("Failed to copy to clipboard:", err);
// Show the text in a prompt as last resort
prompt(`Copy this ${label.toLowerCase()}:`, text);
}
};
// Fetch server URL from settings
const { data: settings } = useQuery({
queryKey: ["settings"],
queryFn: () => settingsAPI.get().then((res) => res.data),
enabled: isOpen, // Only fetch when modal is open
});
const serverUrl =
settings?.server_url || window.location.origin.replace(":3000", ":3001");
const getSetupCommands = () => {
// Get current time for crontab scheduling
const now = new Date();
const currentMinute = now.getMinutes();
const currentHour = now.getHours();
return {
oneLine: `curl -sSL ${serverUrl}/api/v1/hosts/install | bash -s -- ${serverUrl} "${host?.api_id}" "${host?.api_key}"`,
download: `# Download and setup PatchMon agent
curl -o /tmp/patchmon-agent.sh ${serverUrl}/api/v1/hosts/agent/download
sudo mkdir -p /etc/patchmon
sudo mv /tmp/patchmon-agent.sh /usr/local/bin/patchmon-agent.sh
sudo chmod +x /usr/local/bin/patchmon-agent.sh`,
configure: `# Configure API credentials
sudo /usr/local/bin/patchmon-agent.sh configure "${host?.api_id}" "${host?.api_key}"`,
test: `# Test the configuration
sudo /usr/local/bin/patchmon-agent.sh test`,
initialUpdate: `# Send initial package data
sudo /usr/local/bin/patchmon-agent.sh update`,
crontab: `# Add to crontab for hourly updates starting at current time (run as root)
echo "${currentMinute} * * * * /usr/local/bin/patchmon-agent.sh update >/dev/null 2>&1" | sudo crontab -`,
fullSetup: `#!/bin/bash
# Complete PatchMon Agent Setup Script
# Run this on the target host: ${host?.friendly_name}
echo "🔄 Setting up PatchMon agent..."
# Download and install agent
echo "📥 Downloading agent script..."
curl -o /tmp/patchmon-agent.sh ${serverUrl}/api/v1/hosts/agent/download
sudo mkdir -p /etc/patchmon
sudo mv /tmp/patchmon-agent.sh /usr/local/bin/patchmon-agent.sh
sudo chmod +x /usr/local/bin/patchmon-agent.sh
# Configure credentials
echo "🔑 Configuring API credentials..."
sudo /usr/local/bin/patchmon-agent.sh configure "${host?.api_id}" "${host?.api_key}"
# Test configuration
echo "🧪 Testing configuration..."
sudo /usr/local/bin/patchmon-agent.sh test
# Send initial update
echo "📊 Sending initial package data..."
sudo /usr/local/bin/patchmon-agent.sh update
# Setup crontab starting at current time
echo "⏰ Setting up hourly crontab starting at ${currentHour.toString().padStart(2, "0")}:${currentMinute.toString().padStart(2, "0")}..."
echo "${currentMinute} * * * * /usr/local/bin/patchmon-agent.sh update >/dev/null 2>&1" | sudo crontab -
echo "✅ PatchMon agent setup complete!"
echo " - Agent installed: /usr/local/bin/patchmon-agent.sh"
echo " - Config directory: /etc/patchmon/"
echo " - Updates: Every hour via crontab (starting at ${currentHour.toString().padStart(2, "0")}:${currentMinute.toString().padStart(2, "0")})"
echo " - View logs: tail -f /var/log/patchmon-agent.log"`,
};
};
if (!isOpen || !host) return null;
const commands = getSetupCommands();
return (
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
<div className="bg-white rounded-lg p-6 w-full max-w-4xl max-h-[90vh] overflow-y-auto">
<div className="flex justify-between items-center mb-4">
<h3 className="text-lg font-medium text-secondary-900">
Host Setup - {host.friendly_name}
</h3>
<button
type="button"
onClick={onClose}
className="text-secondary-400 hover:text-secondary-600"
>
<X className="h-5 w-5" />
</button>
</div>
{/* Tabs */}
<div className="flex space-x-1 mb-6 bg-secondary-100 p-1 rounded-lg">
<button
type="button"
onClick={() => setActiveTab("quick")}
className={`px-4 py-2 rounded-md text-sm font-medium transition-colors ${
activeTab === "quick"
? "bg-white text-secondary-900 shadow-sm"
: "text-secondary-600 hover:text-secondary-900"
}`}
>
Quick Install
</button>
<button
type="button"
onClick={() => setActiveTab("credentials")}
className={`px-4 py-2 rounded-md text-sm font-medium transition-colors ${
activeTab === "credentials"
? "bg-white text-secondary-900 shadow-sm"
: "text-secondary-600 hover:text-secondary-900"
}`}
>
API Credentials
</button>
<button
type="button"
onClick={() => setActiveTab("setup")}
className={`px-4 py-2 rounded-md text-sm font-medium transition-colors ${
activeTab === "setup"
? "bg-white text-secondary-900 shadow-sm"
: "text-secondary-600 hover:text-secondary-900"
}`}
>
Setup Instructions
</button>
<button
type="button"
onClick={() => setActiveTab("script")}
className={`px-4 py-2 rounded-md text-sm font-medium transition-colors ${
activeTab === "script"
? "bg-white text-secondary-900 shadow-sm"
: "text-secondary-600 hover:text-secondary-900"
}`}
>
Auto-Setup Script
</button>
</div>
{/* Tab Content */}
{activeTab === "quick" && (
<div className="space-y-6">
<div className="bg-green-50 border border-green-200 rounded-md p-4">
<h4 className="text-sm font-medium text-green-800 mb-2">
🚀 One-Line Installation
</h4>
<p className="text-sm text-green-700">
Copy and paste this single command on{" "}
<strong>{host.friendly_name}</strong> to install and configure
the PatchMon agent automatically.
</p>
</div>
<div className="border border-secondary-200 rounded-lg p-4">
<h5 className="font-medium text-secondary-900 mb-3">
Installation Command
</h5>
<div className="bg-secondary-900 text-secondary-100 p-3 rounded font-mono text-sm">
<div className="flex justify-between items-start">
<code className="flex-1 whitespace-pre-wrap break-all">
{commands.oneLine}
</code>
<button
type="button"
onClick={() =>
copyToClipboard(commands.oneLine, "Installation command")
}
className="ml-2 text-secondary-400 hover:text-secondary-200 flex-shrink-0"
>
<Copy className="h-4 w-4" />
</button>
</div>
</div>
<p className="text-xs text-secondary-500 mt-2">
This command will download, install, configure, and set up
automatic updates for the PatchMon agent.
</p>
</div>
<div className="bg-blue-50 border border-blue-200 rounded-md p-4">
<h4 className="text-sm font-medium text-blue-800 mb-2">
📋 What This Command Does
</h4>
<ul className="text-sm text-blue-700 space-y-1">
<li> Downloads the PatchMon installation script</li>
<li>
Installs the agent to{" "}
<code>/usr/local/bin/patchmon-agent.sh</code>
</li>
<li>
Configures API credentials for{" "}
<strong>{host.friendly_name}</strong>
</li>
<li> Tests the connection to PatchMon server</li>
<li> Sends initial package data</li>
<li> Sets up hourly automatic updates via crontab</li>
</ul>
</div>
<div className="bg-amber-50 border border-amber-200 rounded-md p-4">
<h4 className="text-sm font-medium text-amber-800 mb-2">
Requirements
</h4>
<ul className="text-sm text-amber-700 space-y-1">
<li>
Must be run as root (use <code>sudo</code>)
</li>
<li> Requires internet connection to download agent</li>
<li>
Requires <code>curl</code> and <code>bash</code> to be
installed
</li>
<li> Host must be able to reach the PatchMon server</li>
</ul>
</div>
</div>
)}
{activeTab === "credentials" && (
<div className="space-y-4">
<div>
<label
htmlFor={apiIdId}
className="block text-sm font-medium text-secondary-700 mb-2"
>
API ID
</label>
<div className="flex">
<input
type="text"
id={apiIdId}
readOnly
value={host.apiId}
className="flex-1 block w-full border-secondary-300 rounded-l-md shadow-sm bg-secondary-50"
/>
<button
type="button"
onClick={() => copyToClipboard(host.apiId, "API ID")}
className="px-3 py-2 border border-l-0 border-secondary-300 rounded-r-md bg-secondary-50 hover:bg-secondary-100"
>
<Copy className="h-4 w-4" />
</button>
</div>
</div>
<div>
<label
htmlFor={apiKeyId}
className="block text-sm font-medium text-secondary-700 mb-2"
>
API Key
</label>
<div className="flex">
<input
type={showApiKey ? "text" : "password"}
id={apiKeyId}
readOnly
value={host.apiKey}
className="flex-1 block w-full border-secondary-300 rounded-l-md shadow-sm bg-secondary-50"
/>
<button
type="button"
onClick={() => setShowApiKey(!showApiKey)}
className="px-3 py-2 border border-l-0 border-r-0 border-secondary-300 bg-secondary-50 hover:bg-secondary-100"
>
{showApiKey ? (
<EyeOff className="h-4 w-4" />
) : (
<Eye className="h-4 w-4" />
)}
</button>
<button
type="button"
onClick={() => copyToClipboard(host.apiKey, "API Key")}
className="px-3 py-2 border border-l-0 border-secondary-300 rounded-r-md bg-secondary-50 hover:bg-secondary-100"
>
<Copy className="h-4 w-4" />
</button>
</div>
</div>
<div className="bg-amber-50 border border-amber-200 rounded-md p-4">
<h4 className="text-sm font-medium text-amber-800 mb-2">
Security Note
</h4>
<p className="text-sm text-amber-700">
Keep these credentials secure. They provide access to update
package information for <strong>{host.friendly_name}</strong>{" "}
only.
</p>
</div>
</div>
)}
{activeTab === "setup" && (
<div className="space-y-6">
<div className="bg-blue-50 border border-blue-200 rounded-md p-4">
<h4 className="text-sm font-medium text-blue-800 mb-2">
📋 Step-by-Step Setup
</h4>
<p className="text-sm text-blue-700">
Follow these commands on <strong>{host.friendly_name}</strong>{" "}
to install and configure the PatchMon agent.
</p>
</div>
{/* Step 1: Download & Install */}
<div className="border border-secondary-200 rounded-lg p-4">
<h5 className="font-medium text-secondary-900 mb-3">
Step 1: Download & Install Agent
</h5>
<div className="bg-secondary-900 text-secondary-100 p-3 rounded font-mono text-sm">
<div className="flex justify-between items-start">
<code className="flex-1 whitespace-pre-wrap">
{commands.download}
</code>
<button
type="button"
onClick={() =>
copyToClipboard(commands.download, "Download commands")
}
className="ml-2 text-secondary-400 hover:text-secondary-200 flex-shrink-0"
>
<Copy className="h-4 w-4" />
</button>
</div>
</div>
</div>
{/* Step 2: Configure */}
<div className="border border-secondary-200 rounded-lg p-4">
<h5 className="font-medium text-secondary-900 mb-3">
Step 2: Configure API Credentials
</h5>
<div className="bg-secondary-900 text-secondary-100 p-3 rounded font-mono text-sm">
<div className="flex justify-between items-start">
<code className="flex-1">{commands.configure}</code>
<button
type="button"
onClick={() =>
copyToClipboard(commands.configure, "Configure command")
}
className="ml-2 text-secondary-400 hover:text-secondary-200"
>
<Copy className="h-4 w-4" />
</button>
</div>
</div>
</div>
{/* Step 3: Test */}
<div className="border border-secondary-200 rounded-lg p-4">
<h5 className="font-medium text-secondary-900 mb-3">
Step 3: Test Configuration
</h5>
<div className="bg-secondary-900 text-secondary-100 p-3 rounded font-mono text-sm">
<div className="flex justify-between items-start">
<code className="flex-1">{commands.test}</code>
<button
type="button"
onClick={() =>
copyToClipboard(commands.test, "Test command")
}
className="ml-2 text-secondary-400 hover:text-secondary-200"
>
<Copy className="h-4 w-4" />
</button>
</div>
</div>
</div>
{/* Step 4: Initial Update */}
<div className="border border-secondary-200 rounded-lg p-4">
<h5 className="font-medium text-secondary-900 mb-3">
Step 4: Send Initial Package Data
</h5>
<p className="text-sm text-secondary-600 mb-3">
This will automatically detect and send system information (OS,
IP, architecture) along with package data.
</p>
<div className="bg-secondary-900 text-secondary-100 p-3 rounded font-mono text-sm">
<div className="flex justify-between items-start">
<code className="flex-1">{commands.initialUpdate}</code>
<button
type="button"
onClick={() =>
copyToClipboard(
commands.initialUpdate,
"Initial update command",
)
}
className="ml-2 text-secondary-400 hover:text-secondary-200"
>
<Copy className="h-4 w-4" />
</button>
</div>
</div>
</div>
{/* Step 5: Crontab */}
<div className="border border-secondary-200 rounded-lg p-4">
<h5 className="font-medium text-secondary-900 mb-3">
Step 5: Setup Hourly Updates
</h5>
<div className="bg-secondary-900 text-secondary-100 p-3 rounded font-mono text-sm">
<div className="flex justify-between items-start">
<code className="flex-1">{commands.crontab}</code>
<button
type="button"
onClick={() =>
copyToClipboard(commands.crontab, "Crontab command")
}
className="ml-2 text-secondary-400 hover:text-secondary-200"
>
<Copy className="h-4 w-4" />
</button>
</div>
</div>
<p className="text-xs text-secondary-500 mt-2">
This sets up automatic package updates every hour at the top of
the hour.
</p>
</div>
</div>
)}
{activeTab === "script" && (
<div className="space-y-4">
<div className="bg-green-50 border border-green-200 rounded-md p-4">
<h4 className="text-sm font-medium text-green-800 mb-2">
🚀 Automated Setup
</h4>
<p className="text-sm text-green-700">
Copy this complete setup script to{" "}
<strong>{host.friendly_name}</strong> and run it to
automatically install and configure everything.
</p>
</div>
<div className="border border-secondary-200 rounded-lg p-4">
<div className="flex justify-between items-center mb-3">
<h5 className="font-medium text-secondary-900">
Complete Setup Script
</h5>
<button
type="button"
onClick={() =>
copyToClipboard(commands.fullSetup, "Complete setup script")
}
className="px-3 py-1 bg-primary-600 text-white rounded text-sm hover:bg-primary-700 flex items-center gap-2"
>
<Copy className="h-4 w-4" />
Copy Script
</button>
</div>
<div className="bg-secondary-900 text-secondary-100 p-4 rounded font-mono text-xs overflow-x-auto">
<pre className="whitespace-pre-wrap">{commands.fullSetup}</pre>
</div>
<div className="mt-3 text-sm text-secondary-600">
<p>
<strong>Usage:</strong>
</p>
<p>1. Copy the script above</p>
<p>
2. Save it to a file on {host.friendly_name} (e.g.,{" "}
<code>setup-patchmon.sh</code>)
</p>
<p>
3. Run:{" "}
<code>
chmod +x setup-patchmon.sh && sudo ./setup-patchmon.sh
</code>
</p>
</div>
</div>
</div>
)}
<div className="mt-6 flex justify-end space-x-3">
<a
href={`${serverUrl}/api/v1/hosts/agent/download`}
download="patchmon-agent.sh"
className="px-4 py-2 text-sm font-medium text-primary-700 bg-primary-50 border border-primary-200 rounded-md hover:bg-primary-100"
>
Download Agent Script
</a>
<button
type="button"
onClick={onClose}
className="px-4 py-2 text-sm font-medium text-secondary-700 bg-white border border-secondary-300 rounded-md hover:bg-secondary-50"
>
Close
</button>
</div>
</div>
</div>
);
};
const Hosts = () => { const Hosts = () => {
const hostGroupFilterId = useId(); const hostGroupFilterId = useId();
const statusFilterId = useId(); const statusFilterId = useId();