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>
254 lines
7.4 KiB
PHP
254 lines
7.4 KiB
PHP
<?php
|
|
/**
|
|
* API Gateway Module
|
|
*
|
|
* OpenAI-compatible AI API gateway module for WPMind.
|
|
*
|
|
* @package WPMind\Modules\ApiGateway
|
|
* @since 1.0.0
|
|
*/
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace WPMind\Modules\ApiGateway;
|
|
|
|
use WPMind\Core\ModuleInterface;
|
|
|
|
// Load module classes.
|
|
require_once __DIR__ . '/includes/SchemaManager.php';
|
|
require_once __DIR__ . '/includes/Auth/ApiKeyHasher.php';
|
|
require_once __DIR__ . '/includes/Auth/ApiKeyRepository.php';
|
|
require_once __DIR__ . '/includes/Auth/ApiKeyAuthResult.php';
|
|
require_once __DIR__ . '/includes/Auth/ApiKeyManager.php';
|
|
require_once __DIR__ . '/includes/GatewayRequestSchema.php';
|
|
require_once __DIR__ . '/includes/Pipeline/GatewayStageInterface.php';
|
|
require_once __DIR__ . '/includes/Pipeline/GatewayRequestContext.php';
|
|
require_once __DIR__ . '/includes/Pipeline/GatewayPipeline.php';
|
|
require_once __DIR__ . '/includes/RateLimit/RateStoreResult.php';
|
|
require_once __DIR__ . '/includes/RateLimit/RateStoreInterface.php';
|
|
require_once __DIR__ . '/includes/RateLimit/RedisRateStore.php';
|
|
require_once __DIR__ . '/includes/RateLimit/TransientRateStore.php';
|
|
require_once __DIR__ . '/includes/RateLimit/RateLimiter.php';
|
|
require_once __DIR__ . '/includes/Pipeline/AuthMiddleware.php';
|
|
require_once __DIR__ . '/includes/Pipeline/BudgetMiddleware.php';
|
|
require_once __DIR__ . '/includes/Pipeline/QuotaMiddleware.php';
|
|
require_once __DIR__ . '/includes/Transform/ModelMapper.php';
|
|
require_once __DIR__ . '/includes/Transform/RequestTransformer.php';
|
|
require_once __DIR__ . '/includes/Transform/ResponseTransformer.php';
|
|
require_once __DIR__ . '/includes/Pipeline/RequestTransformMiddleware.php';
|
|
require_once __DIR__ . '/includes/Pipeline/ResponseTransformMiddleware.php';
|
|
require_once __DIR__ . '/includes/Pipeline/RouteMiddleware.php';
|
|
require_once __DIR__ . '/includes/Stream/CancellationToken.php';
|
|
require_once __DIR__ . '/includes/Stream/SseSlot.php';
|
|
require_once __DIR__ . '/includes/Stream/StreamResult.php';
|
|
require_once __DIR__ . '/includes/Stream/SseConcurrencyGuard.php';
|
|
require_once __DIR__ . '/includes/Stream/UpstreamStreamClient.php';
|
|
require_once __DIR__ . '/includes/Stream/SseStreamController.php';
|
|
require_once __DIR__ . '/includes/Error/ErrorMapper.php';
|
|
require_once __DIR__ . '/includes/Pipeline/ErrorMiddleware.php';
|
|
require_once __DIR__ . '/includes/Pipeline/LogMiddleware.php';
|
|
require_once __DIR__ . '/includes/RestController.php';
|
|
require_once __DIR__ . '/includes/Admin/AuditLogRepository.php';
|
|
require_once __DIR__ . '/includes/Admin/GatewayAjaxController.php';
|
|
|
|
/**
|
|
* Class ApiGatewayModule
|
|
*
|
|
* Main entry point for the API Gateway module.
|
|
*/
|
|
class ApiGatewayModule implements ModuleInterface {
|
|
|
|
/**
|
|
* Get module ID.
|
|
*
|
|
* @return string
|
|
*/
|
|
public function get_id(): string {
|
|
return 'api-gateway';
|
|
}
|
|
|
|
/**
|
|
* Get module name.
|
|
*
|
|
* @return string
|
|
*/
|
|
public function get_name(): string {
|
|
return __( 'API Gateway', 'wpmind' );
|
|
}
|
|
|
|
/**
|
|
* Get module description.
|
|
*
|
|
* @return string
|
|
*/
|
|
public function get_description(): string {
|
|
return __( 'OpenAI 兼容的 AI API 网关 — 将 WordPress 变为自托管 AI 代理', 'wpmind' );
|
|
}
|
|
|
|
/**
|
|
* Get module version.
|
|
*
|
|
* @return string
|
|
*/
|
|
public function get_version(): string {
|
|
return '1.0.0';
|
|
}
|
|
|
|
/**
|
|
* Check dependencies.
|
|
*
|
|
* @return bool
|
|
*/
|
|
public function check_dependencies(): bool {
|
|
return version_compare( PHP_VERSION, '8.1', '>=' );
|
|
}
|
|
|
|
/**
|
|
* Get settings tab slug.
|
|
*
|
|
* @return string|null
|
|
*/
|
|
public function get_settings_tab(): ?string {
|
|
return 'api-gateway';
|
|
}
|
|
|
|
/**
|
|
* Initialize the module.
|
|
*/
|
|
public function init(): void {
|
|
// Ensure database schema is up to date.
|
|
SchemaManager::maybe_upgrade();
|
|
|
|
// Register REST API routes.
|
|
add_action( 'rest_api_init', array( $this, 'register_rest_routes' ) );
|
|
|
|
// Register settings tab.
|
|
add_filter( 'wpmind_settings_tabs', array( $this, 'register_settings_tab' ) );
|
|
|
|
// Register admin AJAX handlers.
|
|
if ( is_admin() ) {
|
|
$ajax_controller = new Admin\GatewayAjaxController();
|
|
$ajax_controller->register_hooks();
|
|
|
|
// Admin assets (only on WPMind settings page).
|
|
add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_admin_assets' ] );
|
|
}
|
|
|
|
// Audit logging for SSE streams (bypasses pipeline finalization).
|
|
add_action( 'wpmind_gateway_sse_complete', array( $this, 'log_sse_completion' ), 10, 3 );
|
|
|
|
/**
|
|
* Fires when API Gateway module is initialized.
|
|
*
|
|
* @param ApiGatewayModule $this Module instance.
|
|
*/
|
|
do_action( 'wpmind_api_gateway_init', $this );
|
|
}
|
|
|
|
/**
|
|
* Enqueue admin assets for the API Gateway tab.
|
|
*
|
|
* @param string $hook_suffix Current page hook suffix.
|
|
*/
|
|
public function enqueue_admin_assets( string $hook_suffix ): void {
|
|
if ( 'toplevel_page_wpmind' !== $hook_suffix ) {
|
|
return;
|
|
}
|
|
wp_enqueue_style(
|
|
'wpmind-api-gateway',
|
|
WPMIND_PLUGIN_URL . 'assets/css/pages/api-gateway.css',
|
|
[ 'wpmind-admin' ],
|
|
WPMIND_VERSION
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Register REST API routes.
|
|
*
|
|
* Instantiates the RestController and registers all gateway endpoints.
|
|
*/
|
|
public function register_rest_routes(): void {
|
|
$controller = new RestController();
|
|
$controller->register_routes();
|
|
}
|
|
|
|
/**
|
|
* Register settings tab.
|
|
*
|
|
* @param array $tabs Existing tabs.
|
|
* @return array Modified tabs.
|
|
*/
|
|
public function register_settings_tab( array $tabs ): array {
|
|
$tabs['api-gateway'] = array(
|
|
'title' => __( 'API Gateway', 'wpmind' ),
|
|
'icon' => 'ri-server-line',
|
|
'template' => WPMIND_PATH . 'modules/api-gateway/templates/settings.php',
|
|
'priority' => 35,
|
|
);
|
|
return $tabs;
|
|
}
|
|
|
|
/**
|
|
* Log SSE stream completion for audit and usage tracking.
|
|
*
|
|
* SSE streams exit() before pipeline finalization, so this
|
|
* hook ensures audit logs and usage counters are still updated.
|
|
*
|
|
* @param string $request_id Request ID.
|
|
* @param string $key_id API key ID.
|
|
* @param Stream\StreamResult $result Stream result.
|
|
*/
|
|
public function log_sse_completion( string $request_id, string $key_id, Stream\StreamResult $result ): void {
|
|
global $wpdb;
|
|
|
|
// Audit log.
|
|
$audit_table = $wpdb->prefix . 'wpmind_api_audit_log';
|
|
$wpdb->insert(
|
|
$audit_table,
|
|
[
|
|
'event_type' => 'api_stream_request',
|
|
'key_id' => $key_id,
|
|
'actor_user_id' => 0,
|
|
'request_id' => $request_id,
|
|
'ip_hash' => hash( 'sha256', sanitize_text_field( wp_unslash( $_SERVER['REMOTE_ADDR'] ?? '' ) ) ),
|
|
'user_agent' => sanitize_text_field( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ?? '' ) ),
|
|
'detail_json' => wp_json_encode(
|
|
[
|
|
'tokens_used' => $result->tokens_used,
|
|
'finish_reason' => $result->finish_reason,
|
|
'stream' => true,
|
|
]
|
|
),
|
|
'created_at' => current_time( 'mysql', true ),
|
|
],
|
|
[ '%s', '%s', '%d', '%s', '%s', '%s', '%s', '%s' ]
|
|
);
|
|
|
|
// Usage upsert.
|
|
$usage_table = $wpdb->prefix . 'wpmind_api_key_usage';
|
|
$window_month = gmdate( 'Y-m' );
|
|
$now = current_time( 'mysql', true );
|
|
$tokens = $result->tokens_used;
|
|
|
|
$wpdb->query(
|
|
$wpdb->prepare(
|
|
'INSERT INTO %i (key_id, window_month, request_count, input_tokens, output_tokens, total_tokens, total_cost_usd, updated_at)
|
|
VALUES (%s, %s, 1, 0, %d, %d, 0, %s)
|
|
ON DUPLICATE KEY UPDATE
|
|
request_count = request_count + 1,
|
|
output_tokens = output_tokens + %d,
|
|
total_tokens = total_tokens + %d,
|
|
updated_at = %s',
|
|
$usage_table,
|
|
$key_id,
|
|
$window_month,
|
|
$tokens,
|
|
$tokens,
|
|
$now,
|
|
$tokens,
|
|
$tokens,
|
|
$now
|
|
)
|
|
);
|
|
}
|
|
}
|