package main import ( "context" "fmt" "net/http" "os" "os/signal" "syscall" "time" "gitlab.global.bsf.tools/esv/bsf/bsf-integration/orchard/orchard-mvp/internal/api" "gitlab.global.bsf.tools/esv/bsf/bsf-integration/orchard/orchard-mvp/internal/config" "gitlab.global.bsf.tools/esv/bsf/bsf-integration/orchard/orchard-mvp/internal/storage" "go.uber.org/zap" ) func main() { // Initialize logger logger, err := zap.NewProduction() if err != nil { fmt.Fprintf(os.Stderr, "failed to create logger: %v\n", err) os.Exit(1) } defer logger.Sync() // Load configuration cfg, err := config.Load() if err != nil { logger.Fatal("failed to load configuration", zap.Error(err)) } // Initialize database db, err := storage.NewDatabase(&cfg.Database) if err != nil { logger.Fatal("failed to connect to database", zap.Error(err)) } defer db.Close() // Initialize S3 storage s3, err := storage.NewS3Storage(&cfg.S3) if err != nil { logger.Fatal("failed to initialize S3 storage", zap.Error(err)) } // Setup router router := api.SetupRouter(db, s3, logger) // Create HTTP server addr := fmt.Sprintf("%s:%d", cfg.Server.Host, cfg.Server.Port) srv := &http.Server{ Addr: addr, Handler: router, ReadTimeout: 30 * time.Second, WriteTimeout: 30 * time.Second, IdleTimeout: 60 * time.Second, } // Start server in goroutine go func() { logger.Info("starting Orchard server", zap.String("address", addr), ) if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed { logger.Fatal("server failed", zap.Error(err)) } }() // Wait for interrupt signal quit := make(chan os.Signal, 1) signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM) <-quit logger.Info("shutting down server...") // Graceful shutdown with timeout ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) defer cancel() if err := srv.Shutdown(ctx); err != nil { logger.Fatal("server forced to shutdown", zap.Error(err)) } logger.Info("server stopped") }