Replaces the hardcoded CP_HOST_AGENT_ADDR single-agent setup with a DB-driven registration system supporting multiple host agents (BYOC). Key changes: - Host agents register via one-time token, receive a 7-day JWT + 60-day refresh token; heartbeat loop auto-refreshes on 401/403 and pauses all sandboxes if refresh fails - HostClientPool: lazy Connect RPC client cache keyed by host ID, replacing the single static agent client throughout the API and service layers - RoundRobinScheduler: picks an online host for each new sandbox via ListActiveHosts; extensible for future scheduling strategies - HostMonitor (replaces Reconciler): passive heartbeat staleness check marks hosts unreachable and sandboxes missing after 90s; active reconciliation per online host restores missing-but-alive sandboxes and stops orphans - Graceful host delete: returns 409 with affected sandbox list without ?force=true; force-delete destroys sandboxes then evicts pool client - Snapshot delete broadcasts to all online hosts (templates have no host_id) - sandbox.Manager.PauseAll: pauses all running VMs on CP connectivity loss - New migration: host_refresh_tokens table with token rotation (issue-then- revoke ordering to prevent lockout on mid-rotation crash) - New sandbox status 'missing' (reversible, unlike 'stopped') and host status 'unreachable'; both reflected in OpenAPI spec - Fix: refresh token auth failure now returns 401 (was 400 via generic 'invalid' substring match in serviceErrToHTTP)
47 lines
1.2 KiB
Go
47 lines
1.2 KiB
Go
package config
|
|
|
|
import (
|
|
"os"
|
|
|
|
"github.com/joho/godotenv"
|
|
)
|
|
|
|
// Config holds the control plane configuration.
|
|
type Config struct {
|
|
DatabaseURL string
|
|
RedisURL string
|
|
ListenAddr string
|
|
JWTSecret string
|
|
|
|
OAuthGitHubClientID string
|
|
OAuthGitHubClientSecret string
|
|
OAuthRedirectURL string
|
|
CPPublicURL string
|
|
}
|
|
|
|
// Load reads configuration from a .env file (if present) and environment variables.
|
|
// Real environment variables take precedence over .env values.
|
|
func Load() Config {
|
|
// Best-effort load — missing .env file is fine.
|
|
_ = godotenv.Load()
|
|
|
|
return Config{
|
|
DatabaseURL: envOrDefault("DATABASE_URL", "postgres://wrenn:wrenn@localhost:5432/wrenn?sslmode=disable"),
|
|
RedisURL: envOrDefault("REDIS_URL", "redis://localhost:6379/0"),
|
|
ListenAddr: envOrDefault("CP_LISTEN_ADDR", ":8080"),
|
|
JWTSecret: os.Getenv("JWT_SECRET"),
|
|
|
|
OAuthGitHubClientID: os.Getenv("OAUTH_GITHUB_CLIENT_ID"),
|
|
OAuthGitHubClientSecret: os.Getenv("OAUTH_GITHUB_CLIENT_SECRET"),
|
|
OAuthRedirectURL: envOrDefault("OAUTH_REDIRECT_URL", "https://app.wrenn.dev"),
|
|
CPPublicURL: os.Getenv("CP_PUBLIC_URL"),
|
|
}
|
|
}
|
|
|
|
func envOrDefault(key, def string) string {
|
|
if v := os.Getenv(key); v != "" {
|
|
return v
|
|
}
|
|
return def
|
|
}
|