feat(channel-monitor): add feature switch settings + fix extra_models save
Settings:
- New "功能开关" tab between 通用设置 and 安全与认证
- ChannelMonitorEnabled toggle: runner skips scheduling when false,
user-facing list returns empty
- ChannelMonitorDefaultIntervalSeconds (15-3600): pre-fills interval
when creating a new monitor; each monitor can still override
Bug fix:
- ModelTagInput now commits pending input on blur, not just Enter/Tab.
Previously clicking "save" with an un-Enter'd extra model would drop
the value (DB stored extra_models=[] even when user typed entries).
Backend:
- domain_constants: SettingKeyChannelMonitor{Enabled,DefaultIntervalSeconds}
- SettingService.GetChannelMonitorRuntime: lightweight getter used by
runner tick + user handler per-request (fail-open on DB error)
- Runner tickDueChecks: bails early when feature disabled
- ChannelMonitorUserHandler: checks feature flag before serving
- Comment on runner doc: scheduler state is implicit (every tick re-reads
ListEnabled from DB), so CRUD ops on monitors self-maintain the schedule
Bump VERSION to 0.1.114.25
This commit is contained in:
@@ -450,6 +450,8 @@ func (s *SettingService) GetPublicSettings(ctx context.Context) (*PublicSettings
|
||||
SettingKeyBalanceLowNotifyThreshold,
|
||||
SettingKeyBalanceLowNotifyRechargeURL,
|
||||
SettingKeyAccountQuotaNotifyEnabled,
|
||||
SettingKeyChannelMonitorEnabled,
|
||||
SettingKeyChannelMonitorDefaultIntervalSeconds,
|
||||
}
|
||||
|
||||
settings, err := s.settingRepo.GetMultiple(ctx, keys)
|
||||
@@ -532,9 +534,67 @@ func (s *SettingService) GetPublicSettings(ctx context.Context) (*PublicSettings
|
||||
AccountQuotaNotifyEnabled: settings[SettingKeyAccountQuotaNotifyEnabled] == "true",
|
||||
BalanceLowNotifyThreshold: balanceLowNotifyThreshold,
|
||||
BalanceLowNotifyRechargeURL: settings[SettingKeyBalanceLowNotifyRechargeURL],
|
||||
|
||||
ChannelMonitorEnabled: !isFalseSettingValue(settings[SettingKeyChannelMonitorEnabled]),
|
||||
ChannelMonitorDefaultIntervalSeconds: parseChannelMonitorInterval(settings[SettingKeyChannelMonitorDefaultIntervalSeconds]),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// channelMonitorIntervalMin / channelMonitorIntervalMax bound the default interval
|
||||
// (mirrors the monitor-level constraint but lives here so setting_service stays decoupled).
|
||||
const (
|
||||
channelMonitorIntervalMin = 15
|
||||
channelMonitorIntervalMax = 3600
|
||||
channelMonitorIntervalFallback = 60
|
||||
)
|
||||
|
||||
// parseChannelMonitorInterval parses the stored string and clamps to [15, 3600].
|
||||
// Empty / invalid input falls back to channelMonitorIntervalFallback.
|
||||
func parseChannelMonitorInterval(raw string) int {
|
||||
v, err := strconv.Atoi(strings.TrimSpace(raw))
|
||||
if err != nil {
|
||||
return channelMonitorIntervalFallback
|
||||
}
|
||||
return clampChannelMonitorInterval(v)
|
||||
}
|
||||
|
||||
// clampChannelMonitorInterval clamps v to the allowed range. 0 means "not provided".
|
||||
func clampChannelMonitorInterval(v int) int {
|
||||
if v <= 0 {
|
||||
return 0
|
||||
}
|
||||
if v < channelMonitorIntervalMin {
|
||||
return channelMonitorIntervalMin
|
||||
}
|
||||
if v > channelMonitorIntervalMax {
|
||||
return channelMonitorIntervalMax
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
// ChannelMonitorRuntime is the lightweight view of the channel monitor feature
|
||||
// consumed by the runner and user-facing handlers.
|
||||
type ChannelMonitorRuntime struct {
|
||||
Enabled bool
|
||||
DefaultIntervalSeconds int
|
||||
}
|
||||
|
||||
// GetChannelMonitorRuntime reads the channel monitor feature flags directly from
|
||||
// the settings store. Fail-open: on error returns Enabled=true with the default interval.
|
||||
func (s *SettingService) GetChannelMonitorRuntime(ctx context.Context) ChannelMonitorRuntime {
|
||||
vals, err := s.settingRepo.GetMultiple(ctx, []string{
|
||||
SettingKeyChannelMonitorEnabled,
|
||||
SettingKeyChannelMonitorDefaultIntervalSeconds,
|
||||
})
|
||||
if err != nil {
|
||||
return ChannelMonitorRuntime{Enabled: true, DefaultIntervalSeconds: channelMonitorIntervalFallback}
|
||||
}
|
||||
return ChannelMonitorRuntime{
|
||||
Enabled: !isFalseSettingValue(vals[SettingKeyChannelMonitorEnabled]),
|
||||
DefaultIntervalSeconds: parseChannelMonitorInterval(vals[SettingKeyChannelMonitorDefaultIntervalSeconds]),
|
||||
}
|
||||
}
|
||||
|
||||
// SetOnUpdateCallback sets a callback function to be called when settings are updated
|
||||
// This is used for cache invalidation (e.g., HTML cache in frontend server)
|
||||
func (s *SettingService) SetOnUpdateCallback(callback func()) {
|
||||
@@ -1085,6 +1145,12 @@ func (s *SettingService) buildSystemSettingsUpdates(ctx context.Context, setting
|
||||
updates[SettingKeyOpsMetricsIntervalSeconds] = strconv.Itoa(settings.OpsMetricsIntervalSeconds)
|
||||
}
|
||||
|
||||
// Channel monitor feature switch
|
||||
updates[SettingKeyChannelMonitorEnabled] = strconv.FormatBool(settings.ChannelMonitorEnabled)
|
||||
if v := clampChannelMonitorInterval(settings.ChannelMonitorDefaultIntervalSeconds); v > 0 {
|
||||
updates[SettingKeyChannelMonitorDefaultIntervalSeconds] = strconv.Itoa(v)
|
||||
}
|
||||
|
||||
// Claude Code version check
|
||||
updates[SettingKeyMinClaudeCodeVersion] = settings.MinClaudeCodeVersion
|
||||
updates[SettingKeyMaxClaudeCodeVersion] = settings.MaxClaudeCodeVersion
|
||||
@@ -1630,6 +1696,10 @@ func (s *SettingService) InitializeDefaultSettings(ctx context.Context) error {
|
||||
SettingKeyOpsQueryModeDefault: "auto",
|
||||
SettingKeyOpsMetricsIntervalSeconds: "60",
|
||||
|
||||
// Channel monitor defaults (enabled, 60s)
|
||||
SettingKeyChannelMonitorEnabled: "true",
|
||||
SettingKeyChannelMonitorDefaultIntervalSeconds: "60",
|
||||
|
||||
// Claude Code version check (default: empty = disabled)
|
||||
SettingKeyMinClaudeCodeVersion: "",
|
||||
SettingKeyMaxClaudeCodeVersion: "",
|
||||
@@ -1932,6 +2002,12 @@ func (s *SettingService) parseSettings(settings map[string]string) *SystemSettin
|
||||
}
|
||||
}
|
||||
|
||||
// Channel monitor feature (default: enabled, 60s)
|
||||
result.ChannelMonitorEnabled = !isFalseSettingValue(settings[SettingKeyChannelMonitorEnabled])
|
||||
result.ChannelMonitorDefaultIntervalSeconds = parseChannelMonitorInterval(
|
||||
settings[SettingKeyChannelMonitorDefaultIntervalSeconds],
|
||||
)
|
||||
|
||||
// Claude Code version check
|
||||
result.MinClaudeCodeVersion = settings[SettingKeyMinClaudeCodeVersion]
|
||||
result.MaxClaudeCodeVersion = settings[SettingKeyMaxClaudeCodeVersion]
|
||||
|
||||
Reference in New Issue
Block a user