package main import ( "context" "log/slog" "net/http" "os" "os/signal" "syscall" "time" "crm-core/internal/api/handlers" "crm-core/internal/api/router" "crm-core/internal/application/accounts" "crm-core/internal/application/activities" "crm-core/internal/application/contacts" "crm-core/internal/application/deals" "crm-core/internal/application/notes" "crm-core/internal/application/stages" "crm-core/internal/application/tags" "crm-core/internal/config" "crm-core/internal/infrastructure/auth" "crm-core/internal/infrastructure/postgres" "crm-core/internal/infrastructure/repositories" "crm-core/internal/observability" ) func main() { cfg := config.MustLoad() logger := observability.NewLogger(cfg.Env) ctx := context.Background() pool, err := postgres.NewPool(ctx, cfg.DatabaseURL) if err != nil { logger.Error("database connection failed", slog.Any("error", err)) os.Exit(1) } defer pool.Close() jwtMiddleware, err := auth.NewMiddleware(cfg.JWKSURL) if err != nil { logger.Error("jwks init failed", slog.Any("error", err)) os.Exit(1) } accountsRepo := repositories.NewAccountsRepository(pool) contactsRepo := repositories.NewContactsRepository(pool) dealsRepo := repositories.NewDealsRepository(pool) stagesRepo := repositories.NewStagesRepository(pool) activitiesRepo := repositories.NewActivitiesRepository(pool) notesRepo := repositories.NewNotesRepository(pool) tagsRepo := repositories.NewTagsRepository(pool) accountsService := accounts.NewService(accountsRepo) contactsService := contacts.NewService(contactsRepo) dealsService := deals.NewService(dealsRepo) stagesService := stages.NewService(stagesRepo) activitiesService := activities.NewService(activitiesRepo) notesService := notes.NewService(notesRepo) tagsService := tags.NewService(tagsRepo) handlerSet := handlers.HandlerSet{ Accounts: handlers.NewAccountsHandler(accountsService), Contacts: handlers.NewContactsHandler(contactsService), Deals: handlers.NewDealsHandler(dealsService), Stages: handlers.NewStagesHandler(stagesService), Activities: handlers.NewActivitiesHandler(activitiesService), Notes: handlers.NewNotesHandler(notesService), Tags: handlers.NewTagsHandler(tagsService), } r := router.NewRouter(logger, jwtMiddleware, handlerSet) srv := &http.Server{ Addr: cfg.HTTPAddr, Handler: r, ReadTimeout: cfg.RequestTimeout, WriteTimeout: cfg.RequestTimeout, IdleTimeout: 30 * time.Second, } go func() { logger.Info("api started", slog.String("addr", cfg.HTTPAddr)) if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed { logger.Error("http server error", slog.Any("error", err)) os.Exit(1) } }() quit := make(chan os.Signal, 1) signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM) <-quit ctxShutdown, cancel := context.WithTimeout(context.Background(), cfg.ShutdownTimeout) defer cancel() if err := srv.Shutdown(ctxShutdown); err != nil { logger.Error("http shutdown error", slog.Any("error", err)) os.Exit(1) } logger.Info("api stopped") }