package config import ( "os" "path/filepath" "strings" "time" "git.misaka.ren/M1saka/zhanlu_proxy/internal/auth" ) type Config struct { ListenAddr string MobileLoginBaseURL string MobileModelBaseURL string UpstreamPath string CredentialsPath string SSOExchangeURL string SSOBaseURL string TokenDecryptKey string PublicKeyPEM string PhonePublicKeyPEM string SM2PrivateKey string OpenAIAPIKey string LoginPassword string PluginVersion string UpstreamTimeout time.Duration StreamIdleTimout time.Duration Debug bool Credentials auth.Credentials } func Load() (Config, error) { cfg := Config{ ListenAddr: getenv("ZHANLU_LISTEN_ADDR", ":8080"), MobileLoginBaseURL: firstNonEmpty(os.Getenv("ZHANLU_MOBILE_LOGIN_BASE_URL"), getenv("ZHANLU_SERVER_BASE_URL", "https://ecloud.10086.cn")), MobileModelBaseURL: getenv("ZHANLU_MOBILE_MODEL_BASE_URL", "https://ecloud.10086.cn/api/query/aigateway"), UpstreamPath: getenv("ZHANLU_UPSTREAM_PATH", "/chat/completions"), CredentialsPath: getenv("ZHANLU_CREDENTIALS_FILE", defaultCredentialsPath()), SSOBaseURL: getenv("ZHANLU_SSO_BASE_URL", "http://4c.hq.cmcc"), SSOExchangeURL: getenv("ZHANLU_SSO_EXCHANGE_URL", "http://rdcloud.4c.hq.cmcc/cmdevops-aiplus-agent-gateway/api/acepilot/zhanlu/authToken"), TokenDecryptKey: getenv("ZHANLU_TOKEN_DECRYPT_KEY", "3jw7woww2rvhla6k"), PublicKeyPEM: getenv("ZHANLU_PUBLIC_KEY_PEM", defaultPublicKeyPEM), PhonePublicKeyPEM: getenv("ZHANLU_PHONE_PUBLIC_KEY_PEM", defaultPhonePublicKeyPEM), SM2PrivateKey: getenv("ZHANLU_APIKEY_AUTH_SM2_PRIVATE_KEY", defaultSM2PrivateKey), OpenAIAPIKey: os.Getenv("OPENAI_COMPAT_API_KEY"), LoginPassword: os.Getenv("ZHANLU_LOGIN_PASSWORD"), PluginVersion: getenv("ZHANLU_PLUGIN_VERSION", "1.4.2"), UpstreamTimeout: durationEnv("ZHANLU_UPSTREAM_TIMEOUT", 300*time.Second), StreamIdleTimout: durationEnv("ZHANLU_STREAM_IDLE_TIMEOUT", 300*time.Second), Debug: strings.EqualFold(os.Getenv("ZHANLU_DEBUG"), "true"), } cfg.Credentials = auth.Credentials{ AccessKey: os.Getenv("ZHANLU_ACCESS_KEY"), SecretKey: os.Getenv("ZHANLU_SECRET_KEY"), Token: os.Getenv("ZHANLU_TOKEN"), APIKey: os.Getenv("ZHANLU_API_KEY"), } if cfg.Credentials.Validate() == nil || cfg.Credentials.HasAPIKey() { return cfg, nil } if creds, err := auth.LoadCredentials(cfg.CredentialsPath); err == nil { cfg.Credentials = creds } return cfg, nil } func getenv(key, fallback string) string { if v := strings.TrimSpace(os.Getenv(key)); v != "" { return v } return fallback } func durationEnv(key string, fallback time.Duration) time.Duration { v := strings.TrimSpace(os.Getenv(key)) if v == "" { return fallback } d, err := time.ParseDuration(v) if err != nil { return fallback } return d } func firstNonEmpty(values ...string) string { for _, v := range values { if strings.TrimSpace(v) != "" { return v } } return "" } func defaultCredentialsPath() string { return filepath.Join(".", "credentials.json") } const defaultPublicKeyPEM = `-----BEGIN PUBLIC KEY----- MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAhxudxTewPgljUHEZHkusP7m3I+zA4/RGvuUMt6TtII/m4zwUOm/Y31zHBTmkCCt8k5vj9y+AmO0TsGmHooNQuMebakdmEWdcA5h7YAHHFbF2w5LcxIXjib08vgVpA+m3R5xPbLK+vfHe2aAX36b5nHReDNncY5vAl3U4CgIEBGPqyG67vJytRWqP+sfEdw5+m192Rf4SCGyiBzRmjiVlH3zeEBjdbOrkAnzKOVz6AHBl2q7LPLJKIzxjoAyhEp5qnDjHUFo5VZUgFwUOt83A/jbGMyzmjRoxBuvKcs9tBuorZyUwIsZN6E+rtQk2YqMPj4RkDsZ7LRmj6on8sN2rHQIDAQAB -----END PUBLIC KEY-----` const defaultPhonePublicKeyPEM = `-----BEGIN PUBLIC KEY----- MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnqiA2qP9BNvKw5DnVnrBVBhd+5gJDVn3mDemCfq/AN1cdaHV57hQo6R1ufp45mOkSwLaJcTE82zFKmgKoEAKwD1SR10rp0xJC7x3yvx2FbpEsiW9TeZlvJdri1BYKUMS8OP8ykjHSJoy0oMaV6e95R2rsu4DEH7JuA9+Bt0sOoLewvHx/fs1e28tH+928uUEKdLug+cv/XTKjLudpLjiSMPZU6EHFqrUhA9zmEasOMmg9Dj0j4sChBooCeCGnh/pYHJaosH5amhlSQ8FnEG0BQBrQbZ+qhRH4LYyqGYN8grDNeSnPj7vPDcwiEm++85i5AngZfEMnGWZg5jYDhO9+QIDAQAB -----END PUBLIC KEY-----` const defaultSM2PrivateKey = "8d6ee90b3c4d299ae5abd655dbc3547c110ae8aeff1de18b0df241f215f90748"