diff --git a/app-modules/database-migration/src/Commands/UpdateStorageUrlsCommand.php b/app-modules/database-migration/src/Commands/UpdateStorageUrlsCommand.php new file mode 100644 index 00000000..76f04a60 --- /dev/null +++ b/app-modules/database-migration/src/Commands/UpdateStorageUrlsCommand.php @@ -0,0 +1,177 @@ + ['body'], + 'threads' => ['body'], + 'replies' => ['body'], + ]; + + public function handle(): int + { + $isDryRun = $this->option('dry-run'); + /** @var string $oldDomain */ + $oldDomain = $this->option('old-domain'); + /** @var string $newDisk */ + $newDisk = $this->option('new-disk'); + + if ($isDryRun) { + $this->warn('🔍 DRY RUN MODE - No URLs will be actually updated'); + } + + $this->info('🔄 Updating storage URLs in database content...'); + $this->newLine(); + + /** @var string $newBaseUrl */ + $newBaseUrl = $this->getNewBaseUrl($newDisk); + + if (! $newBaseUrl) { + $this->error("❌ Could not determine base URL for disk: {$newDisk}"); + + return Command::FAILURE; + } + + $this->info("Old domain: {$oldDomain}"); + $this->info("New base URL: {$newBaseUrl}"); + $this->newLine(); + + $totalUpdated = 0; + $totalRecords = 0; + + foreach ($this->affectedTables as $table => $columns) { + foreach ($columns as $column) { + $records = $this->getRecordsWithOldUrls($table, $column, $oldDomain); + $recordCount = $records->count(); + $totalRecords += $recordCount; + + if ($recordCount === 0) { + continue; + } + + $this->info("📋 Processing {$table}.{$column} - {$recordCount} records found"); + + $progressBar = $this->output->createProgressBar($recordCount); + $progressBar->start(); + + $updated = 0; + foreach ($records as $record) { + $newContent = $this->updateUrlsInContent($record->$column, $oldDomain, $newBaseUrl); + + if ($newContent !== $record->$column) { + if (! $isDryRun) { + DB::table($table) + ->where('id', $record->id) + ->update([$column => $newContent]); + } + $updated++; + } + + $progressBar->advance(); + } + + $progressBar->finish(); + $this->newLine(); + + if ($updated > 0) { + if ($isDryRun) { + $this->line(" Would update {$updated} records in {$table}.{$column}"); + } else { + $this->line(" ✅ Updated {$updated} records in {$table}.{$column}"); + } + } + + $totalUpdated += $updated; + } + } + + $this->newLine(); + if ($isDryRun) { + $this->info("✅ Dry run completed - {$totalUpdated} records would be updated out of {$totalRecords} total records with old URLs"); + } else { + $this->info("✅ URL migration completed - {$totalUpdated} records updated out of {$totalRecords} total records with old URLs"); + } + + return Command::SUCCESS; + } + + private function getNewBaseUrl(string $disk): ?string + { + try { + $diskConfig = config("filesystems.disks.{$disk}"); + + if (! $diskConfig) { + return null; + } + + if ($diskConfig['driver'] === 's3') { + $baseUrl = ''; + + if (isset($diskConfig['url']) && $diskConfig['url']) { + $baseUrl = rtrim($diskConfig['url'], '/'); + } else { + $bucket = $diskConfig['bucket'] ?? ''; + $region = $diskConfig['region'] ?? ''; + if ($bucket && $region) { + $baseUrl = "https://{$bucket}.s3.{$region}.amazonaws.com"; + } + } + + if ($baseUrl && isset($diskConfig['root']) && $diskConfig['root']) { + $root = trim($diskConfig['root'], '/'); + + if (filled($root)) { + $baseUrl .= '/'.$root; + } + } + + return $baseUrl; + } + + return Storage::disk($disk)->url(''); + } catch (\Exception $e) { + $this->error("Error getting base URL for disk {$disk}: ".$e->getMessage()); + + return null; + } + } + + private function getRecordsWithOldUrls(string $table, string $column, string $oldDomain): Collection + { + return collect( + DB::table($table) + ->select('id', $column) + ->where($column, 'LIKE', "%{$oldDomain}%") + ->get() + ); + } + + private function updateUrlsInContent(string $content, string $oldDomain, string $newBaseUrl): string + { + // Pattern to match the old storage URLs + // Example: https://laravel.cm/storage/ODvtYqlGsnpk9gn4hUNfBZfCw25CdlIrFL4GpooD.png + $pattern = '#'.preg_quote($oldDomain, '#').'/([a-zA-Z0-9._-]+\.[a-zA-Z]{2,4})#'; + + return (string) preg_replace_callback($pattern, function (array $matches) use ($newBaseUrl): string { + $filename = $matches[1]; + + return $newBaseUrl.'/'.$filename; + }, $content); + } +} diff --git a/app-modules/database-migration/src/Providers/DatabaseMigrationServiceProvider.php b/app-modules/database-migration/src/Providers/DatabaseMigrationServiceProvider.php index 48bb4b04..23872845 100644 --- a/app-modules/database-migration/src/Providers/DatabaseMigrationServiceProvider.php +++ b/app-modules/database-migration/src/Providers/DatabaseMigrationServiceProvider.php @@ -9,6 +9,7 @@ use Laravelcm\DatabaseMigration\Commands\MigrateFilesToS3Command; use Laravelcm\DatabaseMigration\Commands\ResetPostgresSequencesCommand; use Laravelcm\DatabaseMigration\Commands\SshTunnelCommand; +use Laravelcm\DatabaseMigration\Commands\UpdateStorageUrlsCommand; use Laravelcm\DatabaseMigration\Services\DatabaseMigrationService; use Laravelcm\DatabaseMigration\Services\SshTunnelService; @@ -33,6 +34,7 @@ public function boot(): void MigrateDatabaseCommand::class, MigrateFilesToS3Command::class, ResetPostgresSequencesCommand::class, + UpdateStorageUrlsCommand::class, ]); $this->publishes([ diff --git a/config/filesystems.php b/config/filesystems.php index 39dcacda..279d4fbe 100644 --- a/config/filesystems.php +++ b/config/filesystems.php @@ -86,7 +86,6 @@ 'links' => [ public_path('storage') => storage_path('app/public'), - public_path('media') => storage_path('app/media'), ], ];