diff --git a/public/index.html b/public/index.html
index b6eebb6..c565ee9 100644
--- a/public/index.html
+++ b/public/index.html
@@ -10,6 +10,7 @@
+
×
-
阿里云盘扫码登录v2
+
扫码登录
@@ -147,10 +146,12 @@
隐私政策和使用条款
+
+
@@ -169,13 +170,9 @@
隐私政策和使用条款
const shared_uid_views = document.getElementById('sharepoint-uid-view');
const url_hash = window.location.hash.substring(1); // 去掉地址栏上的#号
const current_host = window.location.protocol + "//" + window.location.host;
- if (window.location.host === "api.oplist.org.cn")
+ if(window.location.host==="api.oplist.org.cn")
document.getElementById('eo-show').hidden = false;
getToken();
- // 阿里云盘扫码v2相关变量
- let alicloud2SessionId = null;
- let alicloud2CheckInterval = null;
- let alicloud2StartTime = null;
let clientFingerprint = null;
// 点击模态框外部关闭
window.onclick = function (event) {
diff --git a/public/static/alitv.js b/public/static/alitv.js
new file mode 100644
index 0000000..63ec90c
--- /dev/null
+++ b/public/static/alitv.js
@@ -0,0 +1,172 @@
+// 阿里云盘TV版扫码登录
+class AliTVQRLogin {
+ constructor() {
+ // 创建QR登录管理器实例
+ this.qrManager = new QRLoginManager({
+ onSuccess: this.handleLoginSuccess.bind(this),
+ maxCheckTime: 30000 // 30秒过期时间
+ });
+ this.sidValue = null; // 存储二维码的sid
+ this.checkAttempts = 0; // 检查尝试次数
+ this.maxCheckAttempts = 12; // 最大检查次数(30秒内,每2.5秒检查一次)
+ }
+
+ // 启动阿里云盘TV版扫码登录
+ async startLogin() {
+ try {
+ // 显示模态框
+ this.qrManager.showModal('阿里云盘TV版扫码登录');
+
+ // 构建请求参数
+ const client_uid = document.getElementById("client-uid-input").value;
+ const client_key = document.getElementById("client-key-input").value;
+ const server_use = document.getElementById("server-use-input").checked;
+
+ // 获取二维码
+ const requestUrl = `/alitv/qrcode?client_uid=${encodeURIComponent(client_uid)}&client_key=${encodeURIComponent(client_key)}&server_use=${server_use}`;
+ const response = await fetch(requestUrl);
+ const result = await response.json();
+
+ if (result.text && result.sid) {
+ this.qrManager.sessionId = this.qrManager.generateSessionId();
+ this.qrManager.provider = 'alitv';
+ this.sidValue = result.sid;
+ this.checkAttempts = 0;
+
+ // 显示二维码
+ this.qrManager.showQRCode(result.text);
+ this.qrManager.setStatus('请使用阿里云盘App扫描二维码登录TV版', 'waiting');
+
+ // 开始检查状态
+ this.qrManager.startStatusCheck(this.checkStatus.bind(this));
+ } else {
+ this.qrManager.setStatus(result.text || '生成二维码失败', 'error');
+ document.getElementById(this.qrManager.refreshBtnId).style.display = 'inline-block';
+ }
+ } catch (error) {
+ console.error('获取TV版二维码失败:', error);
+ this.qrManager.setStatus('网络错误,请重试', 'error');
+ document.getElementById(this.qrManager.refreshBtnId).style.display = 'inline-block';
+ }
+ }
+
+ // 检查登录状态
+ async checkStatus(elapsed) {
+ if (!this.sidValue) return;
+
+ // 检查尝试次数是否达到上限
+ if (this.checkAttempts >= this.maxCheckAttempts) {
+ this.qrManager.setStatus('二维码已过期,请点击刷新重新生成', 'error');
+ this.qrManager.stopStatusCheck();
+ document.getElementById(this.qrManager.refreshBtnId).style.display = 'inline-block';
+ return;
+ }
+
+ this.checkAttempts++;
+
+ try {
+ const waitTime = Math.floor(elapsed / 1000);
+ this.qrManager.setStatus(`等待扫描... (${waitTime}s) 请使用阿里云盘App扫码登录TV版`, 'waiting');
+
+ // 构建请求参数
+ const client_uid = document.getElementById("client-uid-input").value;
+ const client_key = document.getElementById("client-key-input").value;
+ const server_use = document.getElementById("server-use-input").checked;
+
+ // 检查二维码状态
+ const requestUrl = `/alitv/check?sid=${encodeURIComponent(this.sidValue)}&client_uid=${encodeURIComponent(client_uid)}&client_key=${encodeURIComponent(client_key)}&server_use=${server_use}`;
+ const response = await fetch(requestUrl);
+
+ // 如果状态码是202,表示还在等待扫码
+ if (response.status === 202) {
+ return; // 继续等待
+ }
+
+ // 如果状态码是200,尝试解析结果
+ if (response.ok) {
+ const result = await response.json();
+
+ if (result.auth_code) {
+ // 获取到了授权码,表示登录成功
+ this.qrManager.setStatus('登录成功!正在获取Token...', 'success');
+ this.qrManager.stopStatusCheck();
+
+ // 获取token
+ await this.getToken(result.auth_code);
+ }
+ }
+ } catch (error) {
+ // 错误处理,但轮询期间的一些网络错误可能是正常的,不需要中断轮询
+ console.error('检查TV版登录状态失败:', error);
+ // 不显示错误,继续轮询
+ }
+ }
+
+ // 获取Token
+ async getToken(authCode) {
+ try {
+ // 构建请求参数
+ const client_uid = document.getElementById("client-uid-input").value;
+ const client_key = document.getElementById("client-key-input").value;
+ const server_use = document.getElementById("server-use-input").checked;
+
+ const requestUrl = `/alitv/token?auth_code=${encodeURIComponent(authCode)}&client_uid=${encodeURIComponent(client_uid)}&client_key=${encodeURIComponent(client_key)}&server_use=${server_use}`;
+ const response = await fetch(requestUrl);
+
+ if (response.ok) {
+ const tokenData = await response.json();
+
+ // 关闭模态框
+ this.qrManager.closeModal();
+
+ // 显示成功消息
+ await Swal.fire({
+ position: 'top',
+ icon: 'success',
+ title: '登录成功',
+ text: '已成功获取阿里云盘TV版Token',
+ showConfirmButton: true
+ });
+
+ // 填充token字段
+ if (tokenData.access_token) {
+ document.getElementById("access-token").value = tokenData.access_token;
+ }
+ if (tokenData.refresh_token) {
+ document.getElementById("refresh-token").value = tokenData.refresh_token;
+ }
+
+ // 执行成功回调
+ this.qrManager.onSuccess(tokenData);
+ } else {
+ this.qrManager.setStatus('获取Token失败: ' + (await response.text()), 'error');
+ document.getElementById(this.qrManager.refreshBtnId).style.display = 'inline-block';
+ }
+ } catch (error) {
+ console.error('获取TV版Token失败:', error);
+ this.qrManager.setStatus('获取Token失败,请重试', 'error');
+ document.getElementById(this.qrManager.refreshBtnId).style.display = 'inline-block';
+ }
+ }
+
+ // 刷新二维码
+ async refreshQRCode() {
+ document.getElementById(this.qrManager.refreshBtnId).style.display = 'none';
+ this.sidValue = null;
+ this.checkAttempts = 0;
+ await this.startLogin();
+ }
+
+ // 登录成功处理函数
+ handleLoginSuccess(result) {
+ console.log('阿里云盘TV版登录成功');
+ }
+}
+
+// 创建全局实例
+window.alitvQRLogin = new AliTVQRLogin();
+
+// 开始TV版登录
+function startAliTVLogin() {
+ window.alitvQRLogin.startLogin();
+}
\ No newline at end of file
diff --git a/public/static/aliv2.js b/public/static/aliv2.js
index c16de67..c6531b9 100644
--- a/public/static/aliv2.js
+++ b/public/static/aliv2.js
@@ -1,249 +1,195 @@
-// 生成客户端指纹
-function generateClientFingerprint() {
- if (clientFingerprint) return clientFingerprint;
-
- const canvas = document.createElement('canvas');
- const ctx = canvas.getContext('2d');
- ctx.textBaseline = 'top';
- ctx.font = '14px Arial';
- ctx.fillText('Client fingerprint', 2, 2);
-
- const fingerprint = [
- navigator.userAgent,
- navigator.language,
- screen.width + 'x' + screen.height,
- new Date().getTimezoneOffset(),
- canvas.toDataURL(),
- navigator.hardwareConcurrency || 'unknown',
- navigator.deviceMemory || 'unknown'
- ].join('|');
-
- // 生成简单的哈希
- let hash = 0;
- for (let i = 0; i < fingerprint.length; i++) {
- const char = fingerprint.charCodeAt(i);
- hash = ((hash << 5) - hash) + char;
- hash = hash & hash; // 转换为32位整数
+// 阿里云盘扫码登录v2
+class AlicloudQRLogin {
+ constructor() {
+ // 创建QR登录管理器实例
+ this.qrManager = new QRLoginManager({
+ onSuccess: this.handleLoginSuccess.bind(this),
+ maxCheckTime: 30000 // 30秒过期时间
+ });
}
- clientFingerprint = Math.abs(hash).toString(36);
- // console.log('客户端指纹生成:', clientFingerprint);
- return clientFingerprint;
-}
+ // 启动阿里云盘扫码v2登录
+ async startLogin() {
+ try {
+ // 显示模态框
+ this.qrManager.showModal('阿里云盘扫码登录v2');
-// 发送带有客户端指纹的请求
-async function fetchWithFingerprint(url, options = {}) {
- const fingerprint = generateClientFingerprint();
- const headers = {
- 'X-Client-Fingerprint': fingerprint,
- ...options.headers
- };
-
- return fetch(url, {
- ...options,
- headers
- });
-}
+ // 生成二维码
+ const response = await this.qrManager.fetchWithFingerprint('/alicloud2/generate_qr');
+ const result = await response.json();
-// 启动阿里云盘扫码v2登录
-async function startAlicloud2Login() {
- try {
- // 显示模态框
- document.getElementById('qr-modal').style.display = 'block';
- setQRStatus('正在生成二维码...', 'waiting');
-
- // 生成二维码 - 使用带指纹的请求
- const response = await fetchWithFingerprint('/alicloud2/generate_qr');
- const result = await response.json();
-
- if (result.success) {
- alicloud2SessionId = result.session_id;
- alicloud2StartTime = Date.now();
- showQRCode(result.qr_code_url);
- setQRStatus('请使用阿里云盘App扫描二维码', 'waiting');
-
- // 显示过期时间信息
- if (result.expires_in) {
- const expireMinutes = Math.floor(result.expires_in / 60);
- // console.log(`会话将在 ${expireMinutes} 分钟后过期`);
- }
+ if (result.success) {
+ this.qrManager.sessionId = result.session_id;
+ this.qrManager.provider = 'alicloud';
+ this.qrManager.showQRCode(result.qr_code_url);
+ this.qrManager.setStatus('请使用阿里云盘App扫描二维码', 'waiting');
- startStatusCheck();
- } else {
- setQRStatus(result.error || '生成二维码失败', 'error');
- document.getElementById('refresh-qr-btn').style.display = 'inline-block';
+ // 开始检查状态
+ this.qrManager.startStatusCheck(this.checkStatus.bind(this));
+ } else {
+ this.qrManager.setStatus(result.error || '生成二维码失败', 'error');
+ document.getElementById(this.qrManager.refreshBtnId).style.display = 'inline-block';
+ }
+ } catch (error) {
+ console.error('生成二维码失败:', error);
+ this.qrManager.setStatus('网络错误,请重试', 'error');
+ document.getElementById(this.qrManager.refreshBtnId).style.display = 'inline-block';
}
- } catch (error) {
- setQRStatus('网络错误,请重试', 'error');
- document.getElementById('refresh-qr-btn').style.display = 'inline-block';
- console.error('生成二维码失败:', error);
}
-}
-// 显示二维码
-function showQRCode(qrUrl) {
- const qrApiUrl = `https://api.qrserver.com/v1/create-qr-code/?size=200x200&data=${encodeURIComponent(qrUrl)}`;
- document.getElementById('qr-code-display').innerHTML = `

`;
- document.getElementById('qr-code-container').style.display = 'block';
-}
-
-// 设置状态
-function setQRStatus(message, type) {
- const statusEl = document.getElementById('qr-status');
- statusEl.textContent = message;
- statusEl.className = `qr-status ${type}`;
- statusEl.style.display = 'block';
-}
+ // 检查登录状态
+ async checkStatus(elapsed) {
+ if (!this.qrManager.sessionId) return;
-// 开始状态检查
-function startStatusCheck() {
- stopStatusCheck();
- alicloud2CheckInterval = setInterval(checkAlicloud2Status, 2000);
-}
-
-// 停止状态检查
-function stopStatusCheck() {
- if (alicloud2CheckInterval) {
- clearInterval(alicloud2CheckInterval);
- alicloud2CheckInterval = null;
+ try {
+ const response = await this.qrManager.fetchWithFingerprint(
+ `/alicloud2/check_login?session_id=${this.qrManager.sessionId}`
+ );
+ const result = await response.json();
+
+ if (result.success) {
+ switch (result.status) {
+ case 'WAITING':
+ const waitTime = Math.floor(elapsed / 1000);
+ this.qrManager.setStatus(`等待扫描... (${waitTime}s) 请使用阿里云盘App扫码`, 'waiting');
+ break;
+ case 'SCANED':
+ this.qrManager.setStatus('已扫描,请在手机上确认登录', 'scaned');
+ break;
+ case 'CONFIRMED':
+ this.qrManager.setStatus('登录成功!正在获取用户信息...', 'success');
+ this.qrManager.stopStatusCheck();
+ setTimeout(async () => {
+ await this.getUserInfo();
+ }, 1000);
+ break;
+ case 'EXPIRED':
+ this.qrManager.setStatus('二维码已过期,请点击刷新重新生成', 'error');
+ this.qrManager.stopStatusCheck();
+ document.getElementById(this.qrManager.refreshBtnId).style.display = 'inline-block';
+ break;
+ }
+ } else {
+ if (response.status === 403) {
+ this.qrManager.setStatus('会话验证失败,请重新生成二维码', 'error');
+ this.qrManager.stopStatusCheck();
+ document.getElementById(this.qrManager.refreshBtnId).style.display = 'inline-block';
+ } else {
+ this.qrManager.setStatus('检查状态失败: ' + (result.error || '未知错误'), 'error');
+ document.getElementById(this.qrManager.refreshBtnId).style.display = 'inline-block';
+ }
+ }
+ } catch (error) {
+ console.error('检查登录状态失败:', error);
+ this.qrManager.setStatus('网络连接失败,请检查网络后重试', 'error');
+ document.getElementById(this.qrManager.refreshBtnId).style.display = 'inline-block';
+ }
}
-}
-// 检查登录状态
-async function checkAlicloud2Status() {
- if (!alicloud2SessionId) return;
-
- // 检查是否超过3分钟(二维码可能过期)
- const elapsed = Date.now() - alicloud2StartTime;
- if (elapsed > 180000) { // 3分钟
- setQRStatus('二维码可能已过期,建议点击刷新重新生成', 'error');
- document.getElementById('refresh-qr-btn').style.display = 'inline-block';
- stopStatusCheck();
- return;
- }
+ // 获取用户信息
+ async getUserInfo() {
+ if (!this.qrManager.sessionId) return;
- try {
- // 使用带指纹的请求
- const response = await fetchWithFingerprint(`/alicloud2/check_login?session_id=${alicloud2SessionId}`);
- const result = await response.json();
-
- if (result.success) {
- switch (result.status) {
- case 'WAITING':
- const waitTime = Math.floor(elapsed / 1000);
- setQRStatus(`等待扫描... (${waitTime}s) 请使用阿里云盘App扫码`, 'waiting');
- break;
- case 'SCANED':
- setQRStatus('已扫描,请在手机上确认登录', 'scaned');
- break;
- case 'CONFIRMED':
- setQRStatus('登录成功!正在获取用户信息...', 'success');
- stopStatusCheck();
- // 稍等一下确保token已保存
- setTimeout(async () => {
- await getAlicloud2UserInfo();
- }, 1000);
- break;
- case 'EXPIRED':
- setQRStatus('二维码已过期,请点击刷新重新生成', 'error');
- stopStatusCheck();
- document.getElementById('refresh-qr-btn').style.display = 'inline-block';
- break;
- }
- } else {
- // 处理会话验证失败的情况
- if (response.status === 403) {
- setQRStatus('会话验证失败,请重新生成二维码', 'error');
- document.getElementById('refresh-qr-btn').style.display = 'inline-block';
- stopStatusCheck();
+ try {
+ const response = await this.qrManager.fetchWithFingerprint(
+ `/alicloud2/get_user_info?session_id=${this.qrManager.sessionId}`
+ );
+ const result = await response.json();
+
+ if (result.success && result.user_info) {
+ // 关闭模态框
+ this.qrManager.closeModal();
+
+ // 显示成功消息
+ await Swal.fire({
+ position: 'top',
+ icon: 'success',
+ title: '登录成功',
+ html: `
用户: ${result.user_info.nick_name || result.user_info.user_id}
`,
+ showConfirmButton: true
+ });
+
+ // 填充token字段
+ if (result.access_token) {
+ document.getElementById("access-token").value = result.access_token;
+ }
+ if (result.refresh_token) {
+ document.getElementById("refresh-token").value = result.refresh_token;
+ }
+
+ // 执行成功回调
+ this.qrManager.onSuccess(result);
+
+ // 清理会话
+ await this.qrManager.fetchWithFingerprint(`/alicloud2/logout?session_id=${this.qrManager.sessionId}`);
+ this.qrManager.sessionId = null;
} else {
- setQRStatus('检查状态失败: ' + (result.error || '未知错误'), 'error');
- document.getElementById('refresh-qr-btn').style.display = 'inline-block';
+ if (response.status === 403) {
+ this.qrManager.setStatus('会话验证失败,请重新登录', 'error');
+ } else {
+ this.qrManager.setStatus('获取用户信息失败: ' + (result.error || '未知错误'), 'error');
+ }
}
+ } catch (error) {
+ this.qrManager.setStatus('获取用户信息失败', 'error');
+ console.error('获取用户信息失败:', error);
}
- } catch (error) {
- console.error('检查登录状态失败:', error);
- setQRStatus('网络连接失败,请检查网络后重试', 'error');
- document.getElementById('refresh-qr-btn').style.display = 'inline-block';
}
-}
-// 获取用户信息
-async function getAlicloud2UserInfo() {
- if (!alicloud2SessionId) return;
-
- try {
- // 使用带指纹的请求
- const response = await fetchWithFingerprint(`/alicloud2/get_user_info?session_id=${alicloud2SessionId}`);
- const result = await response.json();
-
- if (result.success && result.user_info) {
- // 关闭模态框
- closeQRModal();
-
- // 显示成功消息
- await Swal.fire({
- position: 'top',
- icon: 'success',
- title: '登录成功',
- html: `
用户: ${result.user_info.nick_name || result.user_info.user_id}
`,
- showConfirmButton: true
- });
-
- // 填充token字段(使用真实的tokens)
- if (result.access_token) {
- document.getElementById("access-token").value = result.access_token;
- }
- if (result.refresh_token) {
- document.getElementById("refresh-token").value = result.refresh_token;
- }
+ // 刷新二维码
+ async refreshQRCode() {
+ document.getElementById(this.qrManager.refreshBtnId).style.display = 'none';
- // 清理会话
- await fetchWithFingerprint(`/alicloud2/logout?session_id=${alicloud2SessionId}`);
- alicloud2SessionId = null;
- } else {
- // 处理会话验证失败的情况
- if (response.status === 403) {
- setQRStatus('会话验证失败,请重新登录', 'error');
- } else {
- setQRStatus('获取用户信息失败: ' + (result.error || '未知错误'), 'error');
+ // 清理旧会话
+ if (this.qrManager.sessionId) {
+ try {
+ await this.qrManager.fetchWithFingerprint(`/alicloud2/logout?session_id=${this.qrManager.sessionId}`);
+ } catch (e) {
+ console.error('清理旧会话失败:', e);
}
+ this.qrManager.sessionId = null;
}
- } catch (error) {
- setQRStatus('获取用户信息失败', 'error');
- console.error('获取用户信息失败:', error);
+
+ await this.startLogin();
+ }
+
+ // 登录成功处理函数
+ handleLoginSuccess(result) {
+ console.log('阿里云盘登录成功:', result);
}
}
+// 创建全局实例
+window.alicloudQRLogin = new AlicloudQRLogin();
+
+// 开始登录
+function startAlicloud2Login() {
+ window.alicloudQRLogin.startLogin();
+}
+
// 刷新二维码
-async function refreshQRCode() {
- document.getElementById('refresh-qr-btn').style.display = 'none';
- // 清理旧会话
- if (alicloud2SessionId) {
- try {
- await fetchWithFingerprint(`/alicloud2/logout?session_id=${alicloud2SessionId}`);
- } catch (e) {
- // console.log('清理旧会话失败:', e);
- }
- alicloud2SessionId = null;
- }
- await startAlicloud2Login();
+function refreshQRCode() {
+ window.alicloudQRLogin.refreshQRCode();
}
// 关闭模态框
+// 全局关闭模态框函数
function closeQRModal() {
- document.getElementById('qr-modal').style.display = 'none';
- stopStatusCheck();
-
- // 清理会话
- if (alicloud2SessionId) {
- fetchWithFingerprint(`/alicloud2/logout?session_id=${alicloud2SessionId}`);
- alicloud2SessionId = null;
+ // 直接操作DOM关闭模态框,而不是调用实例方法
+ const modal = document.getElementById('qr-modal');
+ if (modal) {
+ modal.style.display = 'none';
}
- // 重置界面
- document.getElementById('qr-code-container').style.display = 'none';
- document.getElementById('qr-status').style.display = 'none';
- document.getElementById('refresh-qr-btn').style.display = 'none';
-}
+ // 如果有活跃的检查间隔,清除它
+ if (window.alicloudQRLogin && window.alicloudQRLogin.qrManager) {
+ window.alicloudQRLogin.qrManager.stopStatusCheck();
+ window.alicloudQRLogin.qrManager.resetUI();
+ }
+ // 同样处理aliTV的情况
+ if (window.alitvQRLogin && window.alitvQRLogin.qrManager) {
+ window.alitvQRLogin.qrManager.stopStatusCheck();
+ window.alitvQRLogin.qrManager.resetUI();
+ }
+}
\ No newline at end of file
diff --git a/public/static/event.js b/public/static/event.js
index 894c537..a842a8d 100644
--- a/public/static/event.js
+++ b/public/static/event.js
@@ -23,7 +23,11 @@ function onSelect() {
client_uid_input.disabled = false;
client_uid_views.hidden = false;
secret_key_views.hidden = true;
- const driver_pre = driver_txt_input.value.split("_")[0];
+ let driver_pre = driver_txt_input.value.split("_")[0];
+ if (driver_txt_input.value === "alicloud_tv") {
+ driver_pre = "alitv"
+ }
+
direct_url_input.value = `${current_host}/${driver_pre}/callback`;
// 修改一些样式 ========================================================
@@ -39,7 +43,6 @@ function onSelect() {
clientIdContainer.style.display = 'none';
appSecretContainer.style.display = 'none';
serverUseContainer.style.display = 'none';
- callbackContainer.style.display = 'none';
server_use_input.checked = true;
} else {
clientIdContainer.style.display = 'block';
@@ -81,6 +84,7 @@ function onSelect() {
driver_txt_input.value === "onedrive_us" ||
driver_txt_input.value === "onedrive_de" ||
driver_txt_input.value === "alicloud_cs" ||
+ driver_txt_input.value === "alicloud_tv" ||
driver_txt_input.value === "dropboxs_go"
) {
server_use_input.checked = false;
diff --git a/public/static/login.js b/public/static/login.js
index 1feaf31..d9877db 100644
--- a/public/static/login.js
+++ b/public/static/login.js
@@ -31,6 +31,13 @@ async function getLogin(refresh = false) {
await startAlicloud2Login();
return;
}
+
+ // 阿里云盘TV版扫码登录,使用新的专用函数
+ if (driver_txt === "alicloud_tv" && !refresh) {
+ await startAliTVLogin();
+ return;
+ }
+
// 刷新秘钥情况 =================================================
let base_urls = "/requests?client_uid="
if (refresh) {
diff --git a/public/static/qrlogin.js b/public/static/qrlogin.js
new file mode 100644
index 0000000..bf24394
--- /dev/null
+++ b/public/static/qrlogin.js
@@ -0,0 +1,170 @@
+// 通用扫码登录模块
+class QRLoginManager {
+ constructor(options = {}) {
+ this.modalId = options.modalId || 'qr-modal';
+ this.codeContainerId = options.codeContainerId || 'qr-code-container';
+ this.codeDisplayId = options.codeDisplayId || 'qr-code-display';
+ this.statusId = options.statusId || 'qr-status';
+ this.refreshBtnId = options.refreshBtnId || 'refresh-qr-btn';
+ this.closeModal = options.closeModal ? options.closeModal : this.defaultCloseModal.bind(this);
+ this.checkInterval = null;
+ this.sessionId = null;
+ this.startTime = null;
+ this.provider = null; // 提供商名称,如 'alicloud', 'alitv' 等
+ this.onSuccess = options.onSuccess || function() {};
+ this.maxCheckTime = options.maxCheckTime || 180000; // 默认3分钟
+ this.checkFrequency = options.checkFrequency || 2500; // 默认2.5秒检查一次
+ }
+
+ // 显示二维码模态框
+ showModal(title = '扫码登录') {
+ const modal = document.getElementById(this.modalId);
+ if (modal) {
+ // 设置标题
+ const titleElement = modal.querySelector('h4');
+ if (titleElement) {
+ titleElement.textContent = title;
+ }
+ modal.style.display = 'block';
+ this.setStatus('正在生成二维码...', 'waiting');
+ }
+ }
+
+ // 默认关闭模态框方法
+ defaultCloseModal() {
+ const modal = document.getElementById(this.modalId);
+ if (modal) {
+ modal.style.display = 'none';
+ }
+ this.stopStatusCheck();
+ this.resetUI();
+ }
+
+ // 重置UI状态
+ resetUI() {
+ document.getElementById(this.codeContainerId).style.display = 'none';
+ document.getElementById(this.statusId).style.display = 'none';
+ document.getElementById(this.refreshBtnId).style.display = 'none';
+ }
+
+ // 显示二维码
+ showQRCode(qrUrl, size = 200) {
+ try {
+ const codeDisplayElement = document.getElementById(this.codeDisplayId);
+ codeDisplayElement.innerHTML = ''; // 清除之前的内容
+ // 创建canvas元素
+ const canvas = document.createElement('canvas');
+ codeDisplayElement.appendChild(canvas);
+ // 使用新创建的canvas元素
+ QRCode.toCanvas(canvas, qrUrl, {
+ width: size,
+ margin: 1,
+ }, function(error) {
+ if (error) console.error('QR码生成错误:', error);
+ });
+
+ document.getElementById(this.codeContainerId).style.display = 'block';
+ } catch (error) {
+ console.error('生成QR码失败:', error);
+ }
+ }
+
+ // 设置状态
+ setStatus(message, type) {
+ const statusEl = document.getElementById(this.statusId);
+ statusEl.textContent = message;
+ statusEl.className = `qr-status ${type}`;
+ statusEl.style.display = 'block';
+ }
+
+ // 开始状态检查
+ startStatusCheck(checkFunction) {
+ this.stopStatusCheck();
+ this.startTime = Date.now();
+ this.checkInterval = setInterval(() => {
+ // 检查是否超过最大时间
+ const elapsed = Date.now() - this.startTime;
+ if (elapsed > this.maxCheckTime) {
+ this.setStatus('二维码已过期,请点击刷新重新生成', 'error');
+ document.getElementById(this.refreshBtnId).style.display = 'inline-block';
+ this.stopStatusCheck();
+ return;
+ }
+
+ // 执行传入的检查函数
+ if (typeof checkFunction === 'function') {
+ checkFunction(elapsed);
+ }
+ }, this.checkFrequency);
+ }
+
+ // 停止状态检查
+ stopStatusCheck() {
+ if (this.checkInterval) {
+ clearInterval(this.checkInterval);
+ this.checkInterval = null;
+ }
+ }
+
+ // 生成随机会话ID
+ generateSessionId() {
+ return Date.now().toString(36) + Math.random().toString(36).substring(2, 15);
+ }
+
+ // 获取客户端指纹
+ getClientFingerprint() {
+ if (window.clientFingerprint) return window.clientFingerprint;
+
+ // 简单指纹生成
+ const fingerprint = [
+ navigator.userAgent,
+ navigator.language,
+ screen.width + 'x' + screen.height,
+ new Date().getTimezoneOffset(),
+ navigator.hardwareConcurrency || 'unknown',
+ navigator.deviceMemory || 'unknown'
+ ].join('|');
+
+ // 生成简单的哈希
+ let hash = 0;
+ for (let i = 0; i < fingerprint.length; i++) {
+ const char = fingerprint.charCodeAt(i);
+ hash = ((hash << 5) - hash) + char;
+ hash = hash & hash; // 转换为32位整数
+ }
+
+ window.clientFingerprint = Math.abs(hash).toString(36);
+ return window.clientFingerprint;
+ }
+
+ // 发送带有客户端指纹的请求
+ async fetchWithFingerprint(url, options = {}) {
+ const fingerprint = this.getClientFingerprint();
+ const headers = {
+ 'X-Client-Fingerprint': fingerprint,
+ ...options.headers
+ };
+
+ return fetch(url, {
+ ...options,
+ headers
+ });
+ }
+}
+
+// 显示错误消息的辅助函数
+async function showErrorMessage(title, message, code = "") {
+ let errorMsg = message;
+ if (code) errorMsg += ` (错误码: ${code})`;
+
+ await Swal.fire({
+ icon: 'error',
+ title: title + '失败',
+ text: errorMsg,
+ showConfirmButton: true
+ });
+}
+
+// 导出模块
+window.QRLoginManager = QRLoginManager;
+window.showErrorMessage = showErrorMessage;
\ No newline at end of file
diff --git a/public/static/token.js b/public/static/token.js
index 25c4d80..86e1674 100644
--- a/public/static/token.js
+++ b/public/static/token.js
@@ -4,7 +4,6 @@ async function getToken() {
const json_byte = Uint8Array.from(atob(url_hash), c => c.charCodeAt(0));
const json_text = new TextDecoder().decode(json_byte);
const json_data = JSON.parse(json_text);
- console.log(json_data);
const server_use = json_data.server_use;
const client_uid = json_data.client_uid;
const secret_key = json_data.secret_key;
diff --git a/src/driver/alicloud_oa.ts b/src/driver/alicloud_oa.ts
index 3904b7b..7e654de 100644
--- a/src/driver/alicloud_oa.ts
+++ b/src/driver/alicloud_oa.ts
@@ -64,7 +64,6 @@ export async function alyLogin(c: Context) {
const result = await pubLogin(c, JSON.stringify(params_info), request_urls,
false, "POST", "json",
{'Content-Type': 'application/json'});
- console.log(result);
if (!result.qrCodeUrl) return c.json({"text": result.message}, 500);
return c.json({"text": result.qrCodeUrl, "sid": result.sid}, 200);
}
@@ -73,7 +72,6 @@ export async function alyLogin(c: Context) {
// 令牌申请 ##############################################################################
export async function alyToken(c: Context) {
const clients_info: configs.Clients = getCookie(c);
- if (clients_info.drivers == "alicloud_tv") return await checkStatus(c, clients_info);
let oauth_type: string | undefined = c.req.query('grant_type')
if (!clients_info.servers) clients_info.servers = c.req.query('server_use') == "true"
if (!clients_info.drivers) return c.json({text: 'No Cookies',}, 401);
@@ -138,7 +136,6 @@ export async function genToken(c: Context) {
const refresh_text: string | undefined = c.req.query('refresh_ui');
if (!clients_info) return c.json({text: "传入参数缺少"}, 500);
if (!refresh_text) return c.json({text: "缺少刷新令牌"}, 500);
- if (clients_info.drivers == "alicloud_tv") return await refreshToken(c, refresh_text);
// 请求参数 ==========================================================================
const params: Record
= {
client_id: clients_info.servers ? c.env.alicloud_uid : clients_info.app_uid,
diff --git a/src/driver/alicloud_tv.ts b/src/driver/alicloud_tv.ts
index ac71e35..c3eda86 100644
--- a/src/driver/alicloud_tv.ts
+++ b/src/driver/alicloud_tv.ts
@@ -1,17 +1,9 @@
import {Context} from "hono";
-import {encodeCallbackData, Secrets} from "../shares/secrets";
-
-// 用于替代 Node.js 的 crypto.randomUUID
-function generateUUID(): string {
- return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
- const r = Math.random() * 16 | 0;
- const v = c === 'x' ? r : (r & 0x3 | 0x8);
- return v.toString(16);
- });
-}
+import {pubLogin} from "../shares/oauthv2";
+import {Requests} from "../shares/request";
// 定义API响应的接口
-interface ApiResponse {
+interface AliCloudTVApiResponse {
code?: number;
data?: Record;
t?: string;
@@ -34,17 +26,13 @@ class AliyunPanTvToken {
private akv: string;
private apv: string;
private headersBase: Record;
- private initialized: boolean = false;
constructor() {
- // 构造函数中只设置默认值,不执行异步操作
- // this.timestamp = Date.now().toString(); // 设置默认时间戳
- this.timestamp = ''
- this.uniqueId = generateUUID().replace(/-/g, '');
+ this.timestamp = Date.now().toString(); // 设置默认时间戳
+ this.uniqueId = this.generateUUID();
this.wifimac = Math.floor(100000000000 + Math.random() * 900000000000).toString();
- // this.wifimac = "020000000000";
this.model = "SM-S908E";
- this.brand = "samsung";
+ this.brand = "Samsung";
this.akv = "2.6.1143";
this.apv = "1.4.0.2";
@@ -55,30 +43,69 @@ class AliyunPanTvToken {
};
}
- // 懒加载初始化方法,在首次API调用时执行
- private async ensureInitialized(): Promise {
- if (this.initialized) return;
+ private generateUUID(): string {
+ return 'xxxxxxxxxxxx4xxxyxxxxxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
+ const r = Math.random() * 16 | 0;
+ const v = c === 'x' ? r : (r & 0x3 | 0x8);
+ return v.toString(16);
+ });
+ }
+
+ private getHeaders(sign: string): Record {
+ return {
+ ...this.headersBase,
+ "akv": this.akv,
+ "apv": this.apv,
+ "b": this.brand,
+ "d": this.uniqueId,
+ "m": this.model,
+ "n": this.model,
+ "t": this.timestamp,
+ "wifiMac": this.wifimac,
+ "sign": sign,
+ };
+ }
+ private getParams(): Record {
+ return {
+ "akv": this.akv,
+ "apv": this.apv,
+ "b": this.brand,
+ "d": this.uniqueId,
+ "m": this.model,
+ "mac": "",
+ "n": this.model,
+ "t": this.timestamp,
+ "wifiMac": this.wifimac,
+ };
+ }
+
+ // refreshTimestamp 刷新时间戳
+ private async refreshTimestamp(c: Context): Promise {
try {
- const response = await fetch("http://api.extscreen.com/timestamp");
- const data = await response.json() as { data?: { timestamp?: number } };
- if (data?.data?.timestamp) {
- this.timestamp = data.data.timestamp.toString();
+ const response = await pubLogin(
+ c,
+ "",
+ "http://api.extscreen.com/timestamp",
+ false,
+ "GET",
+ "json",
+ this.headersBase,
+ ) as { data?: { timestamp?: number } };
+
+ if (response?.data?.timestamp) {
+ this.timestamp = response.data.timestamp.toString();
}
} catch (error) {
console.error("获取时间戳错误:", error);
- // 保持默认时间戳
- } finally {
- this.initialized = true;
+ this.timestamp = Date.now().toString(); // 如果获取失败,使用当前时间戳
}
}
- // 精确匹配Python版本的h函数
+ // h 根据时间戳和字符数组生成哈希值
private h(charArray: string[], modifier: string): string {
- // 获取唯一字符,与Python的list(dict.fromkeys(char_array))等效
const uniqueChars = Array.from(new Set(charArray));
const modifierStr = String(modifier);
- // 与Python的substring逻辑完全一致
const numericModifierStr = modifierStr.length > 7 ? modifierStr.substring(7) : '0';
let numericModifier: number;
@@ -100,31 +127,13 @@ class AliyunPanTvToken {
newCharCode += 33;
}
- try {
- transformedString += String.fromCharCode(newCharCode);
- } catch {
- // 跳过无效字符,与Python行为一致
- }
+ transformedString += String.fromCharCode(newCharCode);
}
return transformedString;
}
- private getParams(): Record {
- return {
- "akv": this.akv,
- "apv": this.apv,
- "b": this.brand,
- "d": this.uniqueId,
- "m": this.model,
- "mac": "",
- "n": this.model,
- "t": this.timestamp,
- "wifiMac": this.wifimac,
- };
- }
-
- // 与Python版本完全匹配的MD5实现
+ // md5 MD5实现
private async md5(str: string): Promise {
const encoder = new TextEncoder();
const data = encoder.encode(str);
@@ -133,7 +142,7 @@ class AliyunPanTvToken {
return hashArray.map(b => b.toString(16).padStart(2, '0')).join('');
}
- // 与Python版本完全匹配的SHA-256实现
+ // sha256 SHA-256实现
private async sha256(str: string): Promise {
const encoder = new TextEncoder();
const data = encoder.encode(str);
@@ -142,11 +151,10 @@ class AliyunPanTvToken {
return hashArray.map(b => b.toString(16).padStart(2, '0')).join('');
}
- // 精确匹配Python版本的generateKey实现
+ // generateKey 生成 aesKey
private async generateKey(): Promise {
const params = this.getParams();
const sortedKeys = Object.keys(params).sort();
- // 使用与Python完全相同的连接方式
let concatenatedParams = "";
for (const key of sortedKeys) {
if (key !== 't') {
@@ -159,11 +167,11 @@ class AliyunPanTvToken {
return await this.md5(hashedKey);
}
+ // generateKeyWithT 由时间戳参与生成 aesKey
private async generateKeyWithT(t: string): Promise {
const params = this.getParams();
params.t = t;
const sortedKeys = Object.keys(params).sort();
- // 使用与Python完全相同的连接方式
let concatenatedParams = "";
for (const key of sortedKeys) {
if (key !== 't') {
@@ -176,7 +184,7 @@ class AliyunPanTvToken {
return await this.md5(hashedKey);
}
- // 与Python版本完全匹配的随机IV生成
+ // randomIvStr 随机IV生成
private randomIvStr(length: number = 16): string {
const chars = 'abcdefghijklmnopqrstuvwxyz0123456789';
let result = '';
@@ -186,40 +194,36 @@ class AliyunPanTvToken {
return result;
}
- // 与Python版本完全匹配的加密实现
+ // encrypt 加密实现
private async encrypt(plainObj: any): Promise<{ iv: string, ciphertext: string }> {
- // 1. 生成密钥 - 与Python完全一致
+ // 生成 aes密钥
const key = await this.generateKey();
-
- // 2. 生成随机IV - 与Python完全一致
+ // 生成随机IV
const ivStr = this.randomIvStr(16);
-
- // 3. 准备加密数据 - 确保使用与Python相同的JSON序列化格式
- // Python: json.dumps(plain_obj, separators=(',', ':'))
+ // 待加密数据
const plaintext = JSON.stringify(plainObj).replace(/\s/g, '');
- // 4. 创建UTF-8编码的数据
const encoder = new TextEncoder();
const keyBytes = encoder.encode(key);
const ivBytes = encoder.encode(ivStr);
const plaintextBytes = encoder.encode(plaintext);
- // 6. 使用Web Crypto API进行AES-CBC加密
+ // AES-CBC加密
const cryptoKey = await crypto.subtle.importKey(
'raw',
keyBytes,
- {name: 'AES-CBC', length: 128},
+ { name: 'AES-CBC', length: 256 },
false,
['encrypt']
);
const encryptedBuffer = await crypto.subtle.encrypt(
- {name: 'AES-CBC', iv: ivBytes},
+ { name: 'AES-CBC', iv: ivBytes },
cryptoKey,
plaintextBytes
);
- // 7. 转换为Base64编码 - 与Python的base64.b64encode(ciphertext).decode("utf-8")一致
+ // 转换为Base64编码
const encryptedArray = new Uint8Array(encryptedBuffer);
let binary = '';
for (let i = 0; i < encryptedArray.length; i++) {
@@ -227,49 +231,48 @@ class AliyunPanTvToken {
}
const base64Ciphertext = btoa(binary);
- // 8. 返回与Python一致的结构
return {
iv: ivStr,
ciphertext: base64Ciphertext
};
}
- // 与Python版本完全匹配的解密实现
+ // decrypt 解密实现
private async decrypt(ciphertext: string, iv: string, t?: string): Promise {
try {
- // 1. 生成密钥 - 与Python一致
+ // 生成密钥
const key = t ? await this.generateKeyWithT(t) : await this.generateKey();
- // 2. 解码Base64密文 - 与Python一致
+ // 解码Base64密文
const binaryString = atob(ciphertext);
const bytes = new Uint8Array(binaryString.length);
for (let i = 0; i < binaryString.length; i++) {
bytes[i] = binaryString.charCodeAt(i);
}
- // 3. 解码十六进制IV - 与Python中的bytes.fromhex(iv)一致
+ // 解码十六进制IV
const ivBytes = new Uint8Array(iv.length / 2);
for (let i = 0; i < iv.length; i += 2) {
ivBytes[i / 2] = parseInt(iv.substring(i, i + 2), 16);
}
- // 4. 导入密钥
+ // 导入密钥
const cryptoKey = await crypto.subtle.importKey(
'raw',
new TextEncoder().encode(key),
- {name: 'AES-CBC', length: 256},
+ { name: 'AES-CBC', length: 256 },
false,
['decrypt']
);
- // 5. 解密
+ // 解密
const decryptedBuffer = await crypto.subtle.decrypt(
- {name: 'AES-CBC', iv: ivBytes},
+ { name: 'AES-CBC', iv: ivBytes },
cryptoKey,
bytes
);
- // 7. 转换为字符串
+ // 转换为字符串
return new TextDecoder().decode(decryptedBuffer);
} catch (error) {
console.error("解密失败:", error);
@@ -277,23 +280,7 @@ class AliyunPanTvToken {
}
}
- private getHeaders(sign: string): Record {
- const params = this.getParams();
- return {
- ...this.headersBase,
- "akv": this.akv,
- "apv": this.apv,
- "b": this.brand,
- "d": this.uniqueId,
- "m": this.model,
- "n": this.model,
- "t": this.timestamp,
- "wifiMac": this.wifimac,
- "sign": sign,
- };
- }
-
- // 与Python版本完全匹配的计算sign实现
+ // 请求sign生成
private async computeSign(method: string, apiPath: string): Promise {
const apiPathAdjusted = "/api" + apiPath;
const key = await this.generateKey();
@@ -301,110 +288,35 @@ class AliyunPanTvToken {
return await this.sha256(content);
}
- public async getToken(refreshToken: string): Promise {
- // 确保已初始化
- await this.ensureInitialized();
+ // getTokenByCode 合并后的函数,处理刷新Token和通过授权码获取Token
+ public async getTokenByCode(c: Context, code: string, isRefreshToken: boolean = false): Promise {
+ // 刷新时间戳
+ await this.refreshTimestamp(c);
try {
- const bodyObj = {refresh_token: refreshToken};
+ // 根据是刷新token还是授权码设置不同的请求体
+ const bodyObj = isRefreshToken ? { refresh_token: code } : { code: code };
const encrypted = await this.encrypt(bodyObj);
const reqBody = {
iv: encrypted.iv,
ciphertext: encrypted.ciphertext
};
-
- // console.log("[*] (Sign) Request Body:", JSON.stringify(reqBody));
-
const sign = await this.computeSign("POST", "/v4/token");
const headers = this.getHeaders(sign);
- const response = await fetch(
+ // 使用pubLogin替代直接调用Requests
+ const responseData = await pubLogin(
+ c,
+ JSON.stringify(reqBody),
"https://api.extscreen.com/aliyundrive/v4/token",
- {
- method: "POST",
- headers: headers,
- body: JSON.stringify(reqBody)
- }
- );
-
- // console.log("[*] (Sign) Response Status:", response.status);
- const responseData = await response.json() as ApiResponse;
- // console.log("[*] (Sign) Response Body:", responseData);
-
- // 类型安全检查
- if (!responseData) {
- throw new Error("Invalid response data");
- }
-
- // 检查响应数据中是否有code字段
- if (responseData.code !== undefined && responseData.code !== 200) {
- throw new Error(JSON.stringify(responseData));
- }
-
- // 类型安全地访问data字段
- if (!responseData.data) {
- throw new Error("Response missing data field");
- }
-
- const tokenData = responseData.data as TokenData;
- const t = responseData.t ? responseData.t.toString() : this.timestamp;
-
- if (!tokenData.ciphertext || !tokenData.iv) {
- throw new Error("Token data missing required fields");
- }
-
- return await this.decrypt(tokenData.ciphertext, tokenData.iv, t);
- } catch (error) {
- console.error("获取Token错误:", error);
- throw error;
- }
- }
-
- public async getRefreshtoken(authToken: string): Promise {
- // 确保已初始化
- await this.ensureInitialized();
-
- try {
- const bodyObj = {code: authToken};
- const encrypted = await this.encrypt(bodyObj);
- const reqBody = {
- iv: encrypted.iv,
- ciphertext: encrypted.ciphertext
- };
-
- console.log("[*] (Sign) Request Body:", JSON.stringify(reqBody));
-
- const sign = await this.computeSign("POST", "/v4/token");
- const headers = this.getHeaders(sign);
-
- console.log("[*] (Sign) Headers:", headers);
-
- const response = await fetch(
- "https://api.extscreen.com/aliyundrive/v4/token",
- {
- method: "POST",
- headers: headers,
- body: JSON.stringify(reqBody)
- }
- );
-
- console.log("[*] (Sign) Response Status:", response.status);
- const responseData = await response.json() as ApiResponse;
- console.log("[*] (Sign) Response Body:", responseData);
-
- // 类型安全检查
- if (!responseData) {
- throw new Error("Invalid response data");
- }
-
- // 检查响应数据中是否有code字段
- if (responseData.code !== undefined && responseData.code !== 200) {
- throw new Error(JSON.stringify(responseData));
- }
+ false,
+ "POST",
+ "json",
+ headers
+ ) as AliCloudTVApiResponse;
- // 类型安全地访问data字段
- if (!responseData.data) {
- throw new Error("Response missing data field");
+ if (!responseData || responseData.code !== 200 || !responseData.data) {
+ throw new Error(responseData ? JSON.stringify(responseData) : "Invalid response data");
}
const tokenData = responseData.data as TokenData;
@@ -416,60 +328,44 @@ class AliyunPanTvToken {
return await this.decrypt(tokenData.ciphertext, tokenData.iv, t);
} catch (error) {
- console.error("获取RefreshToken错误:", error);
+ console.error(`获取${isRefreshToken ? "Token" : "RefreshToken"}错误:`, error);
throw error;
}
}
- public async getQrcodeUrl(): Promise<{ qr_link: string, sid: string }> {
- // 确保已初始化
- await this.ensureInitialized();
+ // getQrcodeUrl 获取二维码链接
+ public async getQrcodeUrl(c: Context): Promise<{ qr_link: string, sid: string }> {
+ // 刷新时间戳
+ await this.refreshTimestamp(c);
try {
- // 使用与Python代码完全一致的参数结构
const bodyObj = {
scopes: ["user:base", "file:all:read", "file:all:write"].join(","),
width: 500,
height: 500
};
-
const encrypted = await this.encrypt(bodyObj);
const reqBody = {
iv: encrypted.iv,
ciphertext: encrypted.ciphertext
};
- console.log("[*] (Qrcode) Request Body:", JSON.stringify(reqBody));
-
const sign = await this.computeSign("POST", "/v2/qrcode");
const headers = this.getHeaders(sign);
- const response = await fetch(
+ // 使用pubLogin替代直接调用Requests
+ const responseData = await pubLogin(
+ c,
+ JSON.stringify(reqBody),
"https://api.extscreen.com/aliyundrive/v2/qrcode",
- {
- method: "POST",
- headers: headers,
- body: JSON.stringify(reqBody)
- }
- );
-
- // console.log("[*] (Qrcode) Response Status:", response.status);
- const responseData = await response.json() as ApiResponse;
- // console.log("[*] (Qrcode) Response Body:", responseData);
-
- // 类型安全检查
- if (!responseData) {
- throw new Error("Invalid response data");
- }
-
- // 检查响应数据中是否有code字段
- if (responseData.code !== undefined && responseData.code !== 200) {
- throw new Error(JSON.stringify(responseData));
- }
+ false,
+ "POST",
+ "json",
+ headers
+ ) as AliCloudTVApiResponse;
- // 类型安全地访问data字段
- if (!responseData.data) {
- throw new Error("Response missing data field");
+ if (!responseData || responseData.code !== 200 || !responseData.data) {
+ throw new Error(responseData ? JSON.stringify(responseData) : "Invalid response data");
}
const qrcodeData = responseData.data as TokenData;
@@ -480,16 +376,14 @@ class AliyunPanTvToken {
}
const decryptedData = await this.decrypt(qrcodeData.ciphertext, qrcodeData.iv, t);
- const data = JSON.parse(decryptedData) as { sid?: string; qrCodeUrl?: string };
- // console.log("[*] (Qrcode) Decrypted Data:", data);
+ const data = JSON.parse(decryptedData) as { sid?: string };
if (!data.sid) {
throw new Error("Missing sid in decrypted data");
}
- // const qrLink = "https://www.aliyundrive.com/o/oauth/authorize?sid=" + data.sid]
- return {qr_link: data.qrCodeUrl, sid: data.sid};
- // return { qr_link: qrLink, sid: data.sid };
+ const qrLink = "https://www.aliyundrive.com/o/oauth/authorize?sid=" + data.sid;
+ return { qr_link: qrLink, sid: data.sid };
} catch (error) {
console.error("获取二维码错误:", error);
throw error;
@@ -497,7 +391,6 @@ class AliyunPanTvToken {
}
}
-// 延迟创建实例 - 不在全局作用域中执行
let clientInstance: AliyunPanTvToken | null = null;
// 获取客户端实例的函数
@@ -508,101 +401,102 @@ function getClient(): AliyunPanTvToken {
return clientInstance;
}
+// checkQrcodeStatus 检查二维码状态
+async function checkQrcodeStatus(c: Context, sid: string): Promise<{ auth_code: string } | null> {
+ let headersBase ;
+ try {
+ headersBase = {
+ "User-Agent": "Mozilla/5.0 (Linux; U; Android 15; zh-cn; SM-S908E Build/UKQ1.231108.001) AppleWebKit/533.1 (KHTML, like Gecko) Mobile Safari/533.1",
+ "Host": "openapi.alipan.com",
+ "Content-Type": "text/plain;charset=UTF-8",
+ };
+ const response = await Requests(
+ c,
+ "",
+ `https://openapi.alipan.com/oauth/qrcode/${sid}/status`,
+ "GET",
+ false,
+ headersBase,
+ "json"
+ );
+
+ if (response.text) {
+ return null;
+ }
+
+ if (response && response.status === "LoginSuccess" && response.authCode) {
+ return { auth_code: response.authCode };
+ }
+
+ return null;
+ } catch (error) {
+ console.error("检查二维码状态错误:", error);
+ throw error;
+ }
+}
-// 导出的接口函数保持不变
+// getQRCode 获取二维码链接
export async function getQRCode(c: Context) {
try {
const client = getClient();
- const qrData = await client.getQrcodeUrl();
- return c.json({text: qrData.qr_link, sid: qrData.sid});
+ const qrData = await client.getQrcodeUrl(c);
+ return c.json({ text: qrData.qr_link, sid: qrData.sid });
} catch (error) {
console.error("获取二维码失败:", error);
- return c.json({text: "获取二维码失败"}, 500);
+ return c.json({ text: "获取二维码失败" }, 500);
}
}
-export async function checkStatus(c: Context, client: Record) {
+// checkStatus 检查二维码状态
+export async function checkStatus(c: Context) {
try {
const sid = c.req.query('sid');
if (!sid) {
- return c.json({text: "缺少sid参数"}, 400);
+ return c.json({ text: "缺少sid参数" }, 400);
}
- const status = await checkQrcodeStatus(sid);
+ const status = await checkQrcodeStatus(c, sid);
if (status) {
- // return c.json(status);
- const result_data: Record = await getTokenByAuthCode(c, status.auth_code)
- const result_json = await result_data.json();
- // console.log(result_json);
- const callbackData: Secrets = {
- access_token: result_json.access_token,
- refresh_token: result_json.refresh_token,
- client_uid: client.app_uid,
- client_key: client.app_key,
- driver_txt: client.drivers,
- server_use: client.servers,
- }
- // console.log(callbackData);
- return c.json(callbackData, 200);
-
+ return c.json(status);
}
- return c.json({text: "等待扫码"}, 202);
+ return c.json({ text: "等待扫码" }, 202);
} catch (error) {
console.error("检查状态失败:", error);
- return c.json({text: "检查状态失败"}, 500);
- }
-}
-
-async function checkQrcodeStatus(sid: string): Promise<{ auth_code: string } | null> {
- try {
- const response = await fetch(
- `https://openapi.alipan.com/oauth/qrcode/${sid}/status`
- );
-
- if (!response.ok) {
- return null;
- }
-
- const data = await response.json() as { status?: string; authCode?: string };
- if (data && data.status === "LoginSuccess" && data.authCode) {
- return {auth_code: data.authCode};
- }
- return null;
- } catch (error) {
- console.error("检查二维码状态错误:", error);
- throw error;
+ return c.json({ text: "检查状态失败" }, 500);
}
}
-export async function getTokenByAuthCode(c: Context, authCode: string = "") {
+// getTokenByAuthCode 使用authCode获取Token
+export async function getTokenByAuthCode(c: Context) {
try {
- // const authCode = c.req.query('auth_code');
+ const authCode = c.req.query('auth_code');
if (!authCode) {
- return c.json({text: "缺少auth_code参数"}, 400);
+ return c.json({ text: "缺少auth_code参数" }, 400);
}
const client = getClient();
- const tokenData = await client.getRefreshtoken(authCode);
+ const tokenData = await client.getTokenByCode(c, authCode, false);
return c.json(JSON.parse(tokenData));
} catch (error) {
console.error("获取Token失败:", error);
- return c.json({text: "获取Token失败"}, 500);
+ return c.json({ text: "获取Token失败" }, 500);
}
}
-export async function refreshToken(c: Context, refreshToken: string) {
+// refreshToken 刷新Token
+export async function refreshToken(c: Context) {
try {
- // const refreshToken = c.req.query('refresh_ui');
+ const refreshToken = c.req.query('refresh_ui');
if (!refreshToken) {
- return c.json({text: "缺少refresh_token参数"}, 400);
+ return c.json({ text: "缺少refresh_token参数" }, 400);
}
const client = getClient();
- const tokenData = await client.getToken(refreshToken);
+ const tokenData = await client.getTokenByCode(c, refreshToken, true);
return c.json(JSON.parse(tokenData));
} catch (error) {
console.error("刷新Token失败:", error);
- return c.json({text: "刷新Token失败"}, 500);
+ return c.json({ text: "刷新Token失败" }, 500);
}
}
\ No newline at end of file
diff --git a/src/index.ts b/src/index.ts
index 112b6bb..887ed46 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -9,6 +9,7 @@ import * as goapi from './driver/googleui_oa';
import * as yanui from './driver/yandexui_oa';
import * as drops from './driver/dropboxs_oa';
import * as quark from './driver/quarkpan_oa';
+import * as alitv from './driver/alicloud_tv';
export type Bindings = {
MAIN_URLS: string, baiduyun_ext: string,
@@ -27,27 +28,22 @@ export const app = new Hono<{ Bindings: Bindings }>()
app.get('/dropboxs/requests', async (c) => {
return drops.getLogin(c);
})
-
// 令牌申请 ##############################################################################
app.get('/dropboxs/callback', async (c) => {
return drops.urlParse(c);
})
-
// 令牌刷新 ##############################################################################
app.get('/dropboxs/renewapi', async (c: Context) => {
return drops.apiRenew(c);
});
-
// 登录申请 ##############################################################################
app.get('/onedrive/requests', async (c) => {
return oneui.oneLogin(c);
})
-
// 令牌申请 ##############################################################################
app.get('/onedrive/callback', async (c) => {
return oneui.oneToken(c);
})
-
// 令牌刷新 ##############################################################################
app.get('/onedrive/renewapi', async (c: Context) => {
return oneui.genToken(c);
@@ -57,12 +53,10 @@ app.get('/onedrive/renewapi', async (c: Context) => {
app.get('/alicloud/requests', async (c: Context) => {
return aliui.alyLogin(c);
});
-
// 令牌申请 ##############################################################################
app.get('/alicloud/callback', async (c: Context) => {
return aliui.alyToken(c);
});
-
// 令牌刷新 ##############################################################################
app.get('/alicloud/renewapi', async (c: Context) => {
return aliui.genToken(c);
@@ -72,17 +66,14 @@ app.get('/alicloud/renewapi', async (c: Context) => {
app.get('/alicloud2/generate_qr', async (c: Context) => {
return aliqr.generateQR(c);
});
-
// 阿里云盘扫码2 - 检查登录状态 ##############################################################################
app.get('/alicloud2/check_login', async (c: Context) => {
return aliqr.checkLogin(c);
});
-
// 令牌刷新 ##############################################################################
app.get('/alicloud2/renewapi', async (c: Context) => {
return aliqr.genToken(c);
});
-
// 阿里云盘扫码2 - 获取用户信息 ##############################################################################
app.get('/alicloud2/get_user_info', async (c: Context) => {
return aliqr.getUserInfo(c);
@@ -183,5 +174,24 @@ app.get('/quarkyun/renewapi', async (c) => {
return await quark.apiRenew(c);
});
+// 阿里云盘TV版 - 获取二维码 ##############################################################################
+app.get('/alitv/qrcode', async (c: Context) => {
+ return await alitv.getQRCode(c);
+});
+
+// 阿里云盘TV版 - 检查扫码状态 ##############################################################################
+app.get('/alitv/check', async (c: Context) => {
+ return await alitv.checkStatus(c);
+});
+
+// 阿里云盘TV版 - 获取Token ##############################################################################
+app.get('/alitv/token', async (c: Context) => {
+ return await alitv.getTokenByAuthCode(c);
+});
+
+// 阿里云盘TV版 - 刷新Token ##############################################################################
+app.get('/alitv/renewapi', async (c: Context) => {
+ return await alitv.refreshToken(c);
+});
export default app