This commit is contained in:
ryan
2026-06-18 17:12:39 +08:00
parent 001f5c968e
commit d7fe4ef8be
4 changed files with 52 additions and 6 deletions
@@ -15,6 +15,7 @@ const (
errNodeIDConflict = "节点标识生成冲突,请重试"
errNodeNotFound = "节点不存在"
errNodeForceSyncFailed = "节点不在线或通过 WebSocket 发送同步指令失败"
errNoActiveConfigVersion = "当前没有激活版本"
errAgentPreviewTagInvalid = "指定版本不是 preview 发布"
errAgentStableTagInvalid = "正式版更新不能选择 preview 发布"
)
+17 -5
View File
@@ -12,7 +12,9 @@ import (
"github.com/Rain-kl/Wavelet/internal/apps/openflare/observability"
"github.com/Rain-kl/Wavelet/internal/apps/openflare/option"
ofws "github.com/Rain-kl/Wavelet/internal/apps/openflare/websocket"
"github.com/Rain-kl/Wavelet/internal/model"
"gorm.io/gorm"
)
// Input is the create/update node payload.
@@ -326,13 +328,23 @@ func RequestOpenrestyRestart(ctx context.Context, id uint) (*View, error) {
return buildNodeView(node), nil
}
// RequestForceSync requests a force sync via websocket (stub until T-AGENT).
// RequestForceSync pushes force_sync_config to a connected agent websocket.
func RequestForceSync(ctx context.Context, id uint) (*View, error) {
node, err := model.GetOpenFlareNodeByID(ctx, id)
if err != nil {
return nil, err
}
if !sendAgentWSForceSyncConfig(node.NodeID) {
activeConfig, err := model.GetActiveConfigVersion(ctx)
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, fmt.Errorf("无法获取当前激活的配置版本:%s", errNoActiveConfigVersion)
}
return nil, fmt.Errorf("无法获取当前激活的配置版本:%v", err)
}
if !ofws.SendForceSyncConfig(node.NodeID, forceSyncConfigPayload{
Version: activeConfig.Version,
Checksum: activeConfig.Checksum,
}) {
return nil, errors.New(errNodeForceSyncFailed)
}
return buildNodeView(node), nil
@@ -371,9 +383,9 @@ func ensureGlobalDiscoveryToken(ctx context.Context) (string, error) {
return token, nil
}
func sendAgentWSForceSyncConfig(nodeID string) bool {
_ = nodeID
return false
type forceSyncConfigPayload struct {
Version string `json:"version"`
Checksum string `json:"checksum"`
}
// ValidateDiscoveryToken validates the global discovery token.
@@ -244,10 +244,26 @@ func TestRequestOpenrestyRestart(t *testing.T) {
assert.True(t, updated.RestartOpenrestyRequested)
}
func TestRequestForceSyncStub(t *testing.T) {
func seedActiveConfigVersion(t *testing.T, ctx context.Context) {
t.Helper()
conn := db.DB(ctx)
require.NotNil(t, conn)
require.NoError(t, conn.AutoMigrate(&model.ConfigVersion{}))
require.NoError(t, conn.Create(&model.ConfigVersion{
Version: "20260618-001",
SnapshotJSON: `{}`,
RenderedConfig: `server {}`,
Checksum: "abc123",
IsActive: true,
CreatedBy: "test",
}).Error)
}
func TestRequestForceSyncRequiresWebSocket(t *testing.T) {
cleanup := setupNodeTestDB(t)
defer cleanup()
ctx := context.Background()
seedActiveConfigVersion(t, ctx)
created, err := CreateNode(ctx, Input{Name: "edge-sync"})
require.NoError(t, err)
@@ -257,6 +273,22 @@ func TestRequestForceSyncStub(t *testing.T) {
assert.Equal(t, errNodeForceSyncFailed, err.Error())
}
func TestRequestForceSyncRequiresActiveConfig(t *testing.T) {
cleanup := setupNodeTestDB(t)
defer cleanup()
ctx := context.Background()
conn := db.DB(ctx)
require.NotNil(t, conn)
require.NoError(t, conn.AutoMigrate(&model.ConfigVersion{}))
created, err := CreateNode(ctx, Input{Name: "edge-sync-active"})
require.NoError(t, err)
_, err = RequestForceSync(ctx, created.ID)
require.Error(t, err)
assert.Contains(t, err.Error(), errNoActiveConfigVersion)
}
func TestGetObservabilityStub(t *testing.T) {
cleanup := setupNodeTestDB(t)
defer cleanup()
+1
View File
@@ -37,6 +37,7 @@ sidebar: false
- 新增 `internal/apps/openflare/tasks/` 集中承载 OpenFlare 定时/后台任务(主进程 cron,非 Asynq),含数据库可观测性自动清理、WAF IP 组周期同步、UptimeKuma 同步、ACME 证书自动续期。
- 实装数据库可观测性手动/自动清理、WAF IP 组订阅/自动同步与测试接口、UptimeKuma 监控同步、TLS ACME 申请/续期(lego DNS-01)。
- 修复 Wavelet Agent WebSocket 未处理 `status` 消息导致 WS 模式下 `last_seen_at` 停止更新、节点超时显示离线的问题;列表「最近心跳」恢复显示「WS 已连接」。
- 修复节点「强制同步」仍为 stub 导致始终返回「节点不在线或通过 WebSocket 发送同步指令失败」的问题。
## [v2.3.4] - 2026-06-17