feat: add AnyTLS support and improve system functionality

- Add AnyTLS protocol support
- Add system logs viewing in admin panel
- Refactor client subscription delivery code
- Refactor hook mechanism
- Add plugin support for Shadowsocks protocol
- Add CSV export option for batch user creation
- Fix mobile admin login page width display issue
This commit is contained in:
xboard
2025-05-22 17:58:22 +08:00
parent 2580475f78
commit fc5a957ddd
35 changed files with 1586 additions and 640 deletions
+130
View File
@@ -0,0 +1,130 @@
<?php
namespace App\Support;
abstract class AbstractProtocol
{
/**
* @var array 用户信息
*/
protected $user;
/**
* @var array 服务器信息
*/
protected $servers;
/**
* @var string|null 客户端名称
*/
protected $clientName;
/**
* @var string|null 客户端版本
*/
protected $clientVersion;
/**
* @var array 协议标识
*/
public $flags = [];
/**
* @var array 协议需求配置
*/
protected $protocolRequirements = [];
/**
* 构造函数
*
* @param array $user 用户信息
* @param array $servers 服务器信息
* @param string|null $clientName 客户端名称
* @param string|null $clientVersion 客户端版本
*/
public function __construct($user, $servers, $clientName = null, $clientVersion = null)
{
$this->user = $user;
$this->servers = $servers;
$this->clientName = $clientName;
$this->clientVersion = $clientVersion;
// 服务器过滤逻辑
$this->servers = $this->filterServersByVersion();
}
/**
* 获取协议标识
*
* @return array
*/
public function getFlags(): array
{
return $this->flags;
}
/**
* 处理请求
*
* @return mixed
*/
abstract public function handle();
/**
* 根据客户端版本过滤不兼容的服务器
*
* @return array
*/
protected function filterServersByVersion()
{
// 如果没有客户端信息,直接返回所有服务器
if (empty($this->clientName) || empty($this->clientVersion)) {
return $this->servers;
}
// 检查当前客户端是否有特殊配置
if (!isset($this->protocolRequirements[$this->clientName])) {
return $this->servers;
}
return collect($this->servers)->filter(function ($server) {
return $this->isCompatible($server);
})->values()->all();
}
/**
* 检查服务器是否与当前客户端兼容
*
* @param array $server 服务器信息
* @return bool
*/
protected function isCompatible($server)
{
$serverType = $server['type'] ?? null;
// 如果该协议没有特定要求,则认为兼容
if (!isset($this->protocolRequirements[$this->clientName][$serverType])) {
return true;
}
$requirements = $this->protocolRequirements[$this->clientName][$serverType];
// 检查每个路径的版本要求
foreach ($requirements as $path => $valueRequirements) {
$actualValue = data_get($server, $path);
if ($actualValue === null) {
continue;
}
if (isset($valueRequirements[$actualValue])) {
$requiredVersion = $valueRequirements[$actualValue];
if (version_compare($this->clientVersion, $requiredVersion, '<')) {
return false;
}
}
}
return true;
}
}
+162
View File
@@ -0,0 +1,162 @@
<?php
namespace App\Support;
use Illuminate\Contracts\Container\Container;
class ProtocolManager
{
/**
* @var Container Laravel容器实例
*/
protected $container;
/**
* @var array 缓存的协议类列表
*/
protected $protocolClasses = [];
/**
* 构造函数
*
* @param Container $container
*/
public function __construct(Container $container)
{
$this->container = $container;
}
/**
* 发现并注册所有协议类
*
* @return self
*/
public function registerAllProtocols()
{
if (empty($this->protocolClasses)) {
$files = glob(app_path('Protocols') . '/*.php');
foreach ($files as $file) {
$className = 'App\\Protocols\\' . basename($file, '.php');
if (class_exists($className) && is_subclass_of($className, AbstractProtocol::class)) {
$this->protocolClasses[] = $className;
}
}
}
return $this;
}
/**
* 获取所有注册的协议类
*
* @return array
*/
public function getProtocolClasses()
{
if (empty($this->protocolClasses)) {
$this->registerAllProtocols();
}
return $this->protocolClasses;
}
/**
* 获取所有协议的标识
*
* @return array
*/
public function getAllFlags()
{
return collect($this->getProtocolClasses())
->map(function ($class) {
try {
$reflection = new \ReflectionClass($class);
if (!$reflection->isInstantiable()) {
return [];
}
// 'flags' is a public property with a default value in AbstractProtocol
$instanceForFlags = $reflection->newInstanceWithoutConstructor();
return $instanceForFlags->flags;
} catch (\ReflectionException $e) {
// Log or handle error if a class is problematic
report($e);
return [];
}
})
->flatten()
->unique()
->values()
->all();
}
/**
* 根据标识匹配合适的协议处理器类名
*
* @param string $flag 请求标识
* @return string|null 协议类名或null
*/
public function matchProtocolClassName(string $flag): ?string
{
// 按照相反顺序,使最新定义的协议有更高优先级
foreach (array_reverse($this->getProtocolClasses()) as $protocolClassString) {
try {
$reflection = new \ReflectionClass($protocolClassString);
if (!$reflection->isInstantiable() || !$reflection->isSubclassOf(AbstractProtocol::class)) {
continue;
}
// 'flags' is a public property in AbstractProtocol
$instanceForFlags = $reflection->newInstanceWithoutConstructor();
$flags = $instanceForFlags->flags;
if (collect($flags)->contains(fn($f) => stripos($flag, (string) $f) !== false)) {
return $protocolClassString; // 返回类名字符串
}
} catch (\ReflectionException $e) {
report($e); // Consider logging this error
continue;
}
}
return null;
}
/**
* 根据标识匹配合适的协议处理器实例 (原有逻辑,如果还需要的话)
*
* @param string $flag 请求标识
* @param array $user 用户信息
* @param array $servers 服务器列表
* @param array $clientInfo 客户端信息
* @return AbstractProtocol|null
*/
public function matchProtocol($flag, $user, $servers, $clientInfo = [])
{
$protocolClassName = $this->matchProtocolClassName($flag);
if ($protocolClassName) {
return $this->makeProtocolInstance($protocolClassName, [
'user' => $user,
'servers' => $servers,
'clientName' => $clientInfo['name'] ?? null,
'clientVersion' => $clientInfo['version'] ?? null
]);
}
return null;
}
/**
* 创建协议实例的通用方法,兼容不同版本的Laravel容器
*
* @param string $class 类名
* @param array $parameters 构造参数
* @return object 实例
*/
protected function makeProtocolInstance($class, array $parameters)
{
// Laravel's make method can accept an array of parameters as its second argument.
// These will be used when resolving the class's dependencies.
return $this->container->make($class, $parameters);
}
}