mirror of
https://github.com/Rain-kl/OpenFlare.git
synced 2026-10-07 08:06:37 +08:00
2f60329886
代码质量全量清理,零行为变化:golangci 扩展集 13 类 linter(gosec/modernize/perfsprint/canonicalheader/usestdlibvars/wastedassign/intrange/errorlint/forcetypeassert/recvcheck/exhaustive/unparam)全量修复,测试代码质量(testifylint/thelper/usetesting)25→0,frpc 进程生命周期真 bug(进程组击杀)、全仓 go test -race 6 类数据竞争(含 1 个生产竞争)、SPDX license 头补齐 131 文件、前端测试套件 next-intl 迁移后 44 失败→全绿、过期 swagger 文档重新生成、pnpm-workspace 构建审批。 Experiments: #2-#17, #18, #20, #21, #23 Metric: total_issues 108 → 8 (-92.6%)
103 lines
3.2 KiB
Go
103 lines
3.2 KiB
Go
// Copyright 2026 Arctel.net
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
// Package heartbeat sends periodic relay node status to the control plane.
|
|
package heartbeat
|
|
|
|
import (
|
|
"context"
|
|
"log/slog"
|
|
|
|
edgeheartbeat "github.com/Rain-kl/Wavelet/internal/apps/edge/heartbeat"
|
|
"github.com/Rain-kl/Wavelet/internal/apps/edge/nodeip"
|
|
"github.com/Rain-kl/Wavelet/internal/apps/relay/config"
|
|
"github.com/Rain-kl/Wavelet/internal/apps/relay/frps"
|
|
"github.com/Rain-kl/Wavelet/internal/apps/relay/httpclient"
|
|
"github.com/Rain-kl/Wavelet/internal/apps/relay/observability"
|
|
"github.com/Rain-kl/Wavelet/internal/apps/relay/state"
|
|
"github.com/Rain-kl/Wavelet/internal/apps/relay/updater"
|
|
service "github.com/Rain-kl/Wavelet/pkg/protocol"
|
|
)
|
|
|
|
// Service sends periodic heartbeat payloads to the server, updates the frps
|
|
// configuration from the server response, and triggers auto-update checks.
|
|
type Service struct {
|
|
client *httpclient.Client
|
|
frpsManager *frps.Manager
|
|
config *config.Config
|
|
stateStore *state.Store
|
|
updater *updater.Service
|
|
}
|
|
|
|
// New constructs a Service using the provided HTTP client, frps manager,
|
|
// configuration, and persistent state store.
|
|
func New(client *httpclient.Client, manager *frps.Manager, cfg *config.Config, stateStore *state.Store) *Service {
|
|
return &Service{
|
|
client: client,
|
|
frpsManager: manager,
|
|
config: cfg,
|
|
stateStore: stateStore,
|
|
updater: updater.New(),
|
|
}
|
|
}
|
|
|
|
// Run starts the heartbeat loop and blocks until ctx is cancelled.
|
|
func (s *Service) Run(ctx context.Context) {
|
|
edgeheartbeat.RunLoop(ctx, s.config.HeartbeatInterval.Duration(), s.doHeartbeat)
|
|
}
|
|
|
|
func (s *Service) doHeartbeat(ctx context.Context) {
|
|
slog.Debug("sending heartbeat")
|
|
|
|
runtimeStatus := s.frpsManager.GetRuntimeStatus()
|
|
|
|
ip := s.config.NodeIP
|
|
if !s.config.NodeIPConfigured {
|
|
ip = nodeip.DetectWithContext(ctx)
|
|
}
|
|
|
|
payload := service.RelayHeartbeatPayload{
|
|
Version: config.Version,
|
|
ExtVersion: s.frpsManager.GetVersion(ctx),
|
|
RelayStatus: runtimeStatus.Status,
|
|
FrpsConnCount: runtimeStatus.Connections,
|
|
FrpsProxyCount: runtimeStatus.ProxyCount,
|
|
FrpsClientCount: runtimeStatus.ClientCount,
|
|
FrpsProxies: runtimeStatus.Proxies,
|
|
Name: s.config.NodeName,
|
|
IP: ip,
|
|
Profile: observability.BuildProfile(s.config, s.stateStore),
|
|
Snapshot: observability.BuildSnapshot(s.config, s.stateStore),
|
|
HealthEvents: observability.BuildHealthEvents(runtimeStatus),
|
|
}
|
|
|
|
resp, err := s.client.Heartbeat(ctx, payload)
|
|
if err != nil {
|
|
slog.Error("heartbeat failed", "error", err)
|
|
return
|
|
}
|
|
slog.Debug("heartbeat succeeded")
|
|
|
|
// Update configs if changed
|
|
if resp != nil {
|
|
s.frpsManager.UpdateConfig(ctx, resp.RelayConfig)
|
|
|
|
if resp.RelaySettings != nil {
|
|
edgeheartbeat.TryAutoUpdate(ctx, s.updater, relaySettingsToAutoUpdate(resp.RelaySettings), "relay")
|
|
}
|
|
}
|
|
}
|
|
|
|
func relaySettingsToAutoUpdate(settings *service.RelaySettings) *edgeheartbeat.AutoUpdateSettings {
|
|
if settings == nil {
|
|
return nil
|
|
}
|
|
return &edgeheartbeat.AutoUpdateSettings{
|
|
AutoUpdate: settings.AutoUpdate,
|
|
UpdateNow: settings.UpdateNow,
|
|
UpdateRepo: settings.UpdateRepo,
|
|
UpdateChannel: settings.UpdateChannel,
|
|
UpdateTag: settings.UpdateTag,
|
|
}
|
|
}
|