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.
78 lines
2.5 KiB
PHP
78 lines
2.5 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Controllers\Admin;
|
|
|
|
use App\Http\Controllers\Controller;
|
|
use App\Models\DocketEntry;
|
|
use App\Models\Document;
|
|
use App\Models\DocumentAccess;
|
|
use App\Models\Subscription;
|
|
use Illuminate\Support\Carbon;
|
|
use Illuminate\Support\Facades\DB;
|
|
use Inertia\Inertia;
|
|
use Inertia\Response;
|
|
|
|
class DashboardController extends Controller
|
|
{
|
|
/**
|
|
* Display the admin dashboard with statistics and recent entries.
|
|
*/
|
|
public function index(): Response
|
|
{
|
|
$stats = [
|
|
'total_entries' => DocketEntry::count(),
|
|
'total_documents' => Document::count(),
|
|
'total_subscribers' => Subscription::where('is_active', true)->count(),
|
|
'recent_entries' => DocketEntry::with('documents')
|
|
->latest('date')
|
|
->take(5)
|
|
->get()
|
|
->map(function ($entry) {
|
|
return [
|
|
'id' => $entry->id,
|
|
'date' => $entry->date->format('m/d/Y'),
|
|
'title' => $entry->title,
|
|
'summary' => $entry->summary,
|
|
'documents_count' => $entry->documents->count(),
|
|
];
|
|
}),
|
|
];
|
|
|
|
$stats += $this->documentAccessSummary();
|
|
|
|
return Inertia::render('Admin/Dashboard', $stats);
|
|
}
|
|
|
|
/**
|
|
* Headline document-access numbers for the last 60 days, plus the most
|
|
* requested documents. Crawlers are excluded so the figures reflect people.
|
|
*/
|
|
private function documentAccessSummary(): array
|
|
{
|
|
$since = Carbon::now()->subDays(60)->startOfDay();
|
|
|
|
return [
|
|
'downloads_60d' => DocumentAccess::excludingBots()
|
|
->where('accessed_at', '>=', $since)
|
|
->count(),
|
|
|
|
'top_documents_60d' => DocumentAccess::query()
|
|
->excludingBots()
|
|
->join('documents', 'documents.id', '=', 'document_accesses.document_id')
|
|
->where('document_accesses.accessed_at', '>=', $since)
|
|
->groupBy('documents.id', 'documents.title')
|
|
->orderByDesc('downloads')
|
|
->take(5)
|
|
->get([
|
|
'documents.id',
|
|
'documents.title',
|
|
DB::raw('count(*) as downloads'),
|
|
])
|
|
->map(fn ($row) => [
|
|
'id' => $row->id,
|
|
'title' => $row->title,
|
|
'downloads' => (int) $row->downloads,
|
|
]),
|
|
];
|
|
}
|
|
}
|