wpmind/modules/api-gateway/includes/Admin/GatewayAjaxController.php
LinuxJoy adb6c49301 style: phpcbf 自动格式化 — 全量 WPCS 3.x 规范对齐
135 文件 19,211 处自动修复:空格→Tab 缩进、括号间距、
函数声明空格、前置自增、尾逗号等纯格式化改动。
零逻辑变更,php -l + token 级对比验证通过。

新增 phpcs.xml.dist / phpstan.neon.dist 项目配置。

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-19 00:48:46 +08:00

307 lines
9.9 KiB
PHP

<?php
/**
* Gateway AJAX Controller
*
* Handles admin AJAX requests for the API Gateway module.
*
* @package WPMind\Modules\ApiGateway\Admin
* @since 1.0.0
*/
declare(strict_types=1);
namespace WPMind\Modules\ApiGateway\Admin;
use WPMind\Modules\ApiGateway\Auth\ApiKeyManager;
use WPMind\Modules\ApiGateway\Auth\ApiKeyRepository;
/**
* Class GatewayAjaxController
*
* Registers and handles all AJAX actions for the API Gateway settings page.
*/
class GatewayAjaxController {
/**
* Register AJAX hooks.
*/
public function register_hooks(): void {
add_action( 'wp_ajax_wpmind_save_gateway_settings', [ $this, 'ajax_save_gateway_settings' ] );
add_action( 'wp_ajax_wpmind_create_api_key', [ $this, 'ajax_create_api_key' ] );
add_action( 'wp_ajax_wpmind_list_api_keys', [ $this, 'ajax_list_api_keys' ] );
add_action( 'wp_ajax_wpmind_revoke_api_key', [ $this, 'ajax_revoke_api_key' ] );
add_action( 'wp_ajax_wpmind_update_api_key', [ $this, 'ajax_update_api_key' ] );
add_action( 'wp_ajax_wpmind_list_audit_logs', [ $this, 'ajax_list_audit_logs' ] );
}
/**
* Save gateway settings.
*/
public function ajax_save_gateway_settings(): void {
check_ajax_referer( 'wpmind_ajax', 'nonce' );
if ( ! current_user_can( 'manage_options' ) ) {
wp_send_json_error( [ 'message' => __( '权限不足', 'wpmind' ) ] );
}
$enabled = ! empty( $_POST['gateway_enabled'] );
$sse_global_limit = absint( wp_unslash( $_POST['sse_global_limit'] ?? 20 ) );
$default_rpm = absint( wp_unslash( $_POST['default_rpm'] ?? 60 ) );
$default_tpm = absint( wp_unslash( $_POST['default_tpm'] ?? 100000 ) );
$max_body_bytes = absint( wp_unslash( $_POST['max_body_bytes'] ?? 0 ) );
$max_tokens_cap = absint( wp_unslash( $_POST['max_tokens_cap'] ?? 0 ) );
$log_prompts = ! empty( $_POST['log_prompts'] );
// Clamp values to reasonable ranges.
$sse_global_limit = max( 1, min( 200, $sse_global_limit ) );
$default_rpm = max( 1, min( 10000, $default_rpm ) );
$default_tpm = max( 1000, min( 10000000, $default_tpm ) );
$max_body_bytes = min( 104857600, $max_body_bytes ); // 100 MB max.
$max_tokens_cap = min( 1000000, $max_tokens_cap );
update_option( 'wpmind_gateway_enabled', $enabled ? '1' : '0' );
update_option( 'wpmind_gateway_sse_global_limit', $sse_global_limit );
update_option( 'wpmind_gateway_default_rpm', $default_rpm );
update_option( 'wpmind_gateway_default_tpm', $default_tpm );
update_option( 'wpmind_gateway_max_body_bytes', $max_body_bytes );
update_option( 'wpmind_gateway_max_tokens_cap', $max_tokens_cap );
update_option( 'wpmind_gateway_log_prompts', $log_prompts ? '1' : '0' );
wp_send_json_success( [ 'message' => __( '网关设置已保存', 'wpmind' ) ] );
}
/**
* Create a new API key.
*/
public function ajax_create_api_key(): void {
check_ajax_referer( 'wpmind_ajax', 'nonce' );
if ( ! current_user_can( 'manage_options' ) ) {
wp_send_json_error( [ 'message' => __( '权限不足', 'wpmind' ) ] );
}
$name = sanitize_text_field( wp_unslash( $_POST['name'] ?? '' ) );
$rpm_limit = absint( wp_unslash( $_POST['rpm_limit'] ?? 60 ) );
$tpm_limit = absint( wp_unslash( $_POST['tpm_limit'] ?? 100000 ) );
$concurrency_limit = absint( wp_unslash( $_POST['concurrency_limit'] ?? 2 ) );
$monthly_budget = (float) wp_unslash( $_POST['monthly_budget_usd'] ?? 0 );
$ip_whitelist_raw = sanitize_text_field( wp_unslash( $_POST['ip_whitelist'] ?? '' ) );
$expires_at = sanitize_text_field( wp_unslash( $_POST['expires_at'] ?? '' ) );
if ( empty( $name ) ) {
wp_send_json_error( [ 'message' => __( '请输入 Key 名称', 'wpmind' ) ] );
}
// Parse IP whitelist.
$ip_whitelist = [];
if ( ! empty( $ip_whitelist_raw ) ) {
$ips = array_map( 'trim', explode( ',', $ip_whitelist_raw ) );
foreach ( $ips as $ip ) {
if ( filter_var( $ip, FILTER_VALIDATE_IP ) ) {
$ip_whitelist[] = $ip;
}
}
}
// Clamp values.
$rpm_limit = max( 1, min( 10000, $rpm_limit ) );
$tpm_limit = max( 1000, min( 10000000, $tpm_limit ) );
$concurrency_limit = max( 1, min( 100, $concurrency_limit ) );
$monthly_budget = max( 0.0, $monthly_budget );
$attrs = [
'name' => $name,
'owner_user_id' => get_current_user_id(),
'rpm_limit' => $rpm_limit,
'tpm_limit' => $tpm_limit,
'concurrency_limit' => $concurrency_limit,
'monthly_budget_usd' => $monthly_budget,
];
if ( ! empty( $ip_whitelist ) ) {
$attrs['ip_whitelist'] = $ip_whitelist;
}
if ( ! empty( $expires_at ) ) {
$ts = strtotime( $expires_at );
if ( false === $ts ) {
wp_send_json_error( [ 'message' => __( '过期时间格式无效', 'wpmind' ) ] );
}
$attrs['expires_at'] = gmdate( 'Y-m-d H:i:s', $ts );
}
$result = ApiKeyManager::create_api_key( $attrs );
wp_send_json_success(
[
'message' => __( 'API Key 创建成功', 'wpmind' ),
'raw_key' => $result['raw_key'],
'key_id' => $result['key_id'],
'key_prefix' => $result['key_prefix'],
]
);
}
/**
* List all API keys with usage data.
*/
public function ajax_list_api_keys(): void {
check_ajax_referer( 'wpmind_ajax', 'nonce' );
if ( ! current_user_can( 'manage_options' ) ) {
wp_send_json_error( [ 'message' => __( '权限不足', 'wpmind' ) ] );
}
$keys = ApiKeyRepository::list_all_with_usage();
wp_send_json_success( [ 'keys' => $keys ] );
}
/**
* Revoke an API key.
*/
public function ajax_revoke_api_key(): void {
check_ajax_referer( 'wpmind_ajax', 'nonce' );
if ( ! current_user_can( 'manage_options' ) ) {
wp_send_json_error( [ 'message' => __( '权限不足', 'wpmind' ) ] );
}
$key_id = sanitize_text_field( wp_unslash( $_POST['key_id'] ?? '' ) );
if ( empty( $key_id ) ) {
wp_send_json_error( [ 'message' => __( '缺少 Key ID', 'wpmind' ) ] );
}
// Verify key exists.
$row = ApiKeyRepository::find_by_key_id( $key_id );
if ( $row === null ) {
wp_send_json_error( [ 'message' => __( 'API Key 不存在', 'wpmind' ) ] );
}
if ( $row['status'] === 'revoked' ) {
wp_send_json_error( [ 'message' => __( '该 Key 已被吊销', 'wpmind' ) ] );
}
ApiKeyRepository::revoke_key( $key_id, get_current_user_id(), 'admin_revoke' );
wp_send_json_success( [ 'message' => __( 'API Key 已吊销', 'wpmind' ) ] );
}
/**
* Update an existing API key.
*/
public function ajax_update_api_key(): void {
check_ajax_referer( 'wpmind_ajax', 'nonce' );
if ( ! current_user_can( 'manage_options' ) ) {
wp_send_json_error( [ 'message' => __( '权限不足', 'wpmind' ) ] );
}
$key_id = sanitize_text_field( wp_unslash( $_POST['key_id'] ?? '' ) );
if ( empty( $key_id ) ) {
wp_send_json_error( [ 'message' => __( '缺少 Key ID', 'wpmind' ) ] );
}
$row = ApiKeyRepository::find_by_key_id( $key_id );
if ( $row === null ) {
wp_send_json_error( [ 'message' => __( 'API Key 不存在', 'wpmind' ) ] );
}
$data = [];
if ( isset( $_POST['name'] ) ) {
$data['name'] = sanitize_text_field( wp_unslash( $_POST['name'] ) );
}
if ( isset( $_POST['rpm_limit'] ) ) {
$data['rpm_limit'] = max( 1, min( 10000, absint( wp_unslash( $_POST['rpm_limit'] ) ) ) );
}
if ( isset( $_POST['tpm_limit'] ) ) {
$data['tpm_limit'] = max( 1000, min( 10000000, absint( wp_unslash( $_POST['tpm_limit'] ) ) ) );
}
if ( isset( $_POST['concurrency_limit'] ) ) {
$data['concurrency_limit'] = max( 1, min( 100, absint( wp_unslash( $_POST['concurrency_limit'] ) ) ) );
}
if ( isset( $_POST['monthly_budget_usd'] ) ) {
$data['monthly_budget_usd'] = max( 0.0, (float) wp_unslash( $_POST['monthly_budget_usd'] ) );
}
if ( isset( $_POST['ip_whitelist'] ) ) {
$raw = sanitize_text_field( wp_unslash( $_POST['ip_whitelist'] ) );
$ips = [];
if ( ! empty( $raw ) ) {
foreach ( array_map( 'trim', explode( ',', $raw ) ) as $ip ) {
if ( filter_var( $ip, FILTER_VALIDATE_IP ) ) {
$ips[] = $ip;
}
}
}
$data['ip_whitelist'] = ! empty( $ips ) ? $ips : '';
}
if ( isset( $_POST['expires_at'] ) ) {
$exp = sanitize_text_field( wp_unslash( $_POST['expires_at'] ) );
if ( ! empty( $exp ) ) {
$ts = strtotime( $exp );
if ( false === $ts ) {
wp_send_json_error( [ 'message' => __( '过期时间格式无效', 'wpmind' ) ] );
}
$data['expires_at'] = gmdate( 'Y-m-d H:i:s', $ts );
} else {
$data['expires_at'] = null;
}
}
if ( empty( $data ) ) {
wp_send_json_error( [ 'message' => __( '没有需要更新的字段', 'wpmind' ) ] );
}
$ok = ApiKeyRepository::update_key( $key_id, $data );
if ( $ok ) {
wp_send_json_success( [ 'message' => __( 'API Key 已更新', 'wpmind' ) ] );
} else {
wp_send_json_error( [ 'message' => __( '更新失败', 'wpmind' ) ] );
}
}
/**
* List audit logs with pagination and filters.
*/
public function ajax_list_audit_logs(): void {
check_ajax_referer( 'wpmind_ajax', 'nonce' );
if ( ! current_user_can( 'manage_options' ) ) {
wp_send_json_error( [ 'message' => __( '权限不足', 'wpmind' ) ] );
}
$page = max( 1, absint( $_POST['page'] ?? 1 ) );
$per_page = 20;
$filters = [];
if ( ! empty( $_POST['key_id'] ) ) {
$filters['key_id'] = sanitize_text_field( wp_unslash( $_POST['key_id'] ) );
}
if ( ! empty( $_POST['event_type'] ) ) {
$filters['event_type'] = sanitize_text_field( wp_unslash( $_POST['event_type'] ) );
}
if ( ! empty( $_POST['date_from'] ) ) {
$filters['date_from'] = sanitize_text_field( wp_unslash( $_POST['date_from'] ) );
}
if ( ! empty( $_POST['date_to'] ) ) {
$filters['date_to'] = sanitize_text_field( wp_unslash( $_POST['date_to'] ) );
}
$logs = AuditLogRepository::list_logs( $filters, $page, $per_page );
$total = AuditLogRepository::count_logs( $filters );
wp_send_json_success(
[
'logs' => $logs,
'total' => $total,
'page' => $page,
'per_page' => $per_page,
'total_pages' => (int) ceil( $total / $per_page ),
]
);
}
}