6bfcb0ce79
- Added `isUnauthorizedError` and `hasUnauthorizedAuthFailure` to classify and handle unauthorized errors. - Introduced `refreshErrorFromError` to map errors to standardized unauthorized responses. - Modified refresh logic to stop auto-refresh retries for unauthorized errors. - Updated tests to verify unauthorized error handling and refresh retry prevention.
103 lines
2.9 KiB
Go
103 lines
2.9 KiB
Go
package helps
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"net/http"
|
|
"strings"
|
|
|
|
"github.com/router-for-me/CLIProxyAPI/v7/internal/config"
|
|
"github.com/router-for-me/CLIProxyAPI/v7/internal/home"
|
|
cliproxyauth "github.com/router-for-me/CLIProxyAPI/v7/sdk/cliproxy/auth"
|
|
)
|
|
|
|
type homeStatusErr struct {
|
|
code int
|
|
msg string
|
|
}
|
|
|
|
func (e homeStatusErr) Error() string {
|
|
if e.msg != "" {
|
|
return e.msg
|
|
}
|
|
return fmt.Sprintf("status %d", e.code)
|
|
}
|
|
|
|
func (e homeStatusErr) StatusCode() int { return e.code }
|
|
|
|
type homeErrorEnvelope struct {
|
|
Error *homeErrorDetail `json:"error"`
|
|
}
|
|
|
|
type homeErrorDetail struct {
|
|
Type string `json:"type"`
|
|
Message string `json:"message"`
|
|
Code string `json:"code,omitempty"`
|
|
}
|
|
|
|
// RefreshAuthViaHome replaces local refresh logic when home control plane integration is enabled.
|
|
// It returns (updatedAuth, true, nil) when home refresh succeeds; (nil, true, err) when home is
|
|
// enabled but refresh fails; and (nil, false, nil) when home is disabled.
|
|
func RefreshAuthViaHome(ctx context.Context, cfg *config.Config, auth *cliproxyauth.Auth) (*cliproxyauth.Auth, bool, error) {
|
|
if cfg == nil || !cfg.Home.Enabled {
|
|
return nil, false, nil
|
|
}
|
|
if ctx == nil {
|
|
ctx = context.Background()
|
|
}
|
|
if auth == nil {
|
|
return nil, true, homeStatusErr{code: http.StatusInternalServerError, msg: "home refresh: auth is nil"}
|
|
}
|
|
|
|
client := home.Current()
|
|
if client == nil || !client.HeartbeatOK() {
|
|
return nil, true, homeStatusErr{code: http.StatusServiceUnavailable, msg: "home control center unavailable"}
|
|
}
|
|
|
|
authIndex := strings.TrimSpace(auth.Index)
|
|
if authIndex == "" {
|
|
authIndex = strings.TrimSpace(auth.EnsureIndex())
|
|
}
|
|
if authIndex == "" {
|
|
return nil, true, homeStatusErr{code: http.StatusBadGateway, msg: "home refresh: auth_index is empty"}
|
|
}
|
|
|
|
raw, err := client.GetRefreshAuth(ctx, authIndex)
|
|
if err != nil {
|
|
return nil, true, homeStatusErr{code: http.StatusBadGateway, msg: err.Error()}
|
|
}
|
|
|
|
var env homeErrorEnvelope
|
|
if errUnmarshal := json.Unmarshal(raw, &env); errUnmarshal == nil && env.Error != nil {
|
|
code := strings.TrimSpace(env.Error.Type)
|
|
if code == "" {
|
|
code = strings.TrimSpace(env.Error.Code)
|
|
}
|
|
msg := strings.TrimSpace(env.Error.Message)
|
|
if msg == "" {
|
|
msg = "home returned error"
|
|
}
|
|
return nil, true, homeStatusErr{code: statusFromHomeErrorCode(code), msg: msg}
|
|
}
|
|
|
|
var updated cliproxyauth.Auth
|
|
if errUnmarshal := json.Unmarshal(raw, &updated); errUnmarshal != nil {
|
|
return nil, true, homeStatusErr{code: http.StatusBadGateway, msg: "home returned invalid auth payload"}
|
|
}
|
|
updated.Index = authIndex
|
|
updated.EnsureIndex()
|
|
return &updated, true, nil
|
|
}
|
|
|
|
func statusFromHomeErrorCode(code string) int {
|
|
switch strings.ToLower(strings.TrimSpace(code)) {
|
|
case "authentication_error", "unauthorized":
|
|
return http.StatusUnauthorized
|
|
case "model_not_found":
|
|
return http.StatusNotFound
|
|
default:
|
|
return http.StatusBadGateway
|
|
}
|
|
}
|