feat(web): 仪表盘补系统资源/中国 IP 段/通知通道与公网穿透卡,修复 frpc 版本读取
仪表盘:修「服务」卡写死的「正常」——改为按 Fonu/Nginx/DDNS/证书四项实际状态聚合,异常时点名并降级徽标 仪表盘:新增「性能」面板(今日异常/平均响应,/api/status 早已返回但从未展示)与「流量 Top 5」(复用已有逐规则流量轮询);/api/status 每 15 秒刷新并在页面隐藏时暂停(连同流量轮询) 仪表盘:新增「公网穿透」统计卡;运行状态面板增加公网反代行,数据来自新增的 GET /api/frpmulti/routes/summary(逐台服务端聚合,单台失败不中断) 仪表盘:新增一行三卡「系统资源 / 中国 IP 段 / 通知通道」;系统资源为新增跨平台采集 internal/sysinfo(Linux /proc、Windows kernel32,无新依赖) FRP:修复 frpc 版本读不出来——RuntimeStatus.Version 带 omitempty,--version 探测失败时字段被省略,前端只能显示未检测;现回落按 <FrpDir>/bin/<版本>/ 目录名推导(与「FRP 应用配置」弹窗的 active_version 同源),并逐台服务端探测版本 清理:删除 DashboardView 死样式(.dashboard/.stat-highlight/.proxy-metric*)
This commit is contained in:
+78
-2
@@ -836,6 +836,64 @@ func (s *Service) InstallAgentNginx(ctx context.Context, serverID int64) (map[st
|
||||
return client.InstallNginx(ctx)
|
||||
}
|
||||
|
||||
// RouteHealthSummary 公网反代部署健康的汇总(仪表盘用)。
|
||||
// 健康判定与规则页一致:四段(DNS/证书/隧道/内网服务)全通过才算健康;
|
||||
// 一个域名可同时命中多段失败,因此各段计数之和可能大于 Routes-Healthy。
|
||||
type RouteHealthSummary struct {
|
||||
Servers int `json:"servers"`
|
||||
Routes int `json:"routes"`
|
||||
Healthy int `json:"healthy"`
|
||||
DNSFailed int `json:"dns_failed"`
|
||||
CertMissing int `json:"cert_missing"`
|
||||
TunnelDown int `json:"tunnel_down"`
|
||||
ServiceDown int `json:"service_down"`
|
||||
FailedServers []string `json:"failed_servers,omitempty"`
|
||||
}
|
||||
|
||||
// SummarizeRouteHealth 逐台已启用服务端采集公网反代健康后汇总。
|
||||
// 单台服务端采集失败只记名并继续其它服务端,不阻断整轮汇总。
|
||||
func (s *Service) SummarizeRouteHealth(ctx context.Context) (RouteHealthSummary, error) {
|
||||
servers, err := s.store.ListEnabledServers(ctx)
|
||||
if err != nil {
|
||||
return RouteHealthSummary{}, err
|
||||
}
|
||||
var (
|
||||
items []RouteHealthCandidate
|
||||
failed []string
|
||||
)
|
||||
for _, server := range servers {
|
||||
candidates, err := s.RouteHealth(ctx, server.ID)
|
||||
if err != nil {
|
||||
failed = append(failed, server.Name)
|
||||
continue
|
||||
}
|
||||
items = append(items, candidates...)
|
||||
}
|
||||
return summarizeRouteHealth(len(servers), failed, items), nil
|
||||
}
|
||||
|
||||
func summarizeRouteHealth(servers int, failedServers []string, items []RouteHealthCandidate) RouteHealthSummary {
|
||||
summary := RouteHealthSummary{Servers: servers, Routes: len(items), FailedServers: failedServers}
|
||||
for _, item := range items {
|
||||
if !item.DNSOK {
|
||||
summary.DNSFailed++
|
||||
}
|
||||
if !item.CertOK {
|
||||
summary.CertMissing++
|
||||
}
|
||||
if !item.TunnelOK {
|
||||
summary.TunnelDown++
|
||||
}
|
||||
if !item.ServiceOK {
|
||||
summary.ServiceDown++
|
||||
}
|
||||
if item.DNSOK && item.CertOK && item.TunnelOK && item.ServiceOK {
|
||||
summary.Healthy++
|
||||
}
|
||||
}
|
||||
return summary
|
||||
}
|
||||
|
||||
// RouteHealthCandidate 公网反代规则的四段健康状态(DNS/证书/隧道/内网服务)。
|
||||
type RouteHealthCandidate struct {
|
||||
ProxyID int64 `json:"proxy_id"`
|
||||
@@ -1464,11 +1522,14 @@ func (s *Service) Status(ctx context.Context) RuntimeStatus {
|
||||
s.manager.RefreshVersion(versionCtx)
|
||||
servers, err := s.store.ListEnabledServers(ctx)
|
||||
if err != nil {
|
||||
return s.manager.Status()
|
||||
return s.statusWithVersion(s.manager)
|
||||
}
|
||||
statuses := make([]RuntimeStatus, 0, len(servers))
|
||||
for _, server := range servers {
|
||||
current := s.managerForServer(server.ID).Status()
|
||||
manager := s.managerForServer(server.ID)
|
||||
// 每个服务端各自探测版本(共用同一个 2 秒超时上下文)
|
||||
manager.RefreshVersion(versionCtx)
|
||||
current := s.statusWithVersion(manager)
|
||||
current.Configured = s.serverConfigured(server)
|
||||
if proxies, proxyErr := s.store.ListEnabledProxies(ctx, server.ID); proxyErr == nil {
|
||||
current.EnabledProxies = len(proxies)
|
||||
@@ -1478,6 +1539,21 @@ func (s *Service) Status(ctx context.Context) RuntimeStatus {
|
||||
return latestRuntimeStatus(statuses)
|
||||
}
|
||||
|
||||
// statusWithVersion 取服务端运行状态,并在版本探测失败时按二进制目录名推导版本:
|
||||
// frpc 进程未运行或 --version 探测失败时,RuntimeStatus.Version 因带 omitempty 会被省略,
|
||||
// 前端(FRP 穿透页状态卡、仪表盘)就只能显示「未检测 / 未知」。
|
||||
// 这里回落到 <FrpDir>/bin/<版本>/ 的目录约定,与「FRP 应用配置」弹窗的 active_version 同源。
|
||||
func (s *Service) statusWithVersion(manager *Manager) RuntimeStatus {
|
||||
status := manager.Status()
|
||||
if status.Version != "" {
|
||||
return status
|
||||
}
|
||||
if bin, err := manager.BinaryPath(); err == nil {
|
||||
status.Version = versionFromBinaryPath(s.cfg.FrpDir(), bin)
|
||||
}
|
||||
return status
|
||||
}
|
||||
|
||||
func (s *Service) ReadLogs(limit int, serverID int64) ([]string, error) {
|
||||
if limit <= 0 || limit > 500 {
|
||||
limit = 200
|
||||
|
||||
Reference in New Issue
Block a user