feat: 自动备份与保留策略、健康持续化与状态页、指标历史与趋势图,并修隧道判定误报与仪表盘布局(v1.2.11)
- 自动备份:每日一份(按最新备份是否在 24h 内判定,重启不重复)、保留份数可配、列表/下载/恢复/删除,恢复前先校验归档;顺手修 writeArchive 逐个检查 tar/gzip/文件 Close()(审查报告 §二) - 健康持续化:只存状态变更(up/down/unknown)并推导可用率与不可用区间;公开状态页默认关闭、访问码存 SHA-256、按 IP 限流、只暴露域名与可用率 - 隧道判定修复:RouteHealthCandidate 新增 tunnel_known,frps 管理接口不可用时不再把「判不出来」当成「不通」(此前会误报「隧道连续不通」);汇总与徽标同样区分「无法判定」 - 指标历史:metrics_samples 纵向点表(本机 CPU/内存/磁盘 + 每规则上下行,30 天保留、查询按桶平均、不做降采样表);仪表盘趋势卡支持 1h/24h/7d - 仪表盘布局:运行状态并入顶部统计区(运行时长补进「服务」卡);性能并入系统资源那一行,整行 4 列 - 测试:backup/metrics/frp/api/app 新增用例;gofmt、go build/vet/test、vue-tsc、vite build 全绿 - agent 无改动,AgentVersion 保持 0.12.2
This commit is contained in:
@@ -14,6 +14,7 @@ const (
|
||||
EventFrpsServerDown = "frps_server_down"
|
||||
EventAgentUnreachable = "agent_unreachable"
|
||||
EventRouteDrift = "route_drift"
|
||||
EventBackupFailure = "backup_failure"
|
||||
|
||||
MaskedSecret = "********"
|
||||
|
||||
@@ -125,6 +126,7 @@ type Config struct {
|
||||
FrpsFailThreshold int `json:"frps_fail_threshold"`
|
||||
AgentFailThreshold int `json:"agent_fail_threshold"`
|
||||
DriftCooldownHours int `json:"drift_cooldown_hours"`
|
||||
OnBackupFailure bool `json:"on_backup_failure"`
|
||||
}
|
||||
|
||||
type RuntimeConfig struct {
|
||||
@@ -166,6 +168,7 @@ type SaveInput struct {
|
||||
FrpsFailThreshold int
|
||||
AgentFailThreshold int
|
||||
DriftCooldownHours int
|
||||
OnBackupFailure bool
|
||||
}
|
||||
|
||||
type TestInput struct {
|
||||
|
||||
@@ -96,6 +96,8 @@ func eventMetaFor(event string) eventMeta {
|
||||
return eventMeta{"📡", "隧道"}
|
||||
case EventRouteDrift:
|
||||
return eventMeta{"🧭", "配置"}
|
||||
case EventBackupFailure:
|
||||
return eventMeta{"💾", "系统"}
|
||||
case "test":
|
||||
return eventMeta{"🔔", "系统"}
|
||||
default:
|
||||
|
||||
@@ -252,6 +252,8 @@ func (s *Service) enabledFor(cfg RuntimeConfig, event string) bool {
|
||||
return cfg.OnAgentUnreachable
|
||||
case EventRouteDrift:
|
||||
return cfg.OnRouteDrift
|
||||
case EventBackupFailure:
|
||||
return cfg.OnBackupFailure
|
||||
default:
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -99,6 +99,8 @@ func (s *Store) load(ctx context.Context, decrypt bool) (Config, runtimeSecrets,
|
||||
cfg.OnFrpsDown = flag(s.settings, ctx, settings.KeyNotifyOnFrpsDown, false)
|
||||
cfg.OnAgentUnreachable = flag(s.settings, ctx, settings.KeyNotifyOnAgentUnreachable, false)
|
||||
cfg.OnRouteDrift = flag(s.settings, ctx, settings.KeyNotifyOnRouteDrift, false)
|
||||
// 备份失败属于「你以为有备份其实没有」,默认开启
|
||||
cfg.OnBackupFailure = flag(s.settings, ctx, settings.KeyNotifyOnBackupFailure, true)
|
||||
} else {
|
||||
legacyDDNS := flag(s.settings, ctx, settings.KeyNotifyOnDDNSError, true)
|
||||
legacyCert := flag(s.settings, ctx, settings.KeyNotifyOnCertError, true)
|
||||
@@ -202,6 +204,7 @@ func hasNotifyEventSettings(ctx context.Context, store *settings.Store) bool {
|
||||
settings.KeyNotifyOnFrpsDown,
|
||||
settings.KeyNotifyOnAgentUnreachable,
|
||||
settings.KeyNotifyOnRouteDrift,
|
||||
settings.KeyNotifyOnBackupFailure,
|
||||
}
|
||||
for _, key := range keys {
|
||||
raw, err := store.Get(ctx, key)
|
||||
@@ -271,6 +274,9 @@ func (s *Store) Save(ctx context.Context, in SaveInput) error {
|
||||
if err := s.settings.SetBool(ctx, settings.KeyNotifyOnRouteDrift, in.OnRouteDrift); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.settings.SetBool(ctx, settings.KeyNotifyOnBackupFailure, in.OnBackupFailure); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.settings.SetInt(ctx, settings.KeyNotifyFrpsFailThreshold, clampThreshold(in.FrpsFailThreshold, DefaultFrpsFailThreshold)); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -523,6 +529,7 @@ func ParseSaveInputFromMap(values map[string]string) (SaveInput, bool) {
|
||||
in.OnFrpsDown = values[settings.KeyNotifyOnFrpsDown] == "1" || strings.EqualFold(values[settings.KeyNotifyOnFrpsDown], "true")
|
||||
in.OnAgentUnreachable = values[settings.KeyNotifyOnAgentUnreachable] == "1" || strings.EqualFold(values[settings.KeyNotifyOnAgentUnreachable], "true")
|
||||
in.OnRouteDrift = values[settings.KeyNotifyOnRouteDrift] == "1" || strings.EqualFold(values[settings.KeyNotifyOnRouteDrift], "true")
|
||||
in.OnBackupFailure = values[settings.KeyNotifyOnBackupFailure] == "1" || strings.EqualFold(values[settings.KeyNotifyOnBackupFailure], "true")
|
||||
in.FrpsFailThreshold = intSettingFromMap(values, settings.KeyNotifyFrpsFailThreshold, DefaultFrpsFailThreshold)
|
||||
in.AgentFailThreshold = intSettingFromMap(values, settings.KeyNotifyAgentFailThreshold, DefaultAgentFailThreshold)
|
||||
in.DriftCooldownHours = intSettingFromMap(values, settings.KeyNotifyDriftCooldownHours, DefaultDriftCooldownHours)
|
||||
|
||||
Reference in New Issue
Block a user