package server import ( "context" "embed" "errors" "fmt" "net/http" "os" "os/signal" "strings" "time" "github.com/a-h/templ" "github.com/labstack/echo/v4" "github.com/labstack/echo/v4/middleware" "github.com/rjNemo/rentease/config" "github.com/rjNemo/rentease/internal/booking" "github.com/rjNemo/rentease/internal/pdf" ) type Server struct { Router *echo.Echo bs *booking.Service ps *pdf.PdfService hc *config.Host fs *embed.FS addr string } func New(fs *embed.FS, bs *booking.Service, ps *pdf.PdfService, hc *config.Host) *Server { s := &Server{ Router: NewRouter(*fs), bs: bs, ps: ps, hc: hc, addr: fmt.Sprintf("0.0.0.0:%s", os.Getenv("PORT")), } s.MountHandlers() return s } func (s Server) Start() { go func() { if err := s.Router.Start(s.addr); err != nil && !errors.Is(err, http.ErrServerClosed) { s.Router.Logger.Fatalf("shutting down the server: %s", err) } }() quit := make(chan os.Signal, 1) signal.Notify(quit, os.Interrupt) <-quit ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() if err := s.Router.Shutdown(ctx); err != nil { s.Router.Logger.Fatal(err) } } func renderTempl(c echo.Context, status int, t templ.Component) error { c.Response().Writer.WriteHeader(status) err := t.Render(context.Background(), c.Response().Writer) if err != nil { return c.String(http.StatusInternalServerError, "failed to render response template") } return nil } func NewRouter(fs embed.FS) *echo.Echo { e := echo.New() // config e.HideBanner = true e.Debug = strings.ToLower(os.Getenv("DEBUG")) == "true" e.Use(middleware.LoggerWithConfig(middleware.LoggerConfig{ Format: "${time_rfc3339} [${method}: ${status}] ${uri}; ip=${remote_ip}; ${latency_human}; ${user_agent}\n", })) e.HTTPErrorHandler = customHTTPErrorHandler(e) // middlewares e.Use(middleware.Recover()) e.Use(middleware.Secure()) // static assets e.StaticFS("/static", echo.MustSubFS(fs, "assets")) return e } func customHTTPErrorHandler(e *echo.Echo) echo.HTTPErrorHandler { return func(err error, c echo.Context) { code := http.StatusInternalServerError var he *echo.HTTPError if errors.As(err, &he) { code = he.Code } errorPage := fmt.Sprintf("assets/html/HTTP%d.html", code) if err := c.File(errorPage); err != nil { c.Logger().Error(err) } } }