hash-of-wisdom/cmd/server/main.go

110 lines
2.9 KiB
Go
Raw Normal View History

2025-08-23 08:52:41 +03:00
package main
import (
"context"
2025-08-23 12:06:16 +03:00
"flag"
2025-08-23 08:52:41 +03:00
"log/slog"
2025-08-23 12:06:16 +03:00
"net/http"
2025-08-23 08:52:41 +03:00
"os"
"os/signal"
"syscall"
"time"
2025-08-23 12:06:16 +03:00
"hash-of-wisdom/internal/config"
2025-08-23 08:52:41 +03:00
"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"
2025-08-23 12:06:16 +03:00
"github.com/prometheus/client_golang/prometheus/promhttp"
2025-08-23 14:04:18 +03:00
"github.com/prometheus/client_golang/prometheus/collectors"
"github.com/prometheus/client_golang/prometheus"
2025-08-23 12:06:16 +03:00
_ "net/http/pprof"
2025-08-23 08:52:41 +03:00
)
func main() {
2025-08-23 12:06:16 +03:00
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)
2025-08-23 08:52:41 +03:00
}
logger := slog.Default()
2025-08-23 12:06:16 +03:00
logger.Info("starting word of wisdom server", "address", cfg.Server.Address)
2025-08-23 08:52:41 +03:00
2025-08-23 12:06:16 +03:00
// 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)),
)
2025-08-23 08:52:41 +03:00
if err != nil {
2025-08-23 12:06:16 +03:00
logger.Error("failed to create challenge config", sl.Err(err))
2025-08-23 08:52:41 +03:00
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,
},
}
2025-08-23 14:04:18 +03:00
// Register Go runtime metrics
prometheus.MustRegister(collectors.NewGoCollector())
prometheus.MustRegister(collectors.NewProcessCollector(collectors.ProcessCollectorOpts{}))
// 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))
}
}()
2025-08-23 08:52:41 +03:00
// Create server
srv := server.NewTCPServer(wisdomService, serverConfig,
2025-08-23 08:52:41 +03:00
server.WithLogger(logger))
// Start server
ctx := context.Background()
if err := srv.Start(ctx); err != nil {
logger.Error("failed to start server", sl.Err(err))
os.Exit(1)
}
// Wait for interrupt
sigChan := make(chan os.Signal, 1)
signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)
logger.Info("server ready - press ctrl+c to stop")
<-sigChan
// Graceful shutdown
logger.Info("shutting down server")
if err := srv.Stop(); err != nil {
logger.Error("error during shutdown", sl.Err(err))
}
// Give connections time to close
time.Sleep(100 * time.Millisecond)
logger.Info("server stopped")
}