mirror of
https://github.com/Rain-kl/OpenFlare.git
synced 2026-10-01 06:36:38 +08:00
refactor(backend): rename OpenFlare directory to lowercase openflare
This commit is contained in:
@@ -0,0 +1,45 @@
|
||||
// Copyright 2026 Arctel.net
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Package apiutil provides HTTP helpers for OpenFlare v1 custom API handlers.
|
||||
package apiutil
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
|
||||
"Wavelet/pkg/response"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
const errInvalidParams = "参数错误"
|
||||
const errInvalidID = "无效的 ID"
|
||||
|
||||
// BindJSON binds JSON body; returns false after aborting with 400.
|
||||
func BindJSON(c *gin.Context, dst any) bool {
|
||||
if err := c.ShouldBindJSON(dst); err != nil {
|
||||
response.AbortBadRequest(c, errInvalidParams)
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// IDParam parses :id from the URL path.
|
||||
func IDParam(c *gin.Context) (uint, bool) {
|
||||
return NamedIDParam(c, "id")
|
||||
}
|
||||
|
||||
// NamedIDParam parses a named path parameter as a positive uint ID.
|
||||
func NamedIDParam(c *gin.Context, name string) (uint, bool) {
|
||||
raw := c.Param(name)
|
||||
if raw == "" {
|
||||
response.AbortBadRequest(c, errInvalidID)
|
||||
return 0, false
|
||||
}
|
||||
id64, err := strconv.ParseUint(raw, 10, 64)
|
||||
if err != nil || id64 == 0 {
|
||||
response.AbortBadRequest(c, errInvalidID)
|
||||
return 0, false
|
||||
}
|
||||
return uint(id64), true
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
// Copyright 2026 Arctel.net
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package apiutil
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"Wavelet/pkg/response"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// AbortNotFoundIfMissing maps gorm.ErrRecordNotFound to 404; other errors to 400.
|
||||
func AbortNotFoundIfMissing(c *gin.Context, err error, notFoundMsg string) bool {
|
||||
if err == nil {
|
||||
return false
|
||||
}
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
response.AbortNotFound(c, notFoundMsg)
|
||||
return true
|
||||
}
|
||||
response.AbortBadRequest(c, err.Error())
|
||||
return true
|
||||
}
|
||||
|
||||
// AbortBadRequestOnError writes a 400 for any non-nil error.
|
||||
func AbortBadRequestOnError(c *gin.Context, err error) bool {
|
||||
if err == nil {
|
||||
return false
|
||||
}
|
||||
response.AbortBadRequest(c, err.Error())
|
||||
return true
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
// Copyright 2026 Arctel.net
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package apiutil
|
||||
|
||||
import "Wavelet/core/contracts"
|
||||
|
||||
// AdminMiddlewares returns Wavelet-standard middlewares for OpenFlare console routes.
|
||||
// OpenFlare no longer distinguishes Admin vs Root tiers; all management endpoints share
|
||||
// the same gate: RequireAuth + RequireAdmin from the platform AuthService.
|
||||
//
|
||||
// 返回 []any 而非 []gin.HandlerFunc:内核 RouterExtension.Use 收 ...any,
|
||||
// 而 Go 不允许把 []T 直接展开成 ...any。
|
||||
func AdminMiddlewares(auth contracts.AuthService) []any {
|
||||
return []any{auth.RequireAuthMiddleware(), auth.RequireAdminMiddleware()}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
// Copyright 2026 Arctel.net
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package apiutil
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"Wavelet/openflare/plugins/server/kernel/testhelper"
|
||||
"Wavelet/pkg/response"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestAdminMiddlewaresRunsAuthThenAdmin(t *testing.T) {
|
||||
gin.SetMode(gin.TestMode)
|
||||
var order []string
|
||||
auth := sequentialAuth{
|
||||
auth: func(c *gin.Context) {
|
||||
order = append(order, "auth")
|
||||
c.Next()
|
||||
},
|
||||
admin: func(c *gin.Context) {
|
||||
order = append(order, "admin")
|
||||
c.Next()
|
||||
},
|
||||
}
|
||||
|
||||
engine := testhelper.NewTestGinEngine()
|
||||
group := engine.Group("/protected", ginHandlerMiddlewares(AdminMiddlewares(auth)...)...)
|
||||
group.GET("", func(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, response.OKNil())
|
||||
})
|
||||
|
||||
rec := httptest.NewRecorder()
|
||||
req := httptest.NewRequest(http.MethodGet, "/protected", nil)
|
||||
engine.ServeHTTP(rec, req)
|
||||
|
||||
assert.Equal(t, http.StatusOK, rec.Code)
|
||||
assert.Equal(t, []string{"auth", "admin"}, order)
|
||||
}
|
||||
|
||||
func ginHandlerMiddlewares(middlewares ...any) []gin.HandlerFunc {
|
||||
handlers := make([]gin.HandlerFunc, 0, len(middlewares))
|
||||
for _, m := range middlewares {
|
||||
switch h := m.(type) {
|
||||
case gin.HandlerFunc:
|
||||
handlers = append(handlers, h)
|
||||
case func(*gin.Context):
|
||||
handlers = append(handlers, gin.HandlerFunc(h))
|
||||
default:
|
||||
panic("unexpected middleware type")
|
||||
}
|
||||
}
|
||||
return handlers
|
||||
}
|
||||
|
||||
type sequentialAuth struct {
|
||||
testhelper.StubAuth
|
||||
auth, admin gin.HandlerFunc
|
||||
}
|
||||
|
||||
func (s sequentialAuth) RequireAuthMiddleware() any { return s.auth }
|
||||
func (s sequentialAuth) RequireAdminMiddleware() any { return s.admin }
|
||||
@@ -0,0 +1,26 @@
|
||||
// Copyright 2026 Arctel.net
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package apiutil
|
||||
|
||||
import (
|
||||
"Wavelet/core"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// RegisterCollection registers a collection endpoint on both "" and "/" so requests
|
||||
// work with or without a trailing slash.
|
||||
//
|
||||
// 尾部斜杠变体必须用 HandleRaw 注册:RouterExtension.Handle 会经 cleanPath 归一化
|
||||
// 掉尾部斜杠,而部署关闭了 gin 的 RedirectTrailingSlash,缺一条即 404。
|
||||
func RegisterCollection(route core.RouterExtension, method string, handlers ...gin.HandlerFunc) {
|
||||
hs := make([]any, len(handlers))
|
||||
for i, h := range handlers {
|
||||
hs[i] = h
|
||||
}
|
||||
route.Handle(method, "", hs...)
|
||||
if route.BasePath() != "" {
|
||||
route.HandleRaw(method, "/", hs...)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user