user()->events() ->where('client_id', $clientId) ->firstOrFail(); return response()->json([ 'data' => EventMediaResource::collection( $event->media()->orderBy('created_at')->get() ), ]); } public function store( StoreEventMediaRequest $request, EventMediaStore $store, string $clientId ): JsonResponse { $event = $request->user()->events() ->where('client_id', $clientId) ->firstOrFail(); $media = $store->store( $event, $request->file('file'), $request->validated('collection') ?? 'gallery', $request->validated('caption'), ); return (new EventMediaResource($media)) ->response() ->setStatusCode(201); } public function show(Request $request, EventMedia $media, string $variant): Response|BinaryFileResponse { abort_unless(in_array($variant, ['thumb', 'preview', 'original'], true), 404); abort_unless((int) $media->user_id === (int) $request->user()->id, 404); $path = match ($variant) { 'thumb' => $media->thumbnail_path, 'preview' => $media->preview_path ?: $media->thumbnail_path, default => $media->path, }; abort_unless(is_string($path) && $path !== '', 404); abort_unless(Storage::disk($media->disk)->exists($path), 404); $headers = ['Cache-Control' => 'private, max-age=604800']; if ($variant !== 'original') { // Thumbnails and previews are always JPEG, whatever the original. return response(Storage::disk($media->disk)->get($path), 200, [ ...$headers, 'Content-Type' => 'image/jpeg', ]); } // Originals may be videos or audio: a file response streams them and // honours Range requests, which media players need for seeking. return response()->file(Storage::disk($media->disk)->path($path), [ ...$headers, 'Content-Type' => $media->mime_type ?: 'application/octet-stream', ]); } /** * PATCH /api/events/{clientId}/media/{media} — the caption only; the * file itself is immutable. */ public function update(UpdateEventMediaRequest $request, string $clientId, EventMedia $media): EventMediaResource { $event = $request->user()->events() ->where('client_id', $clientId) ->firstOrFail(); abort_unless((int) $media->event_id === (int) $event->id, 404); $media->update(['caption' => $request->validated('caption') ?? '']); return new EventMediaResource($media->fresh()); } public function destroy(Request $request, string $clientId, EventMedia $media): JsonResponse { $event = $request->user()->events() ->where('client_id', $clientId) ->firstOrFail(); abort_unless((int) $media->event_id === (int) $event->id, 404); $wasKeyImage = $media->collection === 'key_image'; $this->mediaCleaner->purgeMedia($media); $media->delete(); if ($wasKeyImage) { $event->update(['image' => null]); } return response()->json(null, 204); } }