- Create modular architecture with separate packages for config, database, HTTP, logging, and state management - Implement Cobra CLI with daemon command - Set up Uber FX dependency injection - Add Chi router with health check and IP lookup endpoints - Implement GeoIP database downloader with automatic updates - Add state persistence for tracking database download times - Include comprehensive test coverage for all components - Configure structured logging with slog - Add Makefile with test, lint, and build targets - Support both IPv4 and IPv6 lookups - Return country, city, ASN, and location data in JSON format
67 lines
1.4 KiB
Go
67 lines
1.4 KiB
Go
package http
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"log/slog"
|
|
"net/http"
|
|
"time"
|
|
|
|
"git.eeqj.de/sneak/ipapi/internal/config"
|
|
"github.com/go-chi/chi/v5"
|
|
)
|
|
|
|
// Server manages the HTTP server lifecycle.
|
|
type Server struct {
|
|
config *config.Config
|
|
logger *slog.Logger
|
|
router chi.Router
|
|
httpServer *http.Server
|
|
}
|
|
|
|
// NewServer creates a new HTTP server instance.
|
|
func NewServer(cfg *config.Config, logger *slog.Logger, router chi.Router) (*Server, error) {
|
|
return &Server{
|
|
config: cfg,
|
|
logger: logger,
|
|
router: router,
|
|
}, nil
|
|
}
|
|
|
|
// Start begins listening for HTTP requests.
|
|
func (s *Server) Start(_ context.Context) error {
|
|
addr := fmt.Sprintf(":%d", s.config.Port)
|
|
s.httpServer = &http.Server{
|
|
Addr: addr,
|
|
Handler: s.router,
|
|
ReadTimeout: 15 * time.Second, //nolint:mnd
|
|
WriteTimeout: 15 * time.Second, //nolint:mnd
|
|
IdleTimeout: 60 * time.Second, //nolint:mnd
|
|
}
|
|
|
|
s.logger.Info("Starting HTTP server", "addr", addr)
|
|
|
|
go func() {
|
|
if err := s.httpServer.ListenAndServe(); err != nil && err != http.ErrServerClosed {
|
|
s.logger.Error("HTTP server error", "error", err)
|
|
}
|
|
}()
|
|
|
|
return nil
|
|
}
|
|
|
|
// Stop gracefully shuts down the HTTP server.
|
|
func (s *Server) Stop(ctx context.Context) error {
|
|
if s.httpServer == nil {
|
|
return nil
|
|
}
|
|
|
|
s.logger.Info("Stopping HTTP server")
|
|
|
|
const shutdownTimeout = 30 * time.Second
|
|
shutdownCtx, cancel := context.WithTimeout(ctx, shutdownTimeout)
|
|
defer cancel()
|
|
|
|
return s.httpServer.Shutdown(shutdownCtx)
|
|
}
|