HIGH 级别修复: - WebhookHandler: 添加 SSRF 防护 (Validator::is_valid_url) - AIGateway: 自定义端点添加 SSRF 验证 - AIGateway: 白名单域名匹配改为大小写不敏感 - CommercialManager: 文件读取添加路径遍历防护 (realpath) - GroupModel: from_array() 添加输入类型验证和清理 - GroupManager: add/remove_source_to_group 添加权限检查 MEDIUM 级别修复: - NotificationManager: 添加 5 分钟速率限制防止通知轰炸 - NotificationManager: 允许第三方扩展通知处理器 - EmailHandler: 添加收件人邮箱格式验证 - EmailHandler: 移除自定义 From 头避免 SPF/DKIM 问题 - CommercialManager: lock/unlock_version 添加权限检查 - CommercialManager: 商业插件列表支持过滤器扩展 - GroupManager: toggle 方法改进原子性,先更新分组再更新源 - AbstractAdapter: 正则匹配添加错误处理 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
172 lines
3.8 KiB
PHP
172 lines
3.8 KiB
PHP
<?php
|
|
/**
|
|
* AI 适配器抽象基类
|
|
*
|
|
* @package WPBridge
|
|
*/
|
|
|
|
namespace WPBridge\AIBridge\Adapters;
|
|
|
|
use WPBridge\Core\Settings;
|
|
use WPBridge\Core\Logger;
|
|
|
|
// 防止直接访问
|
|
if ( ! defined( 'ABSPATH' ) ) {
|
|
exit;
|
|
}
|
|
|
|
/**
|
|
* AI 适配器抽象基类
|
|
*/
|
|
abstract class AbstractAdapter implements AdapterInterface {
|
|
|
|
/**
|
|
* 设置实例
|
|
*
|
|
* @var Settings
|
|
*/
|
|
protected Settings $settings;
|
|
|
|
/**
|
|
* 支持的插件 slug 列表
|
|
*
|
|
* @var array
|
|
*/
|
|
protected array $supported_plugins = [];
|
|
|
|
/**
|
|
* 匹配的 URL 模式
|
|
*
|
|
* @var array
|
|
*/
|
|
protected array $url_patterns = [];
|
|
|
|
/**
|
|
* 构造函数
|
|
*
|
|
* @param Settings $settings 设置实例
|
|
*/
|
|
public function __construct( Settings $settings ) {
|
|
$this->settings = $settings;
|
|
}
|
|
|
|
/**
|
|
* 检查是否支持该插件
|
|
*
|
|
* @param string $plugin_slug 插件 slug
|
|
* @return bool
|
|
*/
|
|
public function supports( string $plugin_slug ): bool {
|
|
return in_array( $plugin_slug, $this->supported_plugins, true );
|
|
}
|
|
|
|
/**
|
|
* 检查请求是否匹配
|
|
*
|
|
* @param string $url 请求 URL
|
|
* @param array $args 请求参数
|
|
* @return bool
|
|
*/
|
|
public function matches( string $url, array $args ): bool {
|
|
foreach ( $this->url_patterns as $pattern ) {
|
|
$result = @preg_match( $pattern, $url );
|
|
if ( $result === false ) {
|
|
$this->log( '无效的 URL 匹配模式', [ 'pattern' => $pattern ] );
|
|
continue;
|
|
}
|
|
if ( $result === 1 ) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* 是否启用
|
|
*
|
|
* @return bool
|
|
*/
|
|
public function is_enabled(): bool {
|
|
$ai_settings = $this->settings->get( 'ai_bridge', [] );
|
|
$adapters = $ai_settings['adapters'] ?? [];
|
|
|
|
return in_array( $this->get_name(), $adapters, true );
|
|
}
|
|
|
|
/**
|
|
* 记录日志
|
|
*
|
|
* @param string $message 消息
|
|
* @param array $context 上下文
|
|
*/
|
|
protected function log( string $message, array $context = [] ): void {
|
|
$context['adapter'] = $this->get_name();
|
|
Logger::debug( $message, $context );
|
|
}
|
|
|
|
/**
|
|
* 获取请求体
|
|
*
|
|
* @param array $args 请求参数
|
|
* @return array|null
|
|
*/
|
|
protected function get_request_body( array $args ): ?array {
|
|
if ( empty( $args['body'] ) ) {
|
|
return null;
|
|
}
|
|
|
|
$body = $args['body'];
|
|
|
|
if ( is_string( $body ) ) {
|
|
$decoded = json_decode( $body, true );
|
|
return json_last_error() === JSON_ERROR_NONE ? $decoded : null;
|
|
}
|
|
|
|
return is_array( $body ) ? $body : null;
|
|
}
|
|
|
|
/**
|
|
* 设置请求体
|
|
*
|
|
* @param array $args 请求参数
|
|
* @param array $body 请求体
|
|
* @return array
|
|
*/
|
|
protected function set_request_body( array $args, array $body ): array {
|
|
$args['body'] = wp_json_encode( $body );
|
|
return $args;
|
|
}
|
|
|
|
/**
|
|
* 获取响应体
|
|
*
|
|
* @param array|\WP_Error $response 响应
|
|
* @return array|null
|
|
*/
|
|
protected function get_response_body( $response ): ?array {
|
|
if ( is_wp_error( $response ) ) {
|
|
return null;
|
|
}
|
|
|
|
$body = wp_remote_retrieve_body( $response );
|
|
|
|
if ( empty( $body ) ) {
|
|
return null;
|
|
}
|
|
|
|
$decoded = json_decode( $body, true );
|
|
return json_last_error() === JSON_ERROR_NONE ? $decoded : null;
|
|
}
|
|
|
|
/**
|
|
* 设置响应体
|
|
*
|
|
* @param array $response 响应
|
|
* @param array $body 响应体
|
|
* @return array
|
|
*/
|
|
protected function set_response_body( array $response, array $body ): array {
|
|
$response['body'] = wp_json_encode( $body );
|
|
return $response;
|
|
}
|
|
}
|