'GET', 'callback' => [$this, 'rest_health_email_verification'], 'permission_callback' => [$this, 'check_permission'], ]); // Health: REST block toggle register_rest_route(self::API_NAMESPACE, '/health/rest-block', [ 'methods' => 'POST', 'callback' => [$this, 'rest_set_rest_block'], 'permission_callback' => [$this, 'check_permission'], ]); // Email rollback: list users with recent email changes register_rest_route(self::API_NAMESPACE, '/email-rollback/users', [ 'methods' => 'GET', 'callback' => [$this, 'rest_email_rollback_list'], 'permission_callback' => [$this, 'check_permission'], ]); // Email rollback: execute rollback register_rest_route(self::API_NAMESPACE, '/email-rollback/users/(?P\d+)/rollback', [ 'methods' => 'POST', 'callback' => [$this, 'rest_email_rollback_execute'], 'permission_callback' => [$this, 'check_permission'], ]); // Tools: system health (compact format for ExtensionTools.vue) register_rest_route(self::API_NAMESPACE, '/tools/health', [ 'methods' => 'GET', 'callback' => [$this, 'get_tools_health'], 'permission_callback' => [$this, 'check_permission'], ]); // Tools: maintenance actions register_rest_route(self::API_NAMESPACE, '/tools/maintenance', [ 'methods' => 'POST', 'callback' => [$this, 'run_maintenance'], 'permission_callback' => [$this, 'check_permission'], ]); // Tools: update center register_rest_route(self::API_NAMESPACE, '/tools/update-info', [ 'methods' => 'GET', 'callback' => [$this, 'get_update_info'], 'permission_callback' => [$this, 'check_permission'], ]); register_rest_route(self::API_NAMESPACE, '/tools/update-check', [ 'methods' => 'POST', 'callback' => [$this, 'detect_update'], 'permission_callback' => [$this, 'check_permission'], ]); register_rest_route(self::API_NAMESPACE, '/tools/update-dismiss', [ 'methods' => 'POST', 'callback' => [$this, 'dismiss_update'], 'permission_callback' => [$this, 'check_permission'], ]); } /** * GET /tools/health — compact health data for the tools page. */ public function get_tools_health(): WP_REST_Response { global $wpdb; $upload_dir = wp_upload_dir(); $upload_ok = !empty($upload_dir['error']) === false && is_dir($upload_dir['basedir']); $db_ok = true; $db_status = '正常'; $expected_tables = [ $wpdb->prefix . 'aksrt_credit_log', $wpdb->prefix . 'aksrt_signin_log', $wpdb->prefix . 'aksrt_invite', $wpdb->prefix . 'aksrt_dl_token', $wpdb->prefix . 'aksrt_notification', ]; $missing = []; foreach ($expected_tables as $table) { if ($wpdb->get_var($wpdb->prepare('SHOW TABLES LIKE %s', $table)) !== $table) { $missing[] = str_replace($wpdb->prefix, '', $table); } } if (!empty($missing)) { $db_ok = false; $db_status = '缺少: ' . implode(', ', $missing); } return $this->success([ 'data' => [ 'php_version' => PHP_VERSION, 'wp_version' => get_bloginfo('version'), 'db_status' => $db_status, 'db_ok' => $db_ok, 'upload_dir' => $upload_dir['basedir'], 'upload_ok' => $upload_ok, 'theme_version' => wp_get_theme()->get('Version') ?? 'unknown', ], ]); } /** * POST /tools/maintenance — run a maintenance action. */ public function run_maintenance(WP_REST_Request $request): WP_REST_Response { $params = $this->get_json_params($request); $action = sanitize_key((string) ($params['action'] ?? '')); switch ($action) { case 'flush_cache': // Flush WordPress object cache wp_cache_flush(); // Clear transients global $wpdb; $wpdb->query("DELETE FROM {$wpdb->options} WHERE option_name LIKE '_transient_aksrt_%' OR option_name LIKE '_transient_timeout_aksrt_%'"); return $this->success(['message' => __('缓存已清除', 'aksrt')]); case 'optimize_db': global $wpdb; $tables = [ $wpdb->prefix . 'aksrt_credit_log', $wpdb->prefix . 'aksrt_signin_log', $wpdb->prefix . 'aksrt_invite', $wpdb->prefix . 'aksrt_dl_token', $wpdb->prefix . 'aksrt_notification', ]; $optimized = 0; foreach ($tables as $table) { if ($wpdb->get_var($wpdb->prepare('SHOW TABLES LIKE %s', $table)) === $table) { $wpdb->query("OPTIMIZE TABLE {$table}"); $optimized++; } } return $this->success(['message' => sprintf(__('已优化 %d 个数据表', 'aksrt'), $optimized)]); case 'rebuild_index': // Rebuild search index / recount stats if (class_exists('AKSRT_Forum') && method_exists(AKSRT_Forum::get_instance(), 'recount_stats')) { AKSRT_Forum::get_instance()->recount_stats(); } return $this->success(['message' => __('索引重建完成', 'aksrt')]); default: return $this->error('未知的维护操作: ' . esc_html($action)); } } public function get_update_info(): WP_REST_Response { $theme = wp_get_theme(); $info = [ 'current_version' => $theme->get('Version') ?: '0.0.0', 'name' => $theme->get('Name') ?: 'AKSRT', 'update_available'=> false, 'remote_version' => '', 'download_url' => '', 'changelog' => '', 'last_check' => 0, 'dismissed' => '', ]; if (class_exists('AKSRT_Update_Checker')) { $stored = AKSRT_Update_Checker::get_instance()->get_update_info(); if (is_array($stored)) { $info = array_merge($info, $stored); } } $info['dismissed'] = (string) get_user_meta(get_current_user_id(), 'aksrt_dismissed_update', true); return $this->success(['data' => $info]); } public function detect_update(): WP_REST_Response { if (!class_exists('AKSRT_Update_Checker')) { return $this->error(__('更新检查器不可用', 'aksrt'), 500); } AKSRT_Update_Checker::get_instance()->do_check(); $stored = AKSRT_Update_Checker::get_instance()->get_update_info(); return $this->success([ 'message' => __('已完成更新检测', 'aksrt'), 'data' => is_array($stored) ? $stored : [], ]); } public function dismiss_update(WP_REST_Request $request): WP_REST_Response { $params = $this->get_json_params($request); $version = sanitize_text_field((string) ($params['version'] ?? '')); if ($version === '') { return $this->error(__('缺少版本号', 'aksrt'), 422); } update_user_meta(get_current_user_id(), 'aksrt_dismissed_update', $version); return $this->success([ 'message' => __('已忽略此版本提醒', 'aksrt'), 'data' => ['version' => $version], ]); } public function rest_health_email_verification(): WP_REST_Response { $feature_enabled = class_exists('AKSRT_Settings') && AKSRT_Settings::registration_requires_email_verification(); $pending_users = 0; if (class_exists('AKSRT_Auth_Email_Verification')) { global $wpdb; $pending_users = (int) $wpdb->get_var( "SELECT COUNT(*) FROM {$wpdb->usermeta} WHERE meta_key = 'aksrt_email_verified' AND CAST(meta_value AS UNSIGNED) = 0" ); } return $this->success(['data' => [ 'feature_enabled' => $feature_enabled, 'pending_users' => $pending_users, 'rest_blocking_active' => !empty(get_option('aksrt_rest_block_email_verification', false)), ]]); } public function rest_set_rest_block(WP_REST_Request $request): WP_REST_Response { $params = $this->get_json_params($request); $enabled = !empty($params['enabled']); update_option('aksrt_rest_block_email_verification', $enabled); return $this->success([ 'message' => $enabled ? __('已启用 REST 邮箱验证阻断', 'aksrt') : __('已关闭 REST 邮箱验证阻断', 'aksrt'), 'data' => ['enabled' => $enabled], ]); } public function rest_email_rollback_list(WP_REST_Request $request): WP_REST_Response { if (!class_exists('AKSRT_Email_Rollback')) { return $this->success(['data' => ['items' => [], 'total' => 0, 'page' => 1, 'per_page' => 20]]); } $page = max(1, (int) $request->get_param('page')); $per_page = min(100, max(1, (int) $request->get_param('per_page'))); $data = AKSRT_Email_Rollback::list_pending($page, $per_page); return $this->success(['data' => $data]); } public function rest_email_rollback_execute(WP_REST_Request $request): WP_REST_Response { if (!class_exists('AKSRT_Email_Rollback')) { return $this->error(__('邮箱回滚功能不可用', 'aksrt'), 500); } $user_id = (int) $request->get_param('id'); $result = AKSRT_Email_Rollback::rollback($user_id, get_current_user_id()); if (empty($result['success'])) { return $this->error((string) ($result['error'] ?? __('邮箱回滚失败', 'aksrt')), 422); } return $this->success([ 'message' => (string) ($result['message'] ?? __('邮箱已回滚', 'aksrt')), 'data' => $result, ]); } }