mirror of
https://github.com/ctwj/urldb.git
synced 2025-11-25 03:15:04 +08:00
update: 迅雷使用账密方式登录
This commit is contained in:
39
common/xunlei_credentials.go
Normal file
39
common/xunlei_credentials.go
Normal file
@@ -0,0 +1,39 @@
|
||||
package pan
|
||||
|
||||
import "encoding/json"
|
||||
|
||||
// XunleiAccountCredentials 迅雷账号凭据结构
|
||||
type XunleiAccountCredentials struct {
|
||||
Username string `json:"username"` // 手机号(不包含+86前缀)
|
||||
Password string `json:"password"` // 密码
|
||||
RefreshToken string `json:"refresh_token"` // 当前有效的refresh_token
|
||||
}
|
||||
|
||||
// ParseCredentialsFromCk 从ck字段解析账号凭据
|
||||
func ParseCredentialsFromCk(ck string) (*XunleiAccountCredentials, error) {
|
||||
var credentials XunleiAccountCredentials
|
||||
if err := json.Unmarshal([]byte(ck), &credentials); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &credentials, nil
|
||||
}
|
||||
|
||||
// IsAccountCredentials 检查ck是否包含账号密码信息
|
||||
func IsAccountCredentials(ck string) bool {
|
||||
var credentials map[string]interface{}
|
||||
if err := json.Unmarshal([]byte(ck), &credentials); err != nil {
|
||||
return false
|
||||
}
|
||||
_, hasUsername := credentials["username"]
|
||||
_, hasPassword := credentials["password"]
|
||||
return hasUsername && hasPassword
|
||||
}
|
||||
|
||||
// ToJsonString 转换为JSON字符串
|
||||
func (c *XunleiAccountCredentials) ToJsonString() (string, error) {
|
||||
data, err := json.Marshal(c)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(data), nil
|
||||
}
|
||||
@@ -1,8 +1,12 @@
|
||||
package pan
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/md5"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
"strconv"
|
||||
@@ -14,6 +18,30 @@ import (
|
||||
"github.com/ctwj/urldb/db/repo"
|
||||
)
|
||||
|
||||
// 新增常量定义
|
||||
const (
|
||||
XLUSER_CLIENT_ID = "XW5SkOhLDjnOZP7J" // 登录
|
||||
PAN_CLIENT_ID = "Xqp0kJBXWhwaTpB6" // 获取文件列表
|
||||
CLIENT_SECRET = "Og9Vr1L8Ee6bh0olFxFDRg"
|
||||
CLIENT_VERSION = "1.92.9" // 更新为与xunlei_3项目相同的版本
|
||||
PACKAG_ENAME = "pan.xunlei.com"
|
||||
)
|
||||
|
||||
var SALTS = []string{
|
||||
"QG3/GhopO+5+T",
|
||||
"1Sv94+ANND3lDmmw",
|
||||
"q2eTxRva8b3B5d",
|
||||
"m2",
|
||||
"VIc5CZRBMU71ENfbOh0+RgWIuzLy",
|
||||
"66M8Wpw6nkBEekOtL6e",
|
||||
"N0rucK7S8W/vrRkfPto5urIJJS8dVY0S",
|
||||
"oLAR7pdUVUAp9xcuHWzrU057aUhdCJrt",
|
||||
"6lxcykBSsfI//GR9",
|
||||
"r50cz+1I4gbU/fk8",
|
||||
"tdwzrTc4SNFC4marNGTgf05flC85A",
|
||||
"qvNVUDFjfsOMqvdi2gB8gCvtaJAIqxXs",
|
||||
}
|
||||
|
||||
// CaptchaData 存储在数据库中的验证码令牌数据
|
||||
type CaptchaData struct {
|
||||
CaptchaToken string `json:"captcha_token"`
|
||||
@@ -32,8 +60,9 @@ type XunleiTokenData struct {
|
||||
}
|
||||
|
||||
type XunleiExtraData struct {
|
||||
Captcha *CaptchaData
|
||||
Token *XunleiTokenData
|
||||
Captcha *CaptchaData `json:"captcha,omitempty"`
|
||||
Token *XunleiTokenData `json:"token,omitempty"`
|
||||
Credentials *XunleiAccountCredentials `json:"credentials,omitempty"` // 账号密码信息
|
||||
}
|
||||
|
||||
type XunleiPanService struct {
|
||||
@@ -65,8 +94,8 @@ func NewXunleiPanService(config *PanConfig) *XunleiPanService {
|
||||
xunleiInstance := &XunleiPanService{
|
||||
BasePanService: NewBasePanService(config),
|
||||
clientId: "Xqp0kJBXWhwaTpB6",
|
||||
deviceId: "925b7631473a13716b791d7f28289cad",
|
||||
extra: XunleiExtraData{}, // Initialize extra with zero values
|
||||
deviceId: "a0a4beea8f0db68a5b7ae95ce7dab335", // 使用与xunlei_3相同的设备ID
|
||||
extra: XunleiExtraData{}, // Initialize extra with zero values
|
||||
}
|
||||
xunleiInstance.SetHeaders(map[string]string{
|
||||
"Accept": "*/;",
|
||||
@@ -100,9 +129,19 @@ func (x *XunleiPanService) SetCKSRepository(cksRepo repo.CksRepository, entity e
|
||||
x.cksRepo = cksRepo
|
||||
x.entity = entity
|
||||
var extra XunleiExtraData
|
||||
if err := json.Unmarshal([]byte(x.entity.Extra), &extra); err != nil {
|
||||
log.Printf("解析 extra 数据失败: %v,使用空数据", err)
|
||||
|
||||
// 解析extra字段
|
||||
if x.entity.Extra != "" {
|
||||
if err := json.Unmarshal([]byte(x.entity.Extra), &extra); err != nil {
|
||||
log.Printf("解析 extra 数据失败: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// 从ck字段解析账号密码
|
||||
if credentials, err := ParseCredentialsFromCk(x.entity.Ck); err == nil {
|
||||
extra.Credentials = credentials
|
||||
}
|
||||
|
||||
x.extra = extra
|
||||
}
|
||||
|
||||
@@ -151,20 +190,51 @@ func (x *XunleiPanService) GetAccessTokenByRefreshToken(refreshToken string) (Xu
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// getAccessToken 获取 Access Token(内部包含缓存判断、刷新、保存)- 匹配 PHP 版本
|
||||
// getAccessToken 获取 Access Token(内部包含缓存判断、刷新、重新登录、保存)
|
||||
func (x *XunleiPanService) getAccessToken() (string, error) {
|
||||
// 检查 Access Token 是否有效
|
||||
currentTime := time.Now().Unix()
|
||||
if x.extra.Token != nil && x.extra.Token.AccessToken != "" && x.extra.Token.ExpiresAt > currentTime {
|
||||
return x.extra.Token.AccessToken, nil
|
||||
}
|
||||
newData, err := x.GetAccessTokenByRefreshToken(x.extra.Token.RefreshToken)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("获取 access_token 失败: %v", err)
|
||||
|
||||
// 尝试使用refresh_token刷新
|
||||
var newData XunleiTokenData
|
||||
var err error
|
||||
|
||||
if x.extra.Token != nil && x.extra.Token.RefreshToken != "" {
|
||||
newData, err = x.GetAccessTokenByRefreshToken(x.extra.Token.RefreshToken)
|
||||
if err != nil {
|
||||
log.Printf("refresh_token刷新失败: %v,尝试使用账号密码重新登录", err)
|
||||
|
||||
// 如果refresh_token失效且有账号密码信息,尝试重新登录
|
||||
if x.extra.Credentials != nil && x.extra.Credentials.Username != "" && x.extra.Credentials.Password != "" {
|
||||
newData, err = x.reloginWithCredentials()
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("重新登录失败: %v", err)
|
||||
}
|
||||
} else {
|
||||
return "", fmt.Errorf("refresh_token失效且无账号密码信息,无法重新登录: %v", err)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return "", fmt.Errorf("无有效的refresh_token")
|
||||
}
|
||||
|
||||
// 更新token信息
|
||||
if x.extra.Token == nil {
|
||||
x.extra.Token = &XunleiTokenData{}
|
||||
}
|
||||
x.extra.Token.AccessToken = newData.AccessToken
|
||||
x.extra.Token.RefreshToken = newData.RefreshToken
|
||||
x.extra.Token.ExpiresAt = newData.ExpiresAt
|
||||
x.extra.Token.ExpiresIn = newData.ExpiresIn
|
||||
x.extra.Token.Sub = newData.Sub
|
||||
x.extra.Token.TokenType = newData.TokenType
|
||||
x.extra.Token.UserId = newData.UserId
|
||||
|
||||
// 更新ck字段中的refresh_token(保持向后兼容)
|
||||
x.entity.Ck = newData.RefreshToken
|
||||
|
||||
// 保存到数据库
|
||||
extraBytes, err := json.Marshal(x.extra)
|
||||
@@ -175,9 +245,128 @@ func (x *XunleiPanService) getAccessToken() (string, error) {
|
||||
if err := x.cksRepo.UpdateWithAllFields(&x.entity); err != nil {
|
||||
return "", fmt.Errorf("保存 access_token 到数据库失败: %v", err)
|
||||
}
|
||||
|
||||
return newData.AccessToken, nil
|
||||
}
|
||||
|
||||
// reloginWithCredentials 使用账号密码重新登录
|
||||
func (x *XunleiPanService) reloginWithCredentials() (XunleiTokenData, error) {
|
||||
if x.extra.Credentials == nil {
|
||||
return XunleiTokenData{}, fmt.Errorf("无账号密码信息")
|
||||
}
|
||||
|
||||
tokenData, err := x.LoginWithCredentials(x.extra.Credentials.Username, x.extra.Credentials.Password)
|
||||
if err != nil {
|
||||
return XunleiTokenData{}, fmt.Errorf("账号密码登录失败: %v", err)
|
||||
}
|
||||
|
||||
log.Printf("账号 %s 重新登录成功", x.extra.Credentials.Username)
|
||||
return tokenData, nil
|
||||
}
|
||||
|
||||
// getTimestamp 获取当前时间戳
|
||||
func (x *XunleiPanService) getTimestamp() int64 {
|
||||
return time.Now().UnixMilli()
|
||||
}
|
||||
|
||||
// captchaSign 生成验证码签名 - 完全复制自xunlei_3项目
|
||||
func (x *XunleiPanService) captchaSign(clientId string, deviceID string, timestamp string) string {
|
||||
sign := clientId + CLIENT_VERSION + PACKAG_ENAME + deviceID + timestamp
|
||||
log.Printf("urldb 签名基础字符串: %s", sign)
|
||||
for _, salt := range SALTS { // salt =
|
||||
hash := md5.Sum([]byte(sign + salt))
|
||||
sign = hex.EncodeToString(hash[:])
|
||||
}
|
||||
log.Printf("urldb 最终签名: 1.%s", sign)
|
||||
return fmt.Sprintf("1.%s", sign)
|
||||
}
|
||||
|
||||
// LoginWithCredentials 使用账号密码登录
|
||||
func (x *XunleiPanService) LoginWithCredentials(username, password string) (XunleiTokenData, error) {
|
||||
loginURL := "https://xluser-ssl.xunlei.com/v1/auth/signin"
|
||||
|
||||
// 初始化验证码 - 完全模仿xunlei_3的CaptchaInit方法
|
||||
captchaURL := "https://xluser-ssl.xunlei.com/v1/shield/captcha/init"
|
||||
|
||||
// 构造meta参数(完全模仿xunlei_3,只包含phone_number)
|
||||
meta := map[string]interface{}{
|
||||
"phone_number": "+86" + username,
|
||||
}
|
||||
|
||||
// 构造验证码请求(完全模仿xunlei_3)
|
||||
captchaBody := map[string]interface{}{
|
||||
"client_id": XLUSER_CLIENT_ID,
|
||||
"action": "POST:/v1/auth/signin",
|
||||
"device_id": x.deviceId,
|
||||
"meta": meta,
|
||||
}
|
||||
|
||||
log.Printf("发送验证码初始化请求: %+v", captchaBody)
|
||||
resp, err := x.sendCaptchaRequest(captchaURL, captchaBody)
|
||||
if err != nil {
|
||||
return XunleiTokenData{}, fmt.Errorf("获取验证码失败: %v", err)
|
||||
}
|
||||
|
||||
if resp["captcha_token"] == nil {
|
||||
return XunleiTokenData{}, fmt.Errorf("获取验证码失败: 响应中没有captcha_token")
|
||||
}
|
||||
|
||||
captchaToken, ok := resp["captcha_token"].(string)
|
||||
if !ok {
|
||||
return XunleiTokenData{}, fmt.Errorf("获取验证码失败: captcha_token格式错误")
|
||||
}
|
||||
log.Printf("成功获取captcha_token: %s", captchaToken)
|
||||
|
||||
// 构造登录请求数据
|
||||
loginData := map[string]interface{}{
|
||||
"client_id": XLUSER_CLIENT_ID,
|
||||
"client_secret": CLIENT_SECRET,
|
||||
"password": password,
|
||||
"username": "+86 " + username,
|
||||
"captcha_token": captchaToken,
|
||||
}
|
||||
|
||||
// 发送登录请求
|
||||
userInfo, err := x.sendCaptchaRequest(loginURL, loginData)
|
||||
if err != nil {
|
||||
return XunleiTokenData{}, fmt.Errorf("登录请求失败: %v", err)
|
||||
}
|
||||
|
||||
// 提取token信息
|
||||
accessToken, ok := userInfo["access_token"].(string)
|
||||
if !ok {
|
||||
return XunleiTokenData{}, fmt.Errorf("登录响应中没有access_token")
|
||||
}
|
||||
|
||||
refreshToken, ok := userInfo["refresh_token"].(string)
|
||||
if !ok {
|
||||
return XunleiTokenData{}, fmt.Errorf("登录响应中没有refresh_token")
|
||||
}
|
||||
|
||||
sub, ok := userInfo["sub"].(string)
|
||||
if !ok {
|
||||
sub = ""
|
||||
}
|
||||
|
||||
// 计算过期时间
|
||||
expiresIn := int64(3600) // 默认1小时
|
||||
if exp, ok := userInfo["expires_in"].(float64); ok {
|
||||
expiresIn = int64(exp)
|
||||
}
|
||||
expiresAt := time.Now().Unix() + expiresIn - 60 // 减去60秒缓冲
|
||||
|
||||
log.Printf("登录成功,获取到token")
|
||||
return XunleiTokenData{
|
||||
AccessToken: accessToken,
|
||||
RefreshToken: refreshToken,
|
||||
ExpiresIn: expiresIn,
|
||||
ExpiresAt: expiresAt,
|
||||
Sub: sub,
|
||||
TokenType: "Bearer",
|
||||
UserId: sub,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// getCaptchaToken 获取 captcha_token - 匹配 PHP 版本
|
||||
func (x *XunleiPanService) getCaptchaToken() (string, error) {
|
||||
// 检查 Captcha Token 是否有效
|
||||
@@ -186,6 +375,10 @@ func (x *XunleiPanService) getCaptchaToken() (string, error) {
|
||||
return x.extra.Captcha.CaptchaToken, nil
|
||||
}
|
||||
|
||||
// 生成动态时间戳和签名
|
||||
timestamp := strconv.FormatInt(x.getTimestamp(), 10)
|
||||
newCaptchaSign := x.captchaSign(x.clientId, x.deviceId, timestamp)
|
||||
|
||||
// 构造请求体
|
||||
body := map[string]interface{}{
|
||||
"client_id": x.clientId,
|
||||
@@ -196,15 +389,15 @@ func (x *XunleiPanService) getCaptchaToken() (string, error) {
|
||||
"phone_number": "",
|
||||
"email": "",
|
||||
"package_name": "pan.xunlei.com",
|
||||
"client_version": "1.45.0",
|
||||
"captcha_sign": "1.fe2108ad808a74c9ac0243309242726c",
|
||||
"timestamp": "1645241033384",
|
||||
"client_version": CLIENT_VERSION,
|
||||
"captcha_sign": newCaptchaSign,
|
||||
"timestamp": timestamp,
|
||||
"user_id": "0",
|
||||
},
|
||||
}
|
||||
|
||||
captchaHeaders := map[string]string{
|
||||
"Content-Type": "application/json",
|
||||
"Content-Type": "application/x-www-form-urlencoded",
|
||||
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36",
|
||||
}
|
||||
|
||||
@@ -265,6 +458,12 @@ func (x *XunleiPanService) requestXunleiApi(url string, method string, data map[
|
||||
}
|
||||
}()
|
||||
|
||||
// 检查是否是验证码初始化请求
|
||||
if strings.Contains(url, "shield/captcha/init") {
|
||||
// 对于验证码初始化,直接发送HTTP请求,不使用BasePanService
|
||||
return x.sendCaptchaRequest(url, data)
|
||||
}
|
||||
|
||||
// 根据方法调用相应的 BasePanService 方法
|
||||
if method == "GET" {
|
||||
respData, err = x.HTTPGet(url, queryParams)
|
||||
@@ -286,6 +485,85 @@ func (x *XunleiPanService) requestXunleiApi(url string, method string, data map[
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// sendCaptchaRequest 发送验证码请求 - 完全复制xunlei_3的sendRequest实现
|
||||
func (x *XunleiPanService) sendCaptchaRequest(url string, data map[string]interface{}) (map[string]interface{}, error) {
|
||||
jsonData, err := json.Marshal(data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
log.Printf("发送验证码请求URL: %s", url)
|
||||
log.Printf("发送验证码请求数据: %s", string(jsonData))
|
||||
|
||||
req, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonData))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 完全复制xunlei_3的请求头设置
|
||||
reqHeaders := x.getHeadersForRequest(nil)
|
||||
// 添加特定的headers
|
||||
reqHeaders["Content-Type"] = "application/x-www-form-urlencoded"
|
||||
reqHeaders["User-Agent"] = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36"
|
||||
|
||||
for k, v := range reqHeaders {
|
||||
req.Header.Set(k, v)
|
||||
}
|
||||
|
||||
// 检查是否需要添加X-Client-Id
|
||||
if !strings.Contains(url, "shield/captcha/init") {
|
||||
req.Header.Set("X-Client-Id", PAN_CLIENT_ID)
|
||||
}
|
||||
|
||||
client := &http.Client{Timeout: 10 * time.Second}
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
log.Printf("验证码响应状态码: %d", resp.StatusCode)
|
||||
log.Printf("验证码响应内容: %s", string(body))
|
||||
|
||||
var result map[string]interface{}
|
||||
if err := json.Unmarshal(body, &result); err != nil {
|
||||
return nil, fmt.Errorf("JSON 解析失败: %v, raw: %s", err, string(body))
|
||||
}
|
||||
|
||||
log.Printf("解析后的响应: %+v", result)
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// getHeadersForRequest 获取请求头
|
||||
func (x *XunleiPanService) getHeadersForRequest(accessToken *string) map[string]string {
|
||||
headers := map[string]string{
|
||||
"Content-Type": "application/json; charset=utf-8",
|
||||
}
|
||||
|
||||
// 这里我们简化处理,因为验证码请求不需要这些
|
||||
// if x.CaptchaToken != nil {
|
||||
// headers["User-Agent"] = x.buildCustomUserAgent()
|
||||
// headers["X-Captcha-Token"] = *x.CaptchaToken
|
||||
// } else {
|
||||
headers["User-Agent"] = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36"
|
||||
// }
|
||||
|
||||
// if accessToken != nil {
|
||||
// headers["Authorization"] = fmt.Sprintf("Bearer %s", *accessToken)
|
||||
// }
|
||||
|
||||
// if x.DeviceID != "" {
|
||||
// headers["X-Device-Id"] = x.DeviceID
|
||||
// }
|
||||
|
||||
return headers
|
||||
}
|
||||
|
||||
func (x *XunleiPanService) UpdateConfig(config *PanConfig) {
|
||||
if config == nil {
|
||||
return
|
||||
|
||||
@@ -2,6 +2,7 @@ package handlers
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"log"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
@@ -110,32 +111,81 @@ func CreateCks(c *gin.Context) {
|
||||
}
|
||||
|
||||
var cks *entity.Cks
|
||||
// 迅雷网盘,添加的时候 只获取token就好, 然后刷新的时候, 再补充用户信息等
|
||||
// 迅雷网盘,使用账号密码登录
|
||||
if serviceType == panutils.Xunlei {
|
||||
xunleiService := service.(*panutils.XunleiPanService)
|
||||
tokenData, err := xunleiService.GetAccessTokenByRefreshToken(req.Ck)
|
||||
// 解析账号密码信息
|
||||
credentials, err := panutils.ParseCredentialsFromCk(req.Ck)
|
||||
if err != nil {
|
||||
ErrorResponse(c, "无法获取有效token: "+err.Error(), http.StatusBadRequest)
|
||||
ErrorResponse(c, "账号密码格式错误: "+err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
// 验证账号密码
|
||||
if credentials.Username == "" || credentials.Password == "" {
|
||||
ErrorResponse(c, "请提供完整的账号和密码", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
var tokenData *panutils.XunleiTokenData
|
||||
var username string
|
||||
|
||||
// 使用账号密码登录
|
||||
xunleiService := service.(*panutils.XunleiPanService)
|
||||
token, err := xunleiService.LoginWithCredentials(credentials.Username, credentials.Password)
|
||||
if err != nil {
|
||||
ErrorResponse(c, "账号密码登录失败: "+err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
tokenData = &token
|
||||
username = credentials.Username
|
||||
|
||||
// 构建extra数据
|
||||
extra := panutils.XunleiExtraData{
|
||||
Token: &tokenData,
|
||||
Token: tokenData,
|
||||
Captcha: &panutils.CaptchaData{},
|
||||
}
|
||||
|
||||
// 如果有账号密码信息,保存到extra中
|
||||
if credentials.Username != "" && credentials.Password != "" {
|
||||
extra.Credentials = credentials
|
||||
}
|
||||
|
||||
extraStr, _ := json.Marshal(extra)
|
||||
|
||||
// 声明userInfo变量
|
||||
var userInfo *panutils.UserInfo
|
||||
|
||||
// 设置CKSRepository以便获取用户信息
|
||||
xunleiService.SetCKSRepository(repoManager.CksRepository, entity.Cks{})
|
||||
|
||||
// 获取用户信息
|
||||
userInfo, err = xunleiService.GetUserInfo(nil)
|
||||
if err != nil {
|
||||
log.Printf("获取迅雷用户信息失败,使用默认值: %v", err)
|
||||
// 如果获取失败,使用默认值
|
||||
userInfo = &panutils.UserInfo{
|
||||
Username: username,
|
||||
VIPStatus: false,
|
||||
ServiceType: "xunlei",
|
||||
TotalSpace: 0,
|
||||
UsedSpace: 0,
|
||||
}
|
||||
}
|
||||
|
||||
leftSpaceBytes := userInfo.TotalSpace - userInfo.UsedSpace
|
||||
|
||||
// 创建Cks实体
|
||||
cks = &entity.Cks{
|
||||
PanID: req.PanID,
|
||||
Idx: req.Idx,
|
||||
Ck: tokenData.RefreshToken,
|
||||
IsValid: true, // 根据VIP状态设置有效性
|
||||
Space: 0,
|
||||
LeftSpace: 0,
|
||||
UsedSpace: 0,
|
||||
Username: "-",
|
||||
VipStatus: false,
|
||||
ServiceType: "xunlei",
|
||||
Ck: req.Ck, // 保持原始输入
|
||||
IsValid: userInfo.VIPStatus, // 根据VIP状态设置有效性
|
||||
Space: userInfo.TotalSpace,
|
||||
LeftSpace: leftSpaceBytes,
|
||||
UsedSpace: userInfo.UsedSpace,
|
||||
Username: userInfo.Username,
|
||||
VipStatus: userInfo.VIPStatus,
|
||||
ServiceType: userInfo.ServiceType,
|
||||
Extra: string(extraStr),
|
||||
Remark: req.Remark,
|
||||
}
|
||||
@@ -388,14 +438,16 @@ func RefreshCapacity(c *gin.Context) {
|
||||
|
||||
var userInfo *panutils.UserInfo
|
||||
service.SetCKSRepository(repoManager.CksRepository, *cks) // 迅雷需要初始化 token 后才能获取,
|
||||
userInfo, err = service.GetUserInfo(&cks.Ck)
|
||||
// switch s := service.(type) {
|
||||
// case *panutils.XunleiPanService:
|
||||
|
||||
// userInfo, err = s.GetUserInfo(nil)
|
||||
// default:
|
||||
// userInfo, err = service.GetUserInfo(&cks.Ck)
|
||||
// }
|
||||
// 根据服务类型调用不同的GetUserInfo方法
|
||||
switch s := service.(type) {
|
||||
case *panutils.XunleiPanService:
|
||||
// 迅雷网盘使用存储在extra中的token,不需要传递ck参数
|
||||
userInfo, err = s.GetUserInfo(nil)
|
||||
default:
|
||||
// 其他网盘使用ck参数
|
||||
userInfo, err = service.GetUserInfo(&cks.Ck)
|
||||
}
|
||||
if err != nil {
|
||||
ErrorResponse(c, "无法获取用户信息,刷新失败: "+err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
|
||||
@@ -218,10 +218,20 @@
|
||||
</div>
|
||||
|
||||
<div v-if="isXunlei">
|
||||
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
|
||||
refresh_token <span class="text-red-500">*</span>
|
||||
</label>
|
||||
<n-input v-model:value="form.ck" type="textarea" placeholder="请输入" :rows="4" required />
|
||||
<div class="space-y-4">
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
|
||||
手机号 <span class="text-red-500">*</span>
|
||||
</label>
|
||||
<n-input v-model:value="xunleiForm.username" placeholder="请输入手机号(不需要+86前缀)" required />
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
|
||||
密码 <span class="text-red-500">*</span>
|
||||
</label>
|
||||
<n-input v-model:value="xunleiForm.password" type="password" placeholder="请输入密码" show-password-on="click" required />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
@@ -280,6 +290,12 @@ const form = ref({
|
||||
remark: ''
|
||||
})
|
||||
|
||||
// 迅雷专用表单数据
|
||||
const xunleiForm = ref({
|
||||
username: '',
|
||||
password: ''
|
||||
})
|
||||
|
||||
const panEnables = ref(['quark', 'xunlei'])
|
||||
// const xunleiEnable = useCookie('xunleiEnable', { default: () => false })
|
||||
// if (xunleiEnable.value && xunleiEnable.value === 'true') {
|
||||
@@ -533,6 +549,25 @@ const editCks = (cks) => {
|
||||
is_valid: cks.is_valid,
|
||||
remark: cks.remark || ''
|
||||
}
|
||||
|
||||
// 如果是迅雷账号,解析ck字段来设置表单
|
||||
if (cks.pan?.name === 'xunlei') {
|
||||
try {
|
||||
// 解析JSON格式
|
||||
const parsed = JSON.parse(cks.ck)
|
||||
xunleiForm.value = {
|
||||
username: parsed.username,
|
||||
password: parsed.password
|
||||
}
|
||||
} catch (e) {
|
||||
// 解析失败,清空表单
|
||||
xunleiForm.value = {
|
||||
username: '',
|
||||
password: ''
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
showEditModal.value = true
|
||||
}
|
||||
|
||||
@@ -547,10 +582,32 @@ const closeModal = () => {
|
||||
is_valid: true,
|
||||
remark: ''
|
||||
}
|
||||
// 重置迅雷表单
|
||||
xunleiForm.value = {
|
||||
username: '',
|
||||
password: ''
|
||||
}
|
||||
}
|
||||
|
||||
// 提交表单
|
||||
const handleSubmit = async () => {
|
||||
// 如果是迅雷账号,需要构造账号密码的JSON格式
|
||||
if (isXunlei.value) {
|
||||
if (!xunleiForm.value.username || !xunleiForm.value.password) {
|
||||
notification.error({
|
||||
title: '失败',
|
||||
content: '请填写完整的账号和密码',
|
||||
duration: 3000
|
||||
})
|
||||
return
|
||||
}
|
||||
form.value.ck = JSON.stringify({
|
||||
username: xunleiForm.value.username,
|
||||
password: xunleiForm.value.password,
|
||||
refresh_token: '' // 初始为空,登录后会填充
|
||||
})
|
||||
}
|
||||
|
||||
if (showEditModal.value) {
|
||||
await updateCks()
|
||||
} else {
|
||||
|
||||
Reference in New Issue
Block a user