package httpapi import ( "context" "crypto/ed25519" "crypto/rand" "encoding/hex" "errors" "net/http" "strings" "time" "evobgp/internal/jobs" "evobgp/internal/store" "github.com/jackc/pgx/v5/pgxpool" ) // Server implements EvoBGP control-plane HTTP API. type Server struct { store store.Backend pgPool *pgxpool.Pool jobs *jobs.Registry bundlePriv ed25519.PrivateKey apiKeys []apiKeyRecord insecureDev bool corsOrigins []string mux *http.ServeMux } // Options configures the API server. type Options struct { APIKeys string // DatabaseURL enables PostgreSQL-backed store (migrations applied on connect). DatabaseURL string InsecureDev bool SeedDemo bool BundleSeedHex string CORSAllowedOrigins string } // New constructs Server and wiring for async jobs. func New(opts Options) (*Server, error) { backend, pool, reg, err := BootstrapWorkers(context.Background(), opts) if err != nil { return nil, err } var priv ed25519.PrivateKey if strings.TrimSpace(opts.BundleSeedHex) != "" { seed, err := hex.DecodeString(strings.TrimSpace(opts.BundleSeedHex)) if err != nil { return nil, err } if len(seed) != ed25519.SeedSize { return nil, errors.New("httpapi: BundleSeedHex must decode to 32 bytes") } priv = ed25519.NewKeyFromSeed(seed) } else { _, priv, _ = ed25519.GenerateKey(rand.Reader) } s := &Server{ store: backend, pgPool: pool, jobs: reg, bundlePriv: priv, apiKeys: parseAPIKeysSpec(opts.APIKeys), insecureDev: opts.InsecureDev && opts.SeedDemo, corsOrigins: parseCORSOrigins(opts.CORSAllowedOrigins), } s.mux = http.NewServeMux() s.registerRoutes() return s, nil } // Close releases database resources. func (s *Server) Close() { if s.pgPool != nil { s.pgPool.Close() } } // Store exposes the backing store (for operators / tests). func (s *Server) Store() store.Backend { return s.store } // Jobs exposes the in-process async job registry (for scheduler / evobgp-all). func (s *Server) Jobs() *jobs.Registry { return s.jobs }