From b95838ae9c65a264dcef8363eb7889ad9c6299b4 Mon Sep 17 00:00:00 2001 From: SaelixCode Date: Fri, 27 Feb 2026 10:17:33 -0500 Subject: [PATCH] feat: remove unused alert management code and optimize notification fetching interval --- backend/src/index.ts | 9 - frontend/src/components/EditorLayout.tsx | 4 +- .../components/NotificationSettingsModal.tsx | 241 +--------------- frontend/src/components/StackAlertSheet.tsx | 260 +++++++++++------- 4 files changed, 164 insertions(+), 350 deletions(-) diff --git a/backend/src/index.ts b/backend/src/index.ts index 1d5cefdf..27e67a87 100644 --- a/backend/src/index.ts +++ b/backend/src/index.ts @@ -663,15 +663,6 @@ app.get('/api/system/stats', async (req: Request, res: Response) => { // --- Notification & Alerting Routes --- -app.get('/api/stacks', async (req: Request, res: Response) => { - try { - const stacks = await fileSystemService.getStacks(); - res.json(stacks); - } catch (error) { - res.status(500).json({ error: 'Failed to fetch stacks' }); - } -}); - app.get('/api/agents', async (req: Request, res: Response) => { try { const agents = DatabaseService.getInstance().getAgents(); diff --git a/frontend/src/components/EditorLayout.tsx b/frontend/src/components/EditorLayout.tsx index 79a3293d..d204ae4c 100644 --- a/frontend/src/components/EditorLayout.tsx +++ b/frontend/src/components/EditorLayout.tsx @@ -139,7 +139,7 @@ export default function EditorLayout() { useEffect(() => { refreshStacks(); fetchNotifications(); - const notificationInterval = setInterval(fetchNotifications, 30000); + const notificationInterval = setInterval(fetchNotifications, 5000); return () => clearInterval(notificationInterval); }, []); @@ -630,7 +630,7 @@ export default function EditorLayout() { openAlertSheet(file)}> - Create Alert + Alerts diff --git a/frontend/src/components/NotificationSettingsModal.tsx b/frontend/src/components/NotificationSettingsModal.tsx index a8513e71..2964c7f7 100644 --- a/frontend/src/components/NotificationSettingsModal.tsx +++ b/frontend/src/components/NotificationSettingsModal.tsx @@ -14,18 +14,6 @@ import { Label } from '@/components/ui/label'; import { Slider } from '@/components/ui/slider'; import { toast } from 'sonner'; import { apiFetch } from '@/lib/api'; -import { Trash2 } from 'lucide-react'; -import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'; - -interface StackAlert { - id?: number; - stack_name: string; - metric: string; - operator: string; - threshold: number; - duration_mins: number; - cooldown_mins: number; -} interface Agent { type: 'discord' | 'slack' | 'webhook'; @@ -55,31 +43,10 @@ export function NotificationSettingsModal({ isOpen, onClose }: NotificationSetti const [isLoading, setIsLoading] = useState(false); - // Central Alerts State - const [alerts, setAlerts] = useState([]); - const [stacks, setStacks] = useState([]); - const [newAlertStack, setNewAlertStack] = useState(''); - const [newAlertMetric, setNewAlertMetric] = useState('cpu_percent'); - const [newAlertOperator, setNewAlertOperator] = useState('>'); - const [newAlertThreshold, setNewAlertThreshold] = useState(''); - const [newAlertDuration, setNewAlertDuration] = useState('5'); - const [newAlertCooldown, setNewAlertCooldown] = useState('60'); - - const metricLabels: Record = { - cpu_percent: 'CPU Usage (%)', - memory_percent: 'Memory Usage (%)', - memory_mb: 'Memory Usage (MB)', - net_rx: 'Network In (MB)', - net_tx: 'Network Out (MB)', - restart_count: 'Restart Count' - }; - useEffect(() => { if (isOpen) { fetchAgents(); fetchSettings(); - fetchAlerts(); - fetchStacks(); } }, [isOpen]); @@ -111,33 +78,6 @@ export function NotificationSettingsModal({ isOpen, onClose }: NotificationSetti } }; - const fetchAlerts = async () => { - try { - const res = await apiFetch('/alerts'); - if (res.ok) { - const data = await res.json(); - setAlerts(data); - } - } catch (e) { - console.error('Failed to fetch alerts', e); - } - }; - - const fetchStacks = async () => { - try { - const res = await apiFetch('/stacks'); - if (res.ok) { - const data = await res.json(); - setStacks(data); - if (data.length > 0 && !newAlertStack) { - setNewAlertStack(data[0]); - } - } - } catch (e) { - console.error('Failed to fetch stacks', e); - } - }; - const handleAgentChange = (type: string, field: keyof Agent, value: any) => { setAgents(prev => ({ ...prev, @@ -209,62 +149,6 @@ export function NotificationSettingsModal({ isOpen, onClose }: NotificationSetti } }; - const addAlert = async () => { - if (!newAlertStack) { - toast.error('Please select a stack.'); - return; - } - if (!newAlertThreshold) { - toast.error('Please enter a threshold.'); - return; - } - - setIsLoading(true); - const newAlert = { - stack_name: newAlertStack, - metric: newAlertMetric, - operator: newAlertOperator, - threshold: parseFloat(newAlertThreshold), - duration_mins: parseInt(newAlertDuration, 10), - cooldown_mins: parseInt(newAlertCooldown, 10) - }; - - try { - const res = await apiFetch('/alerts', { - method: 'POST', - body: JSON.stringify(newAlert) - }); - if (res.ok) { - toast.success('Alert rule added.'); - setNewAlertThreshold(''); - fetchAlerts(); - } else { - toast.error('Failed to add alert rule.'); - } - } catch (e) { - toast.error('Network error.'); - } finally { - setIsLoading(false); - } - }; - - const deleteAlert = async (id: number) => { - setIsLoading(true); - try { - const res = await apiFetch(`/alerts/${id}`, { method: 'DELETE' }); - if (res.ok) { - toast.success('Alert rule deleted.'); - fetchAlerts(); - } else { - toast.error('Failed to delete alert rule.'); - } - } catch (e) { - toast.error('Network error.'); - } finally { - setIsLoading(false); - } - }; - const renderAgentTab = (type: 'discord' | 'slack' | 'webhook', title: string) => (
@@ -302,12 +186,11 @@ export function NotificationSettingsModal({ isOpen, onClose }: NotificationSetti - + Global Discord Slack Webhook - Alert Rules @@ -375,128 +258,6 @@ export function NotificationSettingsModal({ isOpen, onClose }: NotificationSetti {renderAgentTab('discord', 'Discord')} {renderAgentTab('slack', 'Slack')} {renderAgentTab('webhook', 'Custom Webhook')} - - -
-
-

Existing Rules

- {alerts.length === 0 ? ( -
- No active alert rules across all stacks. -
- ) : ( - alerts.map(alert => ( -
-
-
- - [{alert.stack_name}] {metricLabels[alert.metric] || alert.metric} {alert.operator} {alert.threshold} - -
- Trigger after {alert.duration_mins}m • Cooldown: {alert.cooldown_mins}m -
-
- -
-
- )) - )} -
- -
- -
-

Add New Rule

- -
-
- - -
-
- - -
-
- -
-
- - -
-
- - setNewAlertThreshold(e.target.value)} - placeholder="e.g. 90" - /> -
-
- -
-
- - setNewAlertDuration(e.target.value)} - /> -
-
- - setNewAlertCooldown(e.target.value)} - /> -
-
- - -
-
-
diff --git a/frontend/src/components/StackAlertSheet.tsx b/frontend/src/components/StackAlertSheet.tsx index 531897ae..7db64ae9 100644 --- a/frontend/src/components/StackAlertSheet.tsx +++ b/frontend/src/components/StackAlertSheet.tsx @@ -9,14 +9,9 @@ import { import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; import { Label } from '@/components/ui/label'; -import { - Select, - SelectContent, - SelectItem, - SelectTrigger, - SelectValue, -} from '@/components/ui/select'; -import { Trash2 } from 'lucide-react'; +import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'; +import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip'; +import { Trash2, HelpCircle } from 'lucide-react'; import { toast } from 'sonner'; import { apiFetch } from '@/lib/api'; @@ -136,112 +131,179 @@ export function StackAlertSheet({ isOpen, onClose, stackName }: StackAlertSheetP -
- {/* List Existing Alerts */} -
-

Existing Rules

- {alerts.length === 0 ? ( -
- No active alert rules for this stack. -
- ) : ( - alerts.map(alert => ( -
-
-
- - {metricLabels[alert.metric] || alert.metric} {alert.operator} {alert.threshold} - -
- Trigger after {alert.duration_mins}m • Cooldown: {alert.cooldown_mins}m -
-
- -
+ +
+ {/* List Existing Alerts */} +
+

Existing Rules

+ {alerts.length === 0 ? ( +
+ No active alert rules for this stack.
- )) - )} -
- -
- - {/* Add New Alert Form */} -
-

Add New Rule

- -
- - + ) : ( + alerts.map(alert => ( +
+
+
+ + {metricLabels[alert.metric] || alert.metric} {alert.operator} {alert.threshold} + +
+ Trigger after {alert.duration_mins}m • Cooldown: {alert.cooldown_mins}m +
+
+ +
+
+ )) + )}
-
+
+ + {/* Add New Alert Form */} +
+

Add New Rule

+
- - - Greater than - Greater or eq - Less than - Less or eq - Equals + {Object.entries(metricLabels).map(([val, label]) => ( + {label} + ))}
-
- - setThreshold(e.target.value)} - placeholder="e.g. 90" - /> -
-
-
-
- - setDuration(e.target.value)} - /> +
+
+
+ + + + + + +

The comparison condition to trigger the alert against the threshold.

+
+
+
+ +
+
+
+ + + + + + +

The numerical value the metric needs to breach to trigger the conditions.

+
+
+
+ { + let val = e.target.value; + if (val !== '' && Number(val) < 0) val = '0'; + setThreshold(val); + }} + placeholder="e.g. 90" + /> +
-
- - setCooldown(e.target.value)} - /> -
-
- +
+
+
+ + + + + + +

How long the metric must stay in breach of the threshold before sending an alert.

+
+
+
+ { + let val = e.target.value; + if (val !== '' && Number(val) < 0) val = '0'; + setDuration(val); + }} + /> +
+
+
+ + + + + + +

How long to wait before sending another alert if the stack continues to breach.

+
+
+
+ { + let val = e.target.value; + if (val !== '' && Number(val) < 0) val = '0'; + setCooldown(val); + }} + /> +
+
+ + +
-
+ );