Commit 29b0e4a8 authored by Peter's avatar Peter
Browse files

feat(ops): allow hiding alert events

parent af9c4a7d
...@@ -372,6 +372,7 @@ func defaultOpsAdvancedSettings() *OpsAdvancedSettings { ...@@ -372,6 +372,7 @@ func defaultOpsAdvancedSettings() *OpsAdvancedSettings {
IgnoreContextCanceled: true, // Default to true - client disconnects are not errors IgnoreContextCanceled: true, // Default to true - client disconnects are not errors
IgnoreNoAvailableAccounts: false, // Default to false - this is a real routing issue IgnoreNoAvailableAccounts: false, // Default to false - this is a real routing issue
DisplayOpenAITokenStats: false, DisplayOpenAITokenStats: false,
DisplayAlertEvents: true,
AutoRefreshEnabled: false, AutoRefreshEnabled: false,
AutoRefreshIntervalSec: 30, AutoRefreshIntervalSec: 30,
} }
...@@ -439,7 +440,7 @@ func (s *OpsService) GetOpsAdvancedSettings(ctx context.Context) (*OpsAdvancedSe ...@@ -439,7 +440,7 @@ func (s *OpsService) GetOpsAdvancedSettings(ctx context.Context) (*OpsAdvancedSe
return nil, err return nil, err
} }
cfg := &OpsAdvancedSettings{} cfg := defaultOpsAdvancedSettings()
if err := json.Unmarshal([]byte(raw), cfg); err != nil { if err := json.Unmarshal([]byte(raw), cfg); err != nil {
return defaultCfg, nil return defaultCfg, nil
} }
......
...@@ -2,6 +2,7 @@ package service ...@@ -2,6 +2,7 @@ package service
import ( import (
"context" "context"
"encoding/json"
"testing" "testing"
) )
...@@ -16,6 +17,9 @@ func TestGetOpsAdvancedSettings_DefaultHidesOpenAITokenStats(t *testing.T) { ...@@ -16,6 +17,9 @@ func TestGetOpsAdvancedSettings_DefaultHidesOpenAITokenStats(t *testing.T) {
if cfg.DisplayOpenAITokenStats { if cfg.DisplayOpenAITokenStats {
t.Fatalf("DisplayOpenAITokenStats = true, want false by default") t.Fatalf("DisplayOpenAITokenStats = true, want false by default")
} }
if !cfg.DisplayAlertEvents {
t.Fatalf("DisplayAlertEvents = false, want true by default")
}
if repo.setCalls != 1 { if repo.setCalls != 1 {
t.Fatalf("expected defaults to be persisted once, got %d", repo.setCalls) t.Fatalf("expected defaults to be persisted once, got %d", repo.setCalls)
} }
...@@ -27,6 +31,7 @@ func TestUpdateOpsAdvancedSettings_PersistsOpenAITokenStatsVisibility(t *testing ...@@ -27,6 +31,7 @@ func TestUpdateOpsAdvancedSettings_PersistsOpenAITokenStatsVisibility(t *testing
cfg := defaultOpsAdvancedSettings() cfg := defaultOpsAdvancedSettings()
cfg.DisplayOpenAITokenStats = true cfg.DisplayOpenAITokenStats = true
cfg.DisplayAlertEvents = false
updated, err := svc.UpdateOpsAdvancedSettings(context.Background(), cfg) updated, err := svc.UpdateOpsAdvancedSettings(context.Background(), cfg)
if err != nil { if err != nil {
...@@ -35,6 +40,9 @@ func TestUpdateOpsAdvancedSettings_PersistsOpenAITokenStatsVisibility(t *testing ...@@ -35,6 +40,9 @@ func TestUpdateOpsAdvancedSettings_PersistsOpenAITokenStatsVisibility(t *testing
if !updated.DisplayOpenAITokenStats { if !updated.DisplayOpenAITokenStats {
t.Fatalf("DisplayOpenAITokenStats = false, want true") t.Fatalf("DisplayOpenAITokenStats = false, want true")
} }
if updated.DisplayAlertEvents {
t.Fatalf("DisplayAlertEvents = true, want false")
}
reloaded, err := svc.GetOpsAdvancedSettings(context.Background()) reloaded, err := svc.GetOpsAdvancedSettings(context.Background())
if err != nil { if err != nil {
...@@ -43,4 +51,47 @@ func TestUpdateOpsAdvancedSettings_PersistsOpenAITokenStatsVisibility(t *testing ...@@ -43,4 +51,47 @@ func TestUpdateOpsAdvancedSettings_PersistsOpenAITokenStatsVisibility(t *testing
if !reloaded.DisplayOpenAITokenStats { if !reloaded.DisplayOpenAITokenStats {
t.Fatalf("reloaded DisplayOpenAITokenStats = false, want true") t.Fatalf("reloaded DisplayOpenAITokenStats = false, want true")
} }
if reloaded.DisplayAlertEvents {
t.Fatalf("reloaded DisplayAlertEvents = true, want false")
}
}
func TestGetOpsAdvancedSettings_BackfillsNewDisplayFlagsFromDefaults(t *testing.T) {
repo := newRuntimeSettingRepoStub()
svc := &OpsService{settingRepo: repo}
legacyCfg := map[string]any{
"data_retention": map[string]any{
"cleanup_enabled": false,
"cleanup_schedule": "0 2 * * *",
"error_log_retention_days": 30,
"minute_metrics_retention_days": 30,
"hourly_metrics_retention_days": 30,
},
"aggregation": map[string]any{
"aggregation_enabled": false,
},
"ignore_count_tokens_errors": true,
"ignore_context_canceled": true,
"ignore_no_available_accounts": false,
"ignore_invalid_api_key_errors": false,
"auto_refresh_enabled": false,
"auto_refresh_interval_seconds": 30,
}
raw, err := json.Marshal(legacyCfg)
if err != nil {
t.Fatalf("marshal legacy config: %v", err)
}
repo.values[SettingKeyOpsAdvancedSettings] = string(raw)
cfg, err := svc.GetOpsAdvancedSettings(context.Background())
if err != nil {
t.Fatalf("GetOpsAdvancedSettings() error = %v", err)
}
if cfg.DisplayOpenAITokenStats {
t.Fatalf("DisplayOpenAITokenStats = true, want false default backfill")
}
if !cfg.DisplayAlertEvents {
t.Fatalf("DisplayAlertEvents = false, want true default backfill")
}
} }
...@@ -99,6 +99,7 @@ type OpsAdvancedSettings struct { ...@@ -99,6 +99,7 @@ type OpsAdvancedSettings struct {
IgnoreNoAvailableAccounts bool `json:"ignore_no_available_accounts"` IgnoreNoAvailableAccounts bool `json:"ignore_no_available_accounts"`
IgnoreInvalidApiKeyErrors bool `json:"ignore_invalid_api_key_errors"` IgnoreInvalidApiKeyErrors bool `json:"ignore_invalid_api_key_errors"`
DisplayOpenAITokenStats bool `json:"display_openai_token_stats"` DisplayOpenAITokenStats bool `json:"display_openai_token_stats"`
DisplayAlertEvents bool `json:"display_alert_events"`
AutoRefreshEnabled bool `json:"auto_refresh_enabled"` AutoRefreshEnabled bool `json:"auto_refresh_enabled"`
AutoRefreshIntervalSec int `json:"auto_refresh_interval_seconds"` AutoRefreshIntervalSec int `json:"auto_refresh_interval_seconds"`
} }
......
...@@ -842,6 +842,7 @@ export interface OpsAdvancedSettings { ...@@ -842,6 +842,7 @@ export interface OpsAdvancedSettings {
ignore_no_available_accounts: boolean ignore_no_available_accounts: boolean
ignore_invalid_api_key_errors: boolean ignore_invalid_api_key_errors: boolean
display_openai_token_stats: boolean display_openai_token_stats: boolean
display_alert_events: boolean
auto_refresh_enabled: boolean auto_refresh_enabled: boolean
auto_refresh_interval_seconds: number auto_refresh_interval_seconds: number
} }
......
...@@ -3652,6 +3652,8 @@ export default { ...@@ -3652,6 +3652,8 @@ export default {
refreshInterval30s: '30 seconds', refreshInterval30s: '30 seconds',
refreshInterval60s: '60 seconds', refreshInterval60s: '60 seconds',
dashboardCards: 'Dashboard Cards', dashboardCards: 'Dashboard Cards',
displayAlertEvents: 'Display alert events',
displayAlertEventsHint: 'Show or hide the recent alert events card on the ops dashboard. Enabled by default.',
displayOpenAITokenStats: 'Display OpenAI token request stats', displayOpenAITokenStats: 'Display OpenAI token request stats',
displayOpenAITokenStatsHint: 'Show or hide the OpenAI token request stats card on the ops dashboard. Hidden by default.', displayOpenAITokenStatsHint: 'Show or hide the OpenAI token request stats card on the ops dashboard. Hidden by default.',
autoRefreshCountdown: 'Auto refresh: {seconds}s', autoRefreshCountdown: 'Auto refresh: {seconds}s',
......
...@@ -3826,6 +3826,8 @@ export default { ...@@ -3826,6 +3826,8 @@ export default {
refreshInterval30s: '30 秒', refreshInterval30s: '30 秒',
refreshInterval60s: '60 秒', refreshInterval60s: '60 秒',
dashboardCards: '仪表盘卡片', dashboardCards: '仪表盘卡片',
displayAlertEvents: '展示告警事件',
displayAlertEventsHint: '控制运维监控仪表盘中告警事件卡片是否显示,默认开启。',
displayOpenAITokenStats: '展示 OpenAI Token 请求统计', displayOpenAITokenStats: '展示 OpenAI Token 请求统计',
displayOpenAITokenStatsHint: '控制运维监控仪表盘中 OpenAI Token 请求统计卡片是否显示,默认关闭。', displayOpenAITokenStatsHint: '控制运维监控仪表盘中 OpenAI Token 请求统计卡片是否显示,默认关闭。',
autoRefreshCountdown: '自动刷新:{seconds}s', autoRefreshCountdown: '自动刷新:{seconds}s',
......
...@@ -94,7 +94,7 @@ ...@@ -94,7 +94,7 @@
</div> </div>
<!-- Alert Events --> <!-- Alert Events -->
<OpsAlertEventsCard v-if="opsEnabled && !(loading && !hasLoadedOnce)" /> <OpsAlertEventsCard v-if="opsEnabled && showAlertEvents && !(loading && !hasLoadedOnce)" />
<!-- System Logs --> <!-- System Logs -->
<OpsSystemLogTable <OpsSystemLogTable
...@@ -381,6 +381,7 @@ const showSettingsDialog = ref(false) ...@@ -381,6 +381,7 @@ const showSettingsDialog = ref(false)
const showAlertRulesCard = ref(false) const showAlertRulesCard = ref(false)
// Auto refresh settings // Auto refresh settings
const showAlertEvents = ref(true)
const showOpenAITokenStats = ref(false) const showOpenAITokenStats = ref(false)
const autoRefreshEnabled = ref(false) const autoRefreshEnabled = ref(false)
const autoRefreshIntervalMs = ref(30000) // default 30 seconds const autoRefreshIntervalMs = ref(30000) // default 30 seconds
...@@ -413,12 +414,14 @@ const { pause: pauseCountdown, resume: resumeCountdown } = useIntervalFn( ...@@ -413,12 +414,14 @@ const { pause: pauseCountdown, resume: resumeCountdown } = useIntervalFn(
async function loadDashboardAdvancedSettings() { async function loadDashboardAdvancedSettings() {
try { try {
const settings = await opsAPI.getAdvancedSettings() const settings = await opsAPI.getAdvancedSettings()
showAlertEvents.value = settings.display_alert_events
showOpenAITokenStats.value = settings.display_openai_token_stats showOpenAITokenStats.value = settings.display_openai_token_stats
autoRefreshEnabled.value = settings.auto_refresh_enabled autoRefreshEnabled.value = settings.auto_refresh_enabled
autoRefreshIntervalMs.value = settings.auto_refresh_interval_seconds * 1000 autoRefreshIntervalMs.value = settings.auto_refresh_interval_seconds * 1000
autoRefreshCountdown.value = settings.auto_refresh_interval_seconds autoRefreshCountdown.value = settings.auto_refresh_interval_seconds
} catch (err) { } catch (err) {
console.error('[OpsDashboard] Failed to load dashboard advanced settings', err) console.error('[OpsDashboard] Failed to load dashboard advanced settings', err)
showAlertEvents.value = true
showOpenAITokenStats.value = false showOpenAITokenStats.value = false
autoRefreshEnabled.value = false autoRefreshEnabled.value = false
autoRefreshIntervalMs.value = 30000 autoRefreshIntervalMs.value = 30000
......
...@@ -548,6 +548,16 @@ async function saveAllSettings() { ...@@ -548,6 +548,16 @@ async function saveAllSettings() {
<div class="space-y-3"> <div class="space-y-3">
<h5 class="text-xs font-semibold text-gray-700 dark:text-gray-300">{{ t('admin.ops.settings.dashboardCards') }}</h5> <h5 class="text-xs font-semibold text-gray-700 dark:text-gray-300">{{ t('admin.ops.settings.dashboardCards') }}</h5>
<div class="flex items-center justify-between">
<div>
<label class="text-sm font-medium text-gray-700 dark:text-gray-300">{{ t('admin.ops.settings.displayAlertEvents') }}</label>
<p class="mt-1 text-xs text-gray-500">
{{ t('admin.ops.settings.displayAlertEventsHint') }}
</p>
</div>
<Toggle v-model="advancedSettings.display_alert_events" />
</div>
<div class="flex items-center justify-between"> <div class="flex items-center justify-between">
<div> <div>
<label class="text-sm font-medium text-gray-700 dark:text-gray-300">{{ t('admin.ops.settings.displayOpenAITokenStats') }}</label> <label class="text-sm font-medium text-gray-700 dark:text-gray-300">{{ t('admin.ops.settings.displayOpenAITokenStats') }}</label>
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment