fix: manual streaming response for attachments

This commit is contained in:
dyzulk
2026-01-04 23:08:55 +07:00
parent d6b7a51c7f
commit 44b3f75b48

View File

@@ -13,46 +13,63 @@ class AttachmentController extends Controller
* Download a private attachment. * Download a private attachment.
*/ */
public function download(Request $request, TicketAttachment $attachment) public function download(Request $request, TicketAttachment $attachment)
{
public function download(Request $request, TicketAttachment $attachment)
{ {
try { try {
// Paranoid Auth Check
$user = $request->user(); $user = $request->user();
if (!$user) {
return response()->json(['error' => 'Unauthenticated'], 401);
}
if (!$attachment->reply) {
return response()->json(['error' => 'Orphaned Attachment (No Reply)'], 404);
}
if (!$attachment->reply->ticket) {
return response()->json(['error' => 'Orphaned Attachment (No Ticket)'], 404);
}
// 1. Authorization Logic
$attachment->load(['reply.ticket']);
$ticket = $attachment->reply->ticket; $ticket = $attachment->reply->ticket;
if ($ticket->user_id !== $user->id && !$user->isAdminOrOwner()) { if ($ticket->user_id !== $user->id && !$user->isAdminOrOwner()) {
abort(403, 'Unauthorized access to this attachment.'); return response()->json(['error' => 'Unauthorized'], 403);
} }
// 2. Fetch File
$path = $attachment->file_path; $path = $attachment->file_path;
// Legacy URL handling
if (filter_var($path, FILTER_VALIDATE_URL)) { if (filter_var($path, FILTER_VALIDATE_URL)) {
return redirect($path); return redirect($path);
} }
$disk = 'r2-private'; $disk = 'r2-private';
// Use manual file retrieval to avoid header issues with Storage::download
if (!Storage::disk($disk)->exists($path)) { if (!Storage::disk($disk)->exists($path)) {
\Log::error("Attachment 404: Path [$path] not found on disk [$disk]"); return response()->json(['error' => 'File not found on storage'], 404);
abort(404, 'File not found on secure storage.');
} }
return Storage::disk($disk)->download($path, $attachment->file_name); $mimeType = Storage::disk($disk)->mimeType($path) ?? 'application/octet-stream';
$size = Storage::disk($disk)->size($path);
$fileName = $attachment->file_name ?? basename($path);
} catch (\Exception $e) { return response()->stream(function() use ($disk, $path) {
\Log::error("Attachment Download Error: " . $e->getMessage(), [ $stream = Storage::disk($disk)->readStream($path);
'attachment_id' => $attachment->id, fpassthru($stream);
'path' => $attachment->file_path ?? 'unknown', if (is_resource($stream)) {
'trace' => $e->getTraceAsString() fclose($stream);
}
}, 200, [
'Content-Type' => $mimeType,
'Content-Length' => $size,
'Content-Disposition' => 'attachment; filename="' . $fileName . '"',
]); ]);
return response()->json([ } catch (\Exception $e) {
'error' => 'Server Error', \Log::error("Stream Download Error: " . $e->getMessage(), ['trace' => $e->getTraceAsString()]);
'message' => $e->getMessage(), return response()->json(['error' => 'Server Error', 'message' => $e->getMessage()], 500);
'file_path' => $attachment->file_path ?? 'unknown'
], 500);
} }
} }
}
} }