mirror of
https://github.com/MacRimi/ProxMenux.git
synced 2026-02-20 17:36:24 +00:00
Update notification service
This commit is contained in:
@@ -202,6 +202,35 @@ export function NotificationSettings() {
|
|||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Flatten the nested NotificationConfig into the flat key-value map the backend expects. */
|
||||||
|
const flattenConfig = (cfg: NotificationConfig): Record<string, string> => {
|
||||||
|
const flat: Record<string, string> = {
|
||||||
|
enabled: String(cfg.enabled),
|
||||||
|
severity_filter: cfg.severity_filter,
|
||||||
|
ai_enabled: String(cfg.ai_enabled),
|
||||||
|
ai_provider: cfg.ai_provider,
|
||||||
|
ai_api_key: cfg.ai_api_key,
|
||||||
|
ai_model: cfg.ai_model,
|
||||||
|
hostname: cfg.hostname,
|
||||||
|
webhook_secret: cfg.webhook_secret,
|
||||||
|
webhook_allowed_ips: cfg.webhook_allowed_ips,
|
||||||
|
pbs_host: cfg.pbs_host,
|
||||||
|
pve_host: cfg.pve_host,
|
||||||
|
pbs_trusted_sources: cfg.pbs_trusted_sources,
|
||||||
|
}
|
||||||
|
// Flatten channels: { telegram: { enabled, bot_token, chat_id } } -> telegram.enabled, telegram.bot_token, ...
|
||||||
|
for (const [chName, chCfg] of Object.entries(cfg.channels)) {
|
||||||
|
for (const [field, value] of Object.entries(chCfg)) {
|
||||||
|
flat[`${chName}.${field}`] = String(value ?? "")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Flatten event_categories: { system: true, backups: false } -> events.system, events.backups
|
||||||
|
for (const [cat, enabled] of Object.entries(cfg.event_categories)) {
|
||||||
|
flat[`events.${cat}`] = String(enabled)
|
||||||
|
}
|
||||||
|
return flat
|
||||||
|
}
|
||||||
|
|
||||||
const handleSave = async () => {
|
const handleSave = async () => {
|
||||||
setSaving(true)
|
setSaving(true)
|
||||||
try {
|
try {
|
||||||
@@ -217,9 +246,10 @@ export function NotificationSettings() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const payload = flattenConfig(config)
|
||||||
await fetchApi("/api/notifications/settings", {
|
await fetchApi("/api/notifications/settings", {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
body: JSON.stringify(config),
|
body: JSON.stringify(payload),
|
||||||
})
|
})
|
||||||
setOriginalConfig(config)
|
setOriginalConfig(config)
|
||||||
setHasChanges(false)
|
setHasChanges(false)
|
||||||
@@ -244,6 +274,15 @@ export function NotificationSettings() {
|
|||||||
setTesting(channel)
|
setTesting(channel)
|
||||||
setTestResult(null)
|
setTestResult(null)
|
||||||
try {
|
try {
|
||||||
|
// Auto-save current config before testing so backend has latest channel data
|
||||||
|
const payload = flattenConfig(config)
|
||||||
|
await fetchApi("/api/notifications/settings", {
|
||||||
|
method: "POST",
|
||||||
|
body: JSON.stringify(payload),
|
||||||
|
})
|
||||||
|
setOriginalConfig(config)
|
||||||
|
setHasChanges(false)
|
||||||
|
|
||||||
const data = await fetchApi<{
|
const data = await fetchApi<{
|
||||||
success: boolean
|
success: boolean
|
||||||
message?: string
|
message?: string
|
||||||
@@ -642,7 +681,7 @@ matcher: proxmenux-pbs
|
|||||||
<Input
|
<Input
|
||||||
type={showSecrets["tg_token"] ? "text" : "password"}
|
type={showSecrets["tg_token"] ? "text" : "password"}
|
||||||
className="h-7 text-xs font-mono"
|
className="h-7 text-xs font-mono"
|
||||||
placeholder="123456:ABC-DEF1234..."
|
placeholder="7595377878:AAGE6Fb2cy... (with or without 'bot' prefix)"
|
||||||
value={config.channels.telegram?.bot_token || ""}
|
value={config.channels.telegram?.bot_token || ""}
|
||||||
onChange={e => updateChannel("telegram", "bot_token", e.target.value)}
|
onChange={e => updateChannel("telegram", "bot_token", e.target.value)}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -133,7 +133,11 @@ class TelegramChannel(NotificationChannel):
|
|||||||
|
|
||||||
def __init__(self, bot_token: str, chat_id: str):
|
def __init__(self, bot_token: str, chat_id: str):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.bot_token = bot_token.strip()
|
token = bot_token.strip()
|
||||||
|
# Strip 'bot' prefix if user included it (API_BASE already adds it)
|
||||||
|
if token.lower().startswith('bot') and ':' in token[3:]:
|
||||||
|
token = token[3:]
|
||||||
|
self.bot_token = token
|
||||||
self.chat_id = chat_id.strip()
|
self.chat_id = chat_id.strip()
|
||||||
|
|
||||||
def validate_config(self) -> Tuple[bool, str]:
|
def validate_config(self) -> Tuple[bool, str]:
|
||||||
|
|||||||
@@ -937,19 +937,50 @@ class NotificationManager:
|
|||||||
return {'success': False, 'error': str(e)}
|
return {'success': False, 'error': str(e)}
|
||||||
|
|
||||||
def get_settings(self) -> Dict[str, Any]:
|
def get_settings(self) -> Dict[str, Any]:
|
||||||
"""Get all notification settings for the UI."""
|
"""Get all notification settings for the UI.
|
||||||
|
|
||||||
|
Returns a structure matching the frontend's NotificationConfig shape
|
||||||
|
so the round-trip (GET -> edit -> POST) is seamless.
|
||||||
|
"""
|
||||||
if not self._config:
|
if not self._config:
|
||||||
self._load_config()
|
self._load_config()
|
||||||
|
|
||||||
return {
|
# Build nested channels object matching frontend ChannelConfig
|
||||||
|
channels = {}
|
||||||
|
for ch_type, info in CHANNEL_TYPES.items():
|
||||||
|
ch_cfg: Dict[str, Any] = {
|
||||||
|
'enabled': self._config.get(f'{ch_type}.enabled', 'false') == 'true',
|
||||||
|
}
|
||||||
|
for config_key in info['config_keys']:
|
||||||
|
ch_cfg[config_key] = self._config.get(f'{ch_type}.{config_key}', '')
|
||||||
|
channels[ch_type] = ch_cfg
|
||||||
|
|
||||||
|
# Build event_categories dict
|
||||||
|
# EVENT_GROUPS is a dict: { 'system': {...}, 'vm_ct': {...}, ... }
|
||||||
|
event_categories = {}
|
||||||
|
for group_key in EVENT_GROUPS:
|
||||||
|
event_categories[group_key] = self._config.get(f'events.{group_key}', 'true') == 'true'
|
||||||
|
|
||||||
|
config = {
|
||||||
'enabled': self._enabled,
|
'enabled': self._enabled,
|
||||||
'settings': {f'{SETTINGS_PREFIX}{k}': v for k, v in self._config.items()},
|
'channels': channels,
|
||||||
'channels': self.list_channels()['channels'],
|
'severity_filter': self._config.get('severity_filter', 'warning'),
|
||||||
'event_groups': EVENT_GROUPS,
|
'event_categories': event_categories,
|
||||||
'event_types': get_event_types_by_group(),
|
'ai_enabled': self._config.get('ai_enabled', 'false') == 'true',
|
||||||
'default_events': get_default_enabled_events(),
|
'ai_provider': self._config.get('ai_provider', 'openai'),
|
||||||
|
'ai_api_key': self._config.get('ai_api_key', ''),
|
||||||
|
'ai_model': self._config.get('ai_model', ''),
|
||||||
|
'hostname': self._config.get('hostname', ''),
|
||||||
'webhook_secret': self._config.get('webhook_secret', ''),
|
'webhook_secret': self._config.get('webhook_secret', ''),
|
||||||
'webhook_allowed_ips': self._config.get('webhook_allowed_ips', ''),
|
'webhook_allowed_ips': self._config.get('webhook_allowed_ips', ''),
|
||||||
|
'pbs_host': self._config.get('pbs_host', ''),
|
||||||
|
'pve_host': self._config.get('pve_host', ''),
|
||||||
|
'pbs_trusted_sources': self._config.get('pbs_trusted_sources', ''),
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
'success': True,
|
||||||
|
'config': config,
|
||||||
}
|
}
|
||||||
|
|
||||||
def save_settings(self, settings: Dict[str, str]) -> Dict[str, Any]:
|
def save_settings(self, settings: Dict[str, str]) -> Dict[str, Any]:
|
||||||
|
|||||||
Reference in New Issue
Block a user