mirror of https://github.com/Xhofe/alist
				
				
				
			
		
			
				
	
	
		
			253 lines
		
	
	
		
			5.2 KiB
		
	
	
	
		
			Go
		
	
	
			
		
		
	
	
			253 lines
		
	
	
		
			5.2 KiB
		
	
	
	
		
			Go
		
	
	
package xunlei
 | 
						|
 | 
						|
import (
 | 
						|
	"fmt"
 | 
						|
	"net/http"
 | 
						|
	"strings"
 | 
						|
	"sync"
 | 
						|
	"time"
 | 
						|
 | 
						|
	"github.com/Xhofe/alist/drivers/base"
 | 
						|
	"github.com/Xhofe/alist/model"
 | 
						|
	"github.com/Xhofe/alist/utils"
 | 
						|
	"github.com/go-resty/resty/v2"
 | 
						|
	log "github.com/sirupsen/logrus"
 | 
						|
)
 | 
						|
 | 
						|
// 缓存登录状态
 | 
						|
var userClients sync.Map
 | 
						|
 | 
						|
func GetClient(account *model.Account) *Client {
 | 
						|
	if v, ok := userClients.Load(account.Username); ok {
 | 
						|
		return v.(*Client)
 | 
						|
	}
 | 
						|
 | 
						|
	client := &Client{
 | 
						|
		Client: base.RestyClient,
 | 
						|
 | 
						|
		clientID:      account.ClientId,
 | 
						|
		clientSecret:  account.ClientSecret,
 | 
						|
		clientVersion: account.ClientVersion,
 | 
						|
		packageName:   account.PackageName,
 | 
						|
		algorithms:    strings.Split(account.Algorithms, ","),
 | 
						|
		userAgent:     account.UserAgent,
 | 
						|
		deviceID:      account.DeviceId,
 | 
						|
	}
 | 
						|
	userClients.Store(account.Username, client)
 | 
						|
	return client
 | 
						|
}
 | 
						|
 | 
						|
type Client struct {
 | 
						|
	*resty.Client
 | 
						|
	sync.Mutex
 | 
						|
 | 
						|
	clientID      string
 | 
						|
	clientSecret  string
 | 
						|
	clientVersion string
 | 
						|
	packageName   string
 | 
						|
	algorithms    []string
 | 
						|
	userAgent     string
 | 
						|
	deviceID      string
 | 
						|
 | 
						|
	captchaToken string
 | 
						|
 | 
						|
	token        string
 | 
						|
	refreshToken string
 | 
						|
	userID       string
 | 
						|
}
 | 
						|
 | 
						|
// 请求验证码token
 | 
						|
func (c *Client) requestCaptchaToken(action string, meta map[string]string) error {
 | 
						|
	param := CaptchaTokenRequest{
 | 
						|
		Action:       action,
 | 
						|
		CaptchaToken: c.captchaToken,
 | 
						|
		ClientID:     c.clientID,
 | 
						|
		DeviceID:     c.deviceID,
 | 
						|
		Meta:         meta,
 | 
						|
		RedirectUri:  "xlaccsdk01://xunlei.com/callback?state=harbor",
 | 
						|
	}
 | 
						|
 | 
						|
	var e Erron
 | 
						|
	var resp CaptchaTokenResponse
 | 
						|
	_, err := c.Client.R().
 | 
						|
		SetBody(¶m).
 | 
						|
		SetError(&e).
 | 
						|
		SetResult(&resp).
 | 
						|
		Post(XLUSER_API_URL + "/shield/captcha/init")
 | 
						|
	if err != nil {
 | 
						|
		return err
 | 
						|
	}
 | 
						|
	if e.HasError() {
 | 
						|
		return &e
 | 
						|
	}
 | 
						|
 | 
						|
	if resp.Url != "" {
 | 
						|
		return fmt.Errorf("need verify:%s", resp.Url)
 | 
						|
	}
 | 
						|
 | 
						|
	if resp.CaptchaToken == "" {
 | 
						|
		return fmt.Errorf("empty captchaToken")
 | 
						|
	}
 | 
						|
	c.captchaToken = resp.CaptchaToken
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
// 验证码签名
 | 
						|
func (c *Client) captchaSign(time string) string {
 | 
						|
	str := fmt.Sprint(c.clientID, c.clientVersion, c.packageName, c.deviceID, time)
 | 
						|
	for _, algorithm := range c.algorithms {
 | 
						|
		str = utils.GetMD5Encode(str + algorithm)
 | 
						|
	}
 | 
						|
	return "1." + str
 | 
						|
}
 | 
						|
 | 
						|
// 登录
 | 
						|
func (c *Client) Login(account *model.Account) (err error) {
 | 
						|
	c.Lock()
 | 
						|
	defer c.Unlock()
 | 
						|
 | 
						|
	defer func() {
 | 
						|
		if err != nil {
 | 
						|
			account.Status = err.Error()
 | 
						|
		} else {
 | 
						|
			account.Status = "work"
 | 
						|
		}
 | 
						|
		model.SaveAccount(account)
 | 
						|
	}()
 | 
						|
 | 
						|
	url := XLUSER_API_URL + "/auth/signin"
 | 
						|
	err = c.requestCaptchaToken(getAction(http.MethodPost, url), map[string]string{"username": account.Username})
 | 
						|
	if err != nil {
 | 
						|
		return err
 | 
						|
	}
 | 
						|
 | 
						|
	var e Erron
 | 
						|
	var resp TokenResponse
 | 
						|
	_, err = c.Client.R().
 | 
						|
		SetResult(&resp).
 | 
						|
		SetError(&e).
 | 
						|
		SetBody(&SignInRequest{
 | 
						|
			CaptchaToken: c.captchaToken,
 | 
						|
			ClientID:     c.clientID,
 | 
						|
			ClientSecret: c.clientSecret,
 | 
						|
			Username:     account.Username,
 | 
						|
			Password:     account.Password,
 | 
						|
		}).
 | 
						|
		Post(url)
 | 
						|
	if err != nil {
 | 
						|
		return err
 | 
						|
	}
 | 
						|
 | 
						|
	if e.HasError() {
 | 
						|
		return &e
 | 
						|
	}
 | 
						|
 | 
						|
	if resp.RefreshToken == "" {
 | 
						|
		return base.ErrEmptyToken
 | 
						|
	}
 | 
						|
 | 
						|
	c.token = resp.Token()
 | 
						|
	c.refreshToken = resp.RefreshToken
 | 
						|
	c.userID = resp.UserID
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
// 刷新验证码token
 | 
						|
func (c *Client) RefreshCaptchaToken(action string) error {
 | 
						|
	c.Lock()
 | 
						|
	defer c.Unlock()
 | 
						|
 | 
						|
	timestamp := fmt.Sprint(time.Now().UnixMilli())
 | 
						|
	param := map[string]string{
 | 
						|
		"client_version": c.clientVersion,
 | 
						|
		"package_name":   c.packageName,
 | 
						|
		"user_id":        c.userID,
 | 
						|
		"captcha_sign":   c.captchaSign(timestamp),
 | 
						|
		"timestamp":      timestamp,
 | 
						|
	}
 | 
						|
	return c.requestCaptchaToken(action, param)
 | 
						|
}
 | 
						|
 | 
						|
// 刷新token
 | 
						|
func (c *Client) RefreshToken() error {
 | 
						|
	c.Lock()
 | 
						|
	defer c.Unlock()
 | 
						|
 | 
						|
	var e Erron
 | 
						|
	var resp TokenResponse
 | 
						|
	_, err := c.Client.R().
 | 
						|
		SetError(&e).
 | 
						|
		SetResult(&resp).
 | 
						|
		SetBody(&base.Json{
 | 
						|
			"grant_type":    "refresh_token",
 | 
						|
			"refresh_token": c.refreshToken,
 | 
						|
			"client_id":     c.clientID,
 | 
						|
			"client_secret": c.clientSecret,
 | 
						|
		}).
 | 
						|
		Post(XLUSER_API_URL + "/auth/token")
 | 
						|
	if err != nil {
 | 
						|
		return err
 | 
						|
	}
 | 
						|
	if e.HasError() {
 | 
						|
		return &e
 | 
						|
	}
 | 
						|
 | 
						|
	if resp.RefreshToken == "" {
 | 
						|
		return base.ErrEmptyToken
 | 
						|
	}
 | 
						|
 | 
						|
	c.token = resp.TokenType + " " + resp.AccessToken
 | 
						|
	c.refreshToken = resp.RefreshToken
 | 
						|
	c.userID = resp.UserID
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
func (c *Client) Request(method string, url string, callback func(*resty.Request), account *model.Account) (*resty.Response, error) {
 | 
						|
	c.Lock()
 | 
						|
	req := c.Client.R().
 | 
						|
		SetHeaders(map[string]string{
 | 
						|
			"X-Device-Id":     c.deviceID,
 | 
						|
			"Authorization":   c.token,
 | 
						|
			"X-Captcha-Token": c.captchaToken,
 | 
						|
			"User-Agent":      c.userAgent,
 | 
						|
			"client_id":       c.clientID,
 | 
						|
		}).SetQueryParam("client_id", c.clientID)
 | 
						|
	if callback != nil {
 | 
						|
		callback(req)
 | 
						|
	}
 | 
						|
	c.Unlock()
 | 
						|
 | 
						|
	res, err := req.Execute(method, url)
 | 
						|
	if err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
	log.Debug(res.String())
 | 
						|
 | 
						|
	var e Erron
 | 
						|
	if err = utils.Json.Unmarshal(res.Body(), &e); err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
 | 
						|
	// 处理错误
 | 
						|
	switch e.ErrorCode {
 | 
						|
	case 0:
 | 
						|
		return res, nil
 | 
						|
	case 4122, 4121, 10: // token过期
 | 
						|
		if err = c.RefreshToken(); err == nil {
 | 
						|
			break
 | 
						|
		}
 | 
						|
		fallthrough
 | 
						|
	case 16: // 登录失效
 | 
						|
		if err = c.Login(account); err != nil {
 | 
						|
			return nil, err
 | 
						|
		}
 | 
						|
	case 9: // 验证码token过期
 | 
						|
		if err = c.RefreshCaptchaToken(getAction(method, url)); err != nil {
 | 
						|
			return nil, err
 | 
						|
		}
 | 
						|
	default:
 | 
						|
		return nil, &e
 | 
						|
	}
 | 
						|
	return c.Request(method, url, callback, account)
 | 
						|
}
 |