1.修复上传漏洞和SQL注入漏洞-修复服务端无法启动的问题

This commit is contained in:
2026-06-24 10:24:59 +08:00
parent 60f30d8381
commit 546d350f3a
4 changed files with 153 additions and 35 deletions

View File

@@ -23,20 +23,19 @@ class AngpowImportJobs
private const BATCH_LIMIT = 100; private const BATCH_LIMIT = 100;
private const MAX_RETRY = 3; private const MAX_RETRY = 3;
protected Client $http; protected ?Client $http = null;
public function __construct() public function onWorkerStart(Worker $worker): void
{ {
// 确保定时任务只在一个 worker 上运行
if (!Worker::getAllWorkers()) {
return;
}
$this->http = new Client($this->buildGuzzleOptions()); $this->http = new Client($this->buildGuzzleOptions());
Timer::add(self::TIMER_SECONDS, [$this, 'pushPendingOrders']); Timer::add(self::TIMER_SECONDS, [$this, 'pushPendingOrders']);
} }
protected function getHttp(): ?Client
{
return $this->http;
}
/** /**
* Guzzle 默认校验 HTTPSWindows 未配置 CA 时会出现 cURL error 60。 * Guzzle 默认校验 HTTPSWindows 未配置 CA 时会出现 cURL error 60。
* 优先使用 PLAYX_ANGPOW_IMPORT_CACERT 指向 cacert.pem否则可按环境关闭校验仅开发 * 优先使用 PLAYX_ANGPOW_IMPORT_CACERT 指向 cacert.pem否则可按环境关闭校验仅开发
@@ -70,6 +69,11 @@ class AngpowImportJobs
public function pushPendingOrders(): void public function pushPendingOrders(): void
{ {
$http = $this->getHttp();
if ($http === null) {
return;
}
$conf = config('playx.angpow_import'); $conf = config('playx.angpow_import');
if (!is_array($conf)) { if (!is_array($conf)) {
return; return;
@@ -165,7 +169,7 @@ class AngpowImportJobs
$res = null; $res = null;
$body = ''; $body = '';
try { try {
$res = $this->http->post($url, [ $res = $http->post($url, [
'headers' => [ 'headers' => [
'Content-Type' => 'application/json', 'Content-Type' => 'application/json',
'X-Request-Signature' => $signature, 'X-Request-Signature' => $signature,

View File

@@ -16,15 +16,10 @@ use Workerman\Worker;
*/ */
class PlayxJobs class PlayxJobs
{ {
protected Client $http; protected ?Client $http = null;
public function __construct() public function onWorkerStart(Worker $worker): void
{ {
// 确保定时任务只在一个 worker 上运行
if (!Worker::getAllWorkers()) {
return;
}
$this->http = new Client([ $this->http = new Client([
'timeout' => 20, 'timeout' => 20,
'http_errors' => false, 'http_errors' => false,
@@ -34,11 +29,21 @@ class PlayxJobs
Timer::add(60, [$this, 'retryFailedGrants']); Timer::add(60, [$this, 'retryFailedGrants']);
} }
protected function getHttp(): ?Client
{
return $this->http;
}
/** /**
* 轮询:已 accepted 的订单,查询终态 * 轮询:已 accepted 的订单,查询终态
*/ */
public function pollTransactionStatus(): void public function pollTransactionStatus(): void
{ {
$http = $this->getHttp();
if ($http === null) {
return;
}
$baseUrl = strval(config('playx.api.base_url', '')); $baseUrl = strval(config('playx.api.base_url', ''));
if ($baseUrl === '') { if ($baseUrl === '') {
return; return;
@@ -57,7 +62,7 @@ class PlayxJobs
foreach ($list as $order) { foreach ($list as $order) {
/** @var MallOrder $order */ /** @var MallOrder $order */
try { try {
$res = $this->http->get($url, [ $res = $http->get($url, [
'query' => [ 'query' => [
'externalTransactionId' => $order->external_transaction_id, 'externalTransactionId' => $order->external_transaction_id,
], ],

View File

@@ -16,14 +16,20 @@ use support\Log;
use support\Request; use support\Request;
use app\process\Http; use app\process\Http;
use app\process\AngpowImportJobs; use app\process\AngpowImportJobs;
use app\process\PlayxJobs;
global $argv; global $argv;
return [ $workerCount = env('WEBMAN_WORKER_COUNT', '');
if ($workerCount === '' || $workerCount === null) {
$workerCount = max(4, cpu_count() * 4);
}
$process = [
'webman' => [ 'webman' => [
'handler' => Http::class, 'handler' => Http::class,
'listen' => 'http://0.0.0.0:6969', 'listen' => 'http://0.0.0.0:6969',
'count' => cpu_count() * 4, 'count' => (int) $workerCount,
'user' => '', 'user' => '',
'group' => '', 'group' => '',
'reusePort' => false, 'reusePort' => false,
@@ -36,12 +42,10 @@ return [
'publicPath' => public_path() 'publicPath' => public_path()
] ]
], ],
// File update detection and automatic reload
'monitor' => [ 'monitor' => [
'handler' => app\process\Monitor::class, 'handler' => app\process\Monitor::class,
'reloadable' => false, 'reloadable' => false,
'constructor' => [ 'constructor' => [
// Monitor these directories
'monitorDir' => array_merge([ 'monitorDir' => array_merge([
app_path(), app_path(),
config_path(), config_path(),
@@ -49,26 +53,28 @@ return [
base_path() . '/support', base_path() . '/support',
base_path() . '/resource', base_path() . '/resource',
base_path() . '/.env', base_path() . '/.env',
], glob(base_path() . '/plugin/*/app'), glob(base_path() . '/plugin/*/config'), glob(base_path() . '/plugin/*/api')), ], glob(base_path() . '/plugin/*/app') ?: [], glob(base_path() . '/plugin/*/config') ?: [], glob(base_path() . '/plugin/*/api') ?: []),
// Files with these suffixes will be monitored
'monitorExtensions' => [ 'monitorExtensions' => [
'php', 'html', 'htm', 'env' 'php', 'html', 'htm', 'env'
], ],
'options' => [ 'options' => [
'enable_file_monitor' => !in_array('-d', $argv) && DIRECTORY_SEPARATOR === '/', 'enable_file_monitor' => !in_array('-d', $argv ?? []) && DIRECTORY_SEPARATOR === '/',
'enable_memory_monitor' => DIRECTORY_SEPARATOR === '/', 'enable_memory_monitor' => DIRECTORY_SEPARATOR === '/',
] ]
] ]
]
,
// PlayX 闭环任务:轮询交易终态/失败重试
'playx_jobs' => [
'handler' => app\process\PlayxJobs::class,
'reloadable' => false,
],
// Angpow 导入推送任务:订单兑换后推送到对方平台
'angpow_import_jobs' => [
'handler' => AngpowImportJobs::class,
'reloadable' => false,
], ],
]; ];
$disableBackgroundJobs = filter_var(env('PLAYX_DISABLE_BACKGROUND_JOBS', '0'), FILTER_VALIDATE_BOOLEAN);
if (!$disableBackgroundJobs) {
$process['playx_jobs'] = [
'handler' => PlayxJobs::class,
'reloadable' => false,
];
$process['angpow_import_jobs'] = [
'handler' => AngpowImportJobs::class,
'reloadable' => false,
];
}
return $process;

103
scripts/check_start.php Normal file
View File

@@ -0,0 +1,103 @@
#!/usr/bin/env php
<?php
/**
* 启动前自检:在项目根目录执行 php scripts/check_start.php
*/
declare(strict_types=1);
chdir(__DIR__ . '/..');
require_once __DIR__ . '/../vendor/autoload.php';
$errors = [];
$isLinux = DIRECTORY_SEPARATOR === '/';
if ($isLinux) {
foreach (['pcntl', 'posix'] as $ext) {
if (!extension_loaded($ext)) {
$errors[] = "缺少 PHP 扩展: {$ext}Workerman 在 Linux 上必需)";
}
}
}
foreach (['pdo', 'pdo_mysql', 'json'] as $ext) {
if (!extension_loaded($ext)) {
$errors[] = "缺少 PHP 扩展: {$ext}";
}
}
if (!function_exists('cpu_count')) {
$errors[] = '函数 cpu_count() 不存在,请确认 vendor 已完整安装composer install';
}
$runtime = base_path() . '/runtime';
$logs = $runtime . '/logs';
foreach ([$runtime, $logs] as $dir) {
if (!is_dir($dir) && !@mkdir($dir, 0755, true)) {
$errors[] = "无法创建目录: {$dir}";
} elseif (!is_writable($dir)) {
$errors[] = "目录不可写: {$dir}(请 chown/chmod 给运行 PHP 的用户)";
}
}
$pidPath = runtime_path() . '/webman.pid';
if (is_file($pidPath) && !is_writable($pidPath) && !is_writable(dirname($pidPath))) {
$errors[] = "PID 文件目录不可写: {$pidPath}";
}
try {
require base_path() . '/support/bootstrap.php';
} catch (Throwable $e) {
$errors[] = 'bootstrap 失败: ' . $e->getMessage() . ' @ ' . $e->getFile() . ':' . $e->getLine();
}
if (empty($errors)) {
foreach (config('process', []) as $name => $proc) {
$handler = $proc['handler'] ?? '';
if (is_string($handler) && $handler !== '' && !class_exists($handler)) {
$errors[] = "进程 [{$name}] 处理器不存在: {$handler}";
}
}
}
if (empty($errors)) {
try {
support\think\Db::execute('SELECT 1');
} catch (Throwable $e) {
$errors[] = '数据库连接失败: ' . $e->getMessage();
}
}
$listen = config('process.webman.listen', '');
if ($listen !== '' && empty($errors)) {
$parsed = parse_url($listen);
$host = $parsed['host'] ?? '0.0.0.0';
$port = $parsed['port'] ?? 80;
$bindHost = $host === '0.0.0.0' ? '127.0.0.1' : $host;
$sock = @stream_socket_server("tcp://{$bindHost}:{$port}", $errno, $errstr);
if ($sock === false) {
$errors[] = "端口 {$port} 无法监听: [{$errno}] {$errstr}";
$errors[] = "可执行: ss -lntp | grep {$port} 查看是否被占用";
} else {
fclose($sock);
}
}
if ($errors) {
echo "=== 启动自检失败 ===\n";
foreach ($errors as $i => $msg) {
echo ($i + 1) . '. ' . $msg . "\n";
}
echo "\n下一步:\n";
echo " php start.php start # 前台启动看完整报错\n";
echo " tail -n 80 runtime/logs/workerman.log\n";
echo " tail -n 80 runtime/logs/stdout.log\n";
exit(1);
}
echo "=== 启动自检通过 ===\n";
echo "监听: {$listen}\n";
echo "Worker 数: " . (config('process.webman.count') ?? '?') . "\n";
echo "路由数: " . count(Webman\Route::getRoutes()) . "\n";
echo "\n请执行: php start.php start\n";
echo "确认无误后再: php start.php start -d\n";
exit(0);