package main import ( "context" "fmt" "log/slog" "net/http" "os" "os/signal" "syscall" "time" "github.com/food-ai/backend/internal/infra/config" "github.com/food-ai/backend/internal/infra/database" "github.com/food-ai/backend/internal/infra/locale" ) func main() { logger := slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{ Level: slog.LevelInfo, })) slog.SetDefault(logger) if runError := run(); runError != nil { slog.Error("fatal error", "err", runError) os.Exit(1) } } func run() error { appConfig, configError := config.Load() if configError != nil { return fmt.Errorf("load config: %w", configError) } applicationContext, stop := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM) defer stop() pool, poolError := database.NewPool(applicationContext, appConfig.DatabaseURL) if poolError != nil { return fmt.Errorf("connect to database: %w", poolError) } defer pool.Close() slog.Info("connected to database") if loadError := locale.LoadFromDB(applicationContext, pool); loadError != nil { return fmt.Errorf("load languages: %w", loadError) } slog.Info("languages loaded", "count", len(locale.Languages)) router, initError := initRouter(appConfig, pool) if initError != nil { return fmt.Errorf("init router: %w", initError) } httpServer := &http.Server{ Addr: fmt.Sprintf(":%d", appConfig.Port), Handler: router, ReadTimeout: 10 * time.Second, WriteTimeout: 120 * time.Second, // menu generation can take ~60s IdleTimeout: 60 * time.Second, } go func() { slog.Info("server starting", "port", appConfig.Port) if serverError := httpServer.ListenAndServe(); serverError != nil && serverError != http.ErrServerClosed { slog.Error("server error", "err", serverError) } }() <-applicationContext.Done() slog.Info("shutting down...") shutdownContext, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() return httpServer.Shutdown(shutdownContext) }