package server import ( "context" "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" "gorm.io/gorm" "github.com/rjNemo/rentease/constants" "github.com/rjNemo/rentease/internal/domains/booking" ) type Server struct { Router *echo.Echo db *gorm.DB bs *booking.Service addr string } func New(db *gorm.DB) *Server { return &Server{ Router: echo.New(), db: db, bs: booking.NewService(db), addr: fmt.Sprintf("0.0.0.0:%s", os.Getenv("PORT")), } } func (s Server) MountHandlers() { // config s.Router.HideBanner = true s.Router.Debug = strings.ToLower(os.Getenv("DEBUG")) == "true" s.Router.Use(middleware.LoggerWithConfig(middleware.LoggerConfig{ Format: "${time_rfc3339} [${method}: ${status}] ${uri}; ip=${remote_ip}; ${latency_human}; ${user_agent}\n", })) s.Router.HTTPErrorHandler = s.customHTTPErrorHandler // middlewares s.Router.Use(middleware.Recover()) s.Router.Use(middleware.Secure()) // static assets s.Router.Static("/static", "assets") // landing page s.Router.GET("/", s.handleHomePage()) s.Router.GET(constants.RouteNewBooking, s.handleNewBookingPage()) s.Router.POST(constants.RouteNewBooking, s.handleCreateBooking()) s.Router.GET(fmt.Sprintf("%s/:id", constants.RouteBooking), s.handleBookingPage()) s.Router.POST(fmt.Sprintf("%s/:id/items", constants.RouteBooking), s.handleCreateItem()) } func (s Server) Start() { go func() { if err := s.Router.Start(s.addr); err != nil && !errors.Is(err, http.ErrServerClosed) { s.Router.Logger.Fatal("shutting down the server") } }() 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 (s Server) 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 (s Server) customHTTPErrorHandler(err error, c echo.Context) { code := http.StatusInternalServerError var he *echo.HTTPError if errors.As(err, &he) { code = he.Code } s.Router.Logger.Error(err) errorPage := fmt.Sprintf("assets/html/HTTP%d.html", code) if err := c.File(errorPage); err != nil { c.Logger().Error(err) } }