109 lines
3.7 KiB
PHP
109 lines
3.7 KiB
PHP
<?php
|
|
declare(strict_types=1);
|
|
|
|
namespace MageSail\Magesail\Model;
|
|
|
|
use Magento\Framework\App\Config\ScopeConfigInterface;
|
|
use Magento\Framework\Shell;
|
|
use Magento\Framework\Event\ManagerInterface as EventManager;
|
|
|
|
/**
|
|
* Start/stop tunnel process; shared by admin Index and cron auto-restart.
|
|
*/
|
|
class TunnelManager
|
|
{
|
|
private const XML_SCRIPT_PATH = 'magesail/settings/script_path';
|
|
private const DEFAULT_SCRIPT_REL = 'app/code/MageSail/Magesail/scripts';
|
|
|
|
public function __construct(
|
|
private readonly Shell $shell,
|
|
private readonly TunnelStatus $tunnelStatus,
|
|
private readonly ScopeConfigInterface $scopeConfig,
|
|
private readonly EventManager $eventManager
|
|
) {
|
|
}
|
|
|
|
public function getScriptDir(): string
|
|
{
|
|
$configured = trim((string) $this->scopeConfig->getValue(self::XML_SCRIPT_PATH));
|
|
if ($configured !== '') {
|
|
return $configured;
|
|
}
|
|
return BP . '/' . self::DEFAULT_SCRIPT_REL;
|
|
}
|
|
|
|
/**
|
|
* @return array{ok: bool, pid?: int, error?: string}
|
|
*/
|
|
public function start(int $port, bool $secure): array
|
|
{
|
|
if ($this->tunnelStatus->isRunning()) {
|
|
return ['ok' => false, 'error' => 'already_running'];
|
|
}
|
|
if (!\in_array($port, [80, 443], true)) {
|
|
return ['ok' => false, 'error' => 'invalid_port'];
|
|
}
|
|
$scriptDir = $this->getScriptDir();
|
|
$keyFile = $this->tunnelStatus->getKeyFilePath();
|
|
$logFile = $this->tunnelStatus->getLogFilePath();
|
|
$pidFile = $this->tunnelStatus->getPidFilePath();
|
|
$jsArgs = json_encode([
|
|
'port' => $port,
|
|
'secure' => $secure,
|
|
'keyfile' => $keyFile,
|
|
'logfile' => $logFile,
|
|
], JSON_THROW_ON_ERROR);
|
|
$arg = escapeshellarg($jsArgs);
|
|
$dir = escapeshellarg($scriptDir);
|
|
$log = escapeshellarg($logFile);
|
|
$fullCmd = "cd $dir && nohup node start-server.js $arg > $log 2>&1 & echo \$!";
|
|
try {
|
|
$output = $this->shell->execute($fullCmd);
|
|
$pid = (int) trim($output);
|
|
if ($pid > 0) {
|
|
file_put_contents($pidFile, (string) $pid);
|
|
$this->eventManager->dispatch('magesail_tunnel_start_after', [
|
|
'pid' => $pid,
|
|
'port' => $port,
|
|
'secure' => $secure,
|
|
]);
|
|
return ['ok' => true, 'pid' => $pid];
|
|
}
|
|
return ['ok' => false, 'error' => 'pid_capture_failed', 'detail' => $output];
|
|
} catch (\Throwable $e) {
|
|
return ['ok' => false, 'error' => $e->getMessage()];
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @return array{ok: bool, error?: string}
|
|
*/
|
|
public function stop(): array
|
|
{
|
|
$pidFile = $this->tunnelStatus->getPidFilePath();
|
|
$keyFile = $this->tunnelStatus->getKeyFilePath();
|
|
if (!\is_file($pidFile)) {
|
|
return ['ok' => false, 'error' => 'not_running'];
|
|
}
|
|
$pid = (int) trim((string) file_get_contents($pidFile));
|
|
if ($pid <= 0 || !$this->tunnelStatus->isProcessAlive($pid)) {
|
|
@unlink($pidFile);
|
|
if (\is_file($keyFile)) {
|
|
@unlink($keyFile);
|
|
}
|
|
return ['ok' => false, 'error' => 'not_running'];
|
|
}
|
|
try {
|
|
$this->shell->execute('kill %s', [$pid]);
|
|
@unlink($pidFile);
|
|
if (\is_file($keyFile)) {
|
|
@unlink($keyFile);
|
|
}
|
|
$this->eventManager->dispatch('magesail_tunnel_stop_after', ['pid' => $pid]);
|
|
return ['ok' => true];
|
|
} catch (\Throwable $e) {
|
|
return ['ok' => false, 'error' => $e->getMessage()];
|
|
}
|
|
}
|
|
}
|