2026-07-13 14:39:56 +08:00
|
|
|
package config
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"os"
|
|
|
|
|
"strconv"
|
|
|
|
|
"strings"
|
|
|
|
|
"time"
|
|
|
|
|
)
|
|
|
|
|
|
2026-07-14 17:37:08 +08:00
|
|
|
type OAuthClient struct {
|
|
|
|
|
ID string
|
|
|
|
|
Secret string
|
|
|
|
|
RedirectURIs []string
|
|
|
|
|
}
|
|
|
|
|
|
2026-07-13 14:39:56 +08:00
|
|
|
type Config struct {
|
2026-07-30 16:09:13 +08:00
|
|
|
AppEnv string
|
|
|
|
|
HTTPAddr string
|
|
|
|
|
PublicBaseURL string
|
|
|
|
|
MySQLDSN string
|
|
|
|
|
AutoMigrate bool
|
|
|
|
|
SSOEnabled bool
|
|
|
|
|
JWTSecret string
|
|
|
|
|
JWTIssuer string
|
|
|
|
|
JWTTTLMinutes int
|
|
|
|
|
SAMLEntityID string
|
|
|
|
|
SAMLACSURL string
|
|
|
|
|
SAMLSPCert string
|
|
|
|
|
SAMLSPKey string
|
|
|
|
|
SAMLIDPMetaURL string
|
|
|
|
|
SAMLLogoutURL string
|
|
|
|
|
WayenLoginURL string
|
|
|
|
|
WayenTargetURL string
|
|
|
|
|
WayenUsernameKey string
|
|
|
|
|
WayenPasswordKey string
|
|
|
|
|
WayenLoginFormat string
|
|
|
|
|
WayenLoginValue string
|
|
|
|
|
WayenOAuthRef string
|
|
|
|
|
WayenOAuthLoginURL string
|
|
|
|
|
WayneAPIBaseURL string
|
|
|
|
|
WayneAdminUsername string
|
|
|
|
|
WayneAdminPassword string
|
|
|
|
|
WayneTokenTTLMinutes int
|
|
|
|
|
WayneInternalAPIBaseURL string
|
|
|
|
|
WayneServiceName string
|
|
|
|
|
WayneServiceAPISecretKey string
|
|
|
|
|
OAuthClientID string
|
|
|
|
|
OAuthClientSecret string
|
|
|
|
|
OAuthRedirectURI string
|
|
|
|
|
OAuthCodeTTLSeconds int
|
|
|
|
|
OIDCIssuer string
|
|
|
|
|
OIDCAuthorizeURL string
|
|
|
|
|
OIDCTokenURL string
|
|
|
|
|
OIDCUserInfoURL string
|
|
|
|
|
OIDCJWKSURL string
|
|
|
|
|
CloudDMClientID string
|
|
|
|
|
CloudDMClientSecret string
|
|
|
|
|
CloudDMRedirectURI string
|
|
|
|
|
CloudDMTargetURL string
|
|
|
|
|
CloudDMPublicURL string
|
|
|
|
|
CloudDMLoginURL string
|
|
|
|
|
CloudDMAdminUsername string
|
|
|
|
|
CloudDMAdminPassword string
|
|
|
|
|
CloudDMRegisterURL string
|
2026-07-30 18:05:51 +08:00
|
|
|
CloudDMDeleteURL string
|
2026-07-30 16:09:13 +08:00
|
|
|
CloudDMAPIToken string
|
|
|
|
|
AWXBaseURL string
|
|
|
|
|
AWXToken string
|
|
|
|
|
AWXUsername string
|
|
|
|
|
AWXPassword string
|
|
|
|
|
AWXWebhookToken string
|
|
|
|
|
AWXFactsTemplateID uint64
|
|
|
|
|
AWXFactsTimeoutSeconds int
|
|
|
|
|
MySQLDeliveryTemplateName string
|
|
|
|
|
MySQLInspectTemplateName string
|
|
|
|
|
MySQLInspectTimeoutSeconds int
|
|
|
|
|
RollbackTemplateID uint64
|
|
|
|
|
DeliveryServiceToken string
|
|
|
|
|
DeliverySchedulerEnabled bool
|
|
|
|
|
DeliveryDispatchSeconds int
|
2026-07-31 10:30:03 +08:00
|
|
|
DeliveryPollSeconds int
|
2026-07-30 16:09:13 +08:00
|
|
|
DeliveryCallbackBaseURL string
|
|
|
|
|
DeliveryCredentialSecret string
|
|
|
|
|
ReservationTTLMinutes int
|
|
|
|
|
DeliveryGlobalLimit int
|
|
|
|
|
DeliveryTargetLimit int
|
|
|
|
|
DeliveryHostInstanceLimit int
|
2026-07-31 10:30:03 +08:00
|
|
|
DeliveryBusinessLimit int
|
2026-07-30 16:09:13 +08:00
|
|
|
DeliveryDataDisks []string
|
|
|
|
|
SINABaseURL string
|
|
|
|
|
SINAUsername string
|
|
|
|
|
SINAPassword string
|
2026-07-13 14:39:56 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func Load() Config {
|
|
|
|
|
loadDotEnv(".env")
|
|
|
|
|
httpAddr := env("HTTP_ADDR", ":8080")
|
|
|
|
|
publicBaseURL := env("PUBLIC_BASE_URL", defaultPublicBaseURL(httpAddr))
|
|
|
|
|
samlEntityID := env("SAML_ENTITY_ID", strings.TrimRight(publicBaseURL, "/")+"/auth/api/v1/saml/metadata")
|
|
|
|
|
samlACSURL := env("SAML_ACS_URL", strings.TrimRight(publicBaseURL, "/")+"/auth/api/v1/saml/acs")
|
2026-07-14 17:37:08 +08:00
|
|
|
oidcIssuer := env("OIDC_ISSUER", strings.TrimRight(publicBaseURL, "/")+"/auth")
|
|
|
|
|
oidcIssuer = strings.TrimRight(oidcIssuer, "/")
|
2026-07-13 14:39:56 +08:00
|
|
|
|
|
|
|
|
return Config{
|
2026-07-30 16:09:13 +08:00
|
|
|
AppEnv: env("APP_ENV", "dev"),
|
|
|
|
|
HTTPAddr: httpAddr,
|
|
|
|
|
PublicBaseURL: publicBaseURL,
|
|
|
|
|
MySQLDSN: env("MYSQL_DSN", "auth:auth@tcp(127.0.0.1:3306)/authserver?charset=utf8mb4&parseTime=True&loc=Local"),
|
|
|
|
|
AutoMigrate: envBool("AUTO_MIGRATE", true),
|
|
|
|
|
SSOEnabled: envBool("SSO_ENABLED", true),
|
|
|
|
|
JWTSecret: env("JWT_SECRET", "change-this-secret"),
|
|
|
|
|
JWTIssuer: env("JWT_ISSUER", "authserver"),
|
|
|
|
|
JWTTTLMinutes: envInt("JWT_TTL_MINUTES", 120),
|
|
|
|
|
SAMLEntityID: samlEntityID,
|
|
|
|
|
SAMLACSURL: samlACSURL,
|
|
|
|
|
SAMLSPCert: env("SAML_SP_CERT_FILE", "certs/sp.crt"),
|
|
|
|
|
SAMLSPKey: env("SAML_SP_KEY_FILE", "certs/sp.key"),
|
|
|
|
|
SAMLIDPMetaURL: env("SAML_IDP_METADATA_URL", "http://sso-internal.dev.qiniu.io/saml2/meta"),
|
|
|
|
|
SAMLLogoutURL: trimURL(env("SAML_LOGOUT_URL", "")),
|
|
|
|
|
WayenLoginURL: env("WAYEN_LOGIN_URL", ""),
|
|
|
|
|
WayenTargetURL: env("WAYEN_TARGET_URL", ""),
|
|
|
|
|
WayenUsernameKey: env("WAYEN_USERNAME_KEY", "email"),
|
|
|
|
|
WayenPasswordKey: env("WAYEN_PASSWORD_KEY", "password"),
|
|
|
|
|
WayenLoginFormat: env("WAYEN_LOGIN_FORMAT", "form"),
|
|
|
|
|
WayenLoginValue: env("WAYEN_LOGIN_VALUE", "email"),
|
|
|
|
|
WayenOAuthRef: env("WAYEN_OAUTH_REF", "/portal/namespace/1/app"),
|
|
|
|
|
WayenOAuthLoginURL: trimURL(env("WAYEN_OAUTH_LOGIN_URL", "")),
|
|
|
|
|
WayneAPIBaseURL: trimURL(env("WAYNE_API_BASE_URL", env("WAYNE_INTERNAL_API_BASE_URL", ""))),
|
|
|
|
|
WayneAdminUsername: env("WAYNE_ADMIN_USERNAME", ""),
|
|
|
|
|
WayneAdminPassword: env("WAYNE_ADMIN_PASSWORD", ""),
|
|
|
|
|
WayneTokenTTLMinutes: envInt("WAYNE_TOKEN_TTL_MINUTES", 1440),
|
|
|
|
|
WayneInternalAPIBaseURL: trimURL(env("WAYNE_INTERNAL_API_BASE_URL", "")),
|
|
|
|
|
WayneServiceName: env("WAYNE_SERVICE_NAME", "xinfra"),
|
|
|
|
|
WayneServiceAPISecretKey: env("WAYNE_SERVICE_API_SECRET_KEY", ""),
|
|
|
|
|
OAuthClientID: env("OAUTH_WAYNE_CLIENT_ID", "wayne"),
|
|
|
|
|
OAuthClientSecret: env("OAUTH_WAYNE_CLIENT_SECRET", "wayne-secret"),
|
|
|
|
|
OAuthRedirectURI: env("OAUTH_WAYNE_REDIRECT_URI", ""),
|
|
|
|
|
OAuthCodeTTLSeconds: envInt("OAUTH_CODE_TTL_SECONDS", 120),
|
|
|
|
|
OIDCIssuer: oidcIssuer,
|
|
|
|
|
OIDCAuthorizeURL: trimURL(env("OIDC_AUTHORIZATION_ENDPOINT", oidcIssuer+"/oauth/authorize")),
|
|
|
|
|
OIDCTokenURL: trimURL(env("OIDC_TOKEN_ENDPOINT", oidcIssuer+"/oauth/token")),
|
|
|
|
|
OIDCUserInfoURL: trimURL(env("OIDC_USERINFO_ENDPOINT", oidcIssuer+"/oauth/userinfo")),
|
|
|
|
|
OIDCJWKSURL: trimURL(env("OIDC_JWKS_URI", oidcIssuer+"/oauth/jwks")),
|
|
|
|
|
CloudDMClientID: env("OIDC_CLOUDDM_CLIENT_ID", "clouddm"),
|
|
|
|
|
CloudDMClientSecret: env("OIDC_CLOUDDM_CLIENT_SECRET", ""),
|
|
|
|
|
CloudDMRedirectURI: env("OIDC_CLOUDDM_REDIRECT_URI", ""),
|
|
|
|
|
CloudDMTargetURL: env("CLOUDDM_TARGET_URL", ""),
|
|
|
|
|
CloudDMPublicURL: trimURL(env("CLOUDDM_PUBLIC_URL", "")),
|
|
|
|
|
CloudDMLoginURL: trimURL(env("CLOUDDM_LOGIN_URL", "")),
|
|
|
|
|
CloudDMAdminUsername: env("CLOUDDM_ADMIN_USERNAME", ""),
|
|
|
|
|
CloudDMAdminPassword: env("CLOUDDM_ADMIN_PASSWORD", ""),
|
|
|
|
|
CloudDMRegisterURL: trimURL(env("CLOUDDM_REGISTER_URL", "")),
|
2026-07-30 18:05:51 +08:00
|
|
|
CloudDMDeleteURL: trimURL(env("CLOUDDM_DELETE_URL", "")),
|
2026-07-30 16:09:13 +08:00
|
|
|
CloudDMAPIToken: env("CLOUDDM_API_TOKEN", ""),
|
|
|
|
|
AWXBaseURL: trimURL(env("AWX_BASE_URL", "")),
|
|
|
|
|
AWXToken: env("AWX_TOKEN", ""),
|
|
|
|
|
AWXUsername: env("AWX_USERNAME", ""),
|
|
|
|
|
AWXPassword: env("AWX_PASSWORD", ""),
|
|
|
|
|
AWXWebhookToken: env("AWX_WEBHOOK_TOKEN", ""),
|
|
|
|
|
AWXFactsTemplateID: envUint64("AWX_FACTS_TEMPLATE_ID", 0),
|
|
|
|
|
AWXFactsTimeoutSeconds: envInt("AWX_FACTS_TIMEOUT_SECONDS", 45),
|
|
|
|
|
MySQLDeliveryTemplateName: env("DELIVERY_MYSQL_TEMPLATE_NAME", "XINFRA MySQL Native Prototype"),
|
|
|
|
|
MySQLInspectTemplateName: env("DELIVERY_MYSQL_INSPECT_TEMPLATE_NAME", "XINFRA MySQL Inspect"),
|
|
|
|
|
MySQLInspectTimeoutSeconds: envInt("DELIVERY_MYSQL_INSPECT_TIMEOUT_SECONDS", 90),
|
|
|
|
|
RollbackTemplateID: uint64(envInt("DELIVERY_ROLLBACK_TEMPLATE_ID", 0)),
|
|
|
|
|
DeliveryServiceToken: env("DELIVERY_SERVICE_TOKEN", ""),
|
|
|
|
|
DeliverySchedulerEnabled: envBool("DELIVERY_SCHEDULER_ENABLED", false),
|
|
|
|
|
DeliveryDispatchSeconds: envInt("DELIVERY_DISPATCH_SECONDS", 5),
|
2026-07-31 10:30:03 +08:00
|
|
|
DeliveryPollSeconds: envInt("DELIVERY_POLL_SECONDS", 5),
|
2026-07-30 16:09:13 +08:00
|
|
|
DeliveryCallbackBaseURL: trimURL(env("DELIVERY_CALLBACK_BASE_URL", publicBaseURL)),
|
|
|
|
|
DeliveryCredentialSecret: env("DELIVERY_CREDENTIAL_SECRET", env("JWT_SECRET", "change-this-secret")),
|
|
|
|
|
ReservationTTLMinutes: envInt("DELIVERY_RESERVATION_TTL_MINUTES", 120),
|
|
|
|
|
DeliveryGlobalLimit: envInt("DELIVERY_GLOBAL_LIMIT", 2),
|
|
|
|
|
DeliveryTargetLimit: envInt("DELIVERY_TARGET_LIMIT", 2),
|
|
|
|
|
DeliveryHostInstanceLimit: envInt("DELIVERY_HOST_INSTANCE_LIMIT", 4),
|
2026-07-31 10:30:03 +08:00
|
|
|
DeliveryBusinessLimit: envInt("DELIVERY_BUSINESS_LIMIT", 1),
|
2026-07-30 16:09:13 +08:00
|
|
|
DeliveryDataDisks: splitCSV(env("DELIVERY_DATA_DISKS", "/data,/disk1,/mnt,/opt/mysql-delivery")),
|
|
|
|
|
SINABaseURL: trimURL(env("SINA_BASE_URL", "https://sinai.qiniu.io:443")),
|
|
|
|
|
SINAUsername: env("SINA_USERNAME", ""),
|
|
|
|
|
SINAPassword: env("SINA_PASSWORD", ""),
|
2026-07-13 14:39:56 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func loadDotEnv(path string) {
|
|
|
|
|
content, err := os.ReadFile(path)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
lines := strings.Split(string(content), "\n")
|
|
|
|
|
for _, line := range lines {
|
|
|
|
|
line = strings.TrimSpace(line)
|
|
|
|
|
if line == "" || strings.HasPrefix(line, "#") {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
key, value, ok := strings.Cut(line, "=")
|
|
|
|
|
if !ok {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
key = strings.TrimSpace(key)
|
|
|
|
|
value = strings.Trim(strings.TrimSpace(value), `"'`)
|
|
|
|
|
if key == "" || os.Getenv(key) != "" {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
_ = os.Setenv(key, value)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (c Config) JWTTTL() time.Duration {
|
|
|
|
|
return time.Duration(c.JWTTTLMinutes) * time.Minute
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (c Config) OAuthCodeTTL() time.Duration {
|
|
|
|
|
return time.Duration(c.OAuthCodeTTLSeconds) * time.Second
|
|
|
|
|
}
|
|
|
|
|
|
2026-07-14 17:37:08 +08:00
|
|
|
func (c Config) OAuthClients() map[string]OAuthClient {
|
|
|
|
|
clients := make(map[string]OAuthClient)
|
|
|
|
|
addOAuthClient(clients, c.OAuthClientID, c.OAuthClientSecret, c.OAuthRedirectURI)
|
|
|
|
|
addOAuthClient(clients, c.CloudDMClientID, c.CloudDMClientSecret, c.CloudDMRedirectURI)
|
|
|
|
|
return clients
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func addOAuthClient(clients map[string]OAuthClient, id, secret, redirectURIs string) {
|
|
|
|
|
id = strings.TrimSpace(id)
|
|
|
|
|
secret = strings.TrimSpace(secret)
|
|
|
|
|
if id == "" || secret == "" {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
clients[id] = OAuthClient{
|
|
|
|
|
ID: id,
|
|
|
|
|
Secret: secret,
|
|
|
|
|
RedirectURIs: splitCSV(redirectURIs),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func splitCSV(value string) []string {
|
|
|
|
|
parts := strings.Split(value, ",")
|
|
|
|
|
items := make([]string, 0, len(parts))
|
|
|
|
|
for _, part := range parts {
|
|
|
|
|
part = strings.TrimSpace(part)
|
|
|
|
|
if part != "" {
|
|
|
|
|
items = append(items, part)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return items
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func trimURL(value string) string {
|
|
|
|
|
return strings.TrimRight(strings.TrimSpace(value), "/")
|
|
|
|
|
}
|
|
|
|
|
|
2026-07-13 14:39:56 +08:00
|
|
|
func env(key, fallback string) string {
|
|
|
|
|
value := os.Getenv(key)
|
|
|
|
|
if value == "" {
|
|
|
|
|
return fallback
|
|
|
|
|
}
|
|
|
|
|
return value
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func envBool(key string, fallback bool) bool {
|
|
|
|
|
value := os.Getenv(key)
|
|
|
|
|
if value == "" {
|
|
|
|
|
return fallback
|
|
|
|
|
}
|
|
|
|
|
parsed, err := strconv.ParseBool(value)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return fallback
|
|
|
|
|
}
|
|
|
|
|
return parsed
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func envInt(key string, fallback int) int {
|
|
|
|
|
value := os.Getenv(key)
|
|
|
|
|
if value == "" {
|
|
|
|
|
return fallback
|
|
|
|
|
}
|
|
|
|
|
parsed, err := strconv.Atoi(value)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return fallback
|
|
|
|
|
}
|
|
|
|
|
return parsed
|
|
|
|
|
}
|
|
|
|
|
|
2026-07-28 14:45:17 +08:00
|
|
|
func envUint64(key string, fallback uint64) uint64 {
|
|
|
|
|
value := os.Getenv(key)
|
|
|
|
|
if value == "" {
|
|
|
|
|
return fallback
|
|
|
|
|
}
|
|
|
|
|
parsed, err := strconv.ParseUint(value, 10, 64)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return fallback
|
|
|
|
|
}
|
|
|
|
|
return parsed
|
|
|
|
|
}
|
|
|
|
|
|
2026-07-13 14:39:56 +08:00
|
|
|
func defaultPublicBaseURL(httpAddr string) string {
|
|
|
|
|
addr := strings.TrimSpace(httpAddr)
|
|
|
|
|
if addr == "" {
|
|
|
|
|
return "http://localhost:8080"
|
|
|
|
|
}
|
|
|
|
|
if strings.HasPrefix(addr, ":") {
|
|
|
|
|
return "http://localhost" + addr
|
|
|
|
|
}
|
|
|
|
|
return "http://" + addr
|
|
|
|
|
}
|