feat: enhance EvoBGP with new command-line options for the evobgp-agent, including a watch command for periodic configuration updates. Update go.mod with additional dependencies and improve Docker Compose setup for new services, including NATS and various worker components.
CI / changes (push) Successful in 5s
CI / go (push) Successful in 1m40s
CI / openapi (push) Has been skipped
CI / bird2 (push) Successful in 17s

This commit is contained in:
Denozordec
2026-04-05 17:03:21 +07:00
parent bf52b21150
commit 6a55f72ab3
35 changed files with 4609 additions and 193 deletions
+48 -14
View File
@@ -1,21 +1,28 @@
package httpapi
import (
"context"
"crypto/ed25519"
"crypto/rand"
"encoding/hex"
"errors"
"net/http"
"strings"
"time"
"evobgp/internal/db"
"evobgp/internal/jobs"
"evobgp/internal/observability"
"evobgp/internal/repository"
"evobgp/internal/store"
"github.com/jackc/pgx/v5/pgxpool"
)
// Server implements EvoBGP control-plane HTTP API (subset focused on jobs, deploy, node bundle).
// Server implements EvoBGP control-plane HTTP API.
type Server struct {
store *store.Memory
store store.Backend
pgPool *pgxpool.Pool
jobs *jobs.Registry
bundlePriv ed25519.PrivateKey
apiKeys []apiKeyRecord
@@ -26,24 +33,43 @@ type Server struct {
// Options configures the API server.
type Options struct {
// APIKeys is comma-separated "token|tenantUUID|role" (role: viewer, editor, operator, node).
APIKeys string
// InsecureDev with SeedDemo allows Bearer "dev" as operator for the demo tenant (local only).
// DatabaseURL enables PostgreSQL-backed store (migrations applied on connect).
DatabaseURL string
InsecureDev bool
SeedDemo bool
// BundleSeedHex is 64 hex chars (32 bytes) for deterministic Ed25519 bundle signing; if empty, random.
BundleSeedHex string
// CORSAllowedOrigins is comma-separated list of allowed browser Origins (e.g. http://localhost:4173).
CORSAllowedOrigins string
}
// New constructs Server and wiring for async jobs.
func New(opts Options) (*Server, error) {
mem := store.NewMemory()
if opts.SeedDemo {
mem.SeedDemo()
var backend store.Backend
var pool *pgxpool.Pool
ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
defer cancel()
if u := strings.TrimSpace(opts.DatabaseURL); u != "" {
p, err := db.OpenPostgresPool(ctx, u)
if err != nil {
return nil, err
}
pool = p
pgbe, err := repository.NewPostgres(ctx, p, opts.SeedDemo)
if err != nil {
pool.Close()
return nil, err
}
backend = pgbe
} else {
mem := store.NewMemory()
if opts.SeedDemo {
mem.SeedDemo()
}
backend = mem
}
wk := &jobs.Worker{Store: mem}
wk := &jobs.Worker{Store: backend}
reg := jobs.NewRegistry(wk.Process)
var priv ed25519.PrivateKey
@@ -61,18 +87,26 @@ func New(opts Options) (*Server, error) {
}
s := &Server{
store: mem,
store: backend,
pgPool: pool,
jobs: reg,
bundlePriv: priv,
apiKeys: parseAPIKeysSpec(opts.APIKeys),
insecureDev: opts.InsecureDev && opts.SeedDemo,
corsOrigins: parseCORSOrigins(opts.CORSAllowedOrigins),
}
observability.RegisterStoreMetrics(mem)
observability.RegisterStoreBackend(backend)
s.mux = http.NewServeMux()
s.registerRoutes()
return s, nil
}
// Store exposes the in-memory store (for operators / tests).
func (s *Server) Store() *store.Memory { return s.store }
// 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 }