fix(download): pin frontend link generation to fixed relay config

The runtime kept reading aksrt_download_config after the settings page was
removed, so the frontend still issued download links per the stale option
values instead of the built-in defaults.

- Pin endpoint, per-token uses and TTL to built-in constants and drop all
  option reads plus the is_enabled() master switch
- Remove the now-unreachable direct-download branches from the download
  box template and the paid-card download helper
- Lock the pinned contract in the download contract tests
This commit is contained in:
2026-09-24 18:45:08 +08:00
parent 800baca0ca
commit d49fd9f6a4
4 changed files with 51 additions and 130 deletions
-13
View File
@@ -1457,20 +1457,12 @@ if (!function_exists('aksrt_get_paid_download_actions_html')) {
return '<div class="muted-2-color text-center">' . esc_html__('暂无可下载资源', 'aksrt') . '</div>';
}
// 下载总开关关闭 = 直连模式:按钮直接输出原始地址,不走本站中转链接。
$direct_download = class_exists('\AKSRT\Content\Download')
&& !\AKSRT\Content\Download::get_instance()->is_enabled();
$buttons = '';
foreach ($downloads as $index => $download) {
if (!is_array($download)) {
continue;
}
$url = (string) ($download['url'] ?? '');
if ($direct_download && $url === '') {
// 直连模式没有可用的原始地址,空源不渲染。
continue;
}
$label = trim((string) ($download['label'] ?? ''));
$service = function_exists('aksrt_detect_cloud_service')
? aksrt_detect_cloud_service($url)
@@ -1489,13 +1481,8 @@ if (!function_exists('aksrt_get_paid_download_actions_html')) {
$copy_label = $extract_code !== '' ? $extract_code : ($copy_key !== '' ? $copy_key : __('更多内容', 'aksrt'));
$buttons .= '<div class="but-download flex ac">';
if ($direct_download) {
$buttons .= '<a class="mr10 but ' . esc_attr($button_class) . '" href="' . esc_url($url) . '" target="_blank" rel="nofollow noopener">';
$buttons .= '<i class="fa fas fa-download" aria-hidden="true"></i>' . esc_html($label) . '</a>';
} else {
$buttons .= '<button type="button" class="mr10 but ' . esc_attr($button_class) . '" data-action="aksrt-download" data-source-index="' . esc_attr((string) $index) . '" data-source-type="' . esc_attr($type) . '" data-has-extract="' . ($has_extract ? '1' : '0') . '">';
$buttons .= '<i class="fa fas fa-download" aria-hidden="true"></i>' . esc_html($label) . '</button>';
}
if ($copy_value !== '') {
$buttons .= '<button type="button" class="but" data-clipboard-tag="' . esc_attr($copy_key !== '' ? $copy_key : __('内容', 'aksrt')) . '" data-clipboard-text="' . esc_attr($copy_value) . '">' . esc_html($copy_label) . '</button>';
} elseif ($extract_code !== '') {
+13 -56
View File
@@ -12,11 +12,12 @@ class Download {
const TOKEN_LEN = 48;
const QUERY_VAR = 'aksrt_dl_token';
// Hardened fallbacks used when the aksrt_download_config option is
// missing or contains values the admin validation would reject.
const FALLBACK_ENDPOINT = 'download';
const FALLBACK_MAX_USES = 1;
const FALLBACK_TTL_MINUTES = 10;
// Fixed relay configuration. The admin download-config settings page was
// removed, so these constants are the single source of truth for the
// site-routed download system (endpoint, per-token uses, TTL).
const ENDPOINT = 'download';
const MAX_USES = 1;
const TTL_MINUTES = 10;
private static $instance = null;
@@ -69,51 +70,17 @@ class Download {
}
/**
* Master switch for the site-routed download system (aksrt_download_config
* "enabled", default true). When off, download buttons render the raw
* source URLs directly and every token route/endpoint below stays dormant.
*/
public function is_enabled(): bool {
$cfg = get_option('aksrt_download_config', []);
if (!is_array($cfg)) {
return true;
}
return !isset($cfg['enabled']) || !empty($cfg['enabled']);
}
/**
* Effective download settings (aksrt_download_config), clamped so a
* corrupted option can never break routing or accounting.
* Effective download settings for the fixed relay configuration.
*/
private function config(): array {
$cfg = get_option('aksrt_download_config', []);
if (!is_array($cfg)) {
$cfg = [];
}
$alias = trim((string) ($cfg['url_alias'] ?? self::FALLBACK_ENDPOINT));
if ($alias === '' || strlen($alias) > 48 || !preg_match('/^[A-Za-z0-9_-]+$/', $alias)) {
$alias = self::FALLBACK_ENDPOINT;
}
$max_uses = isset($cfg['default_max_uses']) ? (int) $cfg['default_max_uses'] : self::FALLBACK_MAX_USES;
$max_uses = min(100, max(1, $max_uses));
$ttl_minutes = isset($cfg['ttl_minutes']) ? (int) $cfg['ttl_minutes'] : self::FALLBACK_TTL_MINUTES;
$ttl_minutes = min(1440, max(1, $ttl_minutes));
return [
'endpoint' => $alias,
'max_uses' => $max_uses,
'ttl' => $ttl_minutes * MINUTE_IN_SECONDS,
'endpoint' => self::ENDPOINT,
'max_uses' => self::MAX_USES,
'ttl' => self::TTL_MINUTES * MINUTE_IN_SECONDS,
];
}
public function register_rewrite(): void {
if (!$this->is_enabled()) {
return;
}
add_rewrite_rule(
'^' . preg_quote($this->config()['endpoint'], '/') . '/([A-Za-z0-9]{1,64})/?$',
'index.php?' . self::QUERY_VAR . '=$matches[1]',
@@ -122,12 +89,11 @@ class Download {
}
/**
* Re-flush rewrite rules whenever the configured alias or the master
* switch changes, using the same version-marker approach as Auth\Router.
* Re-flush rewrite rules on theme version changes, using the same
* version-marker approach as Auth\Router.
*/
public function maybe_flush_rewrite(): void {
$endpoint = $this->is_enabled() ? $this->config()['endpoint'] : 'off';
$version = AKSRT_VERSION . ':download-endpoint:' . $endpoint;
$version = AKSRT_VERSION . ':download-endpoint:' . self::ENDPOINT;
if ((string) get_option('aksrt_dl_rewrite_version') !== $version) {
flush_rewrite_rules(false);
update_option('aksrt_dl_rewrite_version', $version);
@@ -140,10 +106,6 @@ class Download {
}
public function register_routes(): void {
if (!$this->is_enabled()) {
return;
}
register_rest_route('aksrt/v1', '/download/request', [
'methods' => 'POST',
'callback' => [$this, 'rest_request'],
@@ -257,11 +219,6 @@ class Download {
$this->fail_410();
}
// Master switch off: the token system is retired, links are dead ends.
if (!$this->is_enabled()) {
$this->fail_410();
}
global $wpdb;
$table = \AKSRT\Core\DBMigrator::table('dl_token');
if (!$table) $this->fail_410();
+1 -25
View File
@@ -27,9 +27,6 @@ if ($pay_type !== 'download' || empty($downloads)) {
$is_resource_context = function_exists('aksrt_is_resource_post') && aksrt_is_resource_post($post_id);
$context_variant = $is_resource_context ? 'resource' : 'content';
// 下载总开关关闭 = 直连模式:下载按钮直接输出原始地址,不走本站中转链接。
$direct_download = class_exists('\AKSRT\Content\Download')
&& !\AKSRT\Content\Download::get_instance()->is_enabled();
$subject_label = trim((string) ($meta['subject_label'] ?? ''));
if ($subject_label === '') {
$subject_label = $is_resource_context ? __('资源', 'aksrt') : __('内容', 'aksrt');
@@ -60,9 +57,7 @@ $pay_modo = is_array($priced)
$user_amount = is_array($priced) ? (float) ($priced['amount'] ?? 0) : (float) ($meta['price'] ?? 0);
$cost = function_exists('aksrt_resource_download_cost') ? (int) aksrt_resource_download_cost($post_id) : 0;
// 直连模式下没有“获取链接”请求,验证码容器不会被打开,属死 UI 不渲染。
$captcha_active = !$direct_download
&& function_exists('aksrt_is_captcha_enabled_for')
$captcha_active = function_exists('aksrt_is_captcha_enabled_for')
&& aksrt_is_captcha_enabled_for('download');
$balance = (int) ($gate_data['balance'] ?? (is_user_logged_in() ? \AKSRT\Ledger\LedgerService::get_instance()->balance(get_current_user_id(), \AKSRT\Ledger\AssetType::POINTS) : 0));
$login_url = (string) ($gate_data['login_url'] ?? wp_login_url(get_permalink($post_id)));
@@ -350,10 +345,6 @@ if ($is_need_purchase && $user_amount > 0) {
}
$type = isset($row['type']) ? (string) $row['type'] : 'direct';
$url = isset($row['url']) ? (string) $row['url'] : '';
if ($direct_download && $url === '') {
// 直连模式没有可用的原始地址,空源不渲染。
continue;
}
$extract_code = isset($row['extract_code']) ? trim((string) $row['extract_code']) : '';
$has_extract = $extract_code !== '';
@@ -371,15 +362,6 @@ if ($is_need_purchase && $user_amount > 0) {
$btn_action = '';
$btn_target = '';
$btn_rel = '';
} elseif ($direct_download) {
$btn_tag = 'a';
$btn_href = $url;
$btn_text = __('立即下载', 'aksrt');
$btn_icon = 'fa-download';
$btn_modifier = $has_paid_access ? ' resource-download-box__btn--owned' : '';
$btn_action = '';
$btn_target = ' target="_blank"';
$btn_rel = ' rel="nofollow noopener"';
} else {
$btn_tag = 'button';
$btn_href = '';
@@ -406,12 +388,6 @@ if ($is_need_purchase && $user_amount > 0) {
<?php echo esc_html($service['name']); ?>
</span>
</a>
<?php if ($direct_download && $has_extract) : ?>
<button type="button" class="resource-download-box__btn resource-download-box__btn--extract"
data-clipboard-tag="<?php esc_attr_e('提取码', 'aksrt'); ?>"
data-clipboard-text="<?php echo esc_attr($extract_code); ?>"
><?php echo esc_html($extract_code); ?></button>
<?php endif; ?>
<?php else : ?>
<button
type="button"
@@ -8,13 +8,19 @@ const test = require('node:test');
const root = path.resolve(__dirname, '..', '..');
const read = (file) => fs.readFileSync(path.join(root, file), 'utf8');
test('Download.php derives endpoint, ttl and max uses from configuration', () => {
test('download relay configuration is pinned to built-in constants', () => {
const dl = read('src/Content/Download.php');
// Dead hard-coded values are gone.
assert.doesNotMatch(dl, /const ENDPOINT/);
assert.doesNotMatch(dl, /const TOKEN_TTL/);
assert.doesNotMatch(dl, /aksrt-dl/);
// The admin settings page is gone; the option must never be read again.
assert.doesNotMatch(dl, /aksrt_download_config/);
// The retired master switch is gone from the runtime.
assert.doesNotMatch(dl, /function is_enabled/);
// Single source of truth: fixed endpoint, per-token uses and TTL.
assert.match(dl, /const ENDPOINT\s*=\s*'download'/);
assert.match(dl, /const MAX_USES\s*=\s*1/);
assert.match(dl, /const TTL_MINUTES\s*=\s*10/);
// Rewrite regex and URL building share the configured alias.
assert.match(dl, /preg_quote\(\$this->config\(\)\['endpoint'\], '\/'\)/);
@@ -23,11 +29,34 @@ test('Download.php derives endpoint, ttl and max uses from configuration', () =>
assert.match(dl, /flush_rewrite_rules\(false\)/);
assert.match(dl, /aksrt_dl_rewrite_version/);
// TTL and per-token max uses come from the settings section.
// TTL and per-token max uses come from the fixed configuration.
assert.match(dl, /'expires_in'\s+=> \$config\['ttl'\]/);
assert.match(dl, /'max_uses'\s+=> \(int\) \$config\['max_uses'\]/);
});
test('the retired master switch leaves no direct-download branches behind', () => {
const dl = read('src/Content/Download.php');
const box = read('templates/parts/resource-download-box.php');
const pay = read('inc/helpers/aksrt-pay.php');
// No runtime switch and no option reads remain.
assert.doesNotMatch(dl, /function is_enabled/);
assert.doesNotMatch(dl, /aksrt_download_config/);
// Direct mode was only reachable through the removed switch.
assert.doesNotMatch(box, /direct_download/);
assert.doesNotMatch(pay, /direct_download/);
// Relay buttons stay the only download path on both surfaces; the login
// anchor and the paid-card relay button keep their original markup.
assert.match(box, /data-action="<\?php echo esc_attr\(\$btn_action\); \?>/);
assert.match(box, /href="<\?php echo esc_url\(\$btn_href\); \?>/);
assert.match(pay, /data-action="aksrt-download"/);
// Dead standalone-page helper stays removed.
assert.doesNotMatch(pay, /aksrt_is_download_alone_page_enabled/);
});
test('usage is counted only after full delivery, never for probes', () => {
const dl = read('src/Content/Download.php');
@@ -89,31 +118,3 @@ test('token generation retries on primary-key collisions', () => {
assert.match(dl, /PRIMARY KEY\(token\) already guarantees uniqueness/);
assert.match(dl, /for \(\$attempt = 0; \$attempt < 3; \$attempt\+\+\)/);
});
test('master switch off puts the token system to sleep and renders direct links', () => {
const dl = read('src/Content/Download.php');
const box = read('templates/parts/resource-download-box.php');
const pay = read('inc/helpers/aksrt-pay.php');
// The switch is a public runtime check; missing key means enabled.
assert.match(dl, /public function is_enabled\(\): bool/);
assert.match(dl, /!isset\(\$cfg\['enabled'\]\) \|\| !empty\(\$cfg\['enabled'\]\)/);
// Rewrite, REST routes and token serving all bail out when disabled.
assert.match(dl, /if \(!\$this->is_enabled\(\)\) \{\s*return;\s*\}/);
assert.match(dl, /Master switch off: the token system is retired/);
// The unused policy endpoint stays removed.
assert.doesNotMatch(dl, /download\/policy/);
assert.doesNotMatch(dl, /rest_policy/);
// Direct mode renders anchors to the raw source URL instead of token buttons.
assert.match(box, /\$direct_download = class_exists\('\\AKSRT\\Content\\Download'\)/);
assert.match(box, /elseif \(\$direct_download\) \{/);
assert.match(box, /href="<\?php echo esc_url\(\$btn_href\); \?>/);
assert.match(box, /data-clipboard-text="<\?php echo esc_attr\(\$extract_code\); \?>/);
assert.match(pay, /if \(\$direct_download\) \{\s*\$buttons \.= '<a class="mr10 but /);
// Dead standalone-page helper stays removed.
assert.doesNotMatch(pay, /aksrt_is_download_alone_page_enabled/);
});