Store transfer files on Contabo Object Storage like Ladill Meet.
Deploy Ladill Transfer / deploy (push) Successful in 50s
Deploy Ladill Transfer / deploy (push) Successful in 50s
Add transfer-files S3 disk, config-driven uploads, and an artisan command to migrate existing local blobs to object storage. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,131 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Models\TransferFile;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
|
||||
class MigrateTransferStorageCommand extends Command
|
||||
{
|
||||
protected $signature = 'transfer:migrate-storage
|
||||
{--from=qr : Source disk for legacy transfer blobs}
|
||||
{--to= : Target disk (defaults to transfer.files_disk config)}
|
||||
{--dry-run : List files without copying or updating records}
|
||||
{--delete-source : Remove local blobs after a successful upload}';
|
||||
|
||||
protected $description = 'Copy transfer file blobs from local disk to Contabo object storage and update transfer_files.disk';
|
||||
|
||||
public function handle(): int
|
||||
{
|
||||
$fromDisk = (string) $this->option('from');
|
||||
$toDisk = (string) ($this->option('to') ?: config('transfer.files_disk', 'transfer-files'));
|
||||
$dryRun = (bool) $this->option('dry-run');
|
||||
$deleteSource = (bool) $this->option('delete-source');
|
||||
|
||||
if ($fromDisk === $toDisk) {
|
||||
$this->error('Source and target disks must differ.');
|
||||
|
||||
return self::FAILURE;
|
||||
}
|
||||
|
||||
if (! config("filesystems.disks.{$fromDisk}") || ! config("filesystems.disks.{$toDisk}")) {
|
||||
$this->error('Unknown filesystem disk configured for migration.');
|
||||
|
||||
return self::FAILURE;
|
||||
}
|
||||
|
||||
$files = TransferFile::query()
|
||||
->where('disk', $fromDisk)
|
||||
->orderBy('id')
|
||||
->get();
|
||||
|
||||
if ($files->isEmpty()) {
|
||||
$this->info("No transfer files on disk [{$fromDisk}].");
|
||||
|
||||
return self::SUCCESS;
|
||||
}
|
||||
|
||||
$this->info(sprintf(
|
||||
'%s migrating %d file(s) from [%s] to [%s].',
|
||||
$dryRun ? 'Would migrate' : 'Migrating',
|
||||
$files->count(),
|
||||
$fromDisk,
|
||||
$toDisk,
|
||||
));
|
||||
|
||||
$migrated = 0;
|
||||
$skipped = 0;
|
||||
$failed = 0;
|
||||
|
||||
foreach ($files as $file) {
|
||||
$label = "#{$file->id} {$file->path}";
|
||||
|
||||
if (! Storage::disk($fromDisk)->exists($file->path)) {
|
||||
$this->warn("Missing source blob: {$label}");
|
||||
$failed++;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (Storage::disk($toDisk)->exists($file->path)) {
|
||||
if (! $dryRun) {
|
||||
$file->update(['disk' => $toDisk]);
|
||||
if ($deleteSource) {
|
||||
Storage::disk($fromDisk)->delete($file->path);
|
||||
}
|
||||
}
|
||||
|
||||
$this->line("Already on target, updated record: {$label}");
|
||||
$skipped++;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($dryRun) {
|
||||
$this->line("Would migrate: {$label}");
|
||||
$migrated++;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
$stream = Storage::disk($fromDisk)->readStream($file->path);
|
||||
if ($stream === false) {
|
||||
throw new \RuntimeException('Could not open source stream.');
|
||||
}
|
||||
|
||||
$written = Storage::disk($toDisk)->writeStream($file->path, $stream);
|
||||
|
||||
if (is_resource($stream)) {
|
||||
fclose($stream);
|
||||
}
|
||||
|
||||
if ($written === false) {
|
||||
throw new \RuntimeException('Upload to target disk failed.');
|
||||
}
|
||||
|
||||
if (! Storage::disk($toDisk)->exists($file->path)) {
|
||||
throw new \RuntimeException('Uploaded blob not found on target disk.');
|
||||
}
|
||||
|
||||
$file->update(['disk' => $toDisk]);
|
||||
|
||||
if ($deleteSource) {
|
||||
Storage::disk($fromDisk)->delete($file->path);
|
||||
}
|
||||
|
||||
$this->line("Migrated: {$label}");
|
||||
$migrated++;
|
||||
} catch (\Throwable $e) {
|
||||
$this->error("Failed {$label}: {$e->getMessage()}");
|
||||
$failed++;
|
||||
}
|
||||
}
|
||||
|
||||
$this->newLine();
|
||||
$this->info("Done. migrated={$migrated} skipped={$skipped} failed={$failed}");
|
||||
|
||||
return $failed > 0 ? self::FAILURE : self::SUCCESS;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user