Add projects
This commit is contained in:
parent
2d3a9ad623
commit
8b607dd700
1802 changed files with 503346 additions and 2 deletions
155
backend/internal/runtime/executor/helps/home_refresh.go
Normal file
155
backend/internal/runtime/executor/helps/home_refresh.go
Normal file
|
|
@ -0,0 +1,155 @@
|
|||
package helps
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"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 homeRefreshAuthEnvelope struct {
|
||||
Auth cliproxyauth.Auth `json:"auth"`
|
||||
AuthIndex string `json:"auth_index"`
|
||||
}
|
||||
|
||||
type homeErrorDetail struct {
|
||||
Type string `json:"type"`
|
||||
Message string `json:"message"`
|
||||
Code string `json:"code,omitempty"`
|
||||
}
|
||||
|
||||
type homeRefreshClient interface {
|
||||
HeartbeatOK() bool
|
||||
GetRefreshAuth(ctx context.Context, authIndex string, accessTokenSHA256 string) ([]byte, error)
|
||||
}
|
||||
|
||||
var currentHomeRefreshClient = func() homeRefreshClient {
|
||||
return home.Current()
|
||||
}
|
||||
|
||||
// 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 := currentHomeRefreshClient()
|
||||
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, authAccessTokenSHA256(auth))
|
||||
if err != nil {
|
||||
if errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded) {
|
||||
return nil, true, err
|
||||
}
|
||||
return nil, true, homeStatusErr{code: http.StatusServiceUnavailable, msg: "home refresh temporarily unavailable"}
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
statusCode := statusFromHomeErrorCode(code)
|
||||
message := "credential refresh temporarily unavailable"
|
||||
switch statusCode {
|
||||
case http.StatusUnauthorized:
|
||||
message = "credential unauthorized"
|
||||
case http.StatusNotFound:
|
||||
message = "credential refresh target not found"
|
||||
}
|
||||
return nil, true, homeStatusErr{code: statusCode, msg: message}
|
||||
}
|
||||
|
||||
updated, returnedIndex, errParse := parseHomeRefreshAuth(raw)
|
||||
if errParse != nil {
|
||||
return nil, true, homeStatusErr{code: http.StatusBadGateway, msg: "home returned invalid auth payload"}
|
||||
}
|
||||
if updated.Disabled || updated.Status == cliproxyauth.StatusDisabled {
|
||||
return nil, true, homeStatusErr{code: http.StatusUnauthorized, msg: "credential unauthorized"}
|
||||
}
|
||||
if returnedIndex != "" {
|
||||
authIndex = returnedIndex
|
||||
}
|
||||
updated.Index = authIndex
|
||||
updated.EnsureIndex()
|
||||
return updated, true, nil
|
||||
}
|
||||
|
||||
func authAccessTokenSHA256(auth *cliproxyauth.Auth) string {
|
||||
return cliproxyauth.AccessTokenSHA256(auth)
|
||||
}
|
||||
|
||||
func parseHomeRefreshAuth(raw []byte) (*cliproxyauth.Auth, string, error) {
|
||||
var rawObject map[string]json.RawMessage
|
||||
if errUnmarshal := json.Unmarshal(raw, &rawObject); errUnmarshal != nil {
|
||||
return nil, "", errUnmarshal
|
||||
}
|
||||
if _, ok := rawObject["auth"]; ok {
|
||||
var envelope homeRefreshAuthEnvelope
|
||||
if errUnmarshal := json.Unmarshal(raw, &envelope); errUnmarshal != nil {
|
||||
return nil, "", errUnmarshal
|
||||
}
|
||||
return &envelope.Auth, strings.TrimSpace(envelope.AuthIndex), nil
|
||||
}
|
||||
var updated cliproxyauth.Auth
|
||||
if errUnmarshal := json.Unmarshal(raw, &updated); errUnmarshal != nil {
|
||||
return nil, "", errUnmarshal
|
||||
}
|
||||
return &updated, "", nil
|
||||
}
|
||||
|
||||
func statusFromHomeErrorCode(code string) int {
|
||||
switch strings.ToLower(strings.TrimSpace(code)) {
|
||||
case "authentication_error", "unauthorized", "invalid_grant", "refresh_token_expired", "refresh_token_revoked", "refresh_token_reused":
|
||||
return http.StatusUnauthorized
|
||||
case "model_not_found":
|
||||
return http.StatusNotFound
|
||||
case "auth_not_found", "auth_unavailable", "refresh_temporarily_unavailable", "refresh_unsupported", "home_unavailable":
|
||||
return http.StatusServiceUnavailable
|
||||
default:
|
||||
return http.StatusServiceUnavailable
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue