mirror of
https://github.com/rjNemo/rentease.git
synced 2026-06-06 02:36:49 +00:00
170 lines
3.7 KiB
Go
170 lines
3.7 KiB
Go
package server
|
|
|
|
import (
|
|
"context"
|
|
"embed"
|
|
"errors"
|
|
"fmt"
|
|
"net/http"
|
|
"os"
|
|
"os/signal"
|
|
"time"
|
|
|
|
"github.com/getsentry/sentry-go"
|
|
sentryecho "github.com/getsentry/sentry-go/echo"
|
|
"github.com/gorilla/sessions"
|
|
"github.com/labstack/echo-contrib/session"
|
|
"github.com/labstack/echo/v4"
|
|
"github.com/labstack/echo/v4/middleware"
|
|
|
|
"github.com/rjNemo/rentease/config"
|
|
"github.com/rjNemo/rentease/internal/auth"
|
|
"github.com/rjNemo/rentease/internal/booking"
|
|
"github.com/rjNemo/rentease/internal/pdf"
|
|
)
|
|
|
|
type Server struct {
|
|
Router *echo.Echo
|
|
bs *booking.Service
|
|
as *auth.Service
|
|
ps *pdf.PdfService
|
|
hc *config.Host
|
|
addr string
|
|
secretKey string
|
|
apiKey string
|
|
}
|
|
|
|
type options struct {
|
|
port *int
|
|
fs *embed.FS
|
|
debug *bool
|
|
secretKey *string
|
|
apiKey *string
|
|
origins []string
|
|
}
|
|
|
|
type Option func(*options) error
|
|
|
|
func WithPort(port int) Option {
|
|
return func(o *options) error {
|
|
if port <= 0 {
|
|
return errors.New("port should be positive")
|
|
}
|
|
o.port = &port
|
|
return nil
|
|
}
|
|
}
|
|
|
|
func WithFileSystem(fs embed.FS) Option {
|
|
return func(o *options) error {
|
|
o.fs = &fs
|
|
return nil
|
|
}
|
|
}
|
|
|
|
func WithDebug(debug bool) Option {
|
|
return func(o *options) error {
|
|
o.debug = &debug
|
|
return nil
|
|
}
|
|
}
|
|
|
|
func WithSecretKey(secretKey string) Option {
|
|
return func(o *options) error {
|
|
o.secretKey = &secretKey
|
|
return nil
|
|
}
|
|
}
|
|
|
|
func WithOrigins(origins []string) Option {
|
|
return func(o *options) error {
|
|
o.origins = origins
|
|
return nil
|
|
}
|
|
}
|
|
|
|
func WithApiKey(apiKey string) Option {
|
|
return func(o *options) error {
|
|
o.apiKey = &apiKey
|
|
return nil
|
|
}
|
|
}
|
|
|
|
func New(bs *booking.Service, as *auth.Service, ps *pdf.PdfService, hc *config.Host, opts ...Option) (*Server, error) {
|
|
option := new(options)
|
|
for _, opt := range opts {
|
|
err := opt(option)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
s := &Server{
|
|
Router: NewRouter(*option.fs, *option.debug, *option.secretKey, option.origins),
|
|
bs: bs,
|
|
as: as,
|
|
ps: ps,
|
|
hc: hc,
|
|
addr: fmt.Sprintf("0.0.0.0:%d", *option.port),
|
|
secretKey: *option.secretKey,
|
|
apiKey: *option.apiKey,
|
|
}
|
|
|
|
s.MountHandlers()
|
|
return s, nil
|
|
}
|
|
|
|
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 NewRouter(fs embed.FS, debug bool, secret string, origins []string) *echo.Echo {
|
|
e := echo.New()
|
|
// config
|
|
e.HideBanner = true
|
|
e.Debug = debug
|
|
e.HTTPErrorHandler = func(err error, c echo.Context) {
|
|
if hub := sentryecho.GetHubFromContext(c); hub != nil {
|
|
hub.WithScope(func(s *sentry.Scope) {
|
|
hub.CaptureMessage(err.Error())
|
|
})
|
|
}
|
|
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)
|
|
}
|
|
}
|
|
// middlewares
|
|
e.Use(middleware.LoggerWithConfig(middleware.LoggerConfig{
|
|
Format: "${time_rfc3339} [${method}: ${status}] ${uri}; ip=${remote_ip}; ${latency_human}; ${user_agent}\n",
|
|
}))
|
|
e.Use(middleware.Recover())
|
|
e.Use(middleware.Secure())
|
|
e.Use(middleware.Gzip())
|
|
e.Use(middleware.CORSWithConfig(middleware.CORSConfig{AllowOrigins: origins}))
|
|
e.Use(sentryecho.New(sentryecho.Options{}))
|
|
e.Use(SentryTracingMiddleware)
|
|
e.Use(session.Middleware(sessions.NewCookieStore([]byte(secret))))
|
|
// static assets
|
|
e.StaticFS("/static", echo.MustSubFS(fs, "assets"))
|
|
|
|
return e
|
|
}
|