Files
MageSail/Magesail/Model/TunnelStatus.php
T
snxraven 960db9801c
CI / php (push) Successful in 2m53s
feat(tunnels): add Service type with dynamic env.php endpoint discovery
- Add EnvServicePortResolver (DeploymentConfig) for MySQL, Redis frontends,
  session Redis, AMQP, http_cache_hosts, optional search
- Extend tunnel registry with tunnel_type, service_key, service metadata;
  provisionServiceTunnel without store/NGINX map; teardown guards
- Admin: Type (Website|Service), service dropdown, list Type column; JS toggle
- TunnelManager/start-server: forward to resolved host:port
- Tests: EnvServicePortResolver + service teardown; test stubs for Magento/PSR
- docs: admin-ui Type/Service and security notes
2026-03-21 04:38:37 -05:00

163 lines
5.1 KiB
PHP

<?php
declare(strict_types=1);
namespace MageSail\Magesail\Model;
use Magento\Framework\App\Config\ScopeConfigInterface;
use Magento\Framework\Shell;
/**
* PID/key state and process checks per tunnel id.
*/
class TunnelStatus
{
private const XML_AUTO_RESTART = 'magesail/settings/auto_restart';
public function __construct(
private readonly Shell $shell,
private readonly ScopeConfigInterface $scopeConfig,
private readonly TunnelStoreProvisioner $tunnelStoreProvisioner,
private readonly TunnelRegistry $tunnelRegistry,
private readonly TunnelProcessPaths $tunnelProcessPaths
) {
}
public function getPidFilePath(string $tunnelId): string
{
return $this->tunnelProcessPaths->getPidFilePath($tunnelId);
}
public function getKeyFilePath(string $tunnelId): string
{
return $this->tunnelProcessPaths->getKeyFilePath($tunnelId);
}
public function getLogFilePath(string $tunnelId): string
{
return $this->tunnelProcessPaths->getLogFilePath($tunnelId);
}
/**
* @return array{running: bool, pid: int|null, key: string|null}
*/
public function getStatus(string $tunnelId): array
{
$pidFile = $this->getPidFilePath($tunnelId);
$keyFile = $this->getKeyFilePath($tunnelId);
if (!\is_file($pidFile)) {
return ['running' => false, 'pid' => null, 'key' => null];
}
$pid = (int) trim((string) file_get_contents($pidFile));
if ($pid <= 0) {
return ['running' => false, 'pid' => null, 'key' => null];
}
if (!$this->isProcessAlive($pid)) {
$this->maybeTeardownTunnelStoreForStaleProcess($tunnelId);
@unlink($pidFile);
$kf = $this->getKeyFilePath($tunnelId);
if (\is_file($kf)) {
@unlink($kf);
}
return ['running' => false, 'pid' => null, 'key' => null];
}
$key = null;
if (\is_file($keyFile)) {
$key = trim((string) file_get_contents($keyFile));
if ($key === '') {
$key = null;
}
}
return ['running' => true, 'pid' => $pid, 'key' => $key];
}
public function isRunning(string $tunnelId): bool
{
return $this->getStatus($tunnelId)['running'];
}
/**
* @return list<array{tunnel_id: string, running: bool, pid: int|null, key: string|null, label: string, store_code: string, local_port: int}>
*/
public function getAllTunnelStatuses(): array
{
$out = [];
foreach ($this->tunnelRegistry->listTunnels() as $t) {
$id = (string) ($t['id'] ?? '');
if ($id === '') {
continue;
}
$st = $this->getStatus($id);
$out[] = [
'tunnel_id' => $id,
'tunnel_type' => (string) ($t['tunnel_type'] ?? 'website'),
'label' => (string) ($t['label'] ?? ''),
'store_code' => (string) ($t['store_code'] ?? ''),
'local_port' => (int) ($t['local_port'] ?? 80),
'service_key' => (string) ($t['service_key'] ?? ''),
'service_label' => (string) ($t['service_label'] ?? ''),
'service_target_host' => (string) ($t['service_target_host'] ?? ''),
'running' => $st['running'],
'pid' => $st['pid'],
'key' => $st['key'],
];
}
return $out;
}
public function isProcessAlive(int $pid): bool
{
if ($pid <= 0) {
return false;
}
try {
$this->shell->execute("ps -p %s > /dev/null 2>&1", [$pid]);
return true;
} catch (\Exception $e) {
return false;
}
}
/**
* If PID file exists but process is dead, remove pid/key files and optionally teardown store.
*/
public function cleanupStaleStateForTunnel(string $tunnelId): void
{
$pidFile = $this->getPidFilePath($tunnelId);
if (!\is_file($pidFile)) {
return;
}
$pid = (int) trim((string) file_get_contents($pidFile));
if ($pid <= 0 || !$this->isProcessAlive($pid)) {
$this->maybeTeardownTunnelStoreForStaleProcess($tunnelId);
@unlink($pidFile);
$keyFile = $this->getKeyFilePath($tunnelId);
if (\is_file($keyFile)) {
@unlink($keyFile);
}
}
}
public function cleanupAllStalePidFiles(): void
{
foreach ($this->tunnelRegistry->listTunnels() as $t) {
$id = (string) ($t['id'] ?? '');
if ($id !== '') {
$this->cleanupStaleStateForTunnel($id);
}
}
}
/**
* When the tunnel process is gone and auto-restart is off, remove the tunnel store view.
*/
private function maybeTeardownTunnelStoreForStaleProcess(string $tunnelId): void
{
$autoRestart = (bool) $this->scopeConfig->getValue(self::XML_AUTO_RESTART);
if ($autoRestart) {
return;
}
$this->tunnelStoreProvisioner->teardownAfterStaleProcess($tunnelId);
}
}