mirror of
https://github.com/ctwj/urldb.git
synced 2025-11-25 11:29:37 +08:00
update: xunlei
This commit is contained in:
@@ -54,7 +54,6 @@ func (x *XunleiPanService) apiHost(apiType string) string {
|
||||
return "https://api-pan.xunlei.com"
|
||||
}
|
||||
|
||||
// 工具:自动补全必要 header
|
||||
func (x *XunleiPanService) setCommonHeader(req *http.Request) {
|
||||
for k, v := range x.headers {
|
||||
req.Header.Set(k, v)
|
||||
@@ -116,6 +115,36 @@ func GetXunleiInstance() *XunleiPanService {
|
||||
return NewXunleiPanService(nil)
|
||||
}
|
||||
|
||||
func (x *XunleiPanService) getAccessTokenByRefreshToken(refreshToken string) (map[string]interface{}, error) {
|
||||
// 构造请求体
|
||||
body := map[string]interface{}{
|
||||
"client_id": x.clientId,
|
||||
"grant_type": "refresh_token",
|
||||
"refresh_token": refreshToken,
|
||||
}
|
||||
|
||||
// 过滤 headers(移除 Authorization 和 x-captcha-token)
|
||||
filteredHeaders := make(map[string]string)
|
||||
for k, v := range x.headers {
|
||||
if k != "Authorization" && k != "x-captcha-token" {
|
||||
filteredHeaders[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
// 调用 API 获取新的 token
|
||||
resp, err := x.requestXunleiApi("https://xluser-ssl.xunlei.com/v1/auth/token", "POST", body, nil, filteredHeaders)
|
||||
if err != nil {
|
||||
return map[string]interface{}{}, fmt.Errorf("获取 access_token 请求失败: %v", err)
|
||||
}
|
||||
|
||||
if resp["code"] != 0 || resp["data"] == nil {
|
||||
return map[string]interface{}{}, fmt.Errorf("获取 access_token 失败: %v", resp)
|
||||
}
|
||||
|
||||
data := resp["data"].(map[string]interface{})
|
||||
return data, nil
|
||||
}
|
||||
|
||||
// getAccessToken 获取 Access Token(内部包含缓存判断、刷新、保存)- 匹配 PHP 版本
|
||||
func (x *XunleiPanService) getAccessToken() (string, error) {
|
||||
if x.cksRepo == nil {
|
||||
@@ -148,43 +177,21 @@ func (x *XunleiPanService) getAccessToken() (string, error) {
|
||||
return "", fmt.Errorf("未配置迅雷 refresh_token,请检查 cks 表的 ck 字段")
|
||||
}
|
||||
|
||||
// 构造请求体
|
||||
body := map[string]interface{}{
|
||||
"client_id": x.clientId,
|
||||
"grant_type": "refresh_token",
|
||||
"refresh_token": refreshTokenValue,
|
||||
}
|
||||
|
||||
// 过滤 headers(移除 Authorization 和 x-captcha-token)
|
||||
filteredHeaders := make(map[string]string)
|
||||
for k, v := range x.headers {
|
||||
if k != "Authorization" && k != "x-captcha-token" {
|
||||
filteredHeaders[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
// 调用 API 获取新的 token
|
||||
resp, err := x.requestXunleiApi("https://xluser-ssl.xunlei.com/v1/auth/token", "POST", body, nil, filteredHeaders)
|
||||
newData, err := x.getAccessTokenByRefreshToken(refreshTokenValue)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("获取 access_token 请求失败: %v", err)
|
||||
return "", fmt.Errorf("获取 access_token 失败: %v", err)
|
||||
}
|
||||
|
||||
if resp["code"] != 0 || resp["data"] == nil {
|
||||
return "", fmt.Errorf("获取 access_token 失败: %v", resp)
|
||||
}
|
||||
|
||||
data := resp["data"].(map[string]interface{})
|
||||
newAccessToken := data["access_token"].(string)
|
||||
newAccessToken := newData["access_token"].(string)
|
||||
|
||||
// 计算过期时间(当前时间 + expires_in - 60 秒缓冲)
|
||||
expiresAt := currentTime + int64(data["expires_in"].(float64)) - 60
|
||||
expiresAt := currentTime + int64(newData["expires_in"].(float64)) - 60
|
||||
|
||||
// 更新 extra 数据
|
||||
if extraData.AccessToken == nil {
|
||||
extraData.AccessToken = &AccessTokenData{}
|
||||
}
|
||||
extraData.AccessToken.AccessToken = newAccessToken
|
||||
extraData.AccessToken.RefreshToken = data["refresh_token"].(string)
|
||||
extraData.AccessToken.RefreshToken = newData["refresh_token"].(string)
|
||||
extraData.AccessToken.ExpiresAt = expiresAt
|
||||
|
||||
// 保存到数据库
|
||||
|
||||
79
web/composables/useCookie.ts
Normal file
79
web/composables/useCookie.ts
Normal file
@@ -0,0 +1,79 @@
|
||||
export const useCookie = () => {
|
||||
const get = (name: string): string | null => {
|
||||
if (process.server) {
|
||||
// 服务端处理
|
||||
const { req } = useRequestEvent()
|
||||
const cookies = parse(req.headers.cookie || '')
|
||||
return cookies[name] || null
|
||||
} else {
|
||||
// 客户端处理
|
||||
const cookieString = document.cookie
|
||||
const cookies = cookieString.split('; ')
|
||||
for (const cookie of cookies) {
|
||||
const [cookieName, cookieValue] = cookie.split('=')
|
||||
if (cookieName === name) {
|
||||
return decodeURIComponent(cookieValue)
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
const set = (name: string, value: string, options?: any) => {
|
||||
if (process.server) {
|
||||
// 服务端设置 cookie
|
||||
const { res } = useRequestEvent()
|
||||
res.setHeader('Set-Cookie', `${name}=${encodeURIComponent(value)}; ${serializeOptions(options)}`)
|
||||
} else {
|
||||
// 客户端设置 cookie
|
||||
document.cookie = `${name}=${encodeURIComponent(value)}; ${serializeOptions(options)}`
|
||||
}
|
||||
}
|
||||
|
||||
const remove = (name: string) => {
|
||||
set(name, '', { maxAge: -1 })
|
||||
}
|
||||
|
||||
// 序列化 cookie 选项
|
||||
const serializeOptions = (options: any = {}): string => {
|
||||
const {
|
||||
maxAge,
|
||||
expires,
|
||||
path = '/',
|
||||
domain,
|
||||
secure,
|
||||
httpOnly,
|
||||
sameSite
|
||||
} = options
|
||||
|
||||
let result = ''
|
||||
|
||||
if (maxAge !== undefined) result += `Max-Age=${maxAge}; `
|
||||
if (expires instanceof Date) result += `Expires=${expires.toUTCString()}; `
|
||||
if (path) result += `Path=${path}; `
|
||||
if (domain) result += `Domain=${domain}; `
|
||||
if (secure) result += 'Secure; '
|
||||
if (httpOnly) result += 'HttpOnly; '
|
||||
if (sameSite) result += `SameSite=${sameSite}; `
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// 解析 cookie 字符串
|
||||
const parse = (cookieString: string): Record<string, string> => {
|
||||
const cookies: Record<string, string> = {}
|
||||
cookieString.split(';').forEach(cookie => {
|
||||
const [name, value] = cookie.split('=')
|
||||
if (name && value) {
|
||||
cookies[name.trim()] = decodeURIComponent(value.trim())
|
||||
}
|
||||
})
|
||||
return cookies
|
||||
}
|
||||
|
||||
return {
|
||||
get,
|
||||
set,
|
||||
remove
|
||||
}
|
||||
}
|
||||
@@ -191,7 +191,7 @@
|
||||
平台类型 <span class="text-red-500">*</span>
|
||||
</label>
|
||||
<n-select v-model:value="form.pan_id" placeholder="请选择平台"
|
||||
:options="platforms.filter(pan => pan.name === 'quark' || pan.name === 'xunlei').map(pan => ({ label: pan.remark, value: pan.id }))"
|
||||
:options="platforms.filter(pan => panEnables.includes(pan.name)).map(pan => ({ label: pan.remark, value: pan.id }))"
|
||||
:disabled="showEditModal" required />
|
||||
<p v-if="showEditModal" class="mt-1 text-xs text-gray-500">编辑时不允许修改平台类型</p>
|
||||
</div>
|
||||
@@ -210,14 +210,7 @@
|
||||
|
||||
<div v-if="isXunlei">
|
||||
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
|
||||
Authorization <span class="text-red-500">*</span>
|
||||
</label>
|
||||
<n-input v-model:value="form.ck" type="textarea" placeholder="请输入Authorization内容,带 Berear" :rows="4" required />
|
||||
</div>
|
||||
|
||||
<div v-if="isXunlei">
|
||||
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
|
||||
Token <span class="text-red-500">*</span>
|
||||
refresh_token <span class="text-red-500">*</span>
|
||||
</label>
|
||||
<n-input v-model:value="form.ck" type="textarea" placeholder="请输入" :rows="4" required />
|
||||
</div>
|
||||
@@ -272,6 +265,14 @@ const form = ref({
|
||||
remark: ''
|
||||
})
|
||||
|
||||
const cookie = useCookie()
|
||||
const panEnables = ref(['quark'])
|
||||
const xunleiEnable = cookie.get('xunleiEnable')
|
||||
console.log(xunleiEnable)
|
||||
if (xunleiEnable && xunleiEnable === 'true') {
|
||||
panEnables.value.push('xunlei')
|
||||
}
|
||||
|
||||
watch(() => form.value.pan_id, (newVal) => {
|
||||
isQuark.value = false
|
||||
isXunlei.value = false
|
||||
|
||||
Reference in New Issue
Block a user