refactor: refactoring of code to meet all golangci-lint requirements
This commit is contained in:
44
.golangci.yml
Normal file
44
.golangci.yml
Normal file
@@ -0,0 +1,44 @@
|
||||
run:
|
||||
timeout: 5m
|
||||
allow-parallel-runners: true
|
||||
|
||||
issues:
|
||||
# don't skip warning about doc comments
|
||||
# don't exclude the default set of lint
|
||||
exclude-use-default: false
|
||||
# restore some of the defaults
|
||||
# (fill in the rest as needed)
|
||||
exclude-rules:
|
||||
- path: "internal/*"
|
||||
linters:
|
||||
- dupl
|
||||
- lll
|
||||
linters:
|
||||
disable-all: true
|
||||
enable:
|
||||
- dupl
|
||||
- errcheck
|
||||
- copyloopvar
|
||||
- ginkgolinter
|
||||
- goconst
|
||||
- gocyclo
|
||||
- gofmt
|
||||
#- goimports
|
||||
- gosimple
|
||||
- govet
|
||||
- ineffassign
|
||||
- lll
|
||||
- misspell
|
||||
- nakedret
|
||||
- prealloc
|
||||
- revive
|
||||
- staticcheck
|
||||
- typecheck
|
||||
- unconvert
|
||||
- unparam
|
||||
- unused
|
||||
|
||||
linters-settings:
|
||||
revive:
|
||||
rules:
|
||||
- name: comment-spacings
|
||||
@@ -19,9 +19,10 @@ package config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
pkg "github.com/jkaninda/goma-gateway/internal"
|
||||
"github.com/spf13/cobra"
|
||||
"os"
|
||||
)
|
||||
|
||||
var CheckConfigCmd = &cobra.Command{
|
||||
|
||||
@@ -18,8 +18,9 @@ package config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/spf13/cobra"
|
||||
"os"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var Cmd = &cobra.Command{
|
||||
|
||||
@@ -17,9 +17,10 @@ limitations under the License.
|
||||
*/
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/jkaninda/goma-gateway/internal"
|
||||
"github.com/spf13/cobra"
|
||||
"os"
|
||||
|
||||
pkg "github.com/jkaninda/goma-gateway/internal"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var InitConfigCmd = &cobra.Command{
|
||||
|
||||
@@ -76,7 +76,7 @@ func (oauth *OauthRulerMiddleware) getUserInfo(token *oauth2.Token) (UserInfo, e
|
||||
defer func(Body io.ReadCloser) {
|
||||
err := Body.Close()
|
||||
if err != nil {
|
||||
|
||||
return
|
||||
}
|
||||
}(resp.Body)
|
||||
|
||||
|
||||
@@ -18,11 +18,12 @@
|
||||
package metrics
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/prometheus/client_golang/prometheus/promauto"
|
||||
"net/http"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type PrometheusRoute struct {
|
||||
|
||||
@@ -42,7 +42,6 @@ func (jwtAuth JwtAuth) AuthMiddleware(next http.Handler) http.Handler {
|
||||
|
||||
}
|
||||
}
|
||||
//token := r.Header.Get("Authorization")
|
||||
authURL, err := url.Parse(jwtAuth.AuthURL)
|
||||
if err != nil {
|
||||
logger.Error("Error parsing auth URL: %v", err)
|
||||
|
||||
@@ -29,8 +29,6 @@ func (rl *TokenRateLimiter) RateLimitMiddleware() mux.MiddlewareFunc {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if !rl.Allow() {
|
||||
logger.Error("Too many requests from IP: %s %s %s", getRealIP(r), r.URL, r.UserAgent())
|
||||
//RespondWithError(w, http.StatusUnauthorized, http.StatusText(http.StatusUnauthorized), basicAuth.ErrorInterceptor)
|
||||
|
||||
// Rate limit exceeded, return a 429 Too Many Requests response
|
||||
w.WriteHeader(http.StatusTooManyRequests)
|
||||
_, err := w.Write([]byte(fmt.Sprintf("%d Too many requests, API requests limit exceeded. Please try again later", http.StatusTooManyRequests)))
|
||||
|
||||
@@ -32,7 +32,6 @@ type RateLimiter struct {
|
||||
clientMap map[string]*Client
|
||||
mu sync.Mutex
|
||||
origins []string
|
||||
//hosts []string
|
||||
redisBased bool
|
||||
}
|
||||
|
||||
|
||||
@@ -22,13 +22,12 @@ import (
|
||||
"github.com/jkaninda/goma-gateway/pkg/logger"
|
||||
)
|
||||
|
||||
func (gatewayServer GatewayServer) initRedis() error {
|
||||
if gatewayServer.gateway.Redis.Addr == "" {
|
||||
return nil
|
||||
}
|
||||
func (gatewayServer GatewayServer) initRedis() {
|
||||
if len(gatewayServer.gateway.Redis.Addr) != 0 {
|
||||
logger.Info("Initializing Redis...")
|
||||
middlewares.InitRedis(gatewayServer.gateway.Redis.Addr, gatewayServer.gateway.Redis.Password)
|
||||
return nil
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (gatewayServer GatewayServer) closeRedis() {
|
||||
|
||||
@@ -32,7 +32,7 @@ func init() {
|
||||
_ = prometheus.Register(metrics.HttpDuration)
|
||||
}
|
||||
|
||||
// Initialize the routes
|
||||
// Initialize initializes the routes
|
||||
func (gatewayServer GatewayServer) Initialize() *mux.Router {
|
||||
gateway := gatewayServer.gateway
|
||||
m := gatewayServer.middlewares
|
||||
@@ -42,6 +42,7 @@ func (gatewayServer GatewayServer) Initialize() *mux.Router {
|
||||
}
|
||||
// Routes background healthcheck
|
||||
routesHealthCheck(gateway.Routes)
|
||||
|
||||
r := mux.NewRouter()
|
||||
heath := HealthCheckRoute{
|
||||
DisableRouteHealthCheckError: gateway.DisableRouteHealthCheckError,
|
||||
@@ -80,16 +81,17 @@ func (gatewayServer GatewayServer) Initialize() *mux.Router {
|
||||
r.Use(limiter.RateLimitMiddleware())
|
||||
}
|
||||
for rIndex, route := range gateway.Routes {
|
||||
if route.Path != "" {
|
||||
if route.Destination == "" && len(route.Backends) == 0 {
|
||||
if len(route.Path) != 0 {
|
||||
// Checks if route destination and backend are empty
|
||||
if len(route.Destination) == 0 && len(route.Backends) == 0 {
|
||||
logger.Fatal("Route %s : destination or backends should not be empty", route.Name)
|
||||
|
||||
}
|
||||
// Apply middlewares to route
|
||||
for _, mid := range route.Middlewares {
|
||||
if mid != "" {
|
||||
// Apply middlewares to the route
|
||||
for _, middleware := range route.Middlewares {
|
||||
if middleware != "" {
|
||||
// Get Access middlewares if it does exist
|
||||
accessMiddleware, err := getMiddleware([]string{mid}, m)
|
||||
accessMiddleware, err := getMiddleware([]string{middleware}, m)
|
||||
if err != nil {
|
||||
logger.Error("Error: %v", err.Error())
|
||||
} else {
|
||||
@@ -105,114 +107,12 @@ func (gatewayServer GatewayServer) Initialize() *mux.Router {
|
||||
|
||||
}
|
||||
// Get route authentication middlewares if it does exist
|
||||
rMiddleware, err := getMiddleware([]string{mid}, m)
|
||||
routeMiddleware, err := getMiddleware([]string{middleware}, m)
|
||||
if err != nil {
|
||||
// Error: middlewares not found
|
||||
logger.Error("Error: %v", err.Error())
|
||||
} else {
|
||||
for _, midPath := range rMiddleware.Paths {
|
||||
proxyRoute := ProxyRoute{
|
||||
path: route.Path,
|
||||
rewrite: route.Rewrite,
|
||||
destination: route.Destination,
|
||||
backends: route.Backends,
|
||||
disableHostFording: route.DisableHostFording,
|
||||
methods: route.Methods,
|
||||
cors: route.Cors,
|
||||
insecureSkipVerify: route.InsecureSkipVerify,
|
||||
}
|
||||
secureRouter := r.PathPrefix(util.ParseRoutePath(route.Path, midPath)).Subrouter()
|
||||
//callBackRouter := r.PathPrefix(util.ParseRoutePath(route.Path, "/callback")).Subrouter()
|
||||
//Check Authentication middlewares
|
||||
switch rMiddleware.Type {
|
||||
case BasicAuth:
|
||||
basicAuth, err := getBasicAuthMiddleware(rMiddleware.Rule)
|
||||
if err != nil {
|
||||
logger.Error("Error: %s", err.Error())
|
||||
} else {
|
||||
amw := middlewares.AuthBasic{
|
||||
Username: basicAuth.Username,
|
||||
Password: basicAuth.Password,
|
||||
Headers: nil,
|
||||
Params: nil,
|
||||
}
|
||||
// Apply JWT authentication middlewares
|
||||
secureRouter.Use(amw.AuthMiddleware)
|
||||
secureRouter.Use(CORSHandler(route.Cors))
|
||||
secureRouter.PathPrefix("/").Handler(proxyRoute.ProxyHandler()) // Proxy handler
|
||||
secureRouter.PathPrefix("").Handler(proxyRoute.ProxyHandler()) // Proxy handler
|
||||
}
|
||||
case JWTAuth:
|
||||
jwt, err := getJWTMiddleware(rMiddleware.Rule)
|
||||
if err != nil {
|
||||
logger.Error("Error: %s", err.Error())
|
||||
} else {
|
||||
amw := middlewares.JwtAuth{
|
||||
AuthURL: jwt.URL,
|
||||
RequiredHeaders: jwt.RequiredHeaders,
|
||||
Headers: jwt.Headers,
|
||||
Params: jwt.Params,
|
||||
Origins: gateway.Cors.Origins,
|
||||
}
|
||||
// Apply JWT authentication middlewares
|
||||
secureRouter.Use(amw.AuthMiddleware)
|
||||
secureRouter.Use(CORSHandler(route.Cors))
|
||||
secureRouter.PathPrefix("/").Handler(proxyRoute.ProxyHandler()) // Proxy handler
|
||||
secureRouter.PathPrefix("").Handler(proxyRoute.ProxyHandler()) // Proxy handler
|
||||
|
||||
}
|
||||
case OAuth, "openid":
|
||||
oauth, err := oAuthMiddleware(rMiddleware.Rule)
|
||||
if err != nil {
|
||||
logger.Error("Error: %s", err.Error())
|
||||
} else {
|
||||
redirectURL := "/callback" + route.Path
|
||||
if oauth.RedirectURL != "" {
|
||||
redirectURL = oauth.RedirectURL
|
||||
}
|
||||
amw := middlewares.Oauth{
|
||||
ClientID: oauth.ClientID,
|
||||
ClientSecret: oauth.ClientSecret,
|
||||
RedirectURL: redirectURL,
|
||||
Scopes: oauth.Scopes,
|
||||
Endpoint: middlewares.OauthEndpoint{
|
||||
AuthURL: oauth.Endpoint.AuthURL,
|
||||
TokenURL: oauth.Endpoint.TokenURL,
|
||||
UserInfoURL: oauth.Endpoint.UserInfoURL,
|
||||
},
|
||||
State: oauth.State,
|
||||
Origins: gateway.Cors.Origins,
|
||||
JWTSecret: oauth.JWTSecret,
|
||||
Provider: oauth.Provider,
|
||||
}
|
||||
oauthRuler := oauthRulerMiddleware(amw)
|
||||
// Check if a cookie path is defined
|
||||
if oauthRuler.CookiePath == "" {
|
||||
oauthRuler.CookiePath = route.Path
|
||||
}
|
||||
// Check if a RedirectPath is defined
|
||||
if oauthRuler.RedirectPath == "" {
|
||||
oauthRuler.RedirectPath = util.ParseRoutePath(route.Path, midPath)
|
||||
}
|
||||
if oauthRuler.Provider == "" {
|
||||
oauthRuler.Provider = "custom"
|
||||
}
|
||||
secureRouter.Use(amw.AuthMiddleware)
|
||||
secureRouter.Use(CORSHandler(route.Cors))
|
||||
secureRouter.PathPrefix("/").Handler(proxyRoute.ProxyHandler()) // Proxy handler
|
||||
secureRouter.PathPrefix("").Handler(proxyRoute.ProxyHandler()) // Proxy handler
|
||||
// Callback route
|
||||
r.HandleFunc(util.UrlParsePath(redirectURL), oauthRuler.callbackHandler).Methods("GET")
|
||||
}
|
||||
default:
|
||||
if !doesExist(rMiddleware.Type) {
|
||||
logger.Error("Unknown middlewares type %s", rMiddleware.Type)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
attachAuthMiddlewares(route, routeMiddleware, gateway, r)
|
||||
}
|
||||
} else {
|
||||
logger.Error("Error, middlewares path is empty")
|
||||
@@ -300,3 +200,107 @@ func (gatewayServer GatewayServer) Initialize() *mux.Router {
|
||||
return r
|
||||
|
||||
}
|
||||
|
||||
func attachAuthMiddlewares(route Route, routeMiddleware Middleware, gateway Gateway, r *mux.Router) {
|
||||
for _, middlewarePath := range routeMiddleware.Paths {
|
||||
proxyRoute := ProxyRoute{
|
||||
path: route.Path,
|
||||
rewrite: route.Rewrite,
|
||||
destination: route.Destination,
|
||||
backends: route.Backends,
|
||||
disableHostFording: route.DisableHostFording,
|
||||
methods: route.Methods,
|
||||
cors: route.Cors,
|
||||
insecureSkipVerify: route.InsecureSkipVerify,
|
||||
}
|
||||
secureRouter := r.PathPrefix(util.ParseRoutePath(route.Path, middlewarePath)).Subrouter()
|
||||
// Check Authentication middleware types
|
||||
switch routeMiddleware.Type {
|
||||
case BasicAuth:
|
||||
basicAuth, err := getBasicAuthMiddleware(routeMiddleware.Rule)
|
||||
if err != nil {
|
||||
logger.Error("Error: %s", err.Error())
|
||||
} else {
|
||||
authBasic := middlewares.AuthBasic{
|
||||
Username: basicAuth.Username,
|
||||
Password: basicAuth.Password,
|
||||
Headers: nil,
|
||||
Params: nil,
|
||||
}
|
||||
// Apply JWT authentication middlewares
|
||||
secureRouter.Use(authBasic.AuthMiddleware)
|
||||
secureRouter.Use(CORSHandler(route.Cors))
|
||||
secureRouter.PathPrefix("/").Handler(proxyRoute.ProxyHandler()) // Proxy handler
|
||||
secureRouter.PathPrefix("").Handler(proxyRoute.ProxyHandler()) // Proxy handler
|
||||
}
|
||||
case JWTAuth:
|
||||
jwt, err := getJWTMiddleware(routeMiddleware.Rule)
|
||||
if err != nil {
|
||||
logger.Error("Error: %s", err.Error())
|
||||
} else {
|
||||
jwtAuth := middlewares.JwtAuth{
|
||||
AuthURL: jwt.URL,
|
||||
RequiredHeaders: jwt.RequiredHeaders,
|
||||
Headers: jwt.Headers,
|
||||
Params: jwt.Params,
|
||||
Origins: gateway.Cors.Origins,
|
||||
}
|
||||
// Apply JWT authentication middlewares
|
||||
secureRouter.Use(jwtAuth.AuthMiddleware)
|
||||
secureRouter.Use(CORSHandler(route.Cors))
|
||||
secureRouter.PathPrefix("/").Handler(proxyRoute.ProxyHandler()) // Proxy handler
|
||||
secureRouter.PathPrefix("").Handler(proxyRoute.ProxyHandler()) // Proxy handler
|
||||
|
||||
}
|
||||
case OAuth:
|
||||
oauth, err := oAuthMiddleware(routeMiddleware.Rule)
|
||||
if err != nil {
|
||||
logger.Error("Error: %s", err.Error())
|
||||
} else {
|
||||
redirectURL := "/callback" + route.Path
|
||||
if oauth.RedirectURL != "" {
|
||||
redirectURL = oauth.RedirectURL
|
||||
}
|
||||
amw := middlewares.Oauth{
|
||||
ClientID: oauth.ClientID,
|
||||
ClientSecret: oauth.ClientSecret,
|
||||
RedirectURL: redirectURL,
|
||||
Scopes: oauth.Scopes,
|
||||
Endpoint: middlewares.OauthEndpoint{
|
||||
AuthURL: oauth.Endpoint.AuthURL,
|
||||
TokenURL: oauth.Endpoint.TokenURL,
|
||||
UserInfoURL: oauth.Endpoint.UserInfoURL,
|
||||
},
|
||||
State: oauth.State,
|
||||
Origins: gateway.Cors.Origins,
|
||||
JWTSecret: oauth.JWTSecret,
|
||||
Provider: oauth.Provider,
|
||||
}
|
||||
oauthRuler := oauthRulerMiddleware(amw)
|
||||
// Check if a cookie path is defined
|
||||
if oauthRuler.CookiePath == "" {
|
||||
oauthRuler.CookiePath = route.Path
|
||||
}
|
||||
// Check if a RedirectPath is defined
|
||||
if oauthRuler.RedirectPath == "" {
|
||||
oauthRuler.RedirectPath = util.ParseRoutePath(route.Path, middlewarePath)
|
||||
}
|
||||
if oauthRuler.Provider == "" {
|
||||
oauthRuler.Provider = "custom"
|
||||
}
|
||||
secureRouter.Use(amw.AuthMiddleware)
|
||||
secureRouter.Use(CORSHandler(route.Cors))
|
||||
secureRouter.PathPrefix("/").Handler(proxyRoute.ProxyHandler()) // Proxy handler
|
||||
secureRouter.PathPrefix("").Handler(proxyRoute.ProxyHandler()) // Proxy handler
|
||||
// Callback route
|
||||
r.HandleFunc(util.UrlParsePath(redirectURL), oauthRuler.callbackHandler).Methods("GET")
|
||||
}
|
||||
default:
|
||||
if !doesExist(routeMiddleware.Type) {
|
||||
logger.Error("Unknown middlewares type %s", routeMiddleware.Type)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,7 +19,6 @@ import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/jkaninda/goma-gateway/pkg/logger"
|
||||
"net/http"
|
||||
"os"
|
||||
@@ -33,9 +32,7 @@ func (gatewayServer GatewayServer) Start() error {
|
||||
logger.Info("Initializing routes...")
|
||||
route := gatewayServer.Initialize()
|
||||
logger.Debug("Routes count=%d, Middlewares count=%d", len(gatewayServer.gateway.Routes), len(gatewayServer.middlewares))
|
||||
if err := gatewayServer.initRedis(); err != nil {
|
||||
return fmt.Errorf("failed to initialize Redis: %w", err)
|
||||
}
|
||||
gatewayServer.initRedis()
|
||||
defer gatewayServer.closeRedis()
|
||||
|
||||
tlsConfig, listenWithTLS, err := gatewayServer.initTLS()
|
||||
@@ -51,9 +48,7 @@ func (gatewayServer GatewayServer) Start() error {
|
||||
httpsServer := gatewayServer.createServer(":8443", route, tlsConfig)
|
||||
|
||||
// Start HTTP/HTTPS servers
|
||||
if err := gatewayServer.startServers(httpServer, httpsServer, listenWithTLS); err != nil {
|
||||
return err
|
||||
}
|
||||
gatewayServer.startServers(httpServer, httpsServer, listenWithTLS)
|
||||
|
||||
// Handle graceful shutdown
|
||||
return gatewayServer.shutdown(httpServer, httpsServer, listenWithTLS)
|
||||
@@ -70,7 +65,7 @@ func (gatewayServer GatewayServer) createServer(addr string, handler http.Handle
|
||||
}
|
||||
}
|
||||
|
||||
func (gatewayServer GatewayServer) startServers(httpServer, httpsServer *http.Server, listenWithTLS bool) error {
|
||||
func (gatewayServer GatewayServer) startServers(httpServer, httpsServer *http.Server, listenWithTLS bool) {
|
||||
go func() {
|
||||
logger.Info("Starting HTTP server on 0.0.0.0:8080")
|
||||
if err := httpServer.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) {
|
||||
@@ -86,8 +81,6 @@ func (gatewayServer GatewayServer) startServers(httpServer, httpsServer *http.Se
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (gatewayServer GatewayServer) shutdown(httpServer, httpsServer *http.Server, listenWithTLS bool) error {
|
||||
|
||||
@@ -123,7 +123,6 @@ type ProxyRoute struct {
|
||||
rewrite string
|
||||
destination string
|
||||
backends []string
|
||||
//healthCheck RouteHealthCheck
|
||||
methods []string
|
||||
cors Cors
|
||||
disableHostFording bool
|
||||
|
||||
@@ -55,7 +55,7 @@ func Fatal(msg string, args ...interface{}) {
|
||||
func Debug(msg string, args ...interface{}) {
|
||||
log.SetOutput(getStd(util.GetStringEnv("GOMA_ACCESS_LOG", "/dev/stdout")))
|
||||
logLevel := util.GetStringEnv("GOMA_LOG_LEVEL", "")
|
||||
if strings.ToLower(logLevel) == "trace" || strings.ToLower(logLevel) == "debug" {
|
||||
if strings.ToLower(logLevel) == traceLog || strings.ToLower(logLevel) == "debug" {
|
||||
logWithCaller("DEBUG", msg, args...)
|
||||
}
|
||||
|
||||
@@ -63,7 +63,7 @@ func Debug(msg string, args ...interface{}) {
|
||||
func Trace(msg string, args ...interface{}) {
|
||||
log.SetOutput(getStd(util.GetStringEnv("GOMA_ACCESS_LOG", "/dev/stdout")))
|
||||
logLevel := util.GetStringEnv("GOMA_LOG_LEVEL", "")
|
||||
if strings.ToLower(logLevel) == "trace" {
|
||||
if strings.ToLower(logLevel) == traceLog {
|
||||
logWithCaller("DEBUG", msg, args...)
|
||||
}
|
||||
|
||||
@@ -86,7 +86,7 @@ func logWithCaller(level, msg string, args ...interface{}) {
|
||||
// Log message with caller information if GOMA_LOG_LEVEL is trace
|
||||
logLevel := util.GetStringEnv("GOMA_LOG_LEVEL", "")
|
||||
if strings.ToLower(logLevel) != "off" {
|
||||
if strings.ToLower(logLevel) == "trace" {
|
||||
if strings.ToLower(logLevel) == traceLog {
|
||||
log.Printf("%s: %s (File: %s, Line: %d)\n", level, formattedMessage, file, line)
|
||||
} else {
|
||||
log.Printf("%s: %s\n", level, formattedMessage)
|
||||
|
||||
20
pkg/logger/var.go
Normal file
20
pkg/logger/var.go
Normal file
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
* Copyright 2024 Jonas Kaninda
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package logger
|
||||
|
||||
const traceLog = "trace"
|
||||
@@ -10,13 +10,14 @@ You may get a copy of the License at
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
*/
|
||||
import (
|
||||
"github.com/robfig/cron/v3"
|
||||
"net/url"
|
||||
"os"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/robfig/cron/v3"
|
||||
)
|
||||
|
||||
// FileExists checks if the file does exist
|
||||
@@ -148,7 +149,7 @@ func Slug(text string) string {
|
||||
text = strings.ToLower(text)
|
||||
|
||||
// Replace spaces and special characters with hyphens
|
||||
re := regexp.MustCompile(`[^\w]+`)
|
||||
re := regexp.MustCompile(`\W+`)
|
||||
text = re.ReplaceAllString(text, "-")
|
||||
|
||||
// Remove leading and trailing hyphens
|
||||
|
||||
Reference in New Issue
Block a user