sessions/cmd/sessions/internal/serve/serve.go

121 lines
2.8 KiB
Go

package serve
import (
"context"
"errors"
"fmt"
"net/http"
"os"
"os/signal"
"strings"
"syscall"
"time"
"gitea.dwysokinski.me/Kichiyaki/chizap"
"gitea.dwysokinski.me/twhelp/sessions/cmd/sessions/internal"
"gitea.dwysokinski.me/twhelp/sessions/internal/bundb"
"gitea.dwysokinski.me/twhelp/sessions/internal/router/meta"
"github.com/uptrace/bun"
"github.com/urfave/cli/v2"
"github.com/go-chi/chi/v5"
"github.com/go-chi/chi/v5/middleware"
"go.uber.org/zap"
)
const (
defaultPort = "9234"
readTimeout = 2 * time.Second
readHeaderTimeout = 2 * time.Second
writeTimeout = 2 * time.Second
idleTimeout = 2 * time.Second
serverShutdownTimeout = 10 * time.Second
metaEndpointsPrefix = "/_meta"
)
func New() *cli.Command {
return &cli.Command{
Name: "serve",
Usage: "Runs the http server",
Action: func(c *cli.Context) error {
db, err := internal.NewBunDB()
if err != nil {
return fmt.Errorf("internal.NewBunDB: %w", err)
}
defer func() {
_ = db.Close()
}()
logger := zap.L()
srv, err := newServer(serverConfig{
appVersion: c.App.Version,
logger: logger,
db: db,
})
if err != nil {
return fmt.Errorf("newServer: %w", err)
}
go runServer(srv, logger)
logger.Info("Server is listening on the port "+defaultPort, zap.String("port", defaultPort))
waitForSignal(c.Context)
ctxShutdown, cancelShutdown := context.WithTimeout(c.Context, serverShutdownTimeout)
defer cancelShutdown()
if err := srv.Shutdown(ctxShutdown); err != nil {
return fmt.Errorf("srv.Shutdown: %w", err)
}
return nil
},
}
}
type serverConfig struct {
appVersion string
logger *zap.Logger
db *bun.DB
}
func newServer(cfg serverConfig) (*http.Server, error) {
// router
r := chi.NewRouter()
r.Use(getChiMiddlewares(cfg.logger)...)
r.Mount(metaEndpointsPrefix, meta.NewRouter([]meta.Checker{bundb.NewChecker(cfg.db)}))
return &http.Server{
Addr: ":" + defaultPort,
Handler: r,
ReadTimeout: readTimeout,
ReadHeaderTimeout: readHeaderTimeout,
WriteTimeout: writeTimeout,
IdleTimeout: idleTimeout,
}, nil
}
func getChiMiddlewares(logger *zap.Logger) chi.Middlewares {
return chi.Middlewares{
middleware.RealIP,
chizap.Logger(logger, chizap.WithFilter(omitMetaEndpoints)),
middleware.Recoverer,
}
}
func runServer(srv *http.Server, logger *zap.Logger) {
if err := srv.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) {
logger.Fatal("srv.ListenAndServe:" + err.Error())
}
}
func waitForSignal(ctx context.Context) {
ctx, stop := signal.NotifyContext(ctx, os.Interrupt, syscall.SIGTERM)
defer stop()
<-ctx.Done()
}
func omitMetaEndpoints(r *http.Request) bool {
return !strings.HasPrefix(r.URL.Path, metaEndpointsPrefix)
}