Files
rogee b6d0af1a56
management-images / build-and-publish (push) Successful in 9m15s
docs: add one-click management deployment and image workflow
2026-09-16 17:57:05 +08:00

121 lines
4.7 KiB
Go

package management
import (
"encoding/json"
"os"
"strings"
"github.com/spf13/viper"
)
type TokenSpec struct {
Principal string `json:"principal"`
Realm string `json:"realm"`
Scopes []string `json:"scopes"`
Resources map[string][]string `json:"resources,omitempty"`
}
type Config struct {
Addr string
Mode string
DBPath string
MockCellApply bool
MockCellFailures map[string]string
SeedDemo bool
AllowPrivateAgent bool
AgentAllowlist map[string]bool
AllowedOrigins map[string]bool
Tokens map[string]TokenSpec
}
func DefaultConfig() Config {
return Config{
Addr: "127.0.0.1:8080", Mode: ModeMock, DBPath: "./data/management.db",
MockCellApply: false, MockCellFailures: map[string]string{}, SeedDemo: false, AgentAllowlist: map[string]bool{}, AllowedOrigins: map[string]bool{"http://localhost:3000": true, "http://127.0.0.1:3000": true}, Tokens: defaultTokens(),
}
}
func defaultTokens() map[string]TokenSpec {
all := []string{"sip.provider.read", "sip.provider.write", "sip.trunk.read", "sip.trunk.write", "sip.trunk.publish", "sip.trunk.disable", "sip.trunk.rollback", "sip.cell.read", "sip.cell.write", "sip.status.read", "sip.statistics.read", "sip.calls.read", "sip.audit.read", "sip.operation.read"}
return map[string]TokenSpec{
"dev-admin-token": {Principal: "operator:local-admin", Realm: "admin", Scopes: all},
"dev-config-token": {Principal: "operator:config", Realm: "admin", Scopes: []string{"sip.provider.read", "sip.provider.write", "sip.trunk.read", "sip.trunk.write", "sip.cell.read", "sip.status.read", "sip.statistics.read", "sip.calls.read"}},
"dev-publisher-token": {Principal: "operator:publisher", Realm: "admin", Scopes: []string{"sip.provider.read", "sip.trunk.read", "sip.trunk.publish", "sip.trunk.disable", "sip.trunk.rollback", "sip.cell.read", "sip.status.read", "sip.operation.read"}},
"dev-observer-token": {Principal: "operator:observer", Realm: "admin", Scopes: []string{"sip.provider.read", "sip.trunk.read", "sip.cell.read", "sip.status.read", "sip.statistics.read", "sip.calls.read"}},
"dev-audit-token": {Principal: "operator:auditor", Realm: "admin", Scopes: []string{"sip.provider.read", "sip.trunk.read", "sip.audit.read", "sip.operation.read"}},
"dev-saas-token": {Principal: "saas:readonly", Realm: "saas", Scopes: []string{"sip.trunk.read"}},
"dev-scheduler-token": {Principal: "scheduler:ordinary", Realm: "scheduler", Scopes: []string{"sip.trunk.read"}},
}
}
func LoadConfig(v *viper.Viper) Config {
cfg := DefaultConfig()
v.SetDefault("addr", cfg.Addr)
v.SetDefault("mode", cfg.Mode)
v.SetDefault("db_path", cfg.DBPath)
v.SetDefault("mock_cell_apply", cfg.MockCellApply)
v.SetDefault("seed_demo", cfg.SeedDemo)
v.SetDefault("allow_private_agent", cfg.AllowPrivateAgent)
v.SetDefault("mock_cell_failures", "")
v.SetDefault("agent_allowlist", "")
v.SetDefault("allowed_origins", "")
v.SetDefault("tokens_json", "")
cfg.Addr = v.GetString("addr")
cfg.Mode = strings.ToLower(v.GetString("mode"))
if cfg.Mode != ModeReal {
cfg.Mode = ModeMock
}
cfg.DBPath = v.GetString("db_path")
cfg.MockCellApply = v.GetBool("mock_cell_apply")
cfg.MockCellFailures = map[string]string{}
for _, item := range strings.Split(v.GetString("mock_cell_failures"), ",") {
parts := strings.SplitN(strings.TrimSpace(item), "=", 2)
if len(parts) == 2 && validID(parts[0]) && parts[1] != "" {
cfg.MockCellFailures[parts[0]] = parts[1]
}
}
cfg.SeedDemo = v.GetBool("seed_demo")
cfg.AllowPrivateAgent = v.GetBool("allow_private_agent")
cfg.AgentAllowlist = map[string]bool{}
for _, host := range strings.Split(v.GetString("agent_allowlist"), ",") {
if host = strings.TrimSpace(strings.ToLower(host)); host != "" {
cfg.AgentAllowlist[host] = true
}
}
cfg.AllowedOrigins = map[string]bool{}
for _, origin := range strings.Split(v.GetString("allowed_origins"), ",") {
if origin = strings.TrimSpace(origin); origin != "" {
cfg.AllowedOrigins[origin] = true
}
}
if len(cfg.AllowedOrigins) == 0 {
cfg.AllowedOrigins = DefaultConfig().AllowedOrigins
}
if raw := v.GetString("tokens_json"); raw != "" {
var tokens map[string]TokenSpec
if json.Unmarshal([]byte(raw), &tokens) == nil && len(tokens) > 0 {
cfg.Tokens = tokens
}
}
return cfg
}
func ConfigFromEnv() Config {
v := viper.New()
v.SetEnvPrefix("MANAGEMENT")
v.SetEnvKeyReplacer(strings.NewReplacer("-", "_"))
v.AutomaticEnv()
return LoadConfig(v)
}
func (c Config) PublicMode() string {
if c.Mode == ModeReal {
return ModeReal
}
return ModeMock
}
func EnsureDevTokenWarning(c Config) bool {
return c.Mode == ModeMock && os.Getenv("MANAGEMENT_SUPPRESS_DEV_TOKEN_WARNING") == ""
}