100 lines
2.6 KiB
PHP
100 lines
2.6 KiB
PHP
<?php
|
|
|
|
namespace App\Support\Ssh;
|
|
|
|
use App\Models\Server;
|
|
use phpseclib3\Net\SFTP as SftpProtocol; // aliased: class names are case-insensitive, "Sftp" == "SFTP"
|
|
use RuntimeException;
|
|
|
|
/**
|
|
* Thin wrapper around phpseclib SFTP for the file manager.
|
|
*/
|
|
class Sftp
|
|
{
|
|
use VerifiesHostKey;
|
|
|
|
private ?SftpProtocol $sftp = null;
|
|
|
|
public function __construct(private readonly CredentialVault $vault) {}
|
|
|
|
public function connect(Server $server): self
|
|
{
|
|
$sftp = new SftpProtocol($server->ip, $server->ssh_port ?: 22);
|
|
|
|
$this->verifyHostKey($sftp, $server);
|
|
|
|
$login = $this->vault->resolve($server);
|
|
|
|
if (! $sftp->login($login['username'], $login['secret'])) {
|
|
throw new RuntimeException("SFTP-Login auf {$server->name} fehlgeschlagen.");
|
|
}
|
|
|
|
$this->sftp = $sftp;
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* @return array<int, array{name: string, type: string, size: int, permissions: string, mtime: int}>
|
|
*/
|
|
public function list(string $path = '.'): array
|
|
{
|
|
$raw = $this->client()->rawlist($path) ?: [];
|
|
$entries = [];
|
|
|
|
foreach ($raw as $name => $info) {
|
|
if ($name === '.' || $name === '..') {
|
|
continue;
|
|
}
|
|
|
|
$entries[] = [
|
|
'name' => (string) $name,
|
|
'type' => ($info['type'] ?? null) === SftpProtocol::TYPE_DIRECTORY ? 'dir' : 'file',
|
|
'size' => (int) ($info['size'] ?? 0),
|
|
'permissions' => isset($info['permissions'])
|
|
? substr(sprintf('%o', $info['permissions']), -4)
|
|
: '',
|
|
'mtime' => (int) ($info['mtime'] ?? 0),
|
|
];
|
|
}
|
|
|
|
return $entries;
|
|
}
|
|
|
|
public function get(string $remote): string
|
|
{
|
|
return (string) $this->client()->get($remote);
|
|
}
|
|
|
|
public function put(string $remote, string $contents): bool
|
|
{
|
|
return $this->client()->put($remote, $contents);
|
|
}
|
|
|
|
public function delete(string $remote): bool
|
|
{
|
|
return $this->client()->delete($remote);
|
|
}
|
|
|
|
public function putFromFile(string $remote, string $localPath): bool
|
|
{
|
|
return $this->client()->put($remote, $localPath, SftpProtocol::SOURCE_LOCAL_FILE);
|
|
}
|
|
|
|
public function size(string $remote): int
|
|
{
|
|
return (int) ($this->client()->filesize($remote) ?: 0);
|
|
}
|
|
|
|
public function disconnect(): void
|
|
{
|
|
$this->sftp?->disconnect();
|
|
$this->sftp = null;
|
|
}
|
|
|
|
private function client(): SftpProtocol
|
|
{
|
|
return $this->sftp ?? throw new RuntimeException('Nicht verbunden — connect() zuerst aufrufen.');
|
|
}
|
|
}
|