media: PM-Medien privat ausliefern + Vorschau-Signatur (M4, M5, Fix#4/#5)

- Neue PM-Bilder/Anhänge liegen auf der privaten local-Disk; Auslieferung
  über kontrollierte Media-Routen mit gemeinsamer Zugriffslogik
  (AuthorizesPressReleaseMediaAccess). Öffentliche Medien werden aus den
  geladenen Modellen ermittelt (weniger Queries) und länger gecacht.
- Anonyme Token-Vorschau lädt Bilder über temporär signierte URLs (Fix#5).
- API-Resource baut Variant-URLs über die Media-Route statt /storage
  (Fix#4). Storage-Schicht prüft zusätzlich den serverseitigen MIME-Type (M5).
- press-releases:privatize-media migriert Bestandsmedien.

Deployment: einmalig `php artisan press-releases:privatize-media` ausführen.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Kevin Adametz 2026-07-14 13:48:06 +00:00
parent 6e84266f90
commit 6250b0bab4
21 changed files with 588 additions and 70 deletions

View file

@ -106,6 +106,7 @@ class ImageService
* the cover variant to keep storage usage predictable.
*
* @return array{
* disk: string,
* path: string,
* variants: array<string, string>,
* width: int|null,
@ -113,8 +114,11 @@ class ImageService
* mime: string|null,
* }
*/
public function storePressReleaseImage(UploadedFile $upload, int $pressReleaseId): array
{
public function storePressReleaseImage(
UploadedFile $upload,
int $pressReleaseId,
string $diskName = 'local',
): array {
$this->assertValidPressReleaseImageUpload($upload);
$directory = sprintf('press-releases/%d/images', $pressReleaseId);
@ -122,8 +126,8 @@ class ImageService
$filename = Str::uuid()->toString().'.'.$extension;
$relativePath = $directory.'/'.$filename;
$disk = $this->disk();
$disk->put($relativePath, $upload->get(), 'public');
$disk = $this->disk($diskName);
$disk->put($relativePath, $upload->get(), $diskName === 'public' ? 'public' : 'private');
$variants = $this->generateVariants(
$disk,
@ -142,6 +146,7 @@ class ImageService
}
return [
'disk' => $diskName,
'path' => $coverPath,
'variants' => $variants,
'width' => is_int($coverSize[0] ?? null) ? $coverSize[0] : null,
@ -435,8 +440,8 @@ class ImageService
}
}
private function disk(): Filesystem
private function disk(?string $diskName = null): Filesystem
{
return Storage::disk($this->disk);
return Storage::disk($diskName ?? $this->disk);
}
}