mirror of
https://github.com/Rain-kl/OpenFlare.git
synced 2026-10-11 17:56:37 +08:00
refactor(openflare): unify protocol API responses to Wavelet format
Migrate Agent/Relay/Tunnel handlers from compat {success,message,data}
to response.OK and response.Abort* with real HTTP status codes. Remove
the compat package and update openflare-agent, openflare-relay, and
openflared clients to parse {error_msg,data}.
This commit is contained in:
@@ -6,7 +6,7 @@ package agent
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/Rain-kl/Wavelet/internal/apps/openflare/compat"
|
||||
"github.com/Rain-kl/Wavelet/internal/common/response"
|
||||
"github.com/Rain-kl/Wavelet/internal/model"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
@@ -22,8 +22,7 @@ func AgentAuth() gin.HandlerFunc {
|
||||
token := strings.TrimSpace(c.GetHeader(agentTokenHeader))
|
||||
node, err := AuthenticateAccessToken(c.Request.Context(), token)
|
||||
if err != nil {
|
||||
compat.Unauthorized(c, errInvalidAgentToken)
|
||||
c.Abort()
|
||||
response.AbortUnauthorized(c, errInvalidAgentToken)
|
||||
return
|
||||
}
|
||||
c.Set(agentNodeContextKey, node)
|
||||
@@ -41,8 +40,7 @@ func AgentRegisterAuth() gin.HandlerFunc {
|
||||
return
|
||||
}
|
||||
if err := ValidateDiscoveryToken(c.Request.Context(), token); err != nil {
|
||||
compat.Unauthorized(c, errInvalidDiscoveryToken)
|
||||
c.Abort()
|
||||
response.AbortUnauthorized(c, errInvalidDiscoveryToken)
|
||||
return
|
||||
}
|
||||
c.Set("discovery_enabled", true)
|
||||
@@ -58,4 +56,4 @@ func AgentNodeFromContext(c *gin.Context) (*model.OpenFlareNode, bool) {
|
||||
}
|
||||
node, ok := value.(*model.OpenFlareNode)
|
||||
return node, ok
|
||||
}
|
||||
}
|
||||
@@ -11,10 +11,11 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/Rain-kl/Wavelet/internal/apps/openflare/compat"
|
||||
"github.com/Rain-kl/Wavelet/internal/apps/openflare/option"
|
||||
"github.com/Rain-kl/Wavelet/internal/common/response"
|
||||
"github.com/Rain-kl/Wavelet/internal/db"
|
||||
"github.com/Rain-kl/Wavelet/internal/model"
|
||||
"github.com/Rain-kl/Wavelet/internal/testhelper"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/glebarez/sqlite"
|
||||
"github.com/stretchr/testify/assert"
|
||||
@@ -107,15 +108,14 @@ func TestAgentAuthMiddleware(t *testing.T) {
|
||||
NodeType: "edge_node",
|
||||
}).Error)
|
||||
|
||||
gin.SetMode(gin.TestMode)
|
||||
router := gin.New()
|
||||
router := testhelper.NewTestGinEngine()
|
||||
router.GET("/protected", AgentAuth(), func(c *gin.Context) {
|
||||
node, ok := AgentNodeFromContext(c)
|
||||
if !ok {
|
||||
c.Status(http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
compat.OK(c, gin.H{"node_id": node.NodeID})
|
||||
c.JSON(http.StatusOK, response.OK(gin.H{"node_id": node.NodeID}))
|
||||
})
|
||||
|
||||
t.Run("authorized request", func(t *testing.T) {
|
||||
@@ -125,9 +125,9 @@ func TestAgentAuthMiddleware(t *testing.T) {
|
||||
router.ServeHTTP(resp, req)
|
||||
|
||||
assert.Equal(t, http.StatusOK, resp.Code)
|
||||
var envelope compat.Envelope
|
||||
require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &envelope))
|
||||
assert.True(t, envelope.Success)
|
||||
var apiResp response.Any
|
||||
require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &apiResp))
|
||||
assert.Empty(t, apiResp.ErrorMsg)
|
||||
})
|
||||
|
||||
t.Run("unauthorized request", func(t *testing.T) {
|
||||
@@ -156,15 +156,14 @@ func TestAgentRegisterAuthMiddleware(t *testing.T) {
|
||||
}).Error)
|
||||
require.NoError(t, model.UpdateOpenFlareOption(ctx, "AgentDiscoveryToken", "discovery-token"))
|
||||
|
||||
gin.SetMode(gin.TestMode)
|
||||
router := gin.New()
|
||||
router := testhelper.NewTestGinEngine()
|
||||
router.POST("/register", AgentRegisterAuth(), func(c *gin.Context) {
|
||||
if node, ok := AgentNodeFromContext(c); ok {
|
||||
compat.OK(c, gin.H{"mode": "node", "node_id": node.NodeID})
|
||||
c.JSON(http.StatusOK, response.OK(gin.H{"mode": "node", "node_id": node.NodeID}))
|
||||
return
|
||||
}
|
||||
if _, ok := c.Get("discovery_enabled"); ok {
|
||||
compat.OK(c, gin.H{"mode": "discovery"})
|
||||
c.JSON(http.StatusOK, response.OK(gin.H{"mode": "discovery"}))
|
||||
return
|
||||
}
|
||||
c.Status(http.StatusInternalServerError)
|
||||
@@ -177,9 +176,9 @@ func TestAgentRegisterAuthMiddleware(t *testing.T) {
|
||||
router.ServeHTTP(resp, req)
|
||||
|
||||
assert.Equal(t, http.StatusOK, resp.Code)
|
||||
var envelope compat.Envelope
|
||||
require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &envelope))
|
||||
data, ok := envelope.Data.(map[string]any)
|
||||
var apiResp response.Any
|
||||
require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &apiResp))
|
||||
data, ok := apiResp.Data.(map[string]any)
|
||||
require.True(t, ok)
|
||||
assert.Equal(t, "node", data["mode"])
|
||||
})
|
||||
@@ -191,19 +190,10 @@ func TestAgentRegisterAuthMiddleware(t *testing.T) {
|
||||
router.ServeHTTP(resp, req)
|
||||
|
||||
assert.Equal(t, http.StatusOK, resp.Code)
|
||||
var envelope compat.Envelope
|
||||
require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &envelope))
|
||||
data, ok := envelope.Data.(map[string]any)
|
||||
var apiResp response.Any
|
||||
require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &apiResp))
|
||||
data, ok := apiResp.Data.(map[string]any)
|
||||
require.True(t, ok)
|
||||
assert.Equal(t, "discovery", data["mode"])
|
||||
})
|
||||
|
||||
t.Run("invalid token", func(t *testing.T) {
|
||||
req := httptest.NewRequest(http.MethodPost, "/register", nil)
|
||||
req.Header.Set(agentTokenHeader, "invalid-token")
|
||||
resp := httptest.NewRecorder()
|
||||
router.ServeHTTP(resp, req)
|
||||
|
||||
assert.Equal(t, http.StatusUnauthorized, resp.Code)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -7,17 +7,17 @@ import (
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/Rain-kl/Wavelet/internal/apps/openflare/compat"
|
||||
"github.com/Rain-kl/Wavelet/internal/apps/openflare/apiutil"
|
||||
"github.com/Rain-kl/Wavelet/internal/apps/openflare/pages"
|
||||
"github.com/Rain-kl/Wavelet/internal/apps/openflare/websocket"
|
||||
"github.com/Rain-kl/Wavelet/internal/common/response"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
|
||||
// RegisterHandler registers or discovers an agent node.
|
||||
func RegisterHandler(c *gin.Context) {
|
||||
var payload NodePayload
|
||||
if !compat.BindJSON(c, &payload) {
|
||||
if !apiutil.BindJSON(c, &payload) {
|
||||
return
|
||||
}
|
||||
payload.IP = resolveReportedNodeIP(payload.IP, c.Request.RemoteAddr)
|
||||
@@ -31,82 +31,73 @@ func RegisterHandler(c *gin.Context) {
|
||||
} else {
|
||||
result, err = RegisterWithDiscovery(c.Request.Context(), payload)
|
||||
}
|
||||
if err != nil {
|
||||
compat.Fail(c, err.Error())
|
||||
if apiutil.AbortBadRequestOnError(c, err) {
|
||||
return
|
||||
}
|
||||
compat.OK(c, result)
|
||||
c.JSON(http.StatusOK, response.OK(result))
|
||||
}
|
||||
|
||||
// HeartbeatHandler records agent heartbeat state.
|
||||
func HeartbeatHandler(c *gin.Context) {
|
||||
var payload NodePayload
|
||||
if !compat.BindJSON(c, &payload) {
|
||||
if !apiutil.BindJSON(c, &payload) {
|
||||
return
|
||||
}
|
||||
payload.IP = resolveReportedNodeIP(payload.IP, c.Request.RemoteAddr)
|
||||
|
||||
authNode, ok := AgentNodeFromContext(c)
|
||||
if !ok {
|
||||
compat.Unauthorized(c, errInvalidAgentToken)
|
||||
response.AbortUnauthorized(c, errInvalidAgentToken)
|
||||
return
|
||||
}
|
||||
|
||||
response, err := HeartbeatNode(c.Request.Context(), authNode, payload)
|
||||
if err != nil {
|
||||
compat.Fail(c, err.Error())
|
||||
heartbeat, err := HeartbeatNode(c.Request.Context(), authNode, payload)
|
||||
if apiutil.AbortBadRequestOnError(c, err) {
|
||||
return
|
||||
}
|
||||
okWithExtras(c, response.Node, gin.H{
|
||||
"agent_settings": response.AgentSettings,
|
||||
"active_config": response.ActiveConfig,
|
||||
"waf_ip_groups": response.WAFIPGroups,
|
||||
})
|
||||
c.JSON(http.StatusOK, response.OK(heartbeat))
|
||||
}
|
||||
|
||||
// GetActiveConfigHandler returns the active configuration version.
|
||||
func GetActiveConfigHandler(c *gin.Context) {
|
||||
if _, ok := AgentNodeFromContext(c); !ok {
|
||||
compat.Unauthorized(c, errNodeMissingFromContext)
|
||||
response.AbortUnauthorized(c, errNodeMissingFromContext)
|
||||
return
|
||||
}
|
||||
config, err := GetActiveConfig(c.Request.Context())
|
||||
if err != nil {
|
||||
compat.Fail(c, err.Error())
|
||||
if apiutil.AbortBadRequestOnError(c, err) {
|
||||
return
|
||||
}
|
||||
compat.OK(c, config)
|
||||
c.JSON(http.StatusOK, response.OK(config))
|
||||
}
|
||||
|
||||
// SyncWAFIPGroupsHandler syncs WAF IP groups for an agent.
|
||||
func SyncWAFIPGroupsHandler(c *gin.Context) {
|
||||
var input WAFIPGroupSyncInput
|
||||
if !compat.BindJSON(c, &input) {
|
||||
if !apiutil.BindJSON(c, &input) {
|
||||
return
|
||||
}
|
||||
result, err := SyncWAFIPGroups(c.Request.Context(), input)
|
||||
if err != nil {
|
||||
compat.Fail(c, err.Error())
|
||||
if apiutil.AbortBadRequestOnError(c, err) {
|
||||
return
|
||||
}
|
||||
compat.OK(c, result)
|
||||
c.JSON(http.StatusOK, response.OK(result))
|
||||
}
|
||||
|
||||
// ReportApplyLogHandler records an agent apply log entry.
|
||||
func ReportApplyLogHandler(c *gin.Context) {
|
||||
var payload ApplyLogPayload
|
||||
if !compat.BindJSON(c, &payload) {
|
||||
if !apiutil.BindJSON(c, &payload) {
|
||||
return
|
||||
}
|
||||
if authNode, ok := AgentNodeFromContext(c); ok {
|
||||
payload.NodeID = authNode.NodeID
|
||||
}
|
||||
log, err := ReportApplyLog(c.Request.Context(), payload)
|
||||
if err != nil {
|
||||
compat.Fail(c, err.Error())
|
||||
if apiutil.AbortBadRequestOnError(c, err) {
|
||||
return
|
||||
}
|
||||
compat.OK(c, log)
|
||||
c.JSON(http.StatusOK, response.OK(log))
|
||||
}
|
||||
|
||||
// DownloadPagesPackageHandler streams the Pages deployment artifact to an authenticated agent.
|
||||
@@ -116,8 +107,7 @@ func DownloadPagesPackageHandler(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
packageObj, fileName, err := pages.OpenDeploymentPackage(c.Request.Context(), deploymentID)
|
||||
if err != nil {
|
||||
compat.Fail(c, err.Error())
|
||||
if apiutil.AbortBadRequestOnError(c, err) {
|
||||
return
|
||||
}
|
||||
defer packageObj.Body.Close()
|
||||
@@ -131,12 +121,12 @@ func DownloadPagesPackageHandler(c *gin.Context) {
|
||||
func pagesDeploymentIDParam(c *gin.Context) (uint, bool) {
|
||||
raw := c.Param("deployment_id")
|
||||
if raw == "" {
|
||||
compat.Fail(c, "无效的 ID")
|
||||
response.AbortBadRequest(c, "无效的 ID")
|
||||
return 0, false
|
||||
}
|
||||
id64, err := strconv.ParseUint(raw, 10, 64)
|
||||
if err != nil || id64 == 0 {
|
||||
compat.Fail(c, "无效的 ID")
|
||||
response.AbortBadRequest(c, "无效的 ID")
|
||||
return 0, false
|
||||
}
|
||||
return uint(id64), true
|
||||
@@ -146,20 +136,8 @@ func pagesDeploymentIDParam(c *gin.Context) (uint, bool) {
|
||||
func AgentWebSocketHandler(c *gin.Context) {
|
||||
authNode, ok := AgentNodeFromContext(c)
|
||||
if !ok {
|
||||
compat.Unauthorized(c, errInvalidAgentToken)
|
||||
response.AbortUnauthorized(c, errInvalidAgentToken)
|
||||
return
|
||||
}
|
||||
websocket.ServeAgent(c, authNode.NodeID, HandleWSStatus)
|
||||
}
|
||||
|
||||
func okWithExtras(c *gin.Context, data any, extras gin.H) {
|
||||
payload := gin.H{
|
||||
"success": true,
|
||||
"message": "",
|
||||
"data": data,
|
||||
}
|
||||
for key, value := range extras {
|
||||
payload[key] = value
|
||||
}
|
||||
c.JSON(http.StatusOK, payload)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user