2023-07-18 14:40:10 +02:00
|
|
|
package ginext
|
|
|
|
|
2023-07-18 15:12:06 +02:00
|
|
|
import (
|
2023-07-24 18:22:36 +02:00
|
|
|
"fmt"
|
2023-07-18 15:12:06 +02:00
|
|
|
"github.com/gin-gonic/gin"
|
2023-07-24 18:34:56 +02:00
|
|
|
"github.com/rs/zerolog/log"
|
2023-07-24 18:22:36 +02:00
|
|
|
"gogs.mikescher.com/BlackForestBytes/goext/langext"
|
|
|
|
"gogs.mikescher.com/BlackForestBytes/goext/mathext"
|
2023-12-07 10:54:36 +01:00
|
|
|
"gogs.mikescher.com/BlackForestBytes/goext/rext"
|
2023-07-24 18:34:56 +02:00
|
|
|
"net"
|
2023-07-18 16:01:34 +02:00
|
|
|
"net/http"
|
2023-12-07 10:54:36 +01:00
|
|
|
"regexp"
|
2023-07-24 18:22:36 +02:00
|
|
|
"strings"
|
2023-07-18 15:12:06 +02:00
|
|
|
"time"
|
|
|
|
)
|
2023-07-18 14:40:10 +02:00
|
|
|
|
|
|
|
type GinWrapper struct {
|
|
|
|
engine *gin.Engine
|
|
|
|
SuppressGinLogs bool
|
|
|
|
|
2023-07-24 11:11:15 +02:00
|
|
|
allowCors bool
|
|
|
|
ginDebug bool
|
2023-08-03 09:09:27 +02:00
|
|
|
bufferBody bool
|
2023-07-24 11:11:15 +02:00
|
|
|
requestTimeout time.Duration
|
2023-07-24 18:22:36 +02:00
|
|
|
|
|
|
|
routeSpecs []ginRouteSpec
|
|
|
|
}
|
|
|
|
|
|
|
|
type ginRouteSpec struct {
|
|
|
|
Method string
|
|
|
|
URL string
|
|
|
|
Middlewares []string
|
|
|
|
Handler string
|
2023-07-18 14:40:10 +02:00
|
|
|
}
|
|
|
|
|
2023-08-03 09:09:27 +02:00
|
|
|
// NewEngine creates a new (wrapped) ginEngine
|
|
|
|
// Parameters are:
|
|
|
|
// - [allowCors] Add cors handler to allow all CORS requests on the default http methods
|
|
|
|
// - [ginDebug] Set gin.debug to true (adds more logs)
|
|
|
|
// - [bufferBody] Buffers the input body stream, this way the ginext error handler can later include the whole request body
|
|
|
|
// - [timeout] The default handler timeout
|
|
|
|
func NewEngine(allowCors bool, ginDebug bool, bufferBody bool, timeout time.Duration) *GinWrapper {
|
2023-07-18 14:40:10 +02:00
|
|
|
engine := gin.New()
|
|
|
|
|
|
|
|
wrapper := &GinWrapper{
|
|
|
|
engine: engine,
|
|
|
|
SuppressGinLogs: false,
|
|
|
|
allowCors: allowCors,
|
|
|
|
ginDebug: ginDebug,
|
2023-08-03 09:09:27 +02:00
|
|
|
bufferBody: bufferBody,
|
2023-07-18 15:12:06 +02:00
|
|
|
requestTimeout: timeout,
|
2023-07-18 14:40:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
engine.RedirectFixedPath = false
|
|
|
|
engine.RedirectTrailingSlash = false
|
|
|
|
|
|
|
|
if allowCors {
|
|
|
|
engine.Use(CorsMiddleware())
|
|
|
|
}
|
|
|
|
|
2023-07-24 18:22:36 +02:00
|
|
|
// do not debug-print routes
|
|
|
|
gin.DebugPrintRouteFunc = func(_, _, _ string, _ int) {}
|
|
|
|
|
2023-10-09 09:23:40 +02:00
|
|
|
if !ginDebug {
|
2023-07-24 18:22:36 +02:00
|
|
|
gin.SetMode(gin.ReleaseMode)
|
|
|
|
|
2023-07-18 14:40:10 +02:00
|
|
|
ginlogger := gin.Logger()
|
|
|
|
engine.Use(func(context *gin.Context) {
|
|
|
|
if !wrapper.SuppressGinLogs {
|
|
|
|
ginlogger(context)
|
|
|
|
}
|
|
|
|
})
|
2023-07-24 18:22:36 +02:00
|
|
|
} else {
|
|
|
|
gin.SetMode(gin.DebugMode)
|
2023-07-18 14:40:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return wrapper
|
|
|
|
}
|
2023-07-18 16:01:34 +02:00
|
|
|
|
2023-07-24 18:34:56 +02:00
|
|
|
func (w *GinWrapper) ListenAndServeHTTP(addr string, postInit func(port string)) (chan error, *http.Server) {
|
2023-07-24 18:38:04 +02:00
|
|
|
|
|
|
|
w.DebugPrintRoutes()
|
|
|
|
|
2023-07-24 18:34:56 +02:00
|
|
|
httpserver := &http.Server{
|
|
|
|
Addr: addr,
|
|
|
|
Handler: w.engine,
|
2023-07-24 18:22:36 +02:00
|
|
|
}
|
|
|
|
|
2023-07-24 18:34:56 +02:00
|
|
|
errChan := make(chan error)
|
|
|
|
|
|
|
|
go func() {
|
|
|
|
|
|
|
|
ln, err := net.Listen("tcp", httpserver.Addr)
|
|
|
|
if err != nil {
|
|
|
|
errChan <- err
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
_, port, err := net.SplitHostPort(ln.Addr().String())
|
|
|
|
if err != nil {
|
|
|
|
errChan <- err
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
log.Info().Str("address", httpserver.Addr).Msg("HTTP-Server started on http://localhost:" + port)
|
|
|
|
|
|
|
|
if postInit != nil {
|
2023-07-24 18:38:04 +02:00
|
|
|
postInit(port) // the net.Listener a few lines above is at this point actually already buffering requests
|
2023-07-24 18:34:56 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
errChan <- httpserver.Serve(ln)
|
|
|
|
}()
|
|
|
|
|
|
|
|
return errChan, httpserver
|
2023-07-18 16:01:34 +02:00
|
|
|
}
|
2023-07-24 18:22:36 +02:00
|
|
|
|
2023-07-24 18:34:56 +02:00
|
|
|
func (w *GinWrapper) DebugPrintRoutes() {
|
|
|
|
if !w.ginDebug {
|
|
|
|
return
|
|
|
|
}
|
2023-07-24 18:22:36 +02:00
|
|
|
|
|
|
|
lines := make([][4]string, 0)
|
|
|
|
|
|
|
|
pad := [4]int{0, 0, 0, 0}
|
|
|
|
|
|
|
|
for _, spec := range w.routeSpecs {
|
|
|
|
|
|
|
|
line := [4]string{
|
|
|
|
spec.Method,
|
|
|
|
spec.URL,
|
2023-12-07 10:54:36 +01:00
|
|
|
strings.Join(langext.ArrMap(spec.Middlewares, w.cleanMiddlewareName), " -> "),
|
2023-12-07 14:42:25 +01:00
|
|
|
w.cleanMiddlewareName(spec.Handler),
|
2023-07-24 18:22:36 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
lines = append(lines, line)
|
|
|
|
|
|
|
|
pad[0] = mathext.Max(pad[0], len(line[0]))
|
|
|
|
pad[1] = mathext.Max(pad[1], len(line[1]))
|
|
|
|
pad[2] = mathext.Max(pad[2], len(line[2]))
|
|
|
|
pad[3] = mathext.Max(pad[3], len(line[3]))
|
|
|
|
}
|
|
|
|
|
2023-12-07 14:42:25 +01:00
|
|
|
fmt.Printf("Gin-Routes:")
|
2023-07-24 18:22:36 +02:00
|
|
|
for _, line := range lines {
|
|
|
|
|
2023-12-07 14:42:25 +01:00
|
|
|
fmt.Printf(" %s %s --> %s --> %s\n",
|
2023-07-24 18:42:33 +02:00
|
|
|
langext.StrPadRight("["+line[0]+"]", " ", pad[0]+2),
|
2023-07-24 18:22:36 +02:00
|
|
|
langext.StrPadRight(line[1], " ", pad[1]),
|
|
|
|
langext.StrPadRight(line[2], " ", pad[2]),
|
|
|
|
langext.StrPadRight(line[3], " ", pad[3]))
|
|
|
|
}
|
|
|
|
}
|
2023-12-07 10:54:36 +01:00
|
|
|
|
|
|
|
func (w *GinWrapper) cleanMiddlewareName(fname string) string {
|
|
|
|
|
|
|
|
funcSuffix := rext.W(regexp.MustCompile(`\.func[0-9]+(?:\.[0-9]+)*$`))
|
|
|
|
if match, ok := funcSuffix.MatchFirst(fname); ok {
|
|
|
|
fname = fname[:len(fname)-match.FullMatch().Length()]
|
|
|
|
}
|
|
|
|
|
|
|
|
if strings.HasSuffix(fname, ".(*GinRoutesWrapper).WithJSONFilter") {
|
|
|
|
fname = "[JSONFilter]"
|
|
|
|
}
|
|
|
|
|
|
|
|
if fname == "ginext.BodyBuffer" {
|
|
|
|
fname = "[BodyBuffer]"
|
|
|
|
}
|
|
|
|
|
2023-12-07 14:42:25 +01:00
|
|
|
skipPrefixes := []string{"api.(*Handler).", "api.", "ginext.", "handler.", "admin-app.", "employee-app.", "employer-app."}
|
2023-12-07 10:54:36 +01:00
|
|
|
for _, pfx := range skipPrefixes {
|
|
|
|
if strings.HasPrefix(fname, pfx) {
|
|
|
|
fname = fname[len(pfx):]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return fname
|
|
|
|
}
|