chore: stabilize lint and verify builds
This commit is contained in:
@@ -13,12 +13,12 @@ type Config struct {
|
||||
StaticPath *string
|
||||
StaticRoute *string
|
||||
BaseURI *string
|
||||
Tls *Tls
|
||||
TLS *TLS
|
||||
Cors *Cors
|
||||
RateLimit *RateLimit
|
||||
}
|
||||
|
||||
type Tls struct {
|
||||
type TLS struct {
|
||||
Cert string
|
||||
Key string
|
||||
}
|
||||
@@ -57,5 +57,6 @@ func (h *Config) Address() string {
|
||||
if h.Host == "" {
|
||||
return fmt.Sprintf("0.0.0.0:%d", h.Port)
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%s:%d", h.Host, h.Port)
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
logrus "github.com/sirupsen/logrus"
|
||||
"go.ipao.vip/atom/container"
|
||||
"go.ipao.vip/atom/opt"
|
||||
|
||||
@@ -40,22 +40,25 @@ type Service struct {
|
||||
Engine *fiber.App
|
||||
}
|
||||
|
||||
var errTLSCertKeyRequired = errors.New("tls cert and key must be set")
|
||||
|
||||
func (svc *Service) listenerConfig() fiber.ListenConfig {
|
||||
listenConfig := fiber.ListenConfig{
|
||||
EnablePrintRoutes: true,
|
||||
// DisableStartupMessage: true,
|
||||
}
|
||||
|
||||
if svc.conf.Tls != nil {
|
||||
if svc.conf.Tls.Cert == "" || svc.conf.Tls.Key == "" {
|
||||
panic(errors.New("tls cert and key must be set"))
|
||||
if svc.conf.TLS != nil {
|
||||
if svc.conf.TLS.Cert == "" || svc.conf.TLS.Key == "" {
|
||||
panic(errTLSCertKeyRequired)
|
||||
}
|
||||
listenConfig.CertFile = svc.conf.Tls.Cert
|
||||
listenConfig.CertKeyFile = svc.conf.Tls.Key
|
||||
listenConfig.CertFile = svc.conf.TLS.Cert
|
||||
listenConfig.CertKeyFile = svc.conf.TLS.Key
|
||||
}
|
||||
container.AddCloseAble(func() {
|
||||
svc.Engine.ShutdownWithTimeout(time.Second * 10)
|
||||
})
|
||||
|
||||
return listenConfig
|
||||
}
|
||||
|
||||
@@ -64,8 +67,6 @@ func (svc *Service) Listener(ln net.Listener) error {
|
||||
}
|
||||
|
||||
func (svc *Service) Serve(ctx context.Context) error {
|
||||
// log.WithField("http_address", svc.conf.Address()).Info("http config address")
|
||||
|
||||
ln, err := net.Listen("tcp4", svc.conf.Address())
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -109,15 +110,13 @@ func Provide(opts ...opt.Option) error {
|
||||
EnableIPValidation: true,
|
||||
})
|
||||
|
||||
// request id first for correlation
|
||||
engine.Use(requestid.New())
|
||||
|
||||
// recover with stack + request id
|
||||
engine.Use(recover.New(recover.Config{
|
||||
EnableStackTrace: true,
|
||||
StackTraceHandler: func(c fiber.Ctx, e any) {
|
||||
rid := c.Get(fiber.HeaderXRequestID)
|
||||
log.WithField("request_id", rid).Error(fmt.Sprintf("panic: %v\n%s\n", e, debug.Stack()))
|
||||
logrus.WithField("request_id", rid).Error(fmt.Sprintf("panic: %v\n%s\n", e, debug.Stack()))
|
||||
},
|
||||
}))
|
||||
|
||||
@@ -133,9 +132,7 @@ func Provide(opts ...opt.Option) error {
|
||||
}
|
||||
}
|
||||
|
||||
// logging with request id and latency
|
||||
engine.Use(logger.New(logger.Config{
|
||||
// requestid middleware stores ctx.Locals("requestid")
|
||||
Format: `${time} [${ip}] ${method} ${status} ${path} ${latency} rid=${locals:requestid} "${ua}"\n`,
|
||||
TimeFormat: time.RFC3339,
|
||||
TimeZone: "Asia/Shanghai",
|
||||
@@ -143,9 +140,9 @@ func Provide(opts ...opt.Option) error {
|
||||
|
||||
// rate limit (by tenant code or IP)
|
||||
if config.RateLimit != nil && config.RateLimit.Enabled {
|
||||
max := config.RateLimit.Max
|
||||
if max <= 0 {
|
||||
max = 120
|
||||
limitMax := config.RateLimit.Max
|
||||
if limitMax <= 0 {
|
||||
limitMax = 120
|
||||
}
|
||||
windowSeconds := config.RateLimit.WindowSeconds
|
||||
if windowSeconds <= 0 {
|
||||
@@ -165,7 +162,7 @@ func Provide(opts ...opt.Option) error {
|
||||
|
||||
skipPrefixes := append([]string{"/healthz", "/readyz"}, config.RateLimit.SkipPaths...)
|
||||
engine.Use(limiter.New(limiter.Config{
|
||||
Max: max,
|
||||
Max: limitMax,
|
||||
Expiration: time.Duration(windowSeconds) * time.Second,
|
||||
Storage: limiterStorage,
|
||||
LimitReached: func(c fiber.Ctx) error {
|
||||
@@ -173,10 +170,11 @@ func Provide(opts ...opt.Option) error {
|
||||
if message != "" {
|
||||
appErr = appErr.WithMsg(message)
|
||||
}
|
||||
|
||||
return errorx.SendError(c, appErr)
|
||||
},
|
||||
Next: func(c fiber.Ctx) bool {
|
||||
path := c.Path()
|
||||
Next: func(requestCtx fiber.Ctx) bool {
|
||||
path := requestCtx.Path()
|
||||
for _, prefix := range skipPrefixes {
|
||||
if prefix == "" {
|
||||
continue
|
||||
@@ -185,31 +183,30 @@ func Provide(opts ...opt.Option) error {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
},
|
||||
KeyGenerator: func(c fiber.Ctx) string {
|
||||
if strings.HasPrefix(c.Path(), "/t/") {
|
||||
if tenantCode := strings.TrimSpace(c.Params("tenantCode")); tenantCode != "" {
|
||||
KeyGenerator: func(requestCtx fiber.Ctx) string {
|
||||
if strings.HasPrefix(requestCtx.Path(), "/t/") {
|
||||
if tenantCode := strings.TrimSpace(requestCtx.Params("tenantCode")); tenantCode != "" {
|
||||
return "tenant:" + tenantCode
|
||||
}
|
||||
}
|
||||
return c.IP()
|
||||
|
||||
return requestCtx.IP()
|
||||
},
|
||||
}))
|
||||
}
|
||||
|
||||
// static files (Fiber v3 Static helper moved; enable via filesystem middleware later)
|
||||
// if config.StaticRoute != nil && config.StaticPath != nil { ... }
|
||||
|
||||
// health endpoints
|
||||
engine.Get("/healthz", func(c fiber.Ctx) error { return c.SendStatus(fiber.StatusNoContent) })
|
||||
engine.Get("/readyz", func(c fiber.Ctx) error { return c.SendStatus(fiber.StatusNoContent) })
|
||||
|
||||
engine.Hooks().OnPostShutdown(func(err error) error {
|
||||
if err != nil {
|
||||
log.Error("http server shutdown error: ", err)
|
||||
logrus.Error("http server shutdown error: ", err)
|
||||
}
|
||||
log.Info("http server has shutdown success")
|
||||
logrus.Info("http server has shutdown success")
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
@@ -235,20 +232,20 @@ func buildCORSConfig(c *Cors) *cors.Config {
|
||||
exposes []string
|
||||
allowCreds bool
|
||||
)
|
||||
for _, w := range c.Whitelist {
|
||||
if w.AllowOrigin != "" {
|
||||
origins = append(origins, w.AllowOrigin)
|
||||
for _, whitelistItem := range c.Whitelist {
|
||||
if whitelistItem.AllowOrigin != "" {
|
||||
origins = append(origins, whitelistItem.AllowOrigin)
|
||||
}
|
||||
if w.AllowHeaders != "" {
|
||||
headers = append(headers, w.AllowHeaders)
|
||||
if whitelistItem.AllowHeaders != "" {
|
||||
headers = append(headers, whitelistItem.AllowHeaders)
|
||||
}
|
||||
if w.AllowMethods != "" {
|
||||
methods = append(methods, w.AllowMethods)
|
||||
if whitelistItem.AllowMethods != "" {
|
||||
methods = append(methods, whitelistItem.AllowMethods)
|
||||
}
|
||||
if w.ExposeHeaders != "" {
|
||||
exposes = append(exposes, w.ExposeHeaders)
|
||||
if whitelistItem.ExposeHeaders != "" {
|
||||
exposes = append(exposes, whitelistItem.ExposeHeaders)
|
||||
}
|
||||
allowCreds = allowCreds || w.AllowCredentials
|
||||
allowCreds = allowCreds || whitelistItem.AllowCredentials
|
||||
}
|
||||
|
||||
cfg := cors.Config{
|
||||
@@ -258,5 +255,6 @@ func buildCORSConfig(c *Cors) *cors.Config {
|
||||
ExposeHeaders: lo.Uniq(exposes),
|
||||
AllowCredentials: allowCreds,
|
||||
}
|
||||
|
||||
return &cfg
|
||||
}
|
||||
|
||||
@@ -15,12 +15,17 @@ type redisLimiterStorage struct {
|
||||
prefix string
|
||||
}
|
||||
|
||||
var (
|
||||
errRateLimitRedisConfigNil = errors.New("rate limit redis config is nil")
|
||||
errRateLimitRedisAddrsEmpty = errors.New("rate limit redis addrs is empty")
|
||||
)
|
||||
|
||||
func newRedisLimiterStorage(config *RateLimitRedis) (fiber.Storage, error) {
|
||||
if config == nil {
|
||||
return nil, errors.New("rate limit redis config is nil")
|
||||
return nil, errRateLimitRedisConfigNil
|
||||
}
|
||||
if len(config.Addrs) == 0 {
|
||||
return nil, errors.New("rate limit redis addrs is empty")
|
||||
return nil, errRateLimitRedisAddrsEmpty
|
||||
}
|
||||
|
||||
client := redis.NewUniversalClient(&redis.UniversalOptions{
|
||||
@@ -34,10 +39,12 @@ func newRedisLimiterStorage(config *RateLimitRedis) (fiber.Storage, error) {
|
||||
defer cancel()
|
||||
if err := client.Ping(ctx).Err(); err != nil {
|
||||
_ = client.Close()
|
||||
|
||||
return nil, err
|
||||
}
|
||||
|
||||
prefix := strings.TrimSpace(config.Prefix)
|
||||
|
||||
return &redisLimiterStorage{
|
||||
client: client,
|
||||
prefix: prefix,
|
||||
@@ -52,6 +59,7 @@ func (s *redisLimiterStorage) GetWithContext(ctx context.Context, key string) ([
|
||||
if errors.Is(err, redis.Nil) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return val, err
|
||||
}
|
||||
|
||||
@@ -63,6 +71,7 @@ func (s *redisLimiterStorage) SetWithContext(ctx context.Context, key string, va
|
||||
if s == nil || key == "" || len(val) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
return s.client.Set(ctx, s.key(key), val, exp).Err()
|
||||
}
|
||||
|
||||
@@ -74,6 +83,7 @@ func (s *redisLimiterStorage) DeleteWithContext(ctx context.Context, key string)
|
||||
if s == nil || key == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
return s.client.Del(ctx, s.key(key)).Err()
|
||||
}
|
||||
|
||||
@@ -85,6 +95,7 @@ func (s *redisLimiterStorage) ResetWithContext(ctx context.Context) error {
|
||||
if s == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return s.client.FlushDB(ctx).Err()
|
||||
}
|
||||
|
||||
@@ -96,6 +107,7 @@ func (s *redisLimiterStorage) Close() error {
|
||||
if s == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return s.client.Close()
|
||||
}
|
||||
|
||||
@@ -103,5 +115,6 @@ func (s *redisLimiterStorage) key(raw string) string {
|
||||
if s.prefix == "" {
|
||||
return raw
|
||||
}
|
||||
|
||||
return s.prefix + raw
|
||||
}
|
||||
|
||||
@@ -44,7 +44,7 @@ type Config struct {
|
||||
|
||||
// Controls the display of operationId in operations list.
|
||||
// default: false
|
||||
DisplayOperationId bool `json:"displayOperationId,omitempty"`
|
||||
DisplayOperationID bool `json:"displayOperationId,omitempty"`
|
||||
|
||||
// The default expansion depth for models (set to -1 completely hide the models).
|
||||
// default: 1
|
||||
@@ -109,7 +109,7 @@ type Config struct {
|
||||
|
||||
// OAuth redirect URL.
|
||||
// default: ""
|
||||
OAuth2RedirectUrl string `json:"oauth2RedirectUrl,omitempty"`
|
||||
OAuth2RedirectURL string `json:"oauth2RedirectUrl,omitempty"`
|
||||
|
||||
// MUST be a function. Function to intercept remote definition, "Try it out", and OAuth 2.0 requests.
|
||||
// Accepts one argument requestInterceptor(request) and must return the modified request, or a Promise that resolves to the modified request.
|
||||
@@ -141,7 +141,7 @@ type Config struct {
|
||||
// For example for locally deployed validators (https://github.com/swagger-api/validator-badge).
|
||||
// Setting it to either none, 127.0.0.1 or localhost will disable validation.
|
||||
// default: ""
|
||||
ValidatorUrl string `json:"validatorUrl,omitempty"`
|
||||
ValidatorURL string `json:"validatorUrl,omitempty"`
|
||||
|
||||
// If set to true, enables passing credentials, as defined in the Fetch standard, in CORS requests that are sent by the browser.
|
||||
// Note that Swagger UI cannot currently set cookies cross-domain (see https://github.com/swagger-api/swagger-js/issues/1163).
|
||||
@@ -174,7 +174,7 @@ type Config struct {
|
||||
// Programmatically set values for an API key or Bearer authorization scheme.
|
||||
// In case of OpenAPI 3.0 Bearer scheme, apiKeyValue must contain just the token itself without the Bearer prefix.
|
||||
// default: ""
|
||||
PreauthorizeApiKey template.JS `json:"-"`
|
||||
PreauthorizeAPIKey template.JS `json:"-"`
|
||||
|
||||
// Applies custom CSS styles.
|
||||
// default: ""
|
||||
@@ -194,6 +194,7 @@ func (fc FilterConfig) Value() interface{} {
|
||||
if fc.Expression != "" {
|
||||
return fc.Expression
|
||||
}
|
||||
|
||||
return fc.Enabled
|
||||
}
|
||||
|
||||
@@ -211,13 +212,14 @@ func (shc SyntaxHighlightConfig) Value() interface{} {
|
||||
if shc.Activate {
|
||||
return shc
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
type OAuthConfig struct {
|
||||
// ID of the client sent to the OAuth2 provider.
|
||||
// default: ""
|
||||
ClientId string `json:"clientId,omitempty"`
|
||||
ClientID string `json:"clientId,omitempty"`
|
||||
|
||||
// Never use this parameter in your production environment.
|
||||
// It exposes cruicial security information. This feature is intended for dev/test environments only.
|
||||
|
||||
@@ -35,13 +35,13 @@ func New(config ...Config) fiber.Handler {
|
||||
once sync.Once
|
||||
)
|
||||
|
||||
return func(c fiber.Ctx) error {
|
||||
return func(ctx fiber.Ctx) error {
|
||||
// Set prefix
|
||||
once.Do(
|
||||
func() {
|
||||
prefix = strings.ReplaceAll(c.Route().Path, "*", "")
|
||||
prefix = strings.ReplaceAll(ctx.Route().Path, "*", "")
|
||||
|
||||
forwardedPrefix := getForwardedPrefix(c)
|
||||
forwardedPrefix := getForwardedPrefix(ctx)
|
||||
if forwardedPrefix != "" {
|
||||
prefix = forwardedPrefix + prefix
|
||||
}
|
||||
@@ -53,26 +53,28 @@ func New(config ...Config) fiber.Handler {
|
||||
},
|
||||
)
|
||||
|
||||
p := c.Path(utils.CopyString(c.Params("*")))
|
||||
p := ctx.Path(utils.CopyString(ctx.Params("*")))
|
||||
|
||||
switch p {
|
||||
case defaultIndex:
|
||||
c.Type("html")
|
||||
return index.Execute(c, cfg)
|
||||
ctx.Type("html")
|
||||
|
||||
return index.Execute(ctx, cfg)
|
||||
case defaultDocURL:
|
||||
var doc string
|
||||
if doc, err = swag.ReadDoc(cfg.InstanceName); err != nil {
|
||||
return err
|
||||
return fmt.Errorf("read swagger doc: %w", err)
|
||||
}
|
||||
return c.Type("json").SendString(doc)
|
||||
|
||||
return ctx.Type("json").SendString(doc)
|
||||
case "", "/":
|
||||
return c.Redirect().To(path.Join(prefix, defaultIndex))
|
||||
return ctx.Redirect().To(path.Join(prefix, defaultIndex))
|
||||
default:
|
||||
// return fs(c)
|
||||
return static.New("/swagger", static.Config{
|
||||
FS: swaggerFiles.FS,
|
||||
Browse: true,
|
||||
})(c)
|
||||
})(ctx)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -95,8 +95,8 @@ const indexTmpl string = `
|
||||
{{if .PreauthorizeBasic}}
|
||||
ui.preauthorizeBasic({{.PreauthorizeBasic}});
|
||||
{{end}}
|
||||
{{if .PreauthorizeApiKey}}
|
||||
ui.preauthorizeApiKey({{.PreauthorizeApiKey}});
|
||||
{{if .PreauthorizeAPIKey}}
|
||||
ui.preauthorizeApiKey({{.PreauthorizeAPIKey}});
|
||||
{{end}}
|
||||
|
||||
window.ui = ui
|
||||
|
||||
Reference in New Issue
Block a user