mirror of
https://github.com/Rain-kl/OpenFlare.git
synced 2026-09-28 05:46:36 +08:00
fix(cloudflare): handle missing nodes in group detail responses
This commit is contained in:
@@ -255,11 +255,11 @@ func validEdgeNode(ctx context.Context, id uint, requireIPv4 bool) (*model.OpenF
|
||||
}
|
||||
|
||||
func buildGroupItem(ctx context.Context, group *model.CFPointingGroup) (*GroupItem, error) {
|
||||
primary, err := repository.GetOpenFlareNodeByID(ctx, group.PrimaryNodeID)
|
||||
primary, err := lookupGroupNode(ctx, group.ID, group.PrimaryNodeID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
active, err := repository.GetOpenFlareNodeByID(ctx, group.ActiveNodeID)
|
||||
active, err := lookupGroupNode(ctx, group.ID, group.ActiveNodeID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -267,18 +267,34 @@ func buildGroupItem(ctx context.Context, group *model.CFPointingGroup) (*GroupIt
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
item := &GroupItem{ID: group.ID, Name: group.Name, PrimaryNode: nodeOption(primary), ActiveNode: nodeOption(active), DefaultProxied: group.DefaultProxied, Enabled: group.Enabled, MemberCount: count, CreatedAt: group.CreatedAt, UpdatedAt: group.UpdatedAt}
|
||||
item := &GroupItem{ID: group.ID, Name: group.Name, PrimaryNode: nodeOptionForID(group.PrimaryNodeID, primary), ActiveNode: nodeOptionForID(group.ActiveNodeID, active), DefaultProxied: group.DefaultProxied, Enabled: group.Enabled, MemberCount: count, CreatedAt: group.CreatedAt, UpdatedAt: group.UpdatedAt}
|
||||
if group.BackupNodeID != nil {
|
||||
backup, backupErr := repository.GetOpenFlareNodeByID(ctx, *group.BackupNodeID)
|
||||
backup, backupErr := lookupGroupNode(ctx, group.ID, *group.BackupNodeID)
|
||||
if backupErr != nil {
|
||||
return nil, backupErr
|
||||
}
|
||||
option := nodeOption(backup)
|
||||
option := nodeOptionForID(*group.BackupNodeID, backup)
|
||||
item.BackupNode = &option
|
||||
}
|
||||
return item, nil
|
||||
}
|
||||
|
||||
func lookupGroupNode(ctx context.Context, groupID, nodeID uint) (*model.OpenFlareNode, error) {
|
||||
node, err := repository.GetOpenFlareNodeByID(ctx, nodeID)
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
logger.WarnF(ctx, "[Cloudflare] pointing group references missing node: group_id=%d node_id=%d", groupID, nodeID)
|
||||
return nil, nil
|
||||
}
|
||||
return node, err
|
||||
}
|
||||
|
||||
func nodeOptionForID(id uint, node *model.OpenFlareNode) NodeOption {
|
||||
if node == nil {
|
||||
return NodeOption{ID: id}
|
||||
}
|
||||
return nodeOption(node)
|
||||
}
|
||||
|
||||
func nodeOption(node *model.OpenFlareNode) NodeOption {
|
||||
return NodeOption{ID: node.ID, Name: node.Name, IP: node.IP}
|
||||
}
|
||||
|
||||
@@ -4,12 +4,14 @@
|
||||
package cloudflare
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
db "github.com/Rain-kl/Wavelet/internal/infra/persistence"
|
||||
"github.com/Rain-kl/Wavelet/internal/model"
|
||||
"github.com/Rain-kl/Wavelet/internal/repository"
|
||||
"github.com/Rain-kl/Wavelet/internal/shared/response"
|
||||
"github.com/gin-gonic/gin"
|
||||
@@ -75,3 +77,56 @@ func TestGetGroupWithOrphanedMemberHealsAndSucceeds(t *testing.T) {
|
||||
t.Errorf("GetCFPointingMemberByID() should return not found after healing")
|
||||
}
|
||||
}
|
||||
|
||||
func TestListGroupsHandlerWithMissingNodeStillSucceeds(t *testing.T) {
|
||||
ctx, _ := setupCloudflareLogicDB(t)
|
||||
if err := db.DB(ctx).Create(&model.CFPointingGroup{
|
||||
Name: "KR",
|
||||
PrimaryNodeID: 12,
|
||||
ActiveNodeID: 12,
|
||||
Enabled: true,
|
||||
}).Error; err != nil {
|
||||
t.Fatalf("Create(missing-node group) error = %v", err)
|
||||
}
|
||||
|
||||
gin.SetMode(gin.TestMode)
|
||||
router := gin.New()
|
||||
router.Use(response.ErrorHandlerMiddleware())
|
||||
router.GET("/groups", ListGroupsHandler)
|
||||
|
||||
recorder := httptest.NewRecorder()
|
||||
router.ServeHTTP(recorder, httptest.NewRequest(http.MethodGet, "/groups", nil))
|
||||
if recorder.Code != http.StatusOK {
|
||||
t.Fatalf("ListGroupsHandler status = %d, body = %s, want %d", recorder.Code, recorder.Body.String(), http.StatusOK)
|
||||
}
|
||||
body := recorder.Body.String()
|
||||
if !strings.Contains(body, `"name":"KR"`) {
|
||||
t.Fatalf("ListGroupsHandler body = %s, want group KR", body)
|
||||
}
|
||||
if !strings.Contains(body, `"name":"primary"`) {
|
||||
t.Fatalf("ListGroupsHandler body = %s, want intact group primary", body)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetGroupHandlerWithMissingNodeStillSucceeds(t *testing.T) {
|
||||
ctx, _ := setupCloudflareLogicDB(t)
|
||||
group := model.CFPointingGroup{Name: "KR", PrimaryNodeID: 12, ActiveNodeID: 12, Enabled: true}
|
||||
if err := db.DB(ctx).Create(&group).Error; err != nil {
|
||||
t.Fatalf("Create(missing-node group) error = %v", err)
|
||||
}
|
||||
|
||||
gin.SetMode(gin.TestMode)
|
||||
router := gin.New()
|
||||
router.Use(response.ErrorHandlerMiddleware())
|
||||
router.GET("/groups/:id", GetGroupHandler)
|
||||
|
||||
recorder := httptest.NewRecorder()
|
||||
request := httptest.NewRequest(http.MethodGet, fmt.Sprintf("/groups/%d", group.ID), nil)
|
||||
router.ServeHTTP(recorder, request)
|
||||
if recorder.Code != http.StatusOK {
|
||||
t.Fatalf("GetGroupHandler status = %d, body = %s, want %d", recorder.Code, recorder.Body.String(), http.StatusOK)
|
||||
}
|
||||
if !strings.Contains(recorder.Body.String(), `"name":"KR"`) {
|
||||
t.Fatalf("GetGroupHandler body = %s, want group KR", recorder.Body.String())
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user