修复cf pages无法使用

This commit is contained in:
iLay 2024-12-08 08:16:12 +08:00
parent 815a2542ef
commit 2b83ecd8dd
8 changed files with 134 additions and 126 deletions

17
package-lock.json generated
View File

@ -11,13 +11,12 @@
"@nuxtjs/tailwindcss": "^6.12.2", "@nuxtjs/tailwindcss": "^6.12.2",
"ant-design-vue": "^4.2.6", "ant-design-vue": "^4.2.6",
"clipboard": "^2.0.11", "clipboard": "^2.0.11",
"crypto-js": "^4.2.0", "crypto-es": "^2.1.0",
"nuxt": "^3.14.1592", "nuxt": "^3.14.1592",
"vue": "latest", "vue": "latest",
"vue-router": "latest" "vue-router": "latest"
}, },
"devDependencies": { "devDependencies": {
"@types/crypto-js": "^4.2.2",
"nitropack": "^2.10.4", "nitropack": "^2.10.4",
"sass-embedded": "^1.82.0" "sass-embedded": "^1.82.0"
} }
@ -2514,12 +2513,6 @@
"node": ">=10.13.0" "node": ">=10.13.0"
} }
}, },
"node_modules/@types/crypto-js": {
"version": "4.2.2",
"resolved": "https://registry.npmjs.org/@types/crypto-js/-/crypto-js-4.2.2.tgz",
"integrity": "sha512-sDOLlVbHhXpAUAL0YHDUUwDZf3iN4Bwi4W6a0W0b+QcAezUbRtH4FVb+9J4h+XFPW7l/gQ9F8qC7P+Ec4k8QVQ==",
"dev": true
},
"node_modules/@types/estree": { "node_modules/@types/estree": {
"version": "1.0.6", "version": "1.0.6",
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz",
@ -3900,10 +3893,10 @@
"uncrypto": "^0.1.3" "uncrypto": "^0.1.3"
} }
}, },
"node_modules/crypto-js": { "node_modules/crypto-es": {
"version": "4.2.0", "version": "2.1.0",
"resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.2.0.tgz", "resolved": "https://registry.npmjs.org/crypto-es/-/crypto-es-2.1.0.tgz",
"integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==" "integrity": "sha512-C5Dbuv4QTPGuloy5c5Vv/FZHtmK+lobLAypFfuRaBbwCsk3qbCWWESCH3MUcBsrgXloRNMrzwUAiPg4U6+IaKA=="
}, },
"node_modules/css-declaration-sorter": { "node_modules/css-declaration-sorter": {
"version": "7.2.0", "version": "7.2.0",

View File

@ -7,20 +7,20 @@
"dev": "nuxt dev", "dev": "nuxt dev",
"start": "nuxt start", "start": "nuxt start",
"generate": "nuxt generate", "generate": "nuxt generate",
"postinstall": "nuxt prepare" "postinstall": "nuxt prepare",
"cf-dev": "npx nuxi build --preset=cloudflare_pages && npx wrangler pages dev dist/"
}, },
"dependencies": { "dependencies": {
"@ant-design-vue/nuxt": "^1.4.6", "@ant-design-vue/nuxt": "^1.4.6",
"@nuxtjs/tailwindcss": "^6.12.2", "@nuxtjs/tailwindcss": "^6.12.2",
"ant-design-vue": "^4.2.6", "ant-design-vue": "^4.2.6",
"clipboard": "^2.0.11", "clipboard": "^2.0.11",
"crypto-js": "^4.2.0", "crypto-es": "^2.1.0",
"nuxt": "^3.14.1592", "nuxt": "^3.14.1592",
"vue": "latest", "vue": "latest",
"vue-router": "latest" "vue-router": "latest"
}, },
"devDependencies": { "devDependencies": {
"@types/crypto-js": "^4.2.2",
"nitropack": "^2.10.4", "nitropack": "^2.10.4",
"sass-embedded": "^1.82.0" "sass-embedded": "^1.82.0"
} }

View File

@ -4,13 +4,11 @@
<div class="mx-auto w-full max-w-3xl bg-white shadow-lg rounded-lg p-8"> <div class="mx-auto w-full max-w-3xl bg-white shadow-lg rounded-lg p-8">
<div class="flex justify-between items-center mb-6"> <div class="flex justify-between items-center mb-6">
<h1 class="text-2xl font-bold text-gray-800">阿里云盘连接</h1> <h1 class="text-2xl font-bold text-gray-800">阿里云盘连接</h1>
<a href="https://ghcr.io/ilay1678/alipan-tv-token" <a href="https://ghcr.io/ilay1678/alipan-tv-token" target="_blank" rel="noopener noreferrer"
target="_blank" class="text-gray-600 hover:text-blue-500 transition-colors" title="Docker Image">
rel="noopener noreferrer"
class="text-gray-600 hover:text-blue-500 transition-colors"
title="Docker Image">
<svg class="h-6 w-6" viewBox="0 0 24 24" fill="currentColor"> <svg class="h-6 w-6" viewBox="0 0 24 24" fill="currentColor">
<path d="M13.983 11.078h2.119a.186.186 0 00.186-.185V9.006a.186.186 0 00-.186-.186h-2.119a.185.185 0 00-.185.185v1.888c0 .102.083.185.185.185m-2.954-5.43h2.118a.186.186 0 00.186-.186V3.574a.186.186 0 00-.186-.185h-2.118a.185.185 0 00-.185.185v1.888c0 .102.082.185.185.185m0 2.716h2.118a.187.187 0 00.186-.186V6.29a.186.186 0 00-.186-.185h-2.118a.185.185 0 00-.185.185v1.887c0 .102.082.185.185.186m-2.93 0h2.12a.186.186 0 00.184-.186V6.29a.185.185 0 00-.185-.185H8.1a.185.185 0 00-.185.185v1.887c0 .102.083.185.185.186m-2.964 0h2.119a.186.186 0 00.185-.186V6.29a.185.185 0 00-.185-.185H5.136a.186.186 0 00-.186.185v1.887c0 .102.084.185.186.186m5.893 2.715h2.118a.186.186 0 00.186-.185V9.006a.186.186 0 00-.186-.186h-2.118a.185.185 0 00-.185.185v1.888c0 .102.082.185.185.185m-2.93 0h2.12a.185.185 0 00.184-.185V9.006a.185.185 0 00-.184-.186h-2.12a.185.185 0 00-.184.185v1.888c0 .102.083.185.185.185m-2.964 0h2.119a.185.185 0 00.185-.185V9.006a.185.185 0 00-.184-.186h-2.12a.186.186 0 00-.186.186v1.887c0 .102.084.185.186.185m-2.92 0h2.12a.185.185 0 00.184-.185V9.006a.185.185 0 00-.184-.186h-2.12a.185.185 0 00-.184.185v1.888c0 .102.082.185.185.185M23.763 9.89c-.065-.051-.672-.51-1.954-.51-.338.001-.676.03-1.01.087-.248-1.7-1.653-2.53-1.716-2.566l-.344-.199-.226.327c-.284.438-.49.922-.612 1.43-.23.97-.09 1.882.403 2.661-.595.332-1.55.413-1.744.42H.751a.751.751 0 00-.75.748 11.376 11.376 0 00.692 4.062c.545 1.428 1.355 2.48 2.41 3.124 1.18.723 3.1 1.137 5.275 1.137.983.003 1.963-.086 2.93-.266a12.248 12.248 0 003.823-1.389c.98-.567 1.86-1.288 2.61-2.136 1.252-1.418 1.998-2.997 2.553-4.4h.221c1.372 0 2.215-.549 2.68-1.009.309-.293.55-.65.707-1.046l.098-.288z"/> <path
d="M13.983 11.078h2.119a.186.186 0 00.186-.185V9.006a.186.186 0 00-.186-.186h-2.119a.185.185 0 00-.185.185v1.888c0 .102.083.185.185.185m-2.954-5.43h2.118a.186.186 0 00.186-.186V3.574a.186.186 0 00-.186-.185h-2.118a.185.185 0 00-.185.185v1.888c0 .102.082.185.185.185m0 2.716h2.118a.187.187 0 00.186-.186V6.29a.186.186 0 00-.186-.185h-2.118a.185.185 0 00-.185.185v1.887c0 .102.082.185.185.186m-2.93 0h2.12a.186.186 0 00.184-.186V6.29a.185.185 0 00-.185-.185H8.1a.185.185 0 00-.185.185v1.887c0 .102.083.185.185.186m-2.964 0h2.119a.186.186 0 00.185-.186V6.29a.185.185 0 00-.185-.185H5.136a.186.186 0 00-.186.185v1.887c0 .102.084.185.186.186m5.893 2.715h2.118a.186.186 0 00.186-.185V9.006a.186.186 0 00-.186-.186h-2.118a.185.185 0 00-.185.185v1.888c0 .102.082.185.185.185m-2.93 0h2.12a.185.185 0 00.184-.185V9.006a.185.185 0 00-.184-.186h-2.12a.185.185 0 00-.184.185v1.888c0 .102.083.185.185.185m-2.964 0h2.119a.185.185 0 00.185-.185V9.006a.185.185 0 00-.184-.186h-2.12a.186.186 0 00-.186.186v1.887c0 .102.084.185.186.185m-2.92 0h2.12a.185.185 0 00.184-.185V9.006a.185.185 0 00-.184-.186h-2.12a.185.185 0 00-.184.185v1.888c0 .102.082.185.185.185M23.763 9.89c-.065-.051-.672-.51-1.954-.51-.338.001-.676.03-1.01.087-.248-1.7-1.653-2.53-1.716-2.566l-.344-.199-.226.327c-.284.438-.49.922-.612 1.43-.23.97-.09 1.882.403 2.661-.595.332-1.55.413-1.744.42H.751a.751.751 0 00-.75.748 11.376 11.376 0 00.692 4.062c.545 1.428 1.355 2.48 2.41 3.124 1.18.723 3.1 1.137 5.275 1.137.983.003 1.963-.086 2.93-.266a12.248 12.248 0 003.823-1.389c.98-.567 1.86-1.288 2.61-2.136 1.252-1.418 1.998-2.997 2.553-4.4h.221c1.372 0 2.215-.549 2.68-1.009.309-.293.55-.65.707-1.046l.098-.288z" />
</svg> </svg>
</a> </a>
</div> </div>
@ -18,25 +16,17 @@
<div class="space-y-8"> <div class="space-y-8">
<div class="space-y-2"> <div class="space-y-2">
<div class="relative"> <div class="relative">
<textarea <textarea id="accessToken" v-model="accessToken"
id="accessToken"
v-model="accessToken"
class="w-full rounded font-mono text-sm leading-normal border-2 border-dashed border-gray-300 p-3 pr-10 bg-white resize-none focus:outline-none focus:border-blue-500 transition-colors min-h-[120px] whitespace-pre-wrap overflow-auto placeholder:text-gray-400" class="w-full rounded font-mono text-sm leading-normal border-2 border-dashed border-gray-300 p-3 pr-10 bg-white resize-none focus:outline-none focus:border-blue-500 transition-colors min-h-[120px] whitespace-pre-wrap overflow-auto placeholder:text-gray-400"
readonly readonly spellcheck="false" placeholder="访问令牌" />
spellcheck="false" <button data-clipboard-target="#accessToken" :class="`absolute top-2 right-2 p-1 rounded transition-colors ${hasAccessToken
placeholder="访问令牌" ? 'hover:bg-gray-100 text-gray-500 hover:text-blue-500'
/> : 'text-gray-300 cursor-not-allowed'
<button }`" :disabled="!hasAccessToken">
data-clipboard-target="#accessToken" <svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24"
:class="`absolute top-2 right-2 p-1 rounded transition-colors ${ stroke="currentColor">
hasAccessToken <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
? 'hover:bg-gray-100 text-gray-500 hover:text-blue-500' d="M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z" />
: 'text-gray-300 cursor-not-allowed'
}`"
:disabled="!hasAccessToken"
>
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z" />
</svg> </svg>
</button> </button>
</div> </div>
@ -44,25 +34,17 @@
<div class="space-y-2"> <div class="space-y-2">
<div class="relative"> <div class="relative">
<textarea <textarea id="refreshToken" v-model="refreshToken"
id="refreshToken"
v-model="refreshToken"
class="w-full rounded font-mono text-sm leading-normal border-2 border-dashed border-gray-300 p-3 pr-10 bg-white resize-none focus:outline-none focus:border-blue-500 transition-colors min-h-[120px] whitespace-pre-wrap overflow-auto placeholder:text-gray-400" class="w-full rounded font-mono text-sm leading-normal border-2 border-dashed border-gray-300 p-3 pr-10 bg-white resize-none focus:outline-none focus:border-blue-500 transition-colors min-h-[120px] whitespace-pre-wrap overflow-auto placeholder:text-gray-400"
readonly readonly spellcheck="false" placeholder="刷新令牌" />
spellcheck="false" <button data-clipboard-target="#refreshToken" :class="`absolute top-2 right-2 p-1 rounded transition-colors ${hasRefreshToken
placeholder="刷新令牌" ? 'hover:bg-gray-100 text-gray-500 hover:text-blue-500'
/> : 'text-gray-300 cursor-not-allowed'
<button }`" :disabled="!hasRefreshToken">
data-clipboard-target="#refreshToken" <svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24"
:class="`absolute top-2 right-2 p-1 rounded transition-colors ${ stroke="currentColor">
hasRefreshToken <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
? 'hover:bg-gray-100 text-gray-500 hover:text-blue-500' d="M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z" />
: 'text-gray-300 cursor-not-allowed'
}`"
:disabled="!hasRefreshToken"
>
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z" />
</svg> </svg>
</button> </button>
</div> </div>
@ -73,18 +55,15 @@
<div class="animate-spin rounded-full h-8 w-8 border-b-2 border-blue-500"></div> <div class="animate-spin rounded-full h-8 w-8 border-b-2 border-blue-500"></div>
<span class="ml-3 text-gray-600">正在获取授权链接...</span> <span class="ml-3 text-gray-600">正在获取授权链接...</span>
</div> </div>
<button <button v-else @click="handleAuth(authUrl)" :disabled="authorizing" :class="`block w-full bg-blue-500 text-white text-center py-3 px-4 rounded transition-colors relative ${authorizing ? 'bg-blue-400 cursor-not-allowed' : 'hover:bg-blue-600'
v-else }`">
@click="handleAuth(authUrl)"
:disabled="authorizing"
:class="`block w-full bg-blue-500 text-white text-center py-3 px-4 rounded transition-colors relative ${
authorizing ? 'bg-blue-400 cursor-not-allowed' : 'hover:bg-blue-600'
}`"
>
<div class="flex items-center justify-center"> <div class="flex items-center justify-center">
<div v-if="authorizing" class="animate-spin rounded-full h-5 w-5 border-2 border-white border-t-transparent mr-2"></div> <div v-if="authorizing"
<svg v-else xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 mr-2" fill="none" viewBox="0 0 24 24" stroke="currentColor"> class="animate-spin rounded-full h-5 w-5 border-2 border-white border-t-transparent mr-2"></div>
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M11 16l-4-4m0 0l4-4m-4 4h14m-5 4v1a3 3 0 01-3 3H6a3 3 0 01-3-3V7a3 3 0 013-3h7a3 3 0 013 3v1" /> <svg v-else xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 mr-2" fill="none" viewBox="0 0 24 24"
stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M11 16l-4-4m0 0l4-4m-4 4h14m-5 4v1a3 3 0 01-3 3H6a3 3 0 01-3-3V7a3 3 0 013-3h7a3 3 0 013 3v1" />
</svg> </svg>
{{ authorizing ? '授权中...' : '授权登录' }} {{ authorizing ? '授权中...' : '授权登录' }}
</div> </div>
@ -94,14 +73,8 @@
</div> </div>
</main> </main>
<a-modal <a-modal v-model:open="isNoticeOpen" title="使用说明" @ok="closeNotice" :maskClosable="false" :closable="false"
v-model:open="isNoticeOpen" :keyboard="false">
title="使用说明"
@ok="closeNotice"
:maskClosable="false"
:closable="false"
:keyboard="false"
>
<p>本工具能帮助你一键获取阿里云盘TV版的刷新令牌完全免费TV接口能绕过三方应用权益包的速率限制但前提你得是SVIP</p> <p>本工具能帮助你一键获取阿里云盘TV版的刷新令牌完全免费TV接口能绕过三方应用权益包的速率限制但前提你得是SVIP</p>
<template #footer> <template #footer>
<a-button type="primary" @click="closeNotice">我知道了</a-button> <a-button type="primary" @click="closeNotice">我知道了</a-button>
@ -110,7 +83,7 @@
</template> </template>
<script setup> <script setup>
import { ref, onMounted, watch } from 'vue' import { ref, onMounted } from 'vue'
import { message } from 'ant-design-vue' import { message } from 'ant-design-vue'
import ClipboardJS from 'clipboard' import ClipboardJS from 'clipboard'
@ -127,8 +100,7 @@ const refreshToken = ref('')
async function generateAuthUrl() { async function generateAuthUrl() {
try { try {
isLoading.value = true isLoading.value = true
const response = await fetch("/generate_qr", { method: "POST" }) const data = await $fetch("/generate_qr", { method: "POST" })
const data = await response.json()
authUrl.value = `https://www.alipan.com/o/oauth/authorize?sid=${data.sid}` authUrl.value = `https://www.alipan.com/o/oauth/authorize?sid=${data.sid}`
checkStatus(data.sid) checkStatus(data.sid)
} finally { } finally {
@ -142,8 +114,7 @@ function closeNotice() {
async function checkStatus(sid) { async function checkStatus(sid) {
try { try {
const response = await fetch("/check_status/" + sid) const data = await $fetch("/check_status/" + sid)
const data = await response.json()
if (data.status === "LoginSuccess") { if (data.status === "LoginSuccess") {
accessToken.value = data.access_token accessToken.value = data.access_token
refreshToken.value = data.refresh_token refreshToken.value = data.refresh_token
@ -191,6 +162,7 @@ const handleAuth = (url) => {
} }
onMounted(() => { onMounted(() => {
//
isNoticeOpen.value = true isNoticeOpen.value = true
if (!hasGenerated.value) { if (!hasGenerated.value) {
generateAuthUrl() generateAuthUrl()
@ -200,6 +172,4 @@ onMounted(() => {
</script> </script>
<style scoped> <style scoped></style>
</style>

View File

@ -1,32 +1,32 @@
import { decrypt, getParams } from '@/utils/decode'; import { decrypt, getParams } from '@/utils/decode';
import { defineEventHandler, getRouterParams } from 'h3' import { defineEventHandler, getRouterParams } from 'h3'
import type { QrCodeStatus, TokenResponseEncrypt, TokenRequest } from '~/types/api'
export const runtime = 'edge' export const runtime = 'edge'
export default defineEventHandler(async (event) => { export default defineEventHandler(async (event) => {
try { try {
const { sid } = getRouterParams(event); const { sid } = getRouterParams(event);
const response = await fetch(`https://openapi.alipan.com/oauth/qrcode/${sid}/status`); const statusData = await $fetch<QrCodeStatus>(`https://openapi.alipan.com/oauth/qrcode/${sid}/status`);
const statusData:any = await response.json(); if (statusData.status === 'LoginSuccess' && statusData.authCode) {
if (statusData.status === 'LoginSuccess') {
try { try {
const authCode = statusData.authCode;
const t = Math.floor(Date.now() / 1000); const t = Math.floor(Date.now() / 1000);
const sendData = { ...getParams(t), code: authCode, "Content-Type": "application/json"}; const sendData = {
...getParams(t),
code: statusData.authCode,
"Content-Type": "application/json"
} as TokenRequest;
const headers = Object.fromEntries( const headers = Object.fromEntries(
Object.entries(sendData).map(([k, v]) => [k, String(v)]) Object.entries(sendData).map(([k, v]) => [k, String(v)])
); );
const tokenResponse = await fetch('http://api.extscreen.com/aliyundrive/v3/token', { const tokenResponse = await $fetch<TokenResponseEncrypt>('http://api.extscreen.com/aliyundrive/v3/token', {
method: 'POST', method: 'POST',
headers: headers, headers: headers,
body: JSON.stringify(sendData) body: JSON.stringify(sendData)
}); });
const plainData = decrypt(tokenResponse.data.ciphertext, tokenResponse.data.iv, t);
const tokenData:any = await tokenResponse.json();
const jsonp = tokenData.data;
const plainData = decrypt(jsonp.ciphertext, jsonp.iv, t);
const tokenInfo = JSON.parse(plainData); const tokenInfo = JSON.parse(plainData);
return { return {
@ -34,13 +34,14 @@ export default defineEventHandler(async (event) => {
refresh_token: tokenInfo.refresh_token, refresh_token: tokenInfo.refresh_token,
access_token: tokenInfo.access_token access_token: tokenInfo.access_token
}; };
} catch (error) { } catch (error) {
return { status: 'LoginFailed' }; return { status: 'LoginFailed' } as QrCodeStatus;
} }
} else {
return { status: statusData.status };
} }
} catch (error:any) {
return statusData;
} catch (error: any) {
throw createError({ throw createError({
statusCode: 500, statusCode: 500,
message: error.message message: error.message

View File

@ -1,8 +1,8 @@
import { defineEventHandler } from 'h3' import { defineEventHandler } from 'h3'
import type { ApiResponse, QrCodeData } from '~/types/api'
export default defineEventHandler(async (event) => { export default defineEventHandler(async (event) => {
try { try {
const response = await fetch('http://api.extscreen.com/aliyundrive/qrcode', { const response = await $fetch<ApiResponse<QrCodeData>>('http://api.extscreen.com/aliyundrive/qrcode', {
method: 'POST', method: 'POST',
headers: { 'Content-Type': 'application/json' }, headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ body: JSON.stringify({
@ -11,10 +11,9 @@ export default defineEventHandler(async (event) => {
height: 500, height: 500,
}) })
}) })
const data:any = await response.json()
return { return {
qr_link: data.data.qrCodeUrl, qr_link: response.data.qrCodeUrl,
sid: data.data.sid sid: response.data.sid
} }
} catch (error: any) { } catch (error: any) {
throw createError({ throw createError({

View File

@ -1,5 +1,6 @@
import { decrypt, getParams } from "@/utils/decode"; import { decrypt, getParams } from "@/utils/decode";
import { defineEventHandler } from 'h3' import { defineEventHandler } from 'h3'
import type { ApiResponse, TokenResponseEncrypt } from '~/types/api'
export const runtime = 'edge' export const runtime = 'edge'
@ -17,15 +18,12 @@ export default defineEventHandler(async (event) => {
Object.entries(sendData).map(([k, v]) => [k, String(v)]) Object.entries(sendData).map(([k, v]) => [k, String(v)])
); );
const response = await fetch('http://api.extscreen.com/aliyundrive/v3/token', { const tokenData = await $fetch<TokenResponseEncrypt>('http://api.extscreen.com/aliyundrive/v3/token', {
method: 'POST', method: 'POST',
headers: headers, headers: headers,
body: JSON.stringify(sendData) body: JSON.stringify(sendData)
}); });
const plainData = decrypt(tokenData.data.ciphertext, tokenData.data.iv, t);
const tokenData :any = await response.json();
const jsonp = tokenData.data;
const plainData = decrypt(jsonp.ciphertext, jsonp.iv, t);
const tokenInfo = JSON.parse(plainData); const tokenInfo = JSON.parse(plainData);
return { return {
@ -34,10 +32,14 @@ export default defineEventHandler(async (event) => {
refresh_token: tokenInfo.refresh_token, refresh_token: tokenInfo.refresh_token,
expires_in: tokenInfo.expires_in expires_in: tokenInfo.expires_in
}; };
// 直接返回响应数据
return tokenData;
} catch (error:any) { } catch (error:any) {
return { return {
error: error.message, code: 500,
statusCode: 500 message: error.message,
} data: null
} as ApiResponse
} }
}) })

45
types/api.ts Normal file
View File

@ -0,0 +1,45 @@
export interface ApiResponse<T = any> {
code: number;
message: string;
data: T;
}
export interface QrCodeData {
qrCodeUrl: string;
sid: string;
}
export interface TokenInfo {
token_type: string;
access_token: string;
refresh_token: string;
expires_in: number;
}
export interface TokenResponseEncrypt extends ApiResponse<{
ciphertext: string;
iv: string;
}> {}
export interface QrCodeStatus {
status: 'WaitLogin' | 'LoginSuccess' | 'LoginFailed';
authCode?: string;
}
export interface DeviceInfo {
akv: string;
apv: string;
b: string;
d: string;
m: string;
mac: string;
n: string;
t: number;
wifiMac: string;
}
export interface TokenRequest extends DeviceInfo {
refresh_token?: string;
code?: string;
'Content-Type': string;
}

View File

@ -1,18 +1,16 @@
import pkg from 'crypto-js'; import CryptoES from 'crypto-es';
const { AES, enc, mode, pad, MD5 } = pkg;
const decrypt = function (ciphertext: string, iv: string, t: number): string { const decrypt = function (ciphertext: string, iv: string, t: number): string {
try { try {
const key = generateKey(t); const key = generateKey(t);
const decrypted = AES.decrypt(ciphertext, enc.Utf8.parse(key), { const decrypted = CryptoES.AES.decrypt(ciphertext, CryptoES.enc.Utf8.parse(key), {
iv: enc.Hex.parse(iv), iv: CryptoES.enc.Hex.parse(iv),
mode:mode.CBC, mode: CryptoES.mode.CBC,
padding: pad.Pkcs7 padding: CryptoES.pad.Pkcs7
}); });
const dec = enc.Utf8.stringify(decrypted).toString(); const dec = CryptoES.enc.Utf8.stringify(decrypted).toString();
return dec; return dec;
} catch (error) { } catch (error) {
console.error("Decryption failed", error);
console.error("Decryption failed", error); console.error("Decryption failed", error);
throw error; throw error;
} }
@ -60,7 +58,7 @@ const generateKey = function (t: number): string {
const keyArray = concatenatedParams.split(""); const keyArray = concatenatedParams.split("");
const hashedKey = h(keyArray, t); const hashedKey = h(keyArray, t);
return MD5(hashedKey).toString(enc.Hex); return CryptoES.MD5(hashedKey).toString(CryptoES.enc.Hex);
}; };
export { decrypt, getParams }; export { decrypt, getParams };