kra-new/internal/server/gin.go

109 lines
3.8 KiB
Go

package server
import (
"log/slog"
"net/http"
"strings"
"time"
"kra/internal/config"
websocket "kra/internal/integration/websocket"
"kra/internal/server/httpx"
"kra/internal/server/middleware"
"kra/internal/server/staticfiles"
systemservice "kra/internal/service/system"
platformmodule "kra/pkg/module"
"github.com/gin-gonic/gin"
kratoshttp "github.com/go-kratos/kratos/v3/transport/http"
)
func NewGinEngineWithRuntime(runtime *config.Store, access *systemservice.AccessControlService, auth middleware.TokenAuthenticator, security *systemservice.SecurityService, audit *systemservice.AuditRecorder, logger *slog.Logger, version string, routes platformmodule.RouteRegistrar, ws *websocket.Server) *gin.Engine {
if runtime == nil {
runtime = config.NewStore(nil)
}
gin.SetMode(gin.ReleaseMode)
engine := gin.New()
if err := engine.SetTrustedProxies(nil); err != nil && logger != nil {
logger.Error("disable trusted proxies failed", "error", err)
}
engine.Use(middleware.RequestMeta(), middleware.Recovery(logger), middleware.AccessLog(runtime, logger, version), middleware.CORS(runtime), middleware.ErrorAudit(logger), middleware.SecurityRateLimit(security))
prefix := ""
snapshot := runtime.Snapshot()
if snapshot != nil && snapshot.Admin != nil {
prefix = strings.TrimSuffix(snapshot.Admin.RouterPrefix, "/")
}
public := engine.Group(prefix)
public.GET("/health", func(c *gin.Context) { c.JSON(http.StatusOK, "ok") })
private := engine.Group(prefix)
// The reference administration behavior installs operation recording after JWT,
// password and Casbin/data-scope middleware. Keeping the recorder after the
// equivalent Kra guards avoids persisting rejected unauthenticated or
// unauthorized requests as successful business operations.
private.Use(middleware.Auth(auth), middleware.MustChangePassword(), middleware.AccessControl(runtime, access), middleware.OperationAudit(runtime, audit))
if routes != nil {
routes.RegisterRoutes(public, private, engine)
}
handleWebSocket := func(c *gin.Context) {
if ws == nil || !ws.Enabled() || c.Request.URL.Path != ws.Path() {
c.Status(http.StatusNotFound)
return
}
if !middleware.AuthenticateWebSocket(c, auth) {
return
}
if err := ws.HandleRequest(c.Writer, c.Request); err != nil && logger != nil {
logger.Warn("websocket request failed", "mod", "websocket", "error", err)
}
}
if ws != nil && ws.Enabled() {
engine.GET(ws.Path(), handleWebSocket)
}
registerSwagger(engine, prefix, version, logger)
staticfiles.Register(engine, runtime)
if logger != nil {
for _, route := range engine.Routes() {
logger.Info("router registered", "method", route.Method, "path", route.Path)
}
logger.Info("router register success", "route_count", len(engine.Routes()))
}
engine.NoRoute(func(c *gin.Context) {
if ws != nil && ws.Enabled() && c.Request.Method == http.MethodGet && c.Request.URL.Path == ws.Path() {
handleWebSocket(c)
return
}
if staticfiles.Serve(c, runtime) {
return
}
httpx.Fail(c, "请求的接口不存在")
})
return engine
}
func NewGinServer(c *config.Server, engine *gin.Engine) *kratoshttp.Server {
network, address := "tcp", ":8000"
if c != nil && c.HTTP != nil {
if c.HTTP.Network != "" {
network = c.HTTP.Network
}
if c.HTTP.Addr != "" {
address = c.HTTP.Addr
}
}
timeout := 10 * time.Minute
if c != nil && c.HTTP != nil && c.HTTP.Timeout > 0 {
timeout = c.HTTP.Timeout
}
server := kratoshttp.NewServer(kratoshttp.Network(network), kratoshttp.Address(address), kratoshttp.Timeout(timeout))
server.HandlePrefix("/", engine)
server.Server.ReadHeaderTimeout = 10 * time.Second
server.Server.ReadTimeout = timeout
server.Server.WriteTimeout = timeout
server.Server.IdleTimeout = 2 * time.Minute
server.Server.MaxHeaderBytes = 1 << 20
return server
}