2025-03-28 08:49:33 +01:00
|
|
|
package web
|
2025-03-27 09:19:08 +01:00
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"fmt"
|
|
|
|
"github.com/labstack/echo/v4"
|
|
|
|
"github.com/labstack/echo/v4/middleware"
|
|
|
|
"log/slog"
|
|
|
|
"net"
|
|
|
|
"net/http"
|
|
|
|
"syscall"
|
|
|
|
"time"
|
|
|
|
)
|
|
|
|
|
|
|
|
type Server struct {
|
|
|
|
module *Module
|
|
|
|
listener *net.Listener
|
|
|
|
csrfConfig *middleware.CSRFConfig
|
|
|
|
router *echo.Echo
|
|
|
|
server *http.Server
|
|
|
|
}
|
|
|
|
|
|
|
|
func NewServer(module *Module) *Server {
|
|
|
|
|
|
|
|
s := &Server{
|
|
|
|
module: module,
|
|
|
|
}
|
|
|
|
|
|
|
|
s.init()
|
|
|
|
|
|
|
|
return s
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *Server) init() {
|
|
|
|
|
|
|
|
s.csrfConfig = &middleware.CSRFConfig{
|
|
|
|
TokenLookup: "form:_csrf,query:csrf,header:X-CSRF-Token",
|
|
|
|
}
|
|
|
|
|
|
|
|
s.router = echo.New()
|
|
|
|
//s.router.Pre(middleware.RemoveTrailingSlash())
|
|
|
|
s.router.Use(middleware.Logger())
|
|
|
|
s.router.Use(middleware.RecoverWithConfig(middleware.RecoverConfig{
|
|
|
|
LogLevel: 4,
|
|
|
|
}))
|
|
|
|
s.router.Use(middleware.CSRFWithConfig(*s.csrfConfig))
|
|
|
|
s.router.Use(CustomContext(s.csrfConfig))
|
|
|
|
if s.module.moduleConfig != nil && s.module.moduleConfig.Middlewares != nil {
|
|
|
|
for _, m := range s.module.moduleConfig.Middlewares {
|
|
|
|
s.router.Use(m())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
tplFS := s.module.getTemplatesFS()
|
|
|
|
s.router.Renderer = NewRenderer(tplFS)
|
|
|
|
|
|
|
|
s.router.StaticFS("/static", s.module.getStaticFS())
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *Server) Start() error {
|
|
|
|
|
|
|
|
config := &net.ListenConfig{Control: s.reusePort}
|
|
|
|
listener, err := config.Listen(
|
|
|
|
context.Background(),
|
|
|
|
"tcp",
|
|
|
|
fmt.Sprintf(":%d", s.module.config.Port),
|
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
s.listener = &listener
|
|
|
|
|
|
|
|
s.server = &http.Server{
|
|
|
|
Addr: fmt.Sprintf(":%d", s.module.config.Port),
|
|
|
|
Handler: s.router,
|
|
|
|
}
|
|
|
|
|
|
|
|
go s.server.Serve(listener)
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *Server) Stop() error {
|
|
|
|
|
|
|
|
if s.server == nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
timeout := 10 * time.Second
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
|
|
|
defer cancel()
|
|
|
|
|
|
|
|
return s.server.Shutdown(ctx)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *Server) GetGroup() *Group {
|
|
|
|
return &Group{
|
|
|
|
s.router.Group("/"),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *Server) GetRenderer() *Renderer {
|
|
|
|
return s.router.Renderer.(*Renderer)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *Server) reusePort(network, address string, conn syscall.RawConn) error {
|
|
|
|
return conn.Control(func(descriptor uintptr) {
|
|
|
|
err := syscall.SetsockoptInt(int(descriptor), syscall.SOL_SOCKET, syscall.SO_REUSEADDR, 1)
|
|
|
|
if err != nil {
|
|
|
|
slog.Error("error during setting reuseport", err)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|