package main import ( "database/sql" "log" "net/http" "nettopo-go/internal/api" "nettopo-go/internal/config" "nettopo-go/internal/scans" "nettopo-go/internal/store" _ "github.com/jackc/pgx/v5/stdlib" ) func main() { cfg := config.FromEnv() scanStore, dbClose := makeStore(cfg) if dbClose != nil { defer dbClose() } runner := scans.NewRunner(scanStore, scans.RunnerConfig{ SNMPEnabled: cfg.SNMPEnabled, SNMPCommunity: cfg.SNMPCommunity, }) handler := api.NewHandler(scanStore, runner, cfg.ResetDBSecret) server := &http.Server{ Addr: cfg.HTTPAddr, Handler: handler.Routes(), } log.Printf("nettopo API is starting on %s", cfg.HTTPAddr) if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed { log.Fatalf("server failed: %v", err) } } func makeStore(cfg config.Config) (scans.Store, func()) { if cfg.StoreBackend != "postgres" { log.Printf("store backend: memory") return scans.NewMemoryStore(), nil } if cfg.DBDSN == "" { log.Fatalf("STORE_BACKEND=postgres requires DB_DSN") } db, err := sql.Open("pgx", cfg.DBDSN) if err != nil { log.Fatalf("db open failed: %v", err) } if err = db.Ping(); err != nil { _ = db.Close() log.Fatalf("db ping failed: %v", err) } db.SetMaxOpenConns(cfg.DBMaxOpenConns) db.SetMaxIdleConns(cfg.DBMaxIdleConns) if cfg.DBConnMaxLifetime > 0 { db.SetConnMaxLifetime(cfg.DBConnMaxLifetime) log.Printf("postgres pool: max_open=%d max_idle=%d conn_max_lifetime=%v", cfg.DBMaxOpenConns, cfg.DBMaxIdleConns, cfg.DBConnMaxLifetime) } else { log.Printf("postgres pool: max_open=%d max_idle=%d conn_max_lifetime=off", cfg.DBMaxOpenConns, cfg.DBMaxIdleConns) } if cfg.RunMigrations { if err = store.RunMigrations(db); err != nil { _ = db.Close() log.Fatalf("db migration failed: %v", err) } } log.Printf("store backend: postgres") return scans.NewPostgresStore(db), func() { _ = db.Close() } }