chore: improve code

This commit is contained in:
peaklabs-dev
2025-01-13 16:43:23 +01:00
parent a8b77b389a
commit f0d15afbf2

View File

@@ -17,17 +17,12 @@ use Visus\Cuid2\Cuid2;
function generate_database_name(string $type): string function generate_database_name(string $type): string
{ {
$cuid = new Cuid2; return $type.'-database-'.(new Cuid2);
return $type.'-database-'.$cuid;
} }
function create_standalone_postgresql($environmentId, $destinationUuid, ?array $otherData = null, string $databaseImage = 'postgres:16-alpine'): StandalonePostgresql function create_standalone_postgresql($environmentId, $destinationUuid, ?array $otherData = null, string $databaseImage = 'postgres:16-alpine'): StandalonePostgresql
{ {
$destination = StandaloneDocker::where('uuid', $destinationUuid)->first(); $destination = StandaloneDocker::where('uuid', $destinationUuid)->firstOrFail();
if (! $destination) {
throw new Exception('Destination not found');
}
$database = new StandalonePostgresql; $database = new StandalonePostgresql;
$database->name = generate_database_name('postgresql'); $database->name = generate_database_name('postgresql');
$database->image = $databaseImage; $database->image = $databaseImage;
@@ -45,10 +40,7 @@ function create_standalone_postgresql($environmentId, $destinationUuid, ?array $
function create_standalone_redis($environment_id, $destination_uuid, ?array $otherData = null): StandaloneRedis function create_standalone_redis($environment_id, $destination_uuid, ?array $otherData = null): StandaloneRedis
{ {
$destination = StandaloneDocker::where('uuid', $destination_uuid)->first(); $destination = StandaloneDocker::where('uuid', $destination_uuid)->firstOrFail();
if (! $destination) {
throw new Exception('Destination not found');
}
$database = new StandaloneRedis; $database = new StandaloneRedis;
$database->name = generate_database_name('redis'); $database->name = generate_database_name('redis');
$redis_password = \Illuminate\Support\Str::password(length: 64, symbols: false); $redis_password = \Illuminate\Support\Str::password(length: 64, symbols: false);
@@ -79,10 +71,7 @@ function create_standalone_redis($environment_id, $destination_uuid, ?array $oth
function create_standalone_mongodb($environment_id, $destination_uuid, ?array $otherData = null): StandaloneMongodb function create_standalone_mongodb($environment_id, $destination_uuid, ?array $otherData = null): StandaloneMongodb
{ {
$destination = StandaloneDocker::where('uuid', $destination_uuid)->first(); $destination = StandaloneDocker::where('uuid', $destination_uuid)->firstOrFail();
if (! $destination) {
throw new Exception('Destination not found');
}
$database = new StandaloneMongodb; $database = new StandaloneMongodb;
$database->name = generate_database_name('mongodb'); $database->name = generate_database_name('mongodb');
$database->mongo_initdb_root_password = \Illuminate\Support\Str::password(length: 64, symbols: false); $database->mongo_initdb_root_password = \Illuminate\Support\Str::password(length: 64, symbols: false);
@@ -96,12 +85,10 @@ function create_standalone_mongodb($environment_id, $destination_uuid, ?array $o
return $database; return $database;
} }
function create_standalone_mysql($environment_id, $destination_uuid, ?array $otherData = null): StandaloneMysql function create_standalone_mysql($environment_id, $destination_uuid, ?array $otherData = null): StandaloneMysql
{ {
$destination = StandaloneDocker::where('uuid', $destination_uuid)->first(); $destination = StandaloneDocker::where('uuid', $destination_uuid)->firstOrFail();
if (! $destination) {
throw new Exception('Destination not found');
}
$database = new StandaloneMysql; $database = new StandaloneMysql;
$database->name = generate_database_name('mysql'); $database->name = generate_database_name('mysql');
$database->mysql_root_password = \Illuminate\Support\Str::password(length: 64, symbols: false); $database->mysql_root_password = \Illuminate\Support\Str::password(length: 64, symbols: false);
@@ -116,12 +103,10 @@ function create_standalone_mysql($environment_id, $destination_uuid, ?array $oth
return $database; return $database;
} }
function create_standalone_mariadb($environment_id, $destination_uuid, ?array $otherData = null): StandaloneMariadb function create_standalone_mariadb($environment_id, $destination_uuid, ?array $otherData = null): StandaloneMariadb
{ {
$destination = StandaloneDocker::where('uuid', $destination_uuid)->first(); $destination = StandaloneDocker::where('uuid', $destination_uuid)->firstOrFail();
if (! $destination) {
throw new Exception('Destination not found');
}
$database = new StandaloneMariadb; $database = new StandaloneMariadb;
$database->name = generate_database_name('mariadb'); $database->name = generate_database_name('mariadb');
$database->mariadb_root_password = \Illuminate\Support\Str::password(length: 64, symbols: false); $database->mariadb_root_password = \Illuminate\Support\Str::password(length: 64, symbols: false);
@@ -129,7 +114,6 @@ function create_standalone_mariadb($environment_id, $destination_uuid, ?array $o
$database->environment_id = $environment_id; $database->environment_id = $environment_id;
$database->destination_id = $destination->id; $database->destination_id = $destination->id;
$database->destination_type = $destination->getMorphClass(); $database->destination_type = $destination->getMorphClass();
if ($otherData) { if ($otherData) {
$database->fill($otherData); $database->fill($otherData);
} }
@@ -137,12 +121,10 @@ function create_standalone_mariadb($environment_id, $destination_uuid, ?array $o
return $database; return $database;
} }
function create_standalone_keydb($environment_id, $destination_uuid, ?array $otherData = null): StandaloneKeydb function create_standalone_keydb($environment_id, $destination_uuid, ?array $otherData = null): StandaloneKeydb
{ {
$destination = StandaloneDocker::where('uuid', $destination_uuid)->first(); $destination = StandaloneDocker::where('uuid', $destination_uuid)->firstOrFail();
if (! $destination) {
throw new Exception('Destination not found');
}
$database = new StandaloneKeydb; $database = new StandaloneKeydb;
$database->name = generate_database_name('keydb'); $database->name = generate_database_name('keydb');
$database->keydb_password = \Illuminate\Support\Str::password(length: 64, symbols: false); $database->keydb_password = \Illuminate\Support\Str::password(length: 64, symbols: false);
@@ -159,10 +141,7 @@ function create_standalone_keydb($environment_id, $destination_uuid, ?array $oth
function create_standalone_dragonfly($environment_id, $destination_uuid, ?array $otherData = null): StandaloneDragonfly function create_standalone_dragonfly($environment_id, $destination_uuid, ?array $otherData = null): StandaloneDragonfly
{ {
$destination = StandaloneDocker::where('uuid', $destination_uuid)->first(); $destination = StandaloneDocker::where('uuid', $destination_uuid)->firstOrFail();
if (! $destination) {
throw new Exception('Destination not found');
}
$database = new StandaloneDragonfly; $database = new StandaloneDragonfly;
$database->name = generate_database_name('dragonfly'); $database->name = generate_database_name('dragonfly');
$database->dragonfly_password = \Illuminate\Support\Str::password(length: 64, symbols: false); $database->dragonfly_password = \Illuminate\Support\Str::password(length: 64, symbols: false);
@@ -176,12 +155,10 @@ function create_standalone_dragonfly($environment_id, $destination_uuid, ?array
return $database; return $database;
} }
function create_standalone_clickhouse($environment_id, $destination_uuid, ?array $otherData = null): StandaloneClickhouse function create_standalone_clickhouse($environment_id, $destination_uuid, ?array $otherData = null): StandaloneClickhouse
{ {
$destination = StandaloneDocker::where('uuid', $destination_uuid)->first(); $destination = StandaloneDocker::where('uuid', $destination_uuid)->firstOrFail();
if (! $destination) {
throw new Exception('Destination not found');
}
$database = new StandaloneClickhouse; $database = new StandaloneClickhouse;
$database->name = generate_database_name('clickhouse'); $database->name = generate_database_name('clickhouse');
$database->clickhouse_admin_password = \Illuminate\Support\Str::password(length: 64, symbols: false); $database->clickhouse_admin_password = \Illuminate\Support\Str::password(length: 64, symbols: false);
@@ -201,29 +178,22 @@ function deleteBackupsLocally(string|array|null $filenames, Server $server): voi
if (empty($filenames)) { if (empty($filenames)) {
return; return;
} }
if (is_string($filenames)) { if (is_string($filenames)) {
$filenames = [$filenames]; $filenames = [$filenames];
} }
$quotedFiles = array_map(fn ($file) => "\"$file\"", $filenames);
$quotedFiles = array_map(function ($file) {
return "\"$file\"";
}, $filenames);
instant_remote_process(['rm -f '.implode(' ', $quotedFiles)], $server, throwError: false); instant_remote_process(['rm -f '.implode(' ', $quotedFiles)], $server, throwError: false);
} }
function deleteBackupsS3(string|array|null $filenames, Server $server, S3Storage $s3): void function deleteBackupsS3(string|array|null $filenames, S3Storage $s3): void
{ {
if (empty($filenames) || ! $s3) { if (empty($filenames) || ! $s3) {
return; return;
} }
if (is_string($filenames)) { if (is_string($filenames)) {
$filenames = [$filenames]; $filenames = [$filenames];
} }
// Initialize S3 client using Laravel's Storage facade
$disk = Storage::build([ $disk = Storage::build([
'driver' => 's3', 'driver' => 's3',
'key' => $s3->key, 'key' => $s3->key,
@@ -232,38 +202,30 @@ function deleteBackupsS3(string|array|null $filenames, Server $server, S3Storage
'bucket' => $s3->bucket, 'bucket' => $s3->bucket,
'endpoint' => $s3->endpoint, 'endpoint' => $s3->endpoint,
'use_path_style_endpoint' => true, 'use_path_style_endpoint' => true,
'bucket_endpoint' => $s3->isHetzner() || $s3->isDigitalOcean(),
'aws_url' => $s3->awsUrl(),
]); ]);
// Delete files in bulk
$disk->delete($filenames); $disk->delete($filenames);
} }
function deleteEmptyBackupFolder($folderPath, Server $server): void function deleteEmptyBackupFolder($folderPath, Server $server): void
{ {
// Properly escape the folder path for shell commands
$escapedPath = escapeshellarg($folderPath); $escapedPath = escapeshellarg($folderPath);
$escapedParentPath = escapeshellarg(dirname($folderPath)); $escapedParentPath = escapeshellarg(dirname($folderPath));
// Check if current folder is empty
$checkEmpty = instant_remote_process(["[ -d $escapedPath ] && [ -z \"$(ls -A $escapedPath)\" ] && echo 'empty' || echo 'not empty'"], $server, throwError: false); $checkEmpty = instant_remote_process(["[ -d $escapedPath ] && [ -z \"$(ls -A $escapedPath)\" ] && echo 'empty' || echo 'not empty'"], $server, throwError: false);
if (trim($checkEmpty) === 'empty') { if (trim($checkEmpty) === 'empty') {
// Remove the empty folder
instant_remote_process(["rmdir $escapedPath"], $server, throwError: false); instant_remote_process(["rmdir $escapedPath"], $server, throwError: false);
$checkParentEmpty = instant_remote_process(["[ -d $escapedParentPath ] && [ -z \"$(ls -A $escapedParentPath)\" ] && echo 'empty' || echo 'not empty'"], $server, throwError: false);
// Check if parent folder exists and is empty
$checkParentEmpty = instant_remote_process([
"[ -d $escapedParentPath ] && [ -z \"$(ls -A $escapedParentPath)\" ] && echo 'empty' || echo 'not empty'",
], $server, throwError: false);
if (trim($checkParentEmpty) === 'empty') { if (trim($checkParentEmpty) === 'empty') {
// Remove the empty parent folder
instant_remote_process(["rmdir $escapedParentPath"], $server, throwError: false); instant_remote_process(["rmdir $escapedParentPath"], $server, throwError: false);
} }
} }
} }
function deleteOldBackupsLocally($backup) function deleteOldBackupsLocally($backup): void
{ {
if (! $backup || ! $backup->executions) { if (! $backup || ! $backup->executions) {
return; return;
@@ -278,7 +240,6 @@ function deleteOldBackupsLocally($backup)
return; return;
} }
// Get retention limits
$retentionAmount = $backup->database_backup_retention_amount_locally; $retentionAmount = $backup->database_backup_retention_amount_locally;
$retentionDays = $backup->database_backup_retention_days_locally; $retentionDays = $backup->database_backup_retention_days_locally;
@@ -288,29 +249,19 @@ function deleteOldBackupsLocally($backup)
$backupsToDelete = collect(); $backupsToDelete = collect();
// Process backups based on retention amount
if ($retentionAmount > 0) { if ($retentionAmount > 0) {
$backupsToDelete = $backupsToDelete->merge( $backupsToDelete = $backupsToDelete->merge($successfulBackups->skip($retentionAmount));
$successfulBackups->skip($retentionAmount)
);
} }
// Process backups based on retention days
if ($retentionDays > 0) { if ($retentionDays > 0) {
$oldestAllowedDate = $successfulBackups->first()->created_at->clone()->utc()->subDays($retentionDays); $oldestAllowedDate = $successfulBackups->first()->created_at->clone()->utc()->subDays($retentionDays);
$oldBackups = $successfulBackups->filter(function ($execution) use ($oldestAllowedDate) { $oldBackups = $successfulBackups->filter(fn ($execution) => $execution->created_at->utc() < $oldestAllowedDate);
return $execution->created_at->utc() < $oldestAllowedDate;
});
$backupsToDelete = $backupsToDelete->merge($oldBackups); $backupsToDelete = $backupsToDelete->merge($oldBackups);
} }
// Get unique backups to delete and chunk them for parallel processing
$backupsToDelete = $backupsToDelete->unique('id'); $backupsToDelete = $backupsToDelete->unique('id');
// Keep track of folders to check
$foldersToCheck = collect(); $foldersToCheck = collect();
// Process deletions in parallel chunks
$backupsToDelete->chunk(10)->each(function ($chunk) use ($backup, &$foldersToCheck) { $backupsToDelete->chunk(10)->each(function ($chunk) use ($backup, &$foldersToCheck) {
$executionIds = []; $executionIds = [];
$filesToDelete = []; $filesToDelete = [];
@@ -319,28 +270,22 @@ function deleteOldBackupsLocally($backup)
if ($execution->filename) { if ($execution->filename) {
$filesToDelete[] = $execution->filename; $filesToDelete[] = $execution->filename;
$executionIds[] = $execution->id; $executionIds[] = $execution->id;
// Add the folder path to check later
$foldersToCheck->push(dirname($execution->filename)); $foldersToCheck->push(dirname($execution->filename));
} }
} }
if (! empty($filesToDelete)) { if (! empty($filesToDelete)) {
deleteBackupsLocally($filesToDelete, $backup->server); deleteBackupsLocally($filesToDelete, $backup->server);
// Bulk delete executions from database
if (! empty($executionIds)) { if (! empty($executionIds)) {
$backup->executions()->whereIn('id', $executionIds)->delete(); $backup->executions()->whereIn('id', $executionIds)->delete();
} }
} }
}); });
// Check and clean up empty folders $foldersToCheck->unique()->each(fn ($folder) => deleteEmptyBackupFolder($folder, $backup->server));
$foldersToCheck->unique()->each(function ($folder) use ($backup) {
deleteEmptyBackupFolder($folder, $backup->server);
});
} }
function deleteOldBackupsFromS3($backup) function deleteOldBackupsFromS3($backup): void
{ {
if (! $backup || ! $backup->executions || ! $backup->s3) { if (! $backup || ! $backup->executions || ! $backup->s3) {
return; return;
@@ -355,7 +300,6 @@ function deleteOldBackupsFromS3($backup)
return; return;
} }
// Get retention limits
$retentionAmount = $backup->database_backup_retention_amount_s3; $retentionAmount = $backup->database_backup_retention_amount_s3;
$retentionDays = $backup->database_backup_retention_days_s3; $retentionDays = $backup->database_backup_retention_days_s3;
$maxStorageGB = $backup->database_backup_retention_max_storage_s3; $maxStorageGB = $backup->database_backup_retention_max_storage_s3;
@@ -366,36 +310,25 @@ function deleteOldBackupsFromS3($backup)
$backupsToDelete = collect(); $backupsToDelete = collect();
// Process backups based on retention amount
if ($retentionAmount > 0) { if ($retentionAmount > 0) {
$backupsToDelete = $backupsToDelete->merge( $backupsToDelete = $backupsToDelete->merge($successfulBackups->skip($retentionAmount));
$successfulBackups->skip($retentionAmount)
);
} }
// Process backups based on retention days
if ($retentionDays > 0) { if ($retentionDays > 0) {
$oldestAllowedDate = $successfulBackups->first()->created_at->clone()->utc()->subDays($retentionDays); $oldestAllowedDate = $successfulBackups->first()->created_at->clone()->utc()->subDays($retentionDays);
$oldBackups = $successfulBackups->filter(function ($execution) use ($oldestAllowedDate) { $oldBackups = $successfulBackups->filter(fn ($execution) => $execution->created_at->utc() < $oldestAllowedDate);
return $execution->created_at->utc() < $oldestAllowedDate;
});
$backupsToDelete = $backupsToDelete->merge($oldBackups); $backupsToDelete = $backupsToDelete->merge($oldBackups);
} }
// Process backups based on total storage limit
if ($maxStorageGB > 0) { if ($maxStorageGB > 0) {
$maxStorageBytes = $maxStorageGB * 1024 * 1024 * 1024; // Convert GB to bytes $maxStorageBytes = $maxStorageGB * 1024 * 1024 * 1024;
$totalSize = 0; $totalSize = 0;
$backupsOverLimit = collect(); $backupsOverLimit = collect();
foreach ($successfulBackups as $backup) { foreach ($successfulBackups as $backup) {
$totalSize += (int) $backup->size; $totalSize += (int) $backup->size;
// If we're over the limit, add this and all older backups to delete list
if ($totalSize > $maxStorageBytes) { if ($totalSize > $maxStorageBytes) {
$backupsOverLimit = $successfulBackups->filter(function ($b) use ($backup) { $backupsOverLimit = $successfulBackups->filter(fn ($b) => $b->created_at->utc() <= $backup->created_at->utc());
return $b->created_at->utc() <= $backup->created_at->utc();
});
break; break;
} }
} }
@@ -403,13 +336,9 @@ function deleteOldBackupsFromS3($backup)
$backupsToDelete = $backupsToDelete->merge($backupsOverLimit); $backupsToDelete = $backupsToDelete->merge($backupsOverLimit);
} }
// Get unique backups to delete and chunk them for parallel processing
$backupsToDelete = $backupsToDelete->unique('id'); $backupsToDelete = $backupsToDelete->unique('id');
// Keep track of folders to check
$foldersToCheck = collect(); $foldersToCheck = collect();
// Process deletions in parallel chunks
$backupsToDelete->chunk(10)->each(function ($chunk) use ($backup, &$foldersToCheck) { $backupsToDelete->chunk(10)->each(function ($chunk) use ($backup, &$foldersToCheck) {
$executionIds = []; $executionIds = [];
$filesToDelete = []; $filesToDelete = [];
@@ -418,15 +347,12 @@ function deleteOldBackupsFromS3($backup)
if ($execution->filename) { if ($execution->filename) {
$filesToDelete[] = $execution->filename; $filesToDelete[] = $execution->filename;
$executionIds[] = $execution->id; $executionIds[] = $execution->id;
// Add the folder path to check later
$foldersToCheck->push(dirname($execution->filename)); $foldersToCheck->push(dirname($execution->filename));
} }
} }
if (! empty($filesToDelete)) { if (! empty($filesToDelete)) {
deleteBackupsS3($filesToDelete, $backup->server, $backup->s3); deleteBackupsS3($filesToDelete, $backup->server, $backup->s3);
// Update executions to mark S3 backup as deleted
if (! empty($executionIds)) { if (! empty($executionIds)) {
$backup->executions() $backup->executions()
->whereIn('id', $executionIds) ->whereIn('id', $executionIds)
@@ -435,10 +361,7 @@ function deleteOldBackupsFromS3($backup)
} }
}); });
// Check and clean up empty folders $foldersToCheck->unique()->each(fn ($folder) => deleteEmptyBackupFolder($folder, $backup->server));
$foldersToCheck->unique()->each(function ($folder) use ($backup) {
deleteEmptyBackupFolder($folder, $backup->server);
});
} }
function isPublicPortAlreadyUsed(Server $server, int $port, ?string $id = null): bool function isPublicPortAlreadyUsed(Server $server, int $port, ?string $id = null): bool