hash-of-wisdom/cmd/server/main.go
2025-08-23 18:52:43 +07:00

105 lines
2.7 KiB
Go

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")
}
}