package web import ( "github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2/middleware/logger" "github.com/gofiber/fiber/v2/middleware/recover" "github.com/gofiber/fiber/v2/middleware/requestid" "github.com/google/uuid" "github.com/jxskiss/base62" "log/slog" "net/http" _ "net/http/pprof" "platform/pkg/u" "platform/web/auth" g "platform/web/globals" "platform/web/globals/orm" m "platform/web/models" q "platform/web/queries" "runtime" "strconv" "strings" "time" ) // region web type Config struct { Listen string } type Server struct { config *Config fiber *fiber.App } func New(config *Config) (*Server, error) { _config := config if config == nil { _config = &Config{} } return &Server{ config: _config, }, nil } func (s *Server) Run() error { // inits g.Init() q.SetDefault(g.DB) // config s.fiber = fiber.New(fiber.Config{ ProxyHeader: fiber.HeaderXForwardedFor, ErrorHandler: ErrorHandler, }) // middlewares s.fiber.Use(newRecover()) s.fiber.Use(newRequestId()) s.fiber.Use(newLogger()) // routes ApplyRouters(s.fiber) // pprof go func() { runtime.SetBlockProfileRate(1) err := http.ListenAndServe(":6060", nil) if err != nil { slog.Error("pprof 服务错误", slog.Any("err", err)) } }() // listen slog.Info("Server started on :8080") err := s.fiber.Listen(":8080") if err != nil { slog.Error("Failed to start server", slog.Any("err", err)) } slog.Info("Server stopped") return nil } func (s *Server) Stop() { err := g.ExitRedis() if err != nil { slog.Error("Failed to close Redis connection", slog.Any("err", err)) } err = g.ExitOrm() if err != nil { slog.Error("Failed to close database connection", slog.Any("err", err)) } err = s.fiber.Shutdown() if err != nil { slog.Error("Failed to shutdown server", slog.Any("err", err)) } } // endregion // region middlewares func newRequestId() fiber.Handler { return requestid.New(requestid.Config{ Generator: func() string { binary, _ := uuid.New().MarshalBinary() return base62.EncodeToString(binary) }, }) } func newLogger() fiber.Handler { return logger.New(logger.Config{ DisableColors: true, Format: "🚀 ${time} | ${locals:authtype} ${locals:authid} | ${method} ${path} | ${status} | ${latency} | ${error}\n", TimeFormat: "2006-01-02 15:04:05", TimeZone: "Asia/Shanghai", Next: func(c *fiber.Ctx) bool { c.Locals("authtype", auth.PayloadNone.ToStr()) c.Locals("authid", 0) return false }, Done: func(c *fiber.Ctx, logBytes []byte) { var logStr = strings.TrimPrefix(string(logBytes), "🚀") var logVars = strings.Split(logStr, "|") var reqTimeStr = strings.TrimSpace(logVars[0]) reqTime, err := time.ParseInLocation("2006-01-02 15:04:05", reqTimeStr, time.Local) if err != nil { slog.Error("时间解析错误", slog.Any("err", err)) return } var authInfo = strings.Split(strings.TrimSpace(logVars[1]), " ") var authType = auth.PayloadTypeFromStr(strings.TrimSpace(authInfo[0])) authID, err := strconv.Atoi(strings.TrimSpace(authInfo[1])) if err != nil { slog.Error("负载ID解析错误", slog.Any("err", err)) return } var latency = strings.TrimSpace(logVars[4]) var errStr = strings.TrimSpace(logVars[5]) var item = &m.LogsRequest{ IP: c.IP(), Ua: u.P(c.Get("User-Agent")), Method: c.Method(), Path: c.Path(), Latency: &latency, Status: int32(c.Response().StatusCode()), Error: &errStr, Time: u.P(orm.LocalDateTime(reqTime)), } if authType != auth.PayloadNone { item.Identity = u.P(int32(authType)) } if authID != 0 { item.Visitor = u.P(int32(authID)) } err = q.LogsRequest.Create(item) if err != nil { slog.Error("日志记录错误", slog.Any("err", err)) return } }, }) } func newRecover() fiber.Handler { return recover.New() } // endregion