feat: FileController para servir archivos privados con autenticación
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -55,7 +55,7 @@ class ChatController extends Controller
|
|||||||
|
|
||||||
if ($request->hasFile('attachment')) {
|
if ($request->hasFile('attachment')) {
|
||||||
$file = $request->file('attachment');
|
$file = $request->file('attachment');
|
||||||
$attachment = $file->store('chat', 'uploads');
|
$attachment = $file->store('chat', 'local');
|
||||||
$mime = $file->getMimeType();
|
$mime = $file->getMimeType();
|
||||||
|
|
||||||
if (str_starts_with($mime, 'image')) $attachmentType = 'image';
|
if (str_starts_with($mime, 'image')) $attachmentType = 'image';
|
||||||
|
|||||||
@@ -0,0 +1,24 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
|
use Illuminate\Support\Facades\Storage;
|
||||||
|
|
||||||
|
class FileController extends Controller
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Sirve archivos privados con autenticación.
|
||||||
|
* El parámetro $path incluye el módulo como primer segmento:
|
||||||
|
* chat/filename.jpg
|
||||||
|
* profiles/avatar.jpg
|
||||||
|
* tasks/homework.pdf
|
||||||
|
*/
|
||||||
|
public function serve(string $path)
|
||||||
|
{
|
||||||
|
if (!Storage::disk('local')->exists($path)) {
|
||||||
|
abort(404);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Storage::disk('local')->response($path);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -49,7 +49,7 @@ return [
|
|||||||
|
|
||||||
'uploads' => [
|
'uploads' => [
|
||||||
'driver' => 'local',
|
'driver' => 'local',
|
||||||
'root' => public_path('uploads'),
|
'root' => env('UPLOADS_DISK_ROOT', public_path('uploads')),
|
||||||
'url' => env('APP_URL').'/uploads',
|
'url' => env('APP_URL').'/uploads',
|
||||||
'visibility' => 'public',
|
'visibility' => 'public',
|
||||||
'throw' => false,
|
'throw' => false,
|
||||||
|
|||||||
@@ -27,17 +27,17 @@
|
|||||||
@endif
|
@endif
|
||||||
@if($msg->attachment)
|
@if($msg->attachment)
|
||||||
@if($msg->attachment_type === 'image')
|
@if($msg->attachment_type === 'image')
|
||||||
<img src="{{ Storage::disk('uploads')->url($msg->attachment) }}" class="img-fluid rounded mt-1">
|
<img src="{{ route('files.serve', ['path' => $msg->attachment]) }}" class="img-fluid rounded mt-1">
|
||||||
@elseif($msg->attachment_type === 'audio')
|
@elseif($msg->attachment_type === 'audio')
|
||||||
<audio controls class="mt-1" style="max-width:250px">
|
<audio controls class="mt-1" style="max-width:250px">
|
||||||
<source src="{{ Storage::disk('uploads')->url($msg->attachment) }}">
|
<source src="{{ route('files.serve', ['path' => $msg->attachment]) }}">
|
||||||
</audio>
|
</audio>
|
||||||
@elseif($msg->attachment_type === 'video')
|
@elseif($msg->attachment_type === 'video')
|
||||||
<video controls class="mt-1 rounded" style="max-width:250px">
|
<video controls class="mt-1 rounded" style="max-width:250px">
|
||||||
<source src="{{ Storage::disk('uploads')->url($msg->attachment) }}">
|
<source src="{{ route('files.serve', ['path' => $msg->attachment]) }}">
|
||||||
</video>
|
</video>
|
||||||
@else
|
@else
|
||||||
<a href="{{ Storage::disk('uploads')->url($msg->attachment) }}" target="_blank" class="btn btn-sm btn-light mt-1">
|
<a href="{{ route('files.serve', ['path' => $msg->attachment]) }}" target="_blank" class="btn btn-sm btn-light mt-1">
|
||||||
<i class="fas fa-file"></i> Ver archivo
|
<i class="fas fa-file"></i> Ver archivo
|
||||||
</a>
|
</a>
|
||||||
@endif
|
@endif
|
||||||
@@ -86,7 +86,7 @@
|
|||||||
<script>
|
<script>
|
||||||
const receiverId = {{ $user->id }};
|
const receiverId = {{ $user->id }};
|
||||||
const authId = {{ auth()->id() }};
|
const authId = {{ auth()->id() }};
|
||||||
const storageBase = '{{ rtrim(Storage::disk('uploads')->url(''), '/') }}';
|
const storageBase = '{{ url('/files') }}';
|
||||||
const chatMessages = document.getElementById('chat-messages');
|
const chatMessages = document.getElementById('chat-messages');
|
||||||
|
|
||||||
// scroll al fondo
|
// scroll al fondo
|
||||||
|
|||||||
+9
-1
@@ -7,6 +7,7 @@ use App\Http\Controllers\AreaController;
|
|||||||
use App\Http\Controllers\CampanController;
|
use App\Http\Controllers\CampanController;
|
||||||
use App\Http\Controllers\CarreraController;
|
use App\Http\Controllers\CarreraController;
|
||||||
use App\Http\Controllers\ChatController;
|
use App\Http\Controllers\ChatController;
|
||||||
|
use App\Http\Controllers\FileController;
|
||||||
use App\Http\Controllers\CodeController;
|
use App\Http\Controllers\CodeController;
|
||||||
use App\Http\Controllers\ConceptoController;
|
use App\Http\Controllers\ConceptoController;
|
||||||
use App\Http\Controllers\DocenteController;
|
use App\Http\Controllers\DocenteController;
|
||||||
@@ -54,7 +55,8 @@ Route::middleware(['auth:sanctum',config('jetstream.auth_session'),'verified',
|
|||||||
Route::resource('/concepto',ConceptoController::class);
|
Route::resource('/concepto',ConceptoController::class);
|
||||||
Route::get('/chat', [ChatController::class, 'index'])->name('chat.index');
|
Route::get('/chat', [ChatController::class, 'index'])->name('chat.index');
|
||||||
Route::get('/chat/{user}', [ChatController::class, 'show'])->name('chat.show');
|
Route::get('/chat/{user}', [ChatController::class, 'show'])->name('chat.show');
|
||||||
Route::post('/chat/send', [ChatController::class, 'send'])->name('chat.send');Route::resource('/docente',DocenteController::class);
|
Route::post('/chat/send', [ChatController::class, 'send'])->name('chat.send');
|
||||||
|
Route::get('/files/{path}', [FileController::class, 'serve'])->where('path', '.*')->name('files.serve');Route::resource('/docente',DocenteController::class);
|
||||||
Route::resource('/documento',DocumentoController::class);
|
Route::resource('/documento',DocumentoController::class);
|
||||||
Route::get('/documento/ver/{id}', function ($id) { $doc = Documento::findOrFail($id); $alumno = auth()->user()->alumnos()->first(); if (!$alumno || $doc->alumno_id !== $alumno->id) { abort(403); } return response()->file( storage_path('app/public/'.$doc->archivo) ); });
|
Route::get('/documento/ver/{id}', function ($id) { $doc = Documento::findOrFail($id); $alumno = auth()->user()->alumnos()->first(); if (!$alumno || $doc->alumno_id !== $alumno->id) { abort(403); } return response()->file( storage_path('app/public/'.$doc->archivo) ); });
|
||||||
Route::get('/documento/ver/{id}', [DocumentoController::class, 'ver'])->name('documento.ver');
|
Route::get('/documento/ver/{id}', [DocumentoController::class, 'ver'])->name('documento.ver');
|
||||||
@@ -84,6 +86,12 @@ Route::middleware(['auth:sanctum',config('jetstream.auth_session'),'verified',
|
|||||||
Route::resource('/plantel',PlantelController::class);
|
Route::resource('/plantel',PlantelController::class);
|
||||||
Route::get('/registros', [CodeController::class,'registros' ])->name('registros');
|
Route::get('/registros', [CodeController::class,'registros' ])->name('registros');
|
||||||
Route::resource('/role',RoleController::class);
|
Route::resource('/role',RoleController::class);
|
||||||
|
Route::get('/storage/{path}', function ($path) { $fullPath = storage_path('app/public/' . $path);
|
||||||
|
if (!file_exists($fullPath)) {
|
||||||
|
abort(404);
|
||||||
|
}
|
||||||
|
return response()->file($fullPath);
|
||||||
|
})->where('path', '.*')->middleware('auth');
|
||||||
// Route::get('/test-chat', function () { broadcast(new MessageSent('Hola realtime ')); return 'Mensaje enviado';});
|
// Route::get('/test-chat', function () { broadcast(new MessageSent('Hola realtime ')); return 'Mensaje enviado';});
|
||||||
Route::resource('/turno',TurnoController::class);
|
Route::resource('/turno',TurnoController::class);
|
||||||
Route::resource('/user',UserController::class);
|
Route::resource('/user',UserController::class);
|
||||||
|
|||||||
Reference in New Issue
Block a user