package main import ( "context" "flag" "log/slog" "net/http" "os" "os/signal" "syscall" "hash-of-wisdom/internal/config" "hash-of-wisdom/internal/lib/sl" "hash-of-wisdom/internal/pow/challenge" "hash-of-wisdom/internal/quotes" "hash-of-wisdom/internal/server" "hash-of-wisdom/internal/service" "github.com/prometheus/client_golang/prometheus/promhttp" _ "net/http/pprof" ) func main() { configPath := flag.String("config", "", "path to configuration file") flag.Parse() // Load configuration cfg, err := config.Load(*configPath) if err != nil { slog.Error("failed to load config", sl.Err(err)) os.Exit(1) } logger := slog.Default() logger.Info("starting word of wisdom server", "address", cfg.Server.Address) // Create components using config challengeConfig, err := challenge.NewConfig( challenge.WithDefaultDifficulty(cfg.PoW.Difficulty), challenge.WithMaxDifficulty(cfg.PoW.MaxDifficulty), challenge.WithChallengeTTL(cfg.PoW.TTL), challenge.WithHMACSecret([]byte(cfg.PoW.HMACSecret)), ) if err != nil { logger.Error("failed to create challenge config", sl.Err(err)) os.Exit(1) } generator := challenge.NewGenerator(challengeConfig) verifier := challenge.NewVerifier(challengeConfig) quoteService := quotes.NewHTTPService() // Wire up service genAdapter := service.NewGeneratorAdapter(generator) wisdomService := service.NewWisdomService(genAdapter, verifier, quoteService) // Create server configuration serverConfig := &server.Config{ Address: cfg.Server.Address, Timeouts: server.TimeoutConfig{ Read: cfg.Server.Timeouts.Read, Write: cfg.Server.Timeouts.Write, Connection: cfg.Server.Timeouts.Connection, }, } // Go runtime metrics are automatically registered by default registry // Start metrics and pprof HTTP server go func() { http.Handle("/metrics", promhttp.Handler()) logger.Info("starting metrics server", "address", cfg.Metrics.Address) if err := http.ListenAndServe(cfg.Metrics.Address, nil); err != nil { logger.Error("metrics server failed", sl.Err(err)) } }() // Create context that cancels on interrupt signals ctx, cancel := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM) defer cancel() // Create server srv := server.NewTCPServer(wisdomService, serverConfig, server.WithLogger(logger)) // Start server if err := srv.Start(ctx); err != nil { logger.Error("failed to start server", sl.Err(err)) os.Exit(1) } logger.Info("server ready - press ctrl+c to stop") // Wait for context cancellation (signal received) <-ctx.Done() // Graceful shutdown logger.Info("shutting down server") if err := srv.Stop(); err != nil { logger.Error("error during shutdown", sl.Err(err)) } else { logger.Info("server stopped gracefully") } }