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.
49 lines
1.2 KiB
Go
49 lines
1.2 KiB
Go
package auth
|
|
|
|
import (
|
|
"errors"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
type Credentials struct {
|
|
AccessKey string `json:"access_key"`
|
|
SecretKey string `json:"secret_key"`
|
|
Token string `json:"token"`
|
|
APIKey string `json:"api_key,omitempty"`
|
|
ModelBaseURL string `json:"model_base_url,omitempty"`
|
|
Email string `json:"email,omitempty"`
|
|
Organization string `json:"organization,omitempty"`
|
|
Team string `json:"team,omitempty"`
|
|
BaseURL string `json:"base_url,omitempty"`
|
|
SavedAt time.Time `json:"saved_at"`
|
|
}
|
|
|
|
type Profile struct {
|
|
Email string
|
|
Organization string
|
|
Team string
|
|
UserName string
|
|
Telephone string
|
|
}
|
|
|
|
func (c Credentials) Validate() error {
|
|
if strings.TrimSpace(c.APIKey) != "" && strings.TrimSpace(c.ModelBaseURL) != "" {
|
|
return nil
|
|
}
|
|
if strings.TrimSpace(c.AccessKey) == "" {
|
|
return errors.New("access_key is required")
|
|
}
|
|
if strings.TrimSpace(c.SecretKey) == "" {
|
|
return errors.New("secret_key is required")
|
|
}
|
|
if strings.TrimSpace(c.Token) == "" {
|
|
return errors.New("token is required")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (c Credentials) HasAPIKey() bool {
|
|
return strings.TrimSpace(c.APIKey) != "" && strings.TrimSpace(c.ModelBaseURL) != ""
|
|
}
|