package main import ( "context" "errors" "flag" "fmt" "log" "net/http" "os" "os/signal" "syscall" "git.dern.ovh/infrastructure/caddy-opnsense-blocker/internal/config" "git.dern.ovh/infrastructure/caddy-opnsense-blocker/internal/investigation" "git.dern.ovh/infrastructure/caddy-opnsense-blocker/internal/opnsense" "git.dern.ovh/infrastructure/caddy-opnsense-blocker/internal/service" "git.dern.ovh/infrastructure/caddy-opnsense-blocker/internal/store" "git.dern.ovh/infrastructure/caddy-opnsense-blocker/internal/web" ) func main() { if err := run(); err != nil { log.Fatal(err) } } func run() error { var configPath string flag.StringVar(&configPath, "config", "./config.yaml", "Path to the YAML configuration file") flag.Parse() cfg, err := config.Load(configPath) if err != nil { return fmt.Errorf("load config: %w", err) } database, err := store.Open(cfg.Storage.Path) if err != nil { return fmt.Errorf("open store: %w", err) } defer database.Close() logger := log.New(os.Stderr, "caddy-opnsense-blocker: ", log.LstdFlags|log.Lmsgprefix) var blocker opnsense.AliasClient if cfg.OPNsense.Enabled { blocker = opnsense.NewClient(cfg.OPNsense) } investigator := investigation.New(cfg.Investigation, logger) svc := service.New(cfg, database, blocker, investigator, logger) handler := web.NewHandler(svc) httpServer := &http.Server{ Addr: cfg.Server.ListenAddress, Handler: handler, ReadTimeout: cfg.Server.ReadTimeout.Duration, WriteTimeout: cfg.Server.WriteTimeout.Duration, } ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM) defer stop() errCh := make(chan error, 2) go func() { if err := svc.Run(ctx); err != nil && !errors.Is(err, context.Canceled) { errCh <- err } }() go func() { logger.Printf("serving on %s", cfg.Server.ListenAddress) if err := httpServer.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) { errCh <- err } }() select { case err := <-errCh: stop() shutdownCtx, cancel := context.WithTimeout(context.Background(), cfg.Server.ShutdownTimeout.Duration) defer cancel() _ = httpServer.Shutdown(shutdownCtx) return err case <-ctx.Done(): } shutdownCtx, cancel := context.WithTimeout(context.Background(), cfg.Server.ShutdownTimeout.Duration) defer cancel() if err := httpServer.Shutdown(shutdownCtx); err != nil && !errors.Is(err, http.ErrServerClosed) { return fmt.Errorf("shutdown http server: %w", err) } return nil }