feat: integrate custom alerts with backend API endpoints

- Replace localStorage with real API calls to /api/alerts/rules
- Load custom alerts from backend via GET /api/alerts/rules
- Create alerts via POST /api/alerts/rules
- Update alerts via PUT /api/alerts/rules/:id
- Delete alerts via DELETE /api/alerts/rules/:id
- Improve alert card display to handle backend data structure
- Add proper error handling for all API operations
This commit is contained in:
courtmanr@gmail.com
2025-06-05 11:57:09 +01:00
parent 97004c10d1
commit faa525ebde
+98 -71
View File
@@ -458,56 +458,49 @@ PulseApp.ui.alertManagementModal = (() => {
systemAlertsContent.innerHTML = systemAlerts.map(alert => createSystemAlertCard(alert)).join('');
}
function loadCustomAlerts() {
async function loadCustomAlerts() {
const customAlertsContent = document.getElementById('custom-alerts-content');
if (!customAlertsContent) return;
// Load custom alerts from local storage (temporary solution until backend integration)
const storedAlerts = getCustomAlertsFromStorage();
if (storedAlerts.length === 0) {
try {
// Load custom alerts from the backend API
const response = await fetch('/api/alerts/rules');
if (!response.ok) {
throw new Error(`Failed to fetch alert rules: ${response.status}`);
}
const data = await response.json();
const customAlerts = data.rules ? data.rules.filter(rule =>
rule.group === 'custom' || rule.type === 'compound_threshold'
) : [];
if (customAlerts.length === 0) {
customAlertsContent.innerHTML = `
<div class="text-center py-6">
<svg class="w-8 h-8 mx-auto mb-3 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 6v6m0 0v6m0-6h6m-6 0H6" />
</svg>
<p class="text-sm text-gray-500 dark:text-gray-400 mb-2">No custom alerts configured</p>
<button onclick="PulseApp.ui.alertManagementModal.openCustomAlertModal()"
class="text-xs text-blue-600 dark:text-blue-400 hover:underline">
Create your first custom alert
</button>
</div>
`;
return;
}
// Display the custom alerts
customAlertsContent.innerHTML = customAlerts.map(alert => createCustomAlertCard(alert)).join('');
} catch (error) {
console.error('[AlertManagementModal] Failed to load custom alerts:', error);
customAlertsContent.innerHTML = `
<div class="text-center py-6">
<svg class="w-8 h-8 mx-auto mb-3 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 6v6m0 0v6m0-6h6m-6 0H6" />
</svg>
<p class="text-sm text-gray-500 dark:text-gray-400 mb-2">No custom alerts configured</p>
<button onclick="PulseApp.ui.alertManagementModal.openCustomAlertModal()"
class="text-xs text-blue-600 dark:text-blue-400 hover:underline">
Create your first custom alert
</button>
<div class="text-center py-6 text-red-500 dark:text-red-400">
<p class="text-sm">Failed to load custom alerts</p>
<p class="text-xs">${error.message}</p>
</div>
`;
return;
}
// Display the custom alerts
customAlertsContent.innerHTML = storedAlerts.map(alert => createCustomAlertCard(alert)).join('');
}
function getCustomAlertsFromStorage() {
try {
const stored = localStorage.getItem('pulse-custom-alerts');
return stored ? JSON.parse(stored) : [];
} catch (error) {
console.warn('[AlertManagementModal] Failed to load custom alerts from storage:', error);
return [];
}
}
function saveCustomAlertToStorage(alertConfig) {
try {
const existingAlerts = getCustomAlertsFromStorage();
const newAlert = {
...alertConfig,
id: `custom-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`
};
existingAlerts.push(newAlert);
localStorage.setItem('pulse-custom-alerts', JSON.stringify(existingAlerts));
return newAlert;
} catch (error) {
console.error('[AlertManagementModal] Failed to save custom alert to storage:', error);
throw error;
}
}
@@ -518,26 +511,30 @@ PulseApp.ui.alertManagementModal = (() => {
</span>`
).join('') || '';
const createdDate = new Date(alert.createdAt).toLocaleDateString();
const createdDate = alert.createdAt ? new Date(alert.createdAt).toLocaleDateString() : 'Unknown';
const targetDisplay = alert.targetType === 'all' ? 'All VMs/LXCs' :
alert.targetType === 'vm' ? 'VMs only' :
alert.targetType === 'lxc' ? 'LXCs only' :
alert.specificTarget || alert.targetType?.toUpperCase() || 'All';
return `
<div class="border border-gray-200 dark:border-gray-600 rounded-lg p-3 bg-white dark:bg-gray-800">
<div class="flex items-start justify-between mb-2">
<div class="flex-1">
<div class="flex items-center gap-2 mb-1">
<h5 class="text-sm font-medium text-gray-900 dark:text-gray-100">${alert.name}</h5>
<span class="text-xs px-2 py-0.5 bg-green-100 dark:bg-green-800 text-green-700 dark:text-green-300 rounded-full">
${alert.enabled ? 'Enabled' : 'Disabled'}
<h5 class="text-sm font-medium text-gray-900 dark:text-gray-100">${alert.name || alert.description || 'Unnamed Alert'}</h5>
<span class="text-xs px-2 py-0.5 ${alert.enabled !== false ? 'bg-green-100 dark:bg-green-800 text-green-700 dark:text-green-300' : 'bg-gray-100 dark:bg-gray-700 text-gray-700 dark:text-gray-300'} rounded-full">
${alert.enabled !== false ? 'Enabled' : 'Disabled'}
</span>
</div>
<div class="text-xs text-gray-500 dark:text-gray-400 mb-2">
Created: ${createdDate} • Target: ${alert.targetType === 'all' ? 'All VMs/LXCs' : alert.targetType.toUpperCase()}
Created: ${createdDate} • Target: ${targetDisplay}
</div>
</div>
<div class="flex gap-1">
<button onclick="PulseApp.ui.alertManagementModal.toggleCustomAlert('${alert.id}', ${!alert.enabled})"
<button onclick="PulseApp.ui.alertManagementModal.toggleCustomAlert('${alert.id}', ${alert.enabled === false})"
class="px-2 py-1 text-xs bg-blue-600 hover:bg-blue-700 text-white rounded">
${alert.enabled ? 'Disable' : 'Enable'}
${alert.enabled !== false ? 'Disable' : 'Enable'}
</button>
<button onclick="PulseApp.ui.alertManagementModal.deleteCustomAlert('${alert.id}')"
class="px-2 py-1 text-xs bg-red-600 hover:bg-red-700 text-white rounded">
@@ -904,7 +901,7 @@ PulseApp.ui.alertManagementModal = (() => {
}
}
function saveCustomAlert() {
async function saveCustomAlert() {
const form = document.getElementById('custom-alert-form');
const formData = new FormData(form);
@@ -955,21 +952,35 @@ PulseApp.ui.alertManagementModal = (() => {
console.log('Creating custom alert:', alertConfig);
try {
// Save to local storage for now
const savedAlert = saveCustomAlertToStorage(alertConfig);
// Save to backend via API
const response = await fetch('/api/alerts/rules', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(alertConfig)
});
if (!response.ok) {
const errorData = await response.json().catch(() => ({}));
throw new Error(errorData.error || `Server error: ${response.status}`);
}
const result = await response.json();
console.log('Alert rule created successfully:', result);
const thresholdSummary = thresholds.map(t => `${t.type.toUpperCase()}: ${t.value}${['cpu', 'memory', 'disk'].includes(t.type) ? '%' : ''}`).join(', ');
alert(`Custom alert created successfully!\n\nAlert: ${alertConfig.name}\nThresholds: ${thresholdSummary}\n\nNote: Currently saved locally. Backend integration coming soon.`);
alert(`Custom alert created successfully!\n\nAlert: ${alertConfig.name}\nThresholds: ${thresholdSummary}\n\nRule ID: ${result.rule?.id || 'Generated'}`);
// Close modal
document.getElementById('custom-alert-modal').remove();
// Refresh the custom alerts list
loadCustomAlerts();
await loadCustomAlerts();
} catch (error) {
console.error('Failed to save custom alert:', error);
alert('Failed to save custom alert. Please try again.');
alert(`Failed to create custom alert: ${error.message}`);
}
}
@@ -1174,35 +1185,51 @@ PulseApp.ui.alertManagementModal = (() => {
console.log('Saving alert configuration...');
}
function toggleCustomAlert(alertId, enabled) {
async function toggleCustomAlert(alertId, enabled) {
try {
const alerts = getCustomAlertsFromStorage();
const alertIndex = alerts.findIndex(a => a.id === alertId);
if (alertIndex >= 0) {
alerts[alertIndex].enabled = enabled;
localStorage.setItem('pulse-custom-alerts', JSON.stringify(alerts));
loadCustomAlerts(); // Refresh display
console.log(`Custom alert ${alertId} ${enabled ? 'enabled' : 'disabled'}`);
const response = await fetch(`/api/alerts/rules/${alertId}`, {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ enabled })
});
if (!response.ok) {
const errorData = await response.json().catch(() => ({}));
throw new Error(errorData.error || `Server error: ${response.status}`);
}
console.log(`Custom alert ${alertId} ${enabled ? 'enabled' : 'disabled'}`);
await loadCustomAlerts(); // Refresh display
} catch (error) {
console.error('Failed to toggle custom alert:', error);
alert(`Failed to ${enabled ? 'enable' : 'disable'} alert: ${error.message}`);
}
}
function deleteCustomAlert(alertId) {
async function deleteCustomAlert(alertId) {
if (!confirm('Are you sure you want to delete this custom alert? This action cannot be undone.')) {
return;
}
try {
const alerts = getCustomAlertsFromStorage();
const filteredAlerts = alerts.filter(a => a.id !== alertId);
localStorage.setItem('pulse-custom-alerts', JSON.stringify(filteredAlerts));
loadCustomAlerts(); // Refresh display
const response = await fetch(`/api/alerts/rules/${alertId}`, {
method: 'DELETE'
});
if (!response.ok) {
const errorData = await response.json().catch(() => ({}));
throw new Error(errorData.error || `Server error: ${response.status}`);
}
console.log(`Custom alert ${alertId} deleted`);
await loadCustomAlerts(); // Refresh display
} catch (error) {
console.error('Failed to delete custom alert:', error);
alert(`Failed to delete alert: ${error.message}`);
}
}