mirror of
https://github.com/CJackHwang/ds2api.git
synced 2026-05-16 14:15:20 +08:00
feat: enhance tool call parsing robustness, authentication flexibility, and streaming output for tool content
This commit is contained in:
@@ -17,7 +17,7 @@ func AdminKey() string {
|
||||
if v := strings.TrimSpace(os.Getenv("DS2API_ADMIN_KEY")); v != "" {
|
||||
return v
|
||||
}
|
||||
return "your-admin-secret-key"
|
||||
return "admin"
|
||||
}
|
||||
|
||||
func jwtSecret() string {
|
||||
|
||||
@@ -15,7 +15,7 @@ type ctxKey string
|
||||
const authCtxKey ctxKey = "auth_context"
|
||||
|
||||
var (
|
||||
ErrUnauthorized = errors.New("unauthorized: missing Bearer token")
|
||||
ErrUnauthorized = errors.New("unauthorized: missing auth token")
|
||||
ErrNoAccount = errors.New("no accounts configured or all accounts are busy")
|
||||
)
|
||||
|
||||
@@ -41,11 +41,10 @@ func NewResolver(store *config.Store, pool *account.Pool, login LoginFunc) *Reso
|
||||
}
|
||||
|
||||
func (r *Resolver) Determine(req *http.Request) (*RequestAuth, error) {
|
||||
authHeader := req.Header.Get("Authorization")
|
||||
if !strings.HasPrefix(authHeader, "Bearer ") {
|
||||
callerKey := extractCallerToken(req)
|
||||
if callerKey == "" {
|
||||
return nil, ErrUnauthorized
|
||||
}
|
||||
callerKey := strings.TrimSpace(strings.TrimPrefix(authHeader, "Bearer "))
|
||||
ctx := req.Context()
|
||||
if !r.Store.HasAPIKey(callerKey) {
|
||||
return &RequestAuth{UseConfigToken: false, DeepSeekToken: callerKey, resolver: r, TriedAccounts: map[string]bool{}}, nil
|
||||
@@ -148,3 +147,14 @@ func (r *Resolver) Release(a *RequestAuth) {
|
||||
}
|
||||
r.Pool.Release(a.AccountID)
|
||||
}
|
||||
|
||||
func extractCallerToken(req *http.Request) string {
|
||||
authHeader := strings.TrimSpace(req.Header.Get("Authorization"))
|
||||
if strings.HasPrefix(strings.ToLower(authHeader), "bearer ") {
|
||||
token := strings.TrimSpace(authHeader[7:])
|
||||
if token != "" {
|
||||
return token
|
||||
}
|
||||
}
|
||||
return strings.TrimSpace(req.Header.Get("x-api-key"))
|
||||
}
|
||||
|
||||
74
internal/auth/request_test.go
Normal file
74
internal/auth/request_test.go
Normal file
@@ -0,0 +1,74 @@
|
||||
package auth
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
"ds2api/internal/account"
|
||||
"ds2api/internal/config"
|
||||
)
|
||||
|
||||
func newTestResolver(t *testing.T) *Resolver {
|
||||
t.Helper()
|
||||
t.Setenv("DS2API_CONFIG_JSON", `{
|
||||
"keys":["managed-key"],
|
||||
"accounts":[{"email":"acc@example.com","password":"pwd","token":"account-token"}]
|
||||
}`)
|
||||
store := config.LoadStore()
|
||||
pool := account.NewPool(store)
|
||||
return NewResolver(store, pool, func(_ context.Context, _ config.Account) (string, error) {
|
||||
return "fresh-token", nil
|
||||
})
|
||||
}
|
||||
|
||||
func TestDetermineWithXAPIKeyUsesDirectToken(t *testing.T) {
|
||||
r := newTestResolver(t)
|
||||
req, _ := http.NewRequest(http.MethodPost, "/anthropic/v1/messages", nil)
|
||||
req.Header.Set("x-api-key", "direct-token")
|
||||
|
||||
auth, err := r.Determine(req)
|
||||
if err != nil {
|
||||
t.Fatalf("determine failed: %v", err)
|
||||
}
|
||||
if auth.UseConfigToken {
|
||||
t.Fatalf("expected direct token mode")
|
||||
}
|
||||
if auth.DeepSeekToken != "direct-token" {
|
||||
t.Fatalf("unexpected token: %q", auth.DeepSeekToken)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDetermineWithXAPIKeyManagedKeyAcquiresAccount(t *testing.T) {
|
||||
r := newTestResolver(t)
|
||||
req, _ := http.NewRequest(http.MethodPost, "/anthropic/v1/messages", nil)
|
||||
req.Header.Set("x-api-key", "managed-key")
|
||||
|
||||
auth, err := r.Determine(req)
|
||||
if err != nil {
|
||||
t.Fatalf("determine failed: %v", err)
|
||||
}
|
||||
defer r.Release(auth)
|
||||
if !auth.UseConfigToken {
|
||||
t.Fatalf("expected managed key mode")
|
||||
}
|
||||
if auth.AccountID != "acc@example.com" {
|
||||
t.Fatalf("unexpected account id: %q", auth.AccountID)
|
||||
}
|
||||
if auth.DeepSeekToken != "account-token" {
|
||||
t.Fatalf("unexpected account token: %q", auth.DeepSeekToken)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDetermineMissingToken(t *testing.T) {
|
||||
r := newTestResolver(t)
|
||||
req, _ := http.NewRequest(http.MethodPost, "/v1/chat/completions", nil)
|
||||
|
||||
_, err := r.Determine(req)
|
||||
if err == nil {
|
||||
t.Fatal("expected unauthorized error")
|
||||
}
|
||||
if err != ErrUnauthorized {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user