feat(download): switch download buttons to direct source links
The token relay still stood between the button and the file: every click issued a short-lived token URL, so downloads never reached the real source address directly. - Render direct anchors (target=_blank) to the raw source URL in the download box and the paid card, with inline extract-code buttons - Retire the relay: delete Content\Download plus its init wiring, header query-var branch and SEO noindex branch, and drop the dead download-request flow from cms-download.js (cashier, scan-pay and error views stay) - Keep the dl_token table and the download logs page as read-only history; unscheduling of the orphaned cleanup cron is preserved
This commit is contained in:
1 parent
bc97c4ddc3
commit
ab495854d5
10 files changed
+118
-841
No files matched your search
+3
-111
@@ -2,9 +2,9 @@
|
||||
* AKSRT CMS Download
|
||||
*
|
||||
* 功能:
|
||||
* - 监听 [data-action="aksrt-download"] 按钮点击
|
||||
* - 调 POST /aksrt/v1/download/request
|
||||
* - 成功后直接打开生成的下载链接;失败/购买等场景仍走弹窗
|
||||
* - 监听购买入口(data-aksrt-pay-content / data-action="aksrt-need-purchase")
|
||||
* - 内容购买收银台、扫码支付与错误提示弹窗
|
||||
* - 下载已改为直连:按钮为源地址锚点,不再经过本站中转请求
|
||||
*
|
||||
* 弹窗视觉走主题 design token(见 assets/css/frontend/cms-download.css),
|
||||
* 付费收银台沿用参考实现的 colorful header 结构。
|
||||
@@ -140,7 +140,6 @@
|
||||
// ===== 统一视图壳(下载 / 错误 / 扫码共用主题化外观) =====
|
||||
// 头部与按钮统一继承运行时主题色(--focus-color),状态用图标 + 标题区分。
|
||||
const DL_VIEWS = {
|
||||
success: { icon: 'fa-cloud-download-alt', title: '下载已就绪' },
|
||||
login: { icon: 'fa-user-lock', title: '请先登录' },
|
||||
purchase: { icon: 'fa-shopping-cart', title: '请先购买' },
|
||||
vip: { icon: 'fa-gem', title: '会员专属' },
|
||||
@@ -251,34 +250,8 @@
|
||||
}
|
||||
}
|
||||
|
||||
// ===== 成功:直接打开生成的下载链接,不再弹窗 =====
|
||||
function startDownload(payload, returnFocusEl) {
|
||||
const url = payload && payload.dl_url ? String(payload.dl_url) : '';
|
||||
if (!url) {
|
||||
renderError('下载链接生成失败,请稍后重试', {}, returnFocusEl);
|
||||
return;
|
||||
}
|
||||
// 与旧版弹窗内「立即下载」一致:新标签页打开本站中转链接。
|
||||
const link = document.createElement('a');
|
||||
link.href = url;
|
||||
link.target = '_blank';
|
||||
link.rel = 'noopener noreferrer';
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
link.remove();
|
||||
}
|
||||
|
||||
// ===== 主流程 =====
|
||||
function captchaScope(sourceEl) {
|
||||
const box = sourceEl ? sourceEl.closest('[data-aksrt-download-box]') : null;
|
||||
return box ? (box.querySelector('[data-aksrt-download-captcha]') || box) : sourceEl;
|
||||
}
|
||||
|
||||
function isCaptchaActive(sourceEl) {
|
||||
const card = sourceEl ? sourceEl.closest('[data-aksrt-download-box]') : null;
|
||||
if (!card) return false;
|
||||
return card.getAttribute('data-captcha') === '1';
|
||||
}
|
||||
|
||||
function cashierHeader() {
|
||||
return '<div class="modal-colorful-header colorful-bg jb-blue">' +
|
||||
@@ -299,70 +272,8 @@
|
||||
);
|
||||
}
|
||||
|
||||
function ensureCaptchaThen(sourceEl, callback) {
|
||||
if (!isCaptchaActive(sourceEl)) { callback({}); return; }
|
||||
const captcha = window.AKSRTCaptcha;
|
||||
if (!captcha) {
|
||||
if (window.console && console.warn) console.warn('[AKSRT] download captcha not ready');
|
||||
alert('验证码组件加载中,请稍候再试');
|
||||
return;
|
||||
}
|
||||
captcha.run('download', captchaScope(sourceEl)).then(function (payload) {
|
||||
if (payload !== null) callback(payload);
|
||||
});
|
||||
}
|
||||
|
||||
function performDownloadRequest(button, captchaPayload) {
|
||||
const card = button.closest('[data-aksrt-download-box]');
|
||||
if (!card) return;
|
||||
const postId = parseInt(card.getAttribute('data-post-id'), 10);
|
||||
const sourceIndex = parseInt(button.getAttribute('data-source-index'), 10);
|
||||
if (!postId || isNaN(sourceIndex)) return;
|
||||
|
||||
button.disabled = true;
|
||||
const original = button.innerHTML;
|
||||
button.innerHTML = '<i class="fa fas fa-spinner fa-spin" aria-hidden="true"></i> 处理中…';
|
||||
|
||||
const restNonce = window.aksrtData.restNonce || '';
|
||||
|
||||
fetch(REST_BASE + '/download/request', {
|
||||
method: 'POST',
|
||||
credentials: 'same-origin',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-WP-Nonce': restNonce,
|
||||
},
|
||||
body: JSON.stringify({
|
||||
post_id: postId,
|
||||
source_index: sourceIndex,
|
||||
captcha: captchaPayload,
|
||||
}),
|
||||
})
|
||||
.then(function (resp) { return resp.json().then(function (j) { return { ok: resp.ok, json: j }; }); })
|
||||
.then(function (r) {
|
||||
button.disabled = false;
|
||||
button.innerHTML = original;
|
||||
if (!r.ok || (r.json && r.json.code)) {
|
||||
renderError(r.json.message || '请稍后重试', r.json.data || {}, button, r.json.code);
|
||||
return;
|
||||
}
|
||||
startDownload(r.json, button);
|
||||
})
|
||||
.catch(function () {
|
||||
button.disabled = false;
|
||||
button.innerHTML = original;
|
||||
renderError('网络错误,请稍后重试', {}, button);
|
||||
})
|
||||
.finally(function () {
|
||||
if (window.AKSRTCaptcha) window.AKSRTCaptcha.reset('download', captchaScope(button));
|
||||
});
|
||||
}
|
||||
|
||||
function requestDownload(button) {
|
||||
ensureCaptchaThen(button, function (captchaPayload) {
|
||||
performDownloadRequest(button, captchaPayload);
|
||||
});
|
||||
}
|
||||
|
||||
function pollOrderPaid(orderNumber, returnFocusEl) {
|
||||
if (!orderNumber) return;
|
||||
@@ -387,12 +298,6 @@
|
||||
}, 3000);
|
||||
}
|
||||
|
||||
// ===== 积分不足按钮(前置拦截,不发请求) =====
|
||||
function showLowCreditModal(button) {
|
||||
const balance = parseInt(button.getAttribute('data-balance'), 10) || 0;
|
||||
const cost = parseInt(button.getAttribute('data-cost'), 10) || 0;
|
||||
renderError('', { balance: balance, cost: cost }, button, 'aksrt_points_insufficient');
|
||||
}
|
||||
|
||||
// ===== 收银台(内容购买) =====
|
||||
function restHeaders(idempotencyKey) {
|
||||
@@ -807,13 +712,6 @@
|
||||
|
||||
// ===== 事件绑定 =====
|
||||
document.addEventListener('click', function (e) {
|
||||
const lowBtn = e.target.closest('[data-action="aksrt-need-credits"]');
|
||||
if (lowBtn) {
|
||||
e.preventDefault();
|
||||
showLowCreditModal(lowBtn);
|
||||
return;
|
||||
}
|
||||
|
||||
const buyBtn = e.target.closest('[data-action="aksrt-need-purchase"], [data-aksrt-pay-content]');
|
||||
if (buyBtn) {
|
||||
e.preventDefault();
|
||||
@@ -828,12 +726,6 @@
|
||||
if (postId) openCashier(postId, buyBtn, priceType);
|
||||
return;
|
||||
}
|
||||
|
||||
const btn = e.target.closest('[data-action="aksrt-download"]');
|
||||
if (!btn) return;
|
||||
if (btn.disabled || btn.getAttribute('aria-disabled') === 'true') return;
|
||||
e.preventDefault();
|
||||
requestDownload(btn);
|
||||
});
|
||||
|
||||
// Public cashier bridge (content pay).
|
||||
|
||||
Reference in new issue
Block a user