feat: platform admin backend — controllers, services, routes, tests

Add cross-organisation admin API endpoints behind role:super_admin middleware:
- AdminOrganisationController: CRUD with search, filter, billing_status management
- AdminUserController: user management with role assignment across orgs
- AdminStatsController: platform-wide aggregate statistics
- AdminActivityLogController: filterable activity log viewer
- AdminImpersonationController + ImpersonationService: user impersonation with
  token-based session management and activity logging
- BillingStatus enum, form requests, API resources, 23 feature tests

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-14 23:33:16 +02:00
parent ec31646a93
commit ddf26dad33
18 changed files with 1299 additions and 0 deletions

View File

@@ -0,0 +1,63 @@
<?php
declare(strict_types=1);
namespace App\Http\Controllers\Api\V1\Admin;
use App\Http\Controllers\Controller;
use Illuminate\Http\JsonResponse;
use Spatie\Activitylog\Models\Activity;
final class AdminActivityLogController extends Controller
{
public function index(): JsonResponse
{
$query = Activity::query()->with('causer')->latest();
if ($causerId = request('causer_id')) {
$query->where('causer_id', $causerId);
}
if ($subjectType = request('subject_type')) {
$query->where('subject_type', $subjectType);
}
if ($logName = request('log_name')) {
$query->where('log_name', $logName);
}
if ($from = request('from')) {
$query->where('created_at', '>=', $from);
}
if ($to = request('to')) {
$query->where('created_at', '<=', $to);
}
$activities = $query->paginate(25);
return response()->json([
'data' => $activities->map(fn (Activity $activity) => [
'id' => $activity->id,
'log_name' => $activity->log_name,
'description' => $activity->description,
'event' => $activity->event,
'causer' => $activity->causer ? [
'id' => $activity->causer->id,
'name' => $activity->causer->full_name ?? $activity->causer->name ?? null,
'email' => $activity->causer->email ?? null,
] : null,
'subject_type' => $activity->subject_type,
'subject_id' => $activity->subject_id,
'properties' => $activity->properties,
'created_at' => $activity->created_at->toIso8601String(),
]),
'meta' => [
'current_page' => $activities->currentPage(),
'last_page' => $activities->lastPage(),
'per_page' => $activities->perPage(),
'total' => $activities->total(),
],
]);
}
}