Maintain local ⭤ remote in sync with automatic AT Protocol parity for Laravel (alpha & unstable)
at dev 4.0 kB view raw
1<?php 2 3namespace SocialDept\AtpParity\Commands; 4 5use Illuminate\Console\Command; 6use SocialDept\AtpParity\Events\ImportProgress; 7use SocialDept\AtpParity\Export\ExportService; 8 9use function Laravel\Prompts\error; 10use function Laravel\Prompts\info; 11use function Laravel\Prompts\note; 12 13class ExportCommand extends Command 14{ 15 protected $signature = 'parity:export 16 {did : The DID to export} 17 {--output= : Output CAR file path} 18 {--import : Import records to database instead of saving CAR file} 19 {--collection=* : Specific collections to import (with --import)} 20 {--since= : Only export changes since this revision} 21 {--status : Show repository status instead of exporting}'; 22 23 protected $description = 'Export an AT Protocol repository as CAR file or import to database'; 24 25 public function handle(ExportService $service): int 26 { 27 $did = $this->argument('did'); 28 29 if (! str_starts_with($did, 'did:')) { 30 error("Invalid DID format: {$did}"); 31 32 return self::FAILURE; 33 } 34 35 if ($this->option('status')) { 36 return $this->handleStatus($service, $did); 37 } 38 39 if ($this->option('import')) { 40 return $this->handleImport($service, $did); 41 } 42 43 return $this->handleExport($service, $did); 44 } 45 46 protected function handleStatus(ExportService $service, string $did): int 47 { 48 info("Getting repository status for {$did}..."); 49 50 try { 51 $commit = $service->getLatestCommit($did); 52 $status = $service->getRepoStatus($did); 53 54 $this->table(['Property', 'Value'], [ 55 ['DID', $did], 56 ['Latest CID', $commit['cid'] ?? 'N/A'], 57 ['Latest Rev', $commit['rev'] ?? 'N/A'], 58 ['Active', ($status['active'] ?? false) ? 'Yes' : 'No'], 59 ['Status', $status['status'] ?? 'N/A'], 60 ]); 61 62 return self::SUCCESS; 63 } catch (\Throwable $e) { 64 error("Failed to get status: {$e->getMessage()}"); 65 66 return self::FAILURE; 67 } 68 } 69 70 protected function handleExport(ExportService $service, string $did): int 71 { 72 $output = $this->option('output') ?? "{$did}.car"; 73 $since = $this->option('since'); 74 75 // Sanitize filename if using DID as filename 76 $output = str_replace([':', '/'], ['_', '_'], $output); 77 78 info("Exporting repository {$did} to {$output}..."); 79 80 $result = $service->exportToFile($did, $output, $since); 81 82 if ($result->isFailed()) { 83 error("Export failed: {$result->error}"); 84 85 return self::FAILURE; 86 } 87 88 $size = $this->formatBytes($result->size); 89 info("Exported {$size} to {$output}"); 90 91 return self::SUCCESS; 92 } 93 94 protected function handleImport(ExportService $service, string $did): int 95 { 96 $collections = $this->option('collection') ?: null; 97 $collectionDisplay = $collections ? implode(', ', $collections) : 'all registered'; 98 99 info("Exporting and importing {$did} ({$collectionDisplay})..."); 100 101 $result = $service->exportAndImport( 102 $did, 103 $collections, 104 function (ImportProgress $progress) { 105 $this->output->write("\r"); 106 $this->output->write(" [{$progress->collection}] {$progress->recordsSynced} records synced"); 107 } 108 ); 109 110 $this->output->write("\n"); 111 112 if ($result->isFailed()) { 113 error("Import failed: {$result->error}"); 114 115 return self::FAILURE; 116 } 117 118 info("Imported {$result->size} records"); 119 120 return self::SUCCESS; 121 } 122 123 protected function formatBytes(int $bytes): string 124 { 125 $units = ['B', 'KB', 'MB', 'GB']; 126 $unit = 0; 127 128 while ($bytes >= 1024 && $unit < count($units) - 1) { 129 $bytes /= 1024; 130 $unit++; 131 } 132 133 return round($bytes, 2).' '.$units[$unit]; 134 } 135}