From 1a7d5af778db716ce058cbb03622163344f5d419 Mon Sep 17 00:00:00 2001 From: Dmitry Korolev Date: Mon, 2 Dec 2024 23:20:15 +0300 Subject: [PATCH 1/2] =?UTF-8?q?-=20=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=20=D0=BE=D0=B1=D1=89=D0=B8=D0=B9=20=D0=BA=D0=BE?= =?UTF-8?q?=D0=BD=D1=84=D0=B8=D0=B3=20-=20=D0=94=D0=BE=D0=B1=D0=B0=D0=B2?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=20=D0=BA=D0=BE=D0=BD=D1=84=D0=B8=D0=B3=20db?= =?UTF-8?q?=20-=20=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB=D0=B5=D0=BD=20?= =?UTF-8?q?=D0=BA=D0=BE=D0=BD=D1=84=D0=B8=D0=B3=20http=20=D0=B8=20=D0=BD?= =?UTF-8?q?=D0=BE=D0=B2=D1=8B=D1=85=20=D0=BF=D0=B0=D1=80=D0=B0=D0=BC=D0=B5?= =?UTF-8?q?=D1=82=D1=80=D0=BE=D0=B2=20=D0=B2=20=D0=BD=D0=B5=D0=B3=D0=BE=20?= =?UTF-8?q?-=20=D0=A3=D0=B1=D1=80=D0=B0=D0=BB=20MaxConns,=20MaxConnIdleTim?= =?UTF-8?q?e,=20context.WithTimeout=20...=D1=82.=D0=BA=20=D1=8D=D1=82?= =?UTF-8?q?=D0=BE=20=D0=B2=D1=81=D1=91=20=D0=BC=D0=BE=D0=B6=D0=BD=D0=BE=20?= =?UTF-8?q?=D0=B7=D0=B0=D0=B4=D0=B0=D1=82=D1=8C=20=D0=B2=20DATABASE=5FURL?= =?UTF-8?q?=20=D0=BA=D0=B0=D0=BA=20=D0=BF=D0=B0=D1=80=D0=B0=D0=BC=D0=B5?= =?UTF-8?q?=D1=82=D1=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/pkg/app/app.go | 4 +- internal/pkg/app/config.go | 13 ++++++ internal/pkg/app/db.go | 14 +----- internal/pkg/app/db_config.go | 24 ++++++++++ internal/pkg/app/http.go | 15 +++--- internal/pkg/app/http_config.go | 83 +++++++++++++++++++++++++++++++++ 6 files changed, 134 insertions(+), 19 deletions(-) create mode 100644 internal/pkg/app/config.go create mode 100644 internal/pkg/app/db_config.go create mode 100644 internal/pkg/app/http_config.go diff --git a/internal/pkg/app/app.go b/internal/pkg/app/app.go index cdd7c7e..bae5d7d 100644 --- a/internal/pkg/app/app.go +++ b/internal/pkg/app/app.go @@ -16,6 +16,8 @@ import ( ) type App struct { + cfg *config + logger *slog.Logger dbConn *pgxpool.Pool @@ -34,7 +36,7 @@ func NewApp() (*App, error) { app := &App{} // - config - // TODO: add config + app.cfg = newConfig() // - logger app.initLogger() // - db diff --git a/internal/pkg/app/config.go b/internal/pkg/app/config.go new file mode 100644 index 0000000..19aa72a --- /dev/null +++ b/internal/pkg/app/config.go @@ -0,0 +1,13 @@ +package app + +type config struct { + HTTP *configHTTP + DB *configDB +} + +func newConfig() *config { + c := new(config) + c.HTTP = newHTTPConfig() + c.DB = newDBConfig() + return c +} diff --git a/internal/pkg/app/db.go b/internal/pkg/app/db.go index e6fa703..788b889 100644 --- a/internal/pkg/app/db.go +++ b/internal/pkg/app/db.go @@ -2,27 +2,17 @@ package app import ( "context" - "os" - "time" "github.com/jackc/pgx/v4/pgxpool" ) func (a *App) initDBConn() error { - databaseURL := os.Getenv("DATABASE_URL") - - config, err := pgxpool.ParseConfig(databaseURL) + cfg, err := pgxpool.ParseConfig(a.cfg.DB.URL) if err != nil { return err } - config.MaxConns = 10 - config.MaxConnIdleTime = 30 * time.Minute - - ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) - defer cancel() - - db, err := pgxpool.ConnectConfig(ctx, config) + db, err := pgxpool.ConnectConfig(context.TODO(), cfg) if err != nil { return err } diff --git a/internal/pkg/app/db_config.go b/internal/pkg/app/db_config.go new file mode 100644 index 0000000..76b5eb8 --- /dev/null +++ b/internal/pkg/app/db_config.go @@ -0,0 +1,24 @@ +package app + +import "os" + +type configDB struct { + URL string +} + +func newDBConfig() *configDB { + c := &configDB{} + c.Load() + c.Validate() + return c +} + +func (c *configDB) Load() { + c.URL = os.Getenv("DATABASE_URL") +} + +func (c *configDB) Validate() { + if c.URL == "" { + panic("env DATABASE_URL must be set") + } +} diff --git a/internal/pkg/app/http.go b/internal/pkg/app/http.go index 63886b7..9ec0c14 100644 --- a/internal/pkg/app/http.go +++ b/internal/pkg/app/http.go @@ -3,9 +3,8 @@ package app import ( "context" "fmt" + "log/slog" "net/http" - "os" - "time" "github.com/gin-gonic/gin" @@ -23,7 +22,7 @@ func (a *App) initServer() error { a.handlers = handlers.NewHandlers(a.logger, a.repository) - //gin.SetMode(gin.ReleaseMode) + gin.SetMode(a.cfg.HTTP.GinMode) router := gin.Default() router.POST("/api/v1/bookings/:workshop_id", a.handlers.CreateBooking) router.GET("/api/v1/bookings/:workshop_id", a.handlers.ListBookings) @@ -31,13 +30,17 @@ func (a *App) initServer() error { a.router = router server := &http.Server{ - Addr: fmt.Sprintf(":%s", os.Getenv("HTTP_PORT")), - Handler: router, + Addr: fmt.Sprintf(":%s", a.cfg.HTTP.Server.Port), + Handler: http.TimeoutHandler(router, a.cfg.HTTP.Server.HandlerTimeout, "Timeout"), + ErrorLog: slog.NewLogLogger(a.logger.Handler(), slog.LevelError), + ReadHeaderTimeout: a.cfg.HTTP.Server.ReadHeaderTimeout, + ReadTimeout: a.cfg.HTTP.Server.ReadTimeout, + IdleTimeout: a.cfg.HTTP.Server.IdleTimeout, } a.http = server a.closers = append(a.closers, func() error { - ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + ctx, cancel := context.WithTimeout(context.Background(), a.cfg.HTTP.Server.ShutdownTimeout) defer cancel() return a.http.Shutdown(ctx) diff --git a/internal/pkg/app/http_config.go b/internal/pkg/app/http_config.go new file mode 100644 index 0000000..8437021 --- /dev/null +++ b/internal/pkg/app/http_config.go @@ -0,0 +1,83 @@ +package app + +import ( + "fmt" + "os" + "time" + + "github.com/gin-gonic/gin" +) + +type configHTTP struct { + Server struct { + Port string + HandlerTimeout time.Duration + ReadHeaderTimeout time.Duration + ReadTimeout time.Duration + IdleTimeout time.Duration + ShutdownTimeout time.Duration + } + GinMode string // "debug" | "release" | "test" +} + +func newHTTPConfig() *configHTTP { + c := &configHTTP{} + + // Set default + c.Server.Port = "8080" + c.Server.HandlerTimeout = 6 * time.Second + c.Server.ReadHeaderTimeout = 3 * time.Second + c.Server.ReadTimeout = 3 * time.Second + c.Server.IdleTimeout = 3 * time.Second + c.Server.ShutdownTimeout = 10 * time.Second + + c.Load() + c.Validate() + return c +} + +func (c *configHTTP) Load() { + if httpPort := os.Getenv("HTTP_PORT"); httpPort != "" { + c.Server.Port = httpPort + } + if httpTimeout := os.Getenv("HTTP_TIMEOUT"); httpTimeout != "" { + handlerTimeoutDuration, err := time.ParseDuration(httpTimeout) + if err != nil { + panic(fmt.Errorf("incorrect env HTTP_TIMEOUT: %w", err)) + } + c.Server.HandlerTimeout = handlerTimeoutDuration + } + if httpReadHeaderTimeout := os.Getenv("HTTP_READ_HEADER_TIMEOUT"); httpReadHeaderTimeout != "" { + readHeaderTimeoutDuration, err := time.ParseDuration(httpReadHeaderTimeout) + if err != nil { + panic(fmt.Errorf("incorrect env HTTP_READ_HEADER_TIMEOUT: %w", err)) + } + c.Server.ReadHeaderTimeout = readHeaderTimeoutDuration + } + if httpReadTimeout := os.Getenv("HTTP_READ_TIMEOUT"); httpReadTimeout != "" { + readTimeoutDuration, err := time.ParseDuration(httpReadTimeout) + if err != nil { + panic(fmt.Errorf("incorrect env HTTP_READ_TIMEOUT: %w", err)) + } + c.Server.ReadTimeout = readTimeoutDuration + } + if httpIdleTimeout := os.Getenv("HTTP_IDLE_TIMEOUT"); httpIdleTimeout != "" { + idleTimeoutDuration, err := time.ParseDuration(httpIdleTimeout) + if err != nil { + panic(fmt.Errorf("incorrect env HTTP_IDLE_TIMEOUT: %w", err)) + } + c.Server.IdleTimeout = idleTimeoutDuration + } + if httpShutdownTimeout := os.Getenv("HTTP_SHUTDOWN_TIMEOUT"); httpShutdownTimeout != "" { + shutdownTimeoutDuration, err := time.ParseDuration(httpShutdownTimeout) + if err != nil { + panic(fmt.Errorf("incorrect env HTTP_SHUTDOWN_TIMEOUT: %w", err)) + } + c.Server.ShutdownTimeout = shutdownTimeoutDuration + } + c.GinMode = os.Getenv(gin.EnvGinMode) +} + +func (c *configHTTP) Validate() { + +} From 10a86d05d45dadb0dece6127ce965e6c4da4b4da Mon Sep 17 00:00:00 2001 From: Dmitry Korolev Date: Wed, 1 Jan 2025 12:46:57 +0300 Subject: [PATCH 2/2] =?UTF-8?q?=D0=BF=D1=80=D0=B0=D0=B2=D0=BA=D0=B8=20?= =?UTF-8?q?=D0=B4=D0=BB=D1=8F=20=D0=B5=D0=B4=D0=B8=D0=BD=D0=BE=D0=BE=D0=B1?= =?UTF-8?q?=D1=80=D0=B0=D0=B7=D0=B8=D1=8F=20=D0=BA=D0=BE=D0=B4=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/pkg/app/app.go | 2 +- internal/pkg/app/config.go | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/internal/pkg/app/app.go b/internal/pkg/app/app.go index bae5d7d..5ee9e6f 100644 --- a/internal/pkg/app/app.go +++ b/internal/pkg/app/app.go @@ -36,7 +36,7 @@ func NewApp() (*App, error) { app := &App{} // - config - app.cfg = newConfig() + app.initConfig() // - logger app.initLogger() // - db diff --git a/internal/pkg/app/config.go b/internal/pkg/app/config.go index 19aa72a..8dd2930 100644 --- a/internal/pkg/app/config.go +++ b/internal/pkg/app/config.go @@ -5,9 +5,9 @@ type config struct { DB *configDB } -func newConfig() *config { +func (a *App) initConfig() { c := new(config) c.HTTP = newHTTPConfig() c.DB = newDBConfig() - return c + a.cfg = c }