build / build (push) Successful in 2m34s
- New internal/stats (types) and internal/store (SQLite owner: requests + credentials tables, WAL); store implements stats.Recorder. - Stream (SSE tee) and non-stream chat paths parse upstream usage incl. cached_tokens and record per-request; add /api/stats, /api/stats/reset, /admin/stats HTML with cache hit rate. - Drop credentials.json: remove auth file I/O and ZHANLU_CREDENTIALS_FILE; credential precedence is env vars > db row.
102 lines
4.0 KiB
Go
102 lines
4.0 KiB
Go
package config
|
|
|
|
import (
|
|
"os"
|
|
"strings"
|
|
"time"
|
|
|
|
"git.misaka.ren/M1saka/zhanlu_proxy/internal/auth"
|
|
)
|
|
|
|
type Config struct {
|
|
ListenAddr string
|
|
MobileLoginBaseURL string
|
|
MobileModelBaseURL string
|
|
UpstreamPath string
|
|
DBPath string
|
|
StatsDisabled bool
|
|
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"),
|
|
DBPath: getenv("ZHANLU_DB_FILE", "zhanlu.db"),
|
|
StatsDisabled: strings.EqualFold(os.Getenv("ZHANLU_STATS_DISABLED"), "true"),
|
|
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"),
|
|
}
|
|
// Credentials from the environment were incomplete; the store serves the
|
|
// persisted row at runtime.
|
|
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 ""
|
|
}
|
|
|
|
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"
|