Records every successful document download and surfaces the counts in the admin area, so the docket owner can see which filings are being pulled and how often. - document_accesses table + DocumentAccess model - DocumentController@download records each successful download with IP and user agent - documents:import-access-log backfills history from an archived nginx log - Dashboard gains a 60-day downloads card and a most-requested list - New Document Access page with a date-range filter and per-day trend Client IPs were only captured from 2026-07-30, when the reverse proxy chain started forwarding X-Forwarded-For correctly. Rows imported from the old nginx log therefore have no IP, and the UI labels those periods as download counts rather than unique visitors instead of showing a misleading number.
47 lines
1.5 KiB
PHP
47 lines
1.5 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Controllers;
|
|
|
|
use App\Models\Document;
|
|
use App\Models\DocumentAccess;
|
|
use Illuminate\Http\Request;
|
|
use Illuminate\Support\Facades\Storage;
|
|
use Symfony\Component\HttpFoundation\StreamedResponse;
|
|
|
|
class DocumentController extends Controller
|
|
{
|
|
/**
|
|
* Download a document by ID.
|
|
*/
|
|
public function download(Request $request, int $id): StreamedResponse
|
|
{
|
|
$document = Document::findOrFail($id);
|
|
|
|
// Files are stored in public disk (storage/app/public/documents/)
|
|
// Check if file exists
|
|
if (!Storage::disk('public')->exists($document->file_path)) {
|
|
abort(404, 'Document file not found.');
|
|
}
|
|
|
|
// Record the access before streaming, so the dashboard reflects it.
|
|
// Only successful downloads reach this point, which matches how the
|
|
// historical nginx rows were filtered (status 200 only).
|
|
DocumentAccess::create([
|
|
'document_id' => $document->id,
|
|
'accessed_at' => now(),
|
|
'ip_address' => $request->ip(),
|
|
'user_agent' => $request->userAgent(),
|
|
'source' => 'live',
|
|
]);
|
|
|
|
// Stream the file to the browser
|
|
return Storage::disk('public')->download(
|
|
$document->file_path,
|
|
$document->original_filename,
|
|
[
|
|
'Content-Type' => $document->mime_type,
|
|
'Content-Disposition' => 'inline; filename="' . $document->original_filename . '"',
|
|
]
|
|
);
|
|
}
|
|
}
|