mirror of
https://github.com/Studio-Saelix/sencho.git
synced 2026-08-18 14:33:19 +00:00
feat: add Notification Settings Modal and Stack Alert Sheet components
- Implemented NotificationSettingsModal for configuring notification agents (Discord, Slack, Webhook) and global settings. - Added StackAlertSheet for managing stack-specific alert rules with metrics, thresholds, and cooldowns. - Introduced reusable UI components: DropdownMenu, Popover, Select, Sheet, Slider, and Switch. - Enhanced Tabs component by removing unnecessary client directive.
This commit is contained in:
@@ -0,0 +1,129 @@
|
||||
import { DatabaseService } from './DatabaseService';
|
||||
|
||||
export class NotificationService {
|
||||
private static instance: NotificationService;
|
||||
private dbService: DatabaseService;
|
||||
|
||||
private constructor() {
|
||||
this.dbService = DatabaseService.getInstance();
|
||||
}
|
||||
|
||||
public static getInstance(): NotificationService {
|
||||
if (!NotificationService.instance) {
|
||||
NotificationService.instance = new NotificationService();
|
||||
}
|
||||
return NotificationService.instance;
|
||||
}
|
||||
|
||||
public async dispatchAlert(level: 'info' | 'warning' | 'error', message: string) {
|
||||
// 1. Log to history
|
||||
this.dbService.addNotificationHistory({
|
||||
level,
|
||||
message,
|
||||
timestamp: Date.now()
|
||||
});
|
||||
|
||||
// 2. Fetch enabled agents
|
||||
const agents = this.dbService.getEnabledAgents();
|
||||
if (agents.length === 0) {
|
||||
console.log('No active notification agents found. Skipping external dispatch.');
|
||||
return;
|
||||
}
|
||||
|
||||
// 3. Dispatch to each agent
|
||||
for (const agent of agents) {
|
||||
try {
|
||||
if (agent.type === 'discord') {
|
||||
await this.sendDiscordWebhook(agent.url, level, message);
|
||||
} else if (agent.type === 'slack') {
|
||||
await this.sendSlackWebhook(agent.url, level, message);
|
||||
} else if (agent.type === 'webhook') {
|
||||
await this.sendCustomWebhook(agent.url, level, message);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`Failed to dispatch notification to ${agent.type}:`, error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public async testDispatch(type: 'discord' | 'slack' | 'webhook', url: string) {
|
||||
const level = 'info';
|
||||
const message = '🔌 Test Notification from Sencho!';
|
||||
|
||||
if (type === 'discord') {
|
||||
await this.sendDiscordWebhook(url, level, message);
|
||||
} else if (type === 'slack') {
|
||||
await this.sendSlackWebhook(url, level, message);
|
||||
} else if (type === 'webhook') {
|
||||
await this.sendCustomWebhook(url, level, message);
|
||||
}
|
||||
}
|
||||
|
||||
private async sendDiscordWebhook(url: string, level: 'info' | 'warning' | 'error', message: string) {
|
||||
const colorMap = {
|
||||
info: 3447003, // Blue
|
||||
warning: 16776960, // Yellow
|
||||
error: 15158332 // Red
|
||||
};
|
||||
|
||||
const payload = {
|
||||
embeds: [{
|
||||
title: `Sencho Alert [${level.toUpperCase()}]`,
|
||||
description: message,
|
||||
color: colorMap[level],
|
||||
timestamp: new Date().toISOString()
|
||||
}]
|
||||
};
|
||||
|
||||
const response = await fetch(url, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(payload)
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`Discord Webhook responded with ${response.status}`);
|
||||
}
|
||||
}
|
||||
|
||||
private async sendSlackWebhook(url: string, level: 'info' | 'warning' | 'error', message: string) {
|
||||
const emojiMap = {
|
||||
info: 'ℹ️',
|
||||
warning: '⚠️',
|
||||
error: '🚨'
|
||||
};
|
||||
|
||||
const payload = {
|
||||
text: `${emojiMap[level]} *Sencho Alert [${level.toUpperCase()}]*\n${message}`
|
||||
};
|
||||
|
||||
const response = await fetch(url, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(payload)
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`Slack Webhook responded with ${response.status}`);
|
||||
}
|
||||
}
|
||||
|
||||
private async sendCustomWebhook(url: string, level: 'info' | 'warning' | 'error', message: string) {
|
||||
const payload = {
|
||||
level,
|
||||
message,
|
||||
timestamp: new Date().toISOString(),
|
||||
source: 'sencho'
|
||||
};
|
||||
|
||||
const response = await fetch(url, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(payload)
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`Custom Webhook responded with ${response.status}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user