139 lines
5.0 KiB
Go
139 lines
5.0 KiB
Go
package server
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"log/slog"
|
|
"net"
|
|
"net/http"
|
|
"os"
|
|
"strings"
|
|
|
|
"kra/internal/conf"
|
|
"kra/internal/server/handler"
|
|
"kra/internal/server/httpx"
|
|
servermiddleware "kra/internal/server/middleware"
|
|
serverrouter "kra/internal/server/router"
|
|
"kra/internal/service"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
)
|
|
|
|
// GinServer adapts Gin to the Kratos transport.Server lifecycle.
|
|
type GinServer struct {
|
|
network string
|
|
address string
|
|
server *http.Server
|
|
listener net.Listener
|
|
logger *slog.Logger
|
|
}
|
|
|
|
func NewGinServer(c *conf.Server, config *conf.AdminBackend, system *service.SystemService, access *service.AccessService, authority *handler.Authority, menu *handler.Menu, api *handler.API, permission *handler.Permission, organization *handler.Organization, announcement *handler.Announcement, email *handler.Email, task *handler.Task, media *handler.Media, auditHandler *handler.Audit, export *handler.Export, version *handler.Version, dictionary *handler.Dictionary, parameter *handler.Parameter, apiToken *handler.APIToken, systemConfig *handler.SystemConfig, publicHandler *handler.Public, user *handler.User, navigation *handler.Navigation, session *handler.Session, settings *service.SettingsService, audit *service.AuditService, emails *service.EmailService, logger *slog.Logger) *GinServer {
|
|
gin.SetMode(gin.ReleaseMode)
|
|
engine := gin.New()
|
|
engine.Use(servermiddleware.RequestMeta(), servermiddleware.EmailErrorAlert(emails, logger), gin.Recovery(), servermiddleware.SecurityRateLimit(system, settings), servermiddleware.OperationAudit(audit))
|
|
|
|
prefix := ""
|
|
if config != nil {
|
|
prefix = strings.TrimSuffix(config.RouterPrefix, "/")
|
|
}
|
|
public := engine.Group(prefix)
|
|
public.GET("/health", func(c *gin.Context) { c.JSON(http.StatusOK, "ok") })
|
|
serverrouter.RegisterPublic(public, engine, publicHandler)
|
|
|
|
private := engine.Group(prefix)
|
|
private.Use(servermiddleware.Auth(config, settings), servermiddleware.MustChangePassword(), servermiddleware.AccessControl(access, audit))
|
|
serverrouter.RegisterUser(private, user)
|
|
serverrouter.RegisterNavigation(private, navigation)
|
|
serverrouter.RegisterSession(private, session)
|
|
serverrouter.RegisterAuthority(private, authority)
|
|
serverrouter.RegisterMenu(private, menu)
|
|
serverrouter.RegisterAPI(private, engine, api)
|
|
serverrouter.RegisterPermission(private, permission)
|
|
serverrouter.RegisterOrganization(private, organization)
|
|
serverrouter.RegisterDictionary(private, dictionary)
|
|
serverrouter.RegisterParameter(private, parameter)
|
|
serverrouter.RegisterAPIToken(private, apiToken)
|
|
serverrouter.RegisterSystemConfig(private, systemConfig)
|
|
serverrouter.RegisterVersion(private, version)
|
|
serverrouter.RegisterExport(private, public, export)
|
|
serverrouter.RegisterAudit(private, public, auditHandler)
|
|
serverrouter.RegisterTask(private, task)
|
|
serverrouter.RegisterMedia(private, media)
|
|
serverrouter.RegisterAnnouncement(private, public, announcement)
|
|
serverrouter.RegisterEmail(private, email)
|
|
|
|
engine.NoRoute(func(c *gin.Context) {
|
|
if serveLocalStorage(c, config) {
|
|
return
|
|
}
|
|
httpx.Fail(c, "请求的接口不存在")
|
|
})
|
|
|
|
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
|
|
}
|
|
}
|
|
return &GinServer{network: network, address: address, server: &http.Server{Addr: address, Handler: engine}, logger: logger}
|
|
}
|
|
|
|
// serveLocalStorage resolves the local path for every request so a config
|
|
// reload takes effect without rebuilding the Gin engine.
|
|
func serveLocalStorage(c *gin.Context, config *conf.AdminBackend) bool {
|
|
if config == nil || config.Local == nil || config.Local.StorePath == "" {
|
|
return false
|
|
}
|
|
if config.Storage != nil && config.Storage.Type != "" && config.Storage.Type != "local" {
|
|
return false
|
|
}
|
|
prefix := "/" + strings.Trim(config.Local.PathPrefix, "/")
|
|
if prefix == "/" || (c.Request.URL.Path != prefix && !strings.HasPrefix(c.Request.URL.Path, prefix+"/")) {
|
|
return false
|
|
}
|
|
http.StripPrefix(prefix, http.FileServer(filesOnly{FileSystem: http.Dir(config.Local.StorePath)})).ServeHTTP(c.Writer, c.Request)
|
|
return true
|
|
}
|
|
|
|
type filesOnly struct{ http.FileSystem }
|
|
|
|
func (f filesOnly) Open(name string) (http.File, error) {
|
|
file, err := f.FileSystem.Open(name)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
info, statErr := file.Stat()
|
|
if statErr != nil {
|
|
_ = file.Close()
|
|
return nil, statErr
|
|
}
|
|
if info.IsDir() {
|
|
_ = file.Close()
|
|
return nil, os.ErrPermission
|
|
}
|
|
return file, nil
|
|
}
|
|
|
|
func (s *GinServer) Start(ctx context.Context) error {
|
|
listener, err := net.Listen(s.network, s.address)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
s.listener = listener
|
|
s.server.BaseContext = func(net.Listener) context.Context { return ctx }
|
|
s.logger.InfoContext(ctx, "Gin HTTP server listening", "addr", listener.Addr().String())
|
|
if err := s.server.Serve(listener); err != nil && !errors.Is(err, http.ErrServerClosed) {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (s *GinServer) Stop(ctx context.Context) error {
|
|
s.logger.InfoContext(ctx, "Gin HTTP server stopping")
|
|
return s.server.Shutdown(ctx)
|
|
}
|