Files
SkinbaseNova/app/Http/Controllers/Admin/AdminController.php

241 lines
9.3 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Http\Controllers\Admin;
use App\Enums\UserRole;
use App\Http\Controllers\Controller;
use App\Models\AuthAuditLog;
use App\Models\Artwork;
use App\Models\Story;
use App\Models\User;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Inertia\Inertia;
use Inertia\Response;
final class AdminController extends Controller
{
// ── Dashboard ────────────────────────────────────────────────────────────
public function dashboard(): Response
{
$stats = [
'total_users' => User::count(),
'new_users_today' => User::whereDate('created_at', today())->count(),
'staff_count' => User::whereIn('role', ['admin', 'manager', 'editorial'])->count(),
'moderator_count' => User::where('role', 'moderator')->count(),
];
return Inertia::render('Admin/Dashboard', [
'stats' => $stats,
]);
}
// ── Users ─────────────────────────────────────────────────────────────────
public function users(Request $request): Response
{
$search = $request->string('search')->trim()->toString();
$roleFilter = $request->string('role')->trim()->toString();
$query = User::select('id', 'name', 'username', 'email', 'role', 'created_at', 'is_active')
->orderByDesc('created_at');
if ($search !== '') {
$query->where(function ($q) use ($search): void {
$q->where('name', 'like', "%{$search}%")
->orWhere('username', 'like', "%{$search}%")
->orWhere('email', 'like', "%{$search}%");
});
}
if ($roleFilter !== '' && $roleFilter !== 'all') {
$query->where('role', $roleFilter);
}
$users = $query->paginate(50)->withQueryString();
return Inertia::render('Admin/Users/Index', [
'users' => $users,
'filters' => ['search' => $search, 'role' => $roleFilter],
'roles' => collect(UserRole::cases())->map(fn ($r) => [
'value' => $r->value,
'label' => $r->label(),
'badge' => $r->badgeClass(),
]),
]);
}
// ── Promote / Demote ──────────────────────────────────────────────────────
public function updateRole(Request $request, User $user): RedirectResponse
{
$request->validate([
'role' => ['required', 'string', 'in:' . implode(',', array_column(UserRole::cases(), 'value'))],
]);
/** @var \App\Models\User $actor */
$actor = $request->user();
// Only admins can set the 'admin' role.
if ($request->input('role') === UserRole::Admin->value && ! $actor->isAdmin()) {
abort(403, 'Only admins can grant the Admin role.');
}
// Prevent self-demotion.
if ($actor->id === $user->id) {
return back()->with('error', 'You cannot change your own role.');
}
$user->update(['role' => $request->input('role')]);
return back()->with('success', "Role updated to \"{$request->input('role')}\" for {$user->name}.");
}
// ── Stories ───────────────────────────────────────────────────────────────
public function stories(Request $request): Response
{
$stories = Story::with('creator:id,name,username')
->select('id', 'title', 'status', 'published_at', 'creator_id')
->orderByDesc('created_at')
->paginate(50)
->withQueryString();
return Inertia::render('Admin/Stories', [
'stories' => $stories,
]);
}
// ── Artworks ──────────────────────────────────────────────────────────────
public function artworks(Request $request): Response
{
$artworks = Artwork::with('user:id,name,username')
->select('id', 'title', 'artwork_status', 'created_at', 'user_id', 'hash', 'thumb_ext')
->orderByDesc('created_at')
->paginate(50)
->withQueryString();
// Normalise status field and add thumb URL
$artworks->getCollection()->transform(function ($artwork) {
return [
'id' => $artwork->id,
'title' => $artwork->title,
'status' => $artwork->artwork_status,
'thumb' => $artwork->thumbUrl('sm') ?? null,
'created_at' => $artwork->created_at,
'user' => $artwork->user,
];
});
return Inertia::render('Admin/Artworks', [
'artworks' => $artworks,
]);
}
// ── Username Queue ────────────────────────────────────────────────────────
public function usernameQueue(): Response
{
return Inertia::render('Admin/UsernameQueue');
}
// ── Upload Queue ──────────────────────────────────────────────────────────
public function uploadQueue(): Response
{
return Inertia::render('Admin/UploadQueue');
}
// ── Settings ──────────────────────────────────────────────────────────────
public function settings(): Response
{
return Inertia::render('Admin/Settings', [
'settings' => [],
]);
}
public function authAudit(Request $request): Response
{
abort_unless($request->user()?->isAdmin(), 403, 'Only admins can access this area.');
$search = $request->string('search')->trim()->toString();
$eventType = $request->string('event')->trim()->toString();
$status = $request->string('status')->trim()->toString();
$query = AuthAuditLog::query()
->with('user:id,name,username,email,role')
->latest('created_at')
->latest('id');
if ($search !== '') {
$query->where(function ($builder) use ($search): void {
$builder
->where('identifier', 'like', "%{$search}%")
->orWhere('ip', 'like', "%{$search}%")
->orWhere('reason', 'like', "%{$search}%")
->orWhereHas('user', function ($userQuery) use ($search): void {
$userQuery
->where('name', 'like', "%{$search}%")
->orWhere('username', 'like', "%{$search}%")
->orWhere('email', 'like', "%{$search}%");
});
});
}
if ($eventType !== '' && $eventType !== 'all') {
$query->where('event_type', $eventType);
}
if ($status !== '' && $status !== 'all') {
$query->where('status', $status);
}
$logs = $query->paginate(50)->withQueryString()->through(function (AuthAuditLog $log): array {
return [
'id' => $log->id,
'event_type' => $log->event_type,
'identifier' => $log->identifier,
'status' => $log->status,
'reason' => $log->reason,
'ip' => $log->ip,
'user_agent' => $log->user_agent,
'metadata' => $log->metadata ?? [],
'created_at' => $log->created_at,
'user' => $log->user ? [
'id' => $log->user->id,
'name' => $log->user->name,
'username' => $log->user->username,
'email' => $log->user->email,
'role' => $log->user->role,
] : null,
];
});
return Inertia::render('Admin/AuthAudit', [
'logs' => $logs,
'filters' => [
'search' => $search,
'event' => $eventType,
'status' => $status,
],
'eventOptions' => [
['value' => 'all', 'label' => 'All events'],
['value' => 'login', 'label' => 'Login'],
['value' => 'register', 'label' => 'Register'],
['value' => 'forgot_password', 'label' => 'Forgot password'],
['value' => 'reset_password', 'label' => 'Reset password'],
],
'statusOptions' => [
['value' => 'all', 'label' => 'All statuses'],
['value' => 'success', 'label' => 'Success'],
['value' => 'failed', 'label' => 'Failed'],
],
]);
}
}