feat: add serve command with graceful shutdown support

This commit is contained in:
amir-ys 2026-04-28 23:34:52 +03:30
parent 9d97a4e313
commit 0948b36012
4 changed files with 108 additions and 23 deletions

View File

@ -1,13 +1,16 @@
package command package command
import ( import (
"fmt" "context"
"log" "log"
"net/http"
"os" "os"
"os/signal" "os/signal"
"strconv"
"syscall" "syscall"
"time"
"git.gocasts.ir/ebhomengo/niki/productapp"
"git.gocasts.ir/ebhomengo/niki/repository/mysql"
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
@ -25,27 +28,53 @@ var serveCmd = &cobra.Command{
func serve() { func serve() {
log.Println("Product Service Starting...") log.Println("Product Service Starting...")
// TODO: Initialize database connection cfg := productapp.Config{
// TODO: Initialize service dependencies HTTPServer: productapp.HTTPServerConfig{
// TODO: Setup HTTP server with routes Port: getEnvInt("HTTP_PORT", 8080),
},
Database: mysql.Config{
Username: getEnv("DB_USERNAME", "root"),
Password: getEnv("DB_PASSWORD", ""),
Port: getEnvInt("DB_PORT", 3306),
Host: getEnv("DB_HOST", "localhost"),
DBName: getEnv("DB_NAME", "niki_db"),
},
}
if p, err := strconv.Atoi(port); err == nil && p > 0 {
cfg.HTTPServer.Port = p
}
app := productapp.Setup(cfg)
// Setup graceful shutdown
go func() { go func() {
sigCh := make(chan os.Signal, 1) app.Start()
signal.Notify(sigCh, syscall.SIGINT, syscall.SIGTERM)
<-sigCh
log.Println("Shutting down Product Service gracefully...")
os.Exit(0)
}() }()
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { quit := make(chan os.Signal, 1)
fmt.Fprintf(w, "Product Service OK!") signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
}) <-quit
log.Printf("Product Service listening on port %s", port) ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
if err := http.ListenAndServe(":"+port, nil); err != nil { defer cancel()
log.Fatalf("Failed to start server: %v", err)
if err := app.Stop(ctx); err != nil {
log.Fatalf("Server shutdown error: %v", err)
} }
log.Println("Product Service stopped.")
}
func getEnvInt(key string, defaultValue int) int {
val := os.Getenv(key)
if val == "" {
return defaultValue
}
n, err := strconv.Atoi(val)
if err != nil {
return defaultValue
}
return n
} }
func init() { func init() {

View File

@ -1,8 +1,64 @@
package productapp package productapp
import "net/http" import (
"context"
"fmt"
"log"
httpserver "git.gocasts.ir/ebhomengo/niki/delivery/http_server"
producthttp "git.gocasts.ir/ebhomengo/niki/productapp/delivery/http"
productdb "git.gocasts.ir/ebhomengo/niki/productapp/repository/database"
"git.gocasts.ir/ebhomengo/niki/repository/mysql"
"github.com/labstack/echo/v4"
"github.com/labstack/echo/v4/middleware"
)
type HTTPServerConfig struct {
Port int
}
type Config struct {
HTTPServer HTTPServerConfig
Database mysql.Config
}
type Application struct { type Application struct {
Config Config Config Config
HTTPServer *http.Server HTTPServer *producthttp.Server
DB *productdb.DB
Echo *echo.Echo
}
func Setup(cfg Config) *Application {
db := mysql.New(cfg.Database)
productDB := productdb.New(db)
e := echo.New()
e.Use(middleware.Recover())
hsrv := &httpserver.Server{
Router: e,
}
srv := producthttp.NewServer(hsrv)
srv.RegisterRoutes()
return &Application{
Config: cfg,
HTTPServer: srv,
DB: productDB,
Echo: e,
}
}
func (app *Application) Start() {
address := fmt.Sprintf(":%d", app.Config.HTTPServer.Port)
log.Printf("Product Service listening on %s", address)
if err := app.Echo.Start(address); err != nil {
log.Fatalf("Failed to start server: %v", err)
}
}
func (app *Application) Stop(ctx context.Context) error {
return app.Echo.Shutdown(ctx)
} }

View File

@ -1,4 +1 @@
package productapp package productapp
type Config struct {
}

View File

@ -20,4 +20,7 @@ func (s *Server) Serve() {
func (s *Server) Stop() {} func (s *Server) Stop() {}
func (s *Server) RegisterRoutes() {} func (s *Server) RegisterRoutes() {
r := s.HTTPServer.Router
r.GET("health-check", s.Handler.HealthCheck)
}