' . esc_html__('暂无可下载资源', 'aksrt') . '
';
}
+ // 下载总开关关闭 = 直连模式:按钮直接输出原始地址,不走本站中转链接。
+ $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)
@@ -1474,8 +1469,13 @@ if (!function_exists('aksrt_get_paid_download_actions_html')) {
$copy_label = $extract_code !== '' ? $extract_code : ($copy_key !== '' ? $copy_key : __('更多内容', 'aksrt'));
$buttons .= '';
- $buttons .= '
';
+ if ($direct_download) {
+ $buttons .= '
';
+ $buttons .= '' . esc_html($label) . '';
+ } else {
+ $buttons .= '
';
+ }
if ($copy_value !== '') {
$buttons .= '
';
} elseif ($extract_code !== '') {
diff --git a/src/Admin/Schema/SchemaPermissions.php b/src/Admin/Schema/SchemaPermissions.php
index 5708a73f..da732cc7 100644
--- a/src/Admin/Schema/SchemaPermissions.php
+++ b/src/Admin/Schema/SchemaPermissions.php
@@ -73,13 +73,16 @@ class SchemaPermissions {
'download' => [
'key' => 'download',
'title' => '下载配置',
- 'version' => '1.0.0',
+ 'version' => '1.1.0',
'option_key' => 'aksrt_download_config',
'groups' => [
- ['key' => 'basic', 'title' => '链接规则', 'fields' => [
- ['key' => 'url_alias', 'label' => '下载链接 URL 别名', 'type' => 'input', 'default' => 'download', 'required' => true, 'help' => '仅允许英文字母、数字、连字符和下划线;禁止中文、空格和 / \\ ? # & 等特殊符号。修改后旧别名路由立即失效,需重新获取下载地址。'],
- ['key' => 'default_max_uses', 'label' => '下载链接默认使用次数', 'type' => 'number', 'default' => 1, 'min' => 1, 'max' => 100, 'help' => '单个链接可完成下载的次数;文件完整送达后才计次(HEAD 探测、传输中断不计次)。耗尽后链接返回 410,需重新获取。'],
- ['key' => 'ttl_minutes', 'label' => '下载链接默认有效期限(分钟)', 'type' => 'number', 'default' => 10, 'min' => 1, 'max' => 1440, 'help' => '链接签发后超过该时长即失效并返回 410,不自动续期,需重新获取。'],
+ ['key' => 'basic', 'title' => '下载总开关', 'fields' => [
+ ['key' => 'enabled', 'label' => '启用本站下载链接', 'type' => 'switch', 'default' => true, 'help' => '总开关:开启后下载通过本站中转链接(别名、次数、有效期生效);关闭后下载按钮直接使用原始下载地址(直连),下方链接规则全部不生效,旧的中转链接同步失效。'],
+ ]],
+ ['key' => 'rules', 'title' => '链接规则', 'fields' => [
+ ['key' => 'url_alias', 'label' => '下载链接 URL 别名', 'type' => 'input', 'default' => 'download', 'required' => true, 'dependsOn' => ['key' => 'enabled', 'value' => true], 'help' => '仅允许英文字母、数字、连字符和下划线;禁止中文、空格和 / \\ ? # & 等特殊符号。修改后旧别名路由立即失效,需重新获取下载地址。'],
+ ['key' => 'default_max_uses', 'label' => '下载链接默认使用次数', 'type' => 'number', 'default' => 1, 'min' => 1, 'max' => 100, 'dependsOn' => ['key' => 'enabled', 'value' => true], 'help' => '单个链接可完成下载的次数;文件完整送达后才计次(HEAD 探测、传输中断不计次)。耗尽后链接返回 410,需重新获取。'],
+ ['key' => 'ttl_minutes', 'label' => '下载链接默认有效期限(分钟)', 'type' => 'number', 'default' => 10, 'min' => 1, 'max' => 1440, 'dependsOn' => ['key' => 'enabled', 'value' => true], 'help' => '链接签发后超过该时长即失效并返回 410,不自动续期,需重新获取。'],
]],
],
],
diff --git a/src/Content/Download.php b/src/Content/Download.php
index 36d09d57..4e1c7341 100644
--- a/src/Content/Download.php
+++ b/src/Content/Download.php
@@ -68,6 +68,19 @@ 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.
@@ -97,6 +110,10 @@ class Download {
}
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]',
@@ -105,11 +122,12 @@ class Download {
}
/**
- * Re-flush rewrite rules whenever the configured alias changes, using
- * the same version-marker approach as Auth\Router.
+ * Re-flush rewrite rules whenever the configured alias or the master
+ * switch changes, using the same version-marker approach as Auth\Router.
*/
public function maybe_flush_rewrite(): void {
- $version = AKSRT_VERSION . ':download-endpoint:' . $this->config()['endpoint'];
+ $endpoint = $this->is_enabled() ? $this->config()['endpoint'] : 'off';
+ $version = AKSRT_VERSION . ':download-endpoint:' . $endpoint;
if ((string) get_option('aksrt_dl_rewrite_version') !== $version) {
flush_rewrite_rules(false);
update_option('aksrt_dl_rewrite_version', $version);
@@ -122,17 +140,15 @@ 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'],
'permission_callback' => [$this, 'check_rest_nonce'],
]);
-
- register_rest_route('aksrt/v1', '/download/policy/(?P
\d+)', [
- 'methods' => 'GET',
- 'callback' => [$this, 'rest_policy'],
- 'permission_callback' => '__return_true',
- ]);
}
public function check_rest_nonce(\WP_REST_Request $request) {
@@ -155,63 +171,6 @@ class Download {
return true;
}
- public function rest_policy(\WP_REST_Request $request) {
- $post_id = (int) $request['id'];
- $post = get_post($post_id);
- if (!$post || !function_exists('aksrt_is_downloadable_content') || !aksrt_is_downloadable_content($post)) {
- return new \WP_Error('aksrt_invalid', __('内容不存在或未启用下载能力', 'aksrt'), ['status' => 404]);
- }
-
- $meta = function_exists('aksrt_get_download_box_meta') ? aksrt_get_download_box_meta($post) : [];
- $cost = aksrt_resource_download_cost($post);
- $uid = get_current_user_id();
- $priced = function_exists('aksrt_get_user_pay_price') ? aksrt_get_user_pay_price($post, $uid) : null;
- $pay_modo = is_array($priced)
- ? ((($priced['modo'] ?? '0') === 'points') ? 'points' : '0')
- : ((($meta['pay_modo'] ?? '0') === 'points') ? 'points' : '0');
- $display_price = is_array($priced)
- ? ($priced['amount'] ?? ($pay_modo === 'points' ? 0 : '0.00'))
- : (($pay_modo === 'points') ? (int) ($meta['points_price'] ?? 0) : (string) ($meta['price'] ?? '0.00'));
-
- $access = class_exists('AKSRT\\Content\\Visibility')
- ? Visibility::get_post_access_state($post, $uid)
- : [];
-
- $need_captcha = function_exists('aksrt_is_captcha_enabled_for') && aksrt_is_captcha_enabled_for('download');
-
- return new \WP_REST_Response([
- 'post_id' => $post_id,
- 'price' => $display_price,
- 'base_price' => is_array($priced) ? ($priced['base'] ?? $display_price) : $display_price,
- 'pay_modo' => $pay_modo,
- 'pay_limit' => (int) ($meta['pay_limit'] ?? 0),
- 'cost' => $cost,
- 'require_login' => !empty($access['need_login']),
- 'require_captcha' => $need_captcha,
- 'logged_in' => $uid > 0,
- 'balance' => $uid > 0 ? \AKSRT\Ledger\LedgerService::get_instance()->balance($uid, \AKSRT\Ledger\AssetType::POINTS) : 0,
- 'has_paid_access' => !empty($access['has_paid_access']),
- 'need_purchase' => !empty($access['need_purchase']),
- 'need_vip' => !empty($access['need_vip']),
- 'need_login' => !empty($access['need_login']) && $uid <= 0,
- 'can_download' => !empty($access['can_download']),
- 'login_url' => wp_login_url(get_permalink($post)),
- 'vip_level' => (int) ($access['user_vip_level'] ?? 0),
- 'free_limit' => $uid > 0 && function_exists('aksrt_get_user_free_download_limit') ? aksrt_get_user_free_download_limit($uid) : 0,
- 'free_used' => $uid > 0 && function_exists('aksrt_get_user_free_downloaded_number') ? aksrt_get_user_free_downloaded_number($uid) : 0,
- 'free_surplus' => $uid > 0 && function_exists('aksrt_get_free_download_surplus') ? aksrt_get_free_download_surplus($uid) : null,
- 'sources' => array_map(function ($row, $idx) {
- $extract_code = trim((string) ($row['extract_code'] ?? ''));
- return [
- 'index' => $idx,
- 'label' => $row['label'] ?? '',
- 'type' => $row['type'] ?? 'direct',
- 'has_extract' => $extract_code !== '',
- ];
- }, $meta['downloads'] ?? [], array_keys($meta['downloads'] ?? [])),
- ], 200);
- }
-
public function rest_request(\WP_REST_Request $request) {
$params = $request->get_json_params();
if (!is_array($params)) $params = $request->get_params();
@@ -303,6 +262,11 @@ 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();
diff --git a/templates/parts/resource-download-box.php b/templates/parts/resource-download-box.php
index 516ea795..87f2a85e 100644
--- a/templates/parts/resource-download-box.php
+++ b/templates/parts/resource-download-box.php
@@ -26,6 +26,10 @@ 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');
@@ -56,7 +60,10 @@ $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;
-$captcha_active = function_exists('aksrt_is_captcha_enabled_for') && aksrt_is_captcha_enabled_for('download');
+// 直连模式下没有“获取链接”请求,验证码容器不会被打开,属死 UI 不渲染。
+$captcha_active = !$direct_download
+ && 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)));
$vip_url = function_exists('aksrt_get_user_center_tab_url')
@@ -343,6 +350,10 @@ 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 !== '';
@@ -353,23 +364,38 @@ if ($is_need_purchase && $user_amount > 0) {
if ($is_need_login) {
$btn_tag = 'a';
+ $btn_href = $login_url;
$btn_text = __('登录后下载', 'aksrt');
$btn_icon = 'fa-sign-in-alt';
$btn_modifier = ' resource-download-box__btn--login';
$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 = '';
$btn_text = __('立即下载', 'aksrt');
$btn_icon = 'fa-download';
$btn_modifier = $has_paid_access ? ' resource-download-box__btn--owned' : '';
$btn_action = 'aksrt-download';
+ $btn_target = '';
+ $btn_rel = '';
}
?>
data-source-index=""
data-source-type=""
style="--service-color: "
@@ -380,6 +406,12 @@ if ($is_need_purchase && $user_amount > 0) {
+
+
+