Files
kirogo/config/config.go
Quorinex c5e6d42163 feat: Kiro API Proxy - OpenAI/Anthropic compatible API service
- Multi-account pool with round-robin load balancing
- Auto token refresh for IAM IdC and Social auth
- Streaming support (SSE)
- Web admin panel with account management
- Docker support with GitHub Actions CI/CD
- Machine ID management per account
- Usage tracking (requests, tokens, credits)
2026-02-04 00:37:05 +08:00

332 lines
7.9 KiB
Go

// Package config 配置管理模块
// 负责账号、设置、统计数据的持久化存储
package config
import (
"crypto/rand"
"encoding/json"
"fmt"
"os"
"sync"
)
// GenerateMachineId 生成 UUID v4 格式的机器码
func GenerateMachineId() string {
bytes := make([]byte, 16)
rand.Read(bytes)
bytes[6] = (bytes[6] & 0x0f) | 0x40 // 版本 4
bytes[8] = (bytes[8] & 0x3f) | 0x80 // 变体
return fmt.Sprintf("%08x-%04x-%04x-%04x-%012x",
bytes[0:4], bytes[4:6], bytes[6:8], bytes[8:10], bytes[10:16])
}
// Account 账号信息
type Account struct {
// 基本信息
ID string `json:"id"`
Email string `json:"email,omitempty"`
UserId string `json:"userId,omitempty"`
Nickname string `json:"nickname,omitempty"`
// 认证信息
AccessToken string `json:"accessToken"`
RefreshToken string `json:"refreshToken"`
ClientID string `json:"clientId,omitempty"`
ClientSecret string `json:"clientSecret,omitempty"`
AuthMethod string `json:"authMethod"` // idc | social
Provider string `json:"provider,omitempty"`
Region string `json:"region"`
StartUrl string `json:"startUrl,omitempty"`
ExpiresAt int64 `json:"expiresAt,omitempty"`
MachineId string `json:"machineId,omitempty"` // UUID 格式机器码
// 状态
Enabled bool `json:"enabled"`
// 订阅信息
SubscriptionType string `json:"subscriptionType,omitempty"` // FREE | PRO | PRO_PLUS | POWER
SubscriptionTitle string `json:"subscriptionTitle,omitempty"`
DaysRemaining int `json:"daysRemaining,omitempty"`
// 使用量
UsageCurrent float64 `json:"usageCurrent,omitempty"`
UsageLimit float64 `json:"usageLimit,omitempty"`
UsagePercent float64 `json:"usagePercent,omitempty"`
NextResetDate string `json:"nextResetDate,omitempty"`
LastRefresh int64 `json:"lastRefresh,omitempty"`
// 运行时统计
RequestCount int `json:"requestCount,omitempty"`
ErrorCount int `json:"errorCount,omitempty"`
LastUsed int64 `json:"lastUsed,omitempty"`
TotalTokens int `json:"totalTokens,omitempty"`
TotalCredits float64 `json:"totalCredits,omitempty"`
}
// Config 全局配置
type Config struct {
Password string `json:"password"`
Port int `json:"port"`
Host string `json:"host"`
ApiKey string `json:"apiKey,omitempty"`
RequireApiKey bool `json:"requireApiKey"`
Accounts []Account `json:"accounts"`
// 全局统计
TotalRequests int `json:"totalRequests,omitempty"`
SuccessRequests int `json:"successRequests,omitempty"`
FailedRequests int `json:"failedRequests,omitempty"`
TotalTokens int `json:"totalTokens,omitempty"`
TotalCredits float64 `json:"totalCredits,omitempty"`
}
// AccountInfo 账户信息更新结构
type AccountInfo struct {
Email string
UserId string
SubscriptionType string
SubscriptionTitle string
DaysRemaining int
UsageCurrent float64
UsageLimit float64
UsagePercent float64
NextResetDate string
LastRefresh int64
}
var (
cfg *Config
cfgLock sync.RWMutex
cfgPath string
)
// Init 初始化配置
func Init(path string) error {
cfgPath = path
return Load()
}
// Load 从文件加载配置
func Load() error {
cfgLock.Lock()
defer cfgLock.Unlock()
data, err := os.ReadFile(cfgPath)
if err != nil {
if os.IsNotExist(err) {
// 创建默认配置
cfg = &Config{
Password: "changeme",
Port: 8080,
Host: "127.0.0.1",
RequireApiKey: false,
Accounts: []Account{},
}
return Save()
}
return err
}
var c Config
if err := json.Unmarshal(data, &c); err != nil {
return err
}
cfg = &c
return nil
}
// Save 保存配置到文件
func Save() error {
data, err := json.MarshalIndent(cfg, "", " ")
if err != nil {
return err
}
return os.WriteFile(cfgPath, data, 0600)
}
// SetPassword 设置密码(用于环境变量覆盖)
func SetPassword(password string) {
cfgLock.Lock()
defer cfgLock.Unlock()
cfg.Password = password
}
func Get() *Config {
cfgLock.RLock()
defer cfgLock.RUnlock()
return cfg
}
func GetPassword() string {
cfgLock.RLock()
defer cfgLock.RUnlock()
return cfg.Password
}
func GetPort() int {
cfgLock.RLock()
defer cfgLock.RUnlock()
if cfg.Port == 0 {
return 8080
}
return cfg.Port
}
func GetHost() string {
cfgLock.RLock()
defer cfgLock.RUnlock()
if cfg.Host == "" {
return "127.0.0.1"
}
return cfg.Host
}
func GetAccounts() []Account {
cfgLock.RLock()
defer cfgLock.RUnlock()
accounts := make([]Account, len(cfg.Accounts))
copy(accounts, cfg.Accounts)
return accounts
}
func GetEnabledAccounts() []Account {
cfgLock.RLock()
defer cfgLock.RUnlock()
var accounts []Account
for _, a := range cfg.Accounts {
if a.Enabled {
accounts = append(accounts, a)
}
}
return accounts
}
func AddAccount(account Account) error {
cfgLock.Lock()
defer cfgLock.Unlock()
cfg.Accounts = append(cfg.Accounts, account)
return Save()
}
func UpdateAccount(id string, account Account) error {
cfgLock.Lock()
defer cfgLock.Unlock()
for i, a := range cfg.Accounts {
if a.ID == id {
cfg.Accounts[i] = account
return Save()
}
}
return nil
}
func DeleteAccount(id string) error {
cfgLock.Lock()
defer cfgLock.Unlock()
for i, a := range cfg.Accounts {
if a.ID == id {
cfg.Accounts = append(cfg.Accounts[:i], cfg.Accounts[i+1:]...)
return Save()
}
}
return nil
}
func UpdateAccountToken(id, accessToken, refreshToken string, expiresAt int64) error {
cfgLock.Lock()
defer cfgLock.Unlock()
for i, a := range cfg.Accounts {
if a.ID == id {
cfg.Accounts[i].AccessToken = accessToken
if refreshToken != "" {
cfg.Accounts[i].RefreshToken = refreshToken
}
cfg.Accounts[i].ExpiresAt = expiresAt
return Save()
}
}
return nil
}
func GetApiKey() string {
cfgLock.RLock()
defer cfgLock.RUnlock()
return cfg.ApiKey
}
func IsApiKeyRequired() bool {
cfgLock.RLock()
defer cfgLock.RUnlock()
return cfg.RequireApiKey
}
func UpdateSettings(apiKey string, requireApiKey bool, password string) error {
cfgLock.Lock()
defer cfgLock.Unlock()
cfg.ApiKey = apiKey
cfg.RequireApiKey = requireApiKey
if password != "" {
cfg.Password = password
}
return Save()
}
func UpdateStats(totalReq, successReq, failedReq, totalTokens int, totalCredits float64) error {
cfgLock.Lock()
defer cfgLock.Unlock()
cfg.TotalRequests = totalReq
cfg.SuccessRequests = successReq
cfg.FailedRequests = failedReq
cfg.TotalTokens = totalTokens
cfg.TotalCredits = totalCredits
return Save()
}
func GetStats() (int, int, int, int, float64) {
cfgLock.RLock()
defer cfgLock.RUnlock()
return cfg.TotalRequests, cfg.SuccessRequests, cfg.FailedRequests, cfg.TotalTokens, cfg.TotalCredits
}
func UpdateAccountStats(id string, requestCount, errorCount, totalTokens int, totalCredits float64, lastUsed int64) error {
cfgLock.Lock()
defer cfgLock.Unlock()
for i, a := range cfg.Accounts {
if a.ID == id {
cfg.Accounts[i].RequestCount = requestCount
cfg.Accounts[i].ErrorCount = errorCount
cfg.Accounts[i].TotalTokens = totalTokens
cfg.Accounts[i].TotalCredits = totalCredits
cfg.Accounts[i].LastUsed = lastUsed
return Save()
}
}
return nil
}
// UpdateAccountInfo 更新账户的订阅和使用量信息
func UpdateAccountInfo(id string, info AccountInfo) error {
cfgLock.Lock()
defer cfgLock.Unlock()
for i, a := range cfg.Accounts {
if a.ID == id {
if info.Email != "" {
cfg.Accounts[i].Email = info.Email
}
if info.UserId != "" {
cfg.Accounts[i].UserId = info.UserId
}
cfg.Accounts[i].SubscriptionType = info.SubscriptionType
cfg.Accounts[i].SubscriptionTitle = info.SubscriptionTitle
cfg.Accounts[i].DaysRemaining = info.DaysRemaining
cfg.Accounts[i].UsageCurrent = info.UsageCurrent
cfg.Accounts[i].UsageLimit = info.UsageLimit
cfg.Accounts[i].UsagePercent = info.UsagePercent
cfg.Accounts[i].NextResetDate = info.NextResetDate
cfg.Accounts[i].LastRefresh = info.LastRefresh
return Save()
}
}
return nil
}