mirror of
https://gitee.com/wanwujie/sub2api
synced 2026-04-02 22:42:14 +08:00
feat(ops): allow hiding alert events
This commit is contained in:
@@ -372,6 +372,7 @@ func defaultOpsAdvancedSettings() *OpsAdvancedSettings {
|
||||
IgnoreContextCanceled: true, // Default to true - client disconnects are not errors
|
||||
IgnoreNoAvailableAccounts: false, // Default to false - this is a real routing issue
|
||||
DisplayOpenAITokenStats: false,
|
||||
DisplayAlertEvents: true,
|
||||
AutoRefreshEnabled: false,
|
||||
AutoRefreshIntervalSec: 30,
|
||||
}
|
||||
@@ -439,7 +440,7 @@ func (s *OpsService) GetOpsAdvancedSettings(ctx context.Context) (*OpsAdvancedSe
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cfg := &OpsAdvancedSettings{}
|
||||
cfg := defaultOpsAdvancedSettings()
|
||||
if err := json.Unmarshal([]byte(raw), cfg); err != nil {
|
||||
return defaultCfg, nil
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"testing"
|
||||
)
|
||||
|
||||
@@ -16,6 +17,9 @@ func TestGetOpsAdvancedSettings_DefaultHidesOpenAITokenStats(t *testing.T) {
|
||||
if cfg.DisplayOpenAITokenStats {
|
||||
t.Fatalf("DisplayOpenAITokenStats = true, want false by default")
|
||||
}
|
||||
if !cfg.DisplayAlertEvents {
|
||||
t.Fatalf("DisplayAlertEvents = false, want true by default")
|
||||
}
|
||||
if repo.setCalls != 1 {
|
||||
t.Fatalf("expected defaults to be persisted once, got %d", repo.setCalls)
|
||||
}
|
||||
@@ -27,6 +31,7 @@ func TestUpdateOpsAdvancedSettings_PersistsOpenAITokenStatsVisibility(t *testing
|
||||
|
||||
cfg := defaultOpsAdvancedSettings()
|
||||
cfg.DisplayOpenAITokenStats = true
|
||||
cfg.DisplayAlertEvents = false
|
||||
|
||||
updated, err := svc.UpdateOpsAdvancedSettings(context.Background(), cfg)
|
||||
if err != nil {
|
||||
@@ -35,6 +40,9 @@ func TestUpdateOpsAdvancedSettings_PersistsOpenAITokenStatsVisibility(t *testing
|
||||
if !updated.DisplayOpenAITokenStats {
|
||||
t.Fatalf("DisplayOpenAITokenStats = false, want true")
|
||||
}
|
||||
if updated.DisplayAlertEvents {
|
||||
t.Fatalf("DisplayAlertEvents = true, want false")
|
||||
}
|
||||
|
||||
reloaded, err := svc.GetOpsAdvancedSettings(context.Background())
|
||||
if err != nil {
|
||||
@@ -43,4 +51,47 @@ func TestUpdateOpsAdvancedSettings_PersistsOpenAITokenStatsVisibility(t *testing
|
||||
if !reloaded.DisplayOpenAITokenStats {
|
||||
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 {
|
||||
IgnoreNoAvailableAccounts bool `json:"ignore_no_available_accounts"`
|
||||
IgnoreInvalidApiKeyErrors bool `json:"ignore_invalid_api_key_errors"`
|
||||
DisplayOpenAITokenStats bool `json:"display_openai_token_stats"`
|
||||
DisplayAlertEvents bool `json:"display_alert_events"`
|
||||
AutoRefreshEnabled bool `json:"auto_refresh_enabled"`
|
||||
AutoRefreshIntervalSec int `json:"auto_refresh_interval_seconds"`
|
||||
}
|
||||
|
||||
@@ -842,6 +842,7 @@ export interface OpsAdvancedSettings {
|
||||
ignore_no_available_accounts: boolean
|
||||
ignore_invalid_api_key_errors: boolean
|
||||
display_openai_token_stats: boolean
|
||||
display_alert_events: boolean
|
||||
auto_refresh_enabled: boolean
|
||||
auto_refresh_interval_seconds: number
|
||||
}
|
||||
|
||||
@@ -3652,6 +3652,8 @@ export default {
|
||||
refreshInterval30s: '30 seconds',
|
||||
refreshInterval60s: '60 seconds',
|
||||
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',
|
||||
displayOpenAITokenStatsHint: 'Show or hide the OpenAI token request stats card on the ops dashboard. Hidden by default.',
|
||||
autoRefreshCountdown: 'Auto refresh: {seconds}s',
|
||||
|
||||
@@ -3826,6 +3826,8 @@ export default {
|
||||
refreshInterval30s: '30 秒',
|
||||
refreshInterval60s: '60 秒',
|
||||
dashboardCards: '仪表盘卡片',
|
||||
displayAlertEvents: '展示告警事件',
|
||||
displayAlertEventsHint: '控制运维监控仪表盘中告警事件卡片是否显示,默认开启。',
|
||||
displayOpenAITokenStats: '展示 OpenAI Token 请求统计',
|
||||
displayOpenAITokenStatsHint: '控制运维监控仪表盘中 OpenAI Token 请求统计卡片是否显示,默认关闭。',
|
||||
autoRefreshCountdown: '自动刷新:{seconds}s',
|
||||
|
||||
@@ -94,7 +94,7 @@
|
||||
</div>
|
||||
|
||||
<!-- Alert Events -->
|
||||
<OpsAlertEventsCard v-if="opsEnabled && !(loading && !hasLoadedOnce)" />
|
||||
<OpsAlertEventsCard v-if="opsEnabled && showAlertEvents && !(loading && !hasLoadedOnce)" />
|
||||
|
||||
<!-- System Logs -->
|
||||
<OpsSystemLogTable
|
||||
@@ -381,6 +381,7 @@ const showSettingsDialog = ref(false)
|
||||
const showAlertRulesCard = ref(false)
|
||||
|
||||
// Auto refresh settings
|
||||
const showAlertEvents = ref(true)
|
||||
const showOpenAITokenStats = ref(false)
|
||||
const autoRefreshEnabled = ref(false)
|
||||
const autoRefreshIntervalMs = ref(30000) // default 30 seconds
|
||||
@@ -413,12 +414,14 @@ const { pause: pauseCountdown, resume: resumeCountdown } = useIntervalFn(
|
||||
async function loadDashboardAdvancedSettings() {
|
||||
try {
|
||||
const settings = await opsAPI.getAdvancedSettings()
|
||||
showAlertEvents.value = settings.display_alert_events
|
||||
showOpenAITokenStats.value = settings.display_openai_token_stats
|
||||
autoRefreshEnabled.value = settings.auto_refresh_enabled
|
||||
autoRefreshIntervalMs.value = settings.auto_refresh_interval_seconds * 1000
|
||||
autoRefreshCountdown.value = settings.auto_refresh_interval_seconds
|
||||
} catch (err) {
|
||||
console.error('[OpsDashboard] Failed to load dashboard advanced settings', err)
|
||||
showAlertEvents.value = true
|
||||
showOpenAITokenStats.value = false
|
||||
autoRefreshEnabled.value = false
|
||||
autoRefreshIntervalMs.value = 30000
|
||||
|
||||
@@ -548,6 +548,16 @@ async function saveAllSettings() {
|
||||
<div class="space-y-3">
|
||||
<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>
|
||||
<label class="text-sm font-medium text-gray-700 dark:text-gray-300">{{ t('admin.ops.settings.displayOpenAITokenStats') }}</label>
|
||||
|
||||
Reference in New Issue
Block a user