1.修复上传漏洞和SQL注入漏洞-修复服务端无法启动的问题
This commit is contained in:
@@ -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 默认校验 HTTPS;Windows 未配置 CA 时会出现 cURL error 60。
|
* Guzzle 默认校验 HTTPS;Windows 未配置 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,
|
||||||
|
|||||||
@@ -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,
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -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
103
scripts/check_start.php
Normal 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);
|
||||||
Reference in New Issue
Block a user