mirror of
https://github.com/coollabsio/coolify.git
synced 2026-11-04 10:42:23 +00:00
Store server IPs in one form (no brackets, short lower-case IPv6) and write each address in the form its context needs: - URLs and host:port (Sentinel, database URLs, SFTP backup download, DNS servers, SMTP, webhook base URLs, links, health checks) get brackets through formatHostForUrl(). - Traefik Host() rules use the address without brackets, so IPv6 domains no longer return 404; saved labels are fixed at deploy. Caddy keeps the brackets. - SSH keeps the plain address. - Hetzner gives an IPv6 /64 network; IPv6-only servers now get the server address (prefix::1) instead of the unusable prefix::64. - sslip labels, registry image names with an IPv6 host, the API domain list, allowlist matching and duplicate checks handle IPv6. A migration normalizes stored IPv6 server addresses, repairs Hetzner servers saved as ::64 that are not reachable, and adds brackets to saved Sentinel URLs.
101 lines
3.2 KiB
PHP
101 lines
3.2 KiB
PHP
<?php
|
|
|
|
use App\Models\PrivateKey;
|
|
use App\Models\Server;
|
|
use Illuminate\Support\Facades\Storage;
|
|
use Symfony\Component\HttpFoundation\File\Exception\FileNotFoundException;
|
|
use Symfony\Component\HttpFoundation\StreamedResponse;
|
|
|
|
function backupDownloadServer(): Server
|
|
{
|
|
$server = new Server;
|
|
$server->forceFill([
|
|
'ip' => '192.0.2.10',
|
|
'port' => 2222,
|
|
'user' => 'root',
|
|
'team_id' => 5,
|
|
]);
|
|
|
|
$privateKey = Mockery::mock(PrivateKey::class)->makePartial();
|
|
$privateKey->forceFill(['team_id' => 5]);
|
|
$privateKey->shouldReceive('getKeyLocation')->andReturn('/tmp/private-key');
|
|
$server->setRelation('privateKey', $privateKey);
|
|
|
|
return $server;
|
|
}
|
|
|
|
it('throws when the backup file does not exist on the server', function () {
|
|
$disk = Mockery::mock();
|
|
$disk->shouldReceive('exists')
|
|
->once()
|
|
->with('/backups/archive.tar.gz')
|
|
->andReturnFalse();
|
|
|
|
Storage::shouldReceive('build')
|
|
->once()
|
|
->with([
|
|
'driver' => 'sftp',
|
|
'host' => '192.0.2.10',
|
|
'port' => 2222,
|
|
'username' => 'root',
|
|
'privateKey' => '/tmp/private-key',
|
|
'root' => '/',
|
|
])
|
|
->andReturn($disk);
|
|
|
|
streamBackupFromServer(backupDownloadServer(), '/backups/archive.tar.gz', 'application/gzip');
|
|
})->throws(FileNotFoundException::class);
|
|
|
|
it('refuses a private key from another team', function () {
|
|
$server = backupDownloadServer();
|
|
$server->privateKey->forceFill(['team_id' => 6]);
|
|
|
|
Storage::shouldReceive('build')->never();
|
|
|
|
streamBackupFromServer($server, '/backups/archive.tar.gz', 'application/gzip');
|
|
})->throws(RuntimeException::class);
|
|
|
|
it('streams a backup file with the requested content type', function () {
|
|
$stream = fopen('php://memory', 'r+');
|
|
fwrite($stream, 'backup contents');
|
|
rewind($stream);
|
|
|
|
$disk = Mockery::mock();
|
|
$disk->shouldReceive('exists')->once()->andReturnTrue();
|
|
$disk->shouldReceive('readStream')
|
|
->once()
|
|
->with('/backups/archive.tar.gz')
|
|
->andReturn($stream);
|
|
Storage::shouldReceive('build')->once()->andReturn($disk);
|
|
|
|
$response = streamBackupFromServer(backupDownloadServer(), '/backups/archive.tar.gz', 'application/gzip');
|
|
|
|
expect($response)->toBeInstanceOf(StreamedResponse::class)
|
|
->and($response->headers->get('content-type'))->toBe('application/gzip')
|
|
->and($response->headers->get('content-disposition'))->toBe('attachment; filename="archive.tar.gz"');
|
|
|
|
ob_start();
|
|
ob_start();
|
|
$response->sendContent();
|
|
$contents = ob_get_clean();
|
|
|
|
expect($contents)->toBe('backup contents');
|
|
});
|
|
|
|
it('brackets an IPv6 server address for the sftp host', function (string $ip) {
|
|
$server = backupDownloadServer();
|
|
$server->forceFill(['ip' => $ip]);
|
|
|
|
$disk = Mockery::mock();
|
|
$disk->shouldReceive('exists')->once()->andReturnFalse();
|
|
Storage::shouldReceive('build')
|
|
->once()
|
|
->withArgs(fn (array $config): bool => $config['host'] === '[2a01:4f8::1]')
|
|
->andReturn($disk);
|
|
|
|
streamBackupFromServer($server, '/backups/archive.tar.gz', 'application/gzip');
|
|
})->with([
|
|
'raw' => '2a01:4f8::1',
|
|
'bracketed' => '[2a01:4f8::1]',
|
|
])->throws(FileNotFoundException::class);
|