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:
parent
0b35c3b04d
commit
000ae93e3b
21 changed files with 588 additions and 70 deletions
|
|
@ -7,7 +7,6 @@ use Illuminate\Database\Eloquent\Factories\HasFactory;
|
|||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
|
||||
class PressReleaseAttachment extends Model
|
||||
{
|
||||
|
|
@ -52,16 +51,6 @@ class PressReleaseAttachment extends Model
|
|||
return asset('storage/'.ltrim((string) $this->path, '/'));
|
||||
}
|
||||
|
||||
try {
|
||||
$disk = Storage::disk($this->disk);
|
||||
|
||||
if (method_exists($disk, 'url')) {
|
||||
return $disk->url($this->path);
|
||||
}
|
||||
} catch (\Throwable) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return null;
|
||||
return route('media.press-release-attachments.show', ['attachment' => $this->getKey()]);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ use App\Enums\ImageLicenseType;
|
|||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Illuminate\Support\Facades\URL;
|
||||
|
||||
class PressReleaseImage extends Model
|
||||
{
|
||||
|
|
@ -64,42 +64,60 @@ class PressReleaseImage extends Model
|
|||
return $this->belongsTo(PressRelease::class);
|
||||
}
|
||||
|
||||
public function url(): ?string
|
||||
public function url(bool $signed = false): ?string
|
||||
{
|
||||
if (blank($this->path)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $this->resolveDiskUrl($this->path);
|
||||
return $this->resolveDiskUrl($this->path, signed: $signed);
|
||||
}
|
||||
|
||||
public function variantUrl(string $key): ?string
|
||||
public function variantUrl(string $key, bool $signed = false): ?string
|
||||
{
|
||||
$variantPath = $this->variants[$key] ?? null;
|
||||
$variantPath = $this->pathForVariant($key);
|
||||
|
||||
if (! is_string($variantPath) || blank($variantPath)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $this->resolveDiskUrl($variantPath);
|
||||
return $this->resolveDiskUrl($variantPath, $key, $signed);
|
||||
}
|
||||
|
||||
private function resolveDiskUrl(string $relativePath): ?string
|
||||
public function pathForVariant(?string $key = null): ?string
|
||||
{
|
||||
if ($key === null || $key === 'original') {
|
||||
return $this->path;
|
||||
}
|
||||
|
||||
$variants = $this->getAttribute('variants');
|
||||
|
||||
return is_array($variants) && is_string($variants[$key] ?? null)
|
||||
? $variants[$key]
|
||||
: null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Löst die URL je nach Disk auf: public → direkt, sonst über die geschützte
|
||||
* Media-Route. `signed=true` erzeugt eine temporär signierte Route, damit
|
||||
* die anonyme Token-Vorschau (unveröffentlichte PM) die Bilder laden kann,
|
||||
* ohne die Media-Route generell zu öffnen.
|
||||
*/
|
||||
private function resolveDiskUrl(string $relativePath, ?string $variant = null, bool $signed = false): string
|
||||
{
|
||||
if ($this->disk === 'public') {
|
||||
return asset('storage/'.ltrim($relativePath, '/'));
|
||||
}
|
||||
|
||||
try {
|
||||
$disk = Storage::disk($this->disk);
|
||||
$parameters = array_filter([
|
||||
'image' => $this->getKey(),
|
||||
'variant' => $variant,
|
||||
]);
|
||||
|
||||
if (method_exists($disk, 'url')) {
|
||||
return $disk->url($relativePath);
|
||||
}
|
||||
|
||||
return null;
|
||||
} catch (\Throwable) {
|
||||
return null;
|
||||
if ($signed) {
|
||||
return URL::temporarySignedRoute('media.press-release-images.show', now()->addHours(6), $parameters);
|
||||
}
|
||||
|
||||
return route('media.press-release-images.show', $parameters);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue