Maintain local ⭤ remote in sync with automatic AT Protocol parity for Laravel (alpha & unstable)
at dev 3.8 kB view raw
1<?php 2 3namespace SocialDept\AtpParity\Export; 4 5use BackedEnum; 6use Generator; 7use SocialDept\AtpClient\Facades\Atp; 8use SocialDept\AtpParity\Import\ImportService; 9use SocialDept\AtpParity\MapperRegistry; 10use Throwable; 11 12/** 13 * Service for exporting AT Protocol repositories. 14 */ 15class ExportService 16{ 17 public function __construct( 18 protected MapperRegistry $registry, 19 protected ImportService $importService 20 ) {} 21 22 /** 23 * Download a user's repository as CAR data. 24 */ 25 public function downloadRepo(string $did, ?string $since = null): RepoExport 26 { 27 $response = Atp::public()->atproto->sync->getRepo($did, $since); 28 $carData = $response->body(); 29 30 return new RepoExport( 31 did: $did, 32 carData: $carData, 33 size: strlen($carData), 34 ); 35 } 36 37 /** 38 * Export a repository to a local file. 39 */ 40 public function exportToFile(string $did, string $path, ?string $since = null): ExportResult 41 { 42 try { 43 $export = $this->downloadRepo($did, $since); 44 45 if (! $export->saveTo($path)) { 46 return ExportResult::failed("Failed to write to file: {$path}"); 47 } 48 49 return ExportResult::success($path, $export->size); 50 } catch (Throwable $e) { 51 return ExportResult::failed($e->getMessage()); 52 } 53 } 54 55 /** 56 * Export and import records from a repository. 57 * 58 * This downloads the repository and imports records using the normal import pipeline. 59 * It's useful for bulk importing all records from a user. 60 * 61 * @param array<string>|null $collections Specific collections to import (null = all registered) 62 */ 63 public function exportAndImport( 64 string $did, 65 ?array $collections = null, 66 ?callable $onProgress = null 67 ): ExportResult { 68 try { 69 // Use the import service to import the user's records 70 $result = $this->importService->importUser($did, $collections, $onProgress); 71 72 if ($result->isFailed()) { 73 return ExportResult::failed($result->error ?? 'Import failed'); 74 } 75 76 return ExportResult::success( 77 path: "imported:{$did}", 78 size: $result->recordsSynced 79 ); 80 } catch (Throwable $e) { 81 return ExportResult::failed($e->getMessage()); 82 } 83 } 84 85 /** 86 * List available blobs for a repository. 87 * 88 * @return Generator<string> Yields blob CIDs 89 */ 90 public function listBlobs(string $did, ?string $since = null): Generator 91 { 92 $cursor = null; 93 94 do { 95 $response = Atp::public()->atproto->sync->listBlobs( 96 did: $did, 97 since: $since, 98 limit: 500, 99 cursor: $cursor, 100 ); 101 102 foreach ($response->cids as $cid) { 103 yield $cid; 104 } 105 106 $cursor = $response->cursor; 107 } while ($cursor !== null); 108 } 109 110 /** 111 * Download a specific blob. 112 */ 113 public function downloadBlob(string $did, string $cid): string 114 { 115 $response = Atp::public()->atproto->sync->getBlob($did, $cid); 116 117 return $response->body(); 118 } 119 120 /** 121 * Get the latest commit for a repository. 122 */ 123 public function getLatestCommit(string $did): array 124 { 125 $commit = Atp::public()->atproto->sync->getLatestCommit($did); 126 127 return [ 128 'cid' => $commit->cid, 129 'rev' => $commit->rev, 130 ]; 131 } 132 133 /** 134 * Get the hosting status for a repository. 135 */ 136 public function getRepoStatus(string $did): array 137 { 138 $status = Atp::public()->atproto->sync->getRepoStatus($did); 139 140 return $status->toArray(); 141 } 142}