Parse and validate AT Protocol Lexicons with DTO generation for Laravel
1<?php
2
3namespace SocialDept\AtpSchema\Support;
4
5use Illuminate\Http\UploadedFile;
6use Illuminate\Support\Facades\Storage;
7use SocialDept\AtpSchema\Contracts\BlobHandler;
8use SocialDept\AtpSchema\Data\BlobReference;
9
10class DefaultBlobHandler implements BlobHandler
11{
12 /**
13 * Storage disk to use.
14 */
15 protected string $disk;
16
17 /**
18 * Storage path prefix.
19 */
20 protected string $path;
21
22 /**
23 * Create a new DefaultBlobHandler.
24 */
25 public function __construct(
26 ?string $disk = null,
27 string $path = 'blobs'
28 ) {
29 $this->disk = $disk ?? config('filesystems.default', 'local');
30 $this->path = $path;
31 }
32
33 /**
34 * Upload blob and create reference.
35 */
36 public function upload(UploadedFile $file): BlobReference
37 {
38 $hash = hash_file('sha256', $file->getPathname());
39 $mimeType = $file->getMimeType() ?? 'application/octet-stream';
40 $size = $file->getSize();
41
42 // Store with hash as filename to enable deduplication
43 $storagePath = $this->path.'/'.$hash;
44 Storage::disk($this->disk)->put($storagePath, file_get_contents($file->getPathname()));
45
46 return new BlobReference(
47 cid: $hash, // Using hash as CID for simplicity
48 mimeType: $mimeType,
49 size: $size
50 );
51 }
52
53 /**
54 * Upload blob from path.
55 */
56 public function uploadFromPath(string $path): BlobReference
57 {
58 $hash = hash_file('sha256', $path);
59 $mimeType = mime_content_type($path) ?: 'application/octet-stream';
60 $size = filesize($path);
61
62 // Store with hash as filename
63 $storagePath = $this->path.'/'.$hash;
64 Storage::disk($this->disk)->put($storagePath, file_get_contents($path));
65
66 return new BlobReference(
67 cid: $hash,
68 mimeType: $mimeType,
69 size: $size
70 );
71 }
72
73 /**
74 * Upload blob from content.
75 */
76 public function uploadFromContent(string $content, string $mimeType): BlobReference
77 {
78 $hash = hash('sha256', $content);
79 $size = strlen($content);
80
81 // Store with hash as filename
82 $storagePath = $this->path.'/'.$hash;
83 Storage::disk($this->disk)->put($storagePath, $content);
84
85 return new BlobReference(
86 cid: $hash,
87 mimeType: $mimeType,
88 size: $size
89 );
90 }
91
92 /**
93 * Download blob content.
94 */
95 public function download(BlobReference $blob): string
96 {
97 $storagePath = $this->path.'/'.$blob->cid;
98
99 if (! Storage::disk($this->disk)->exists($storagePath)) {
100 throw new \RuntimeException("Blob not found: {$blob->cid}");
101 }
102
103 return Storage::disk($this->disk)->get($storagePath);
104 }
105
106 /**
107 * Generate signed URL for blob.
108 */
109 public function url(BlobReference $blob): string
110 {
111 $storagePath = $this->path.'/'.$blob->cid;
112
113 // Try to generate a temporary URL if the disk supports it
114 try {
115 return Storage::disk($this->disk)->temporaryUrl(
116 $storagePath,
117 now()->addHour()
118 );
119 } catch (\RuntimeException $e) {
120 // Fallback to regular URL for disks that don't support temporary URLs
121 return Storage::disk($this->disk)->url($storagePath);
122 }
123 }
124
125 /**
126 * Check if blob exists in storage.
127 */
128 public function exists(BlobReference $blob): bool
129 {
130 $storagePath = $this->path.'/'.$blob->cid;
131
132 return Storage::disk($this->disk)->exists($storagePath);
133 }
134
135 /**
136 * Delete blob from storage.
137 */
138 public function delete(BlobReference $blob): bool
139 {
140 $storagePath = $this->path.'/'.$blob->cid;
141
142 if (! Storage::disk($this->disk)->exists($storagePath)) {
143 return false;
144 }
145
146 return Storage::disk($this->disk)->delete($storagePath);
147 }
148}