@@ -0,0 +1,101 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Models\User;
|
||||
use App\Services\OrganizationAccess;
|
||||
use Illuminate\Database\Query\Builder;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Validation\Rule;
|
||||
use Inertia\Inertia;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
class AuditMetadataController extends Controller
|
||||
{
|
||||
private function actor(Request $request): User
|
||||
{
|
||||
$user = $request->user('web');
|
||||
abort_unless($user instanceof User && $user->tenant_id !== null, 403);
|
||||
$user = User::query()->where('tenant_id', $user->tenant_id)->findOrFail($user->id);
|
||||
$access = app(OrganizationAccess::class);
|
||||
abort_unless(! $access->maintenanceOnly($user) && in_array('audit_metadata_read', $access->permissions($user), true), 403);
|
||||
|
||||
return $user;
|
||||
}
|
||||
|
||||
/** @return array<string,mixed> */
|
||||
private function filters(Request $request): array
|
||||
{
|
||||
return $request->validate(['action' => 'nullable|string|max:100', 'actor_id' => 'nullable|integer|min:1',
|
||||
'from' => 'nullable|date_format:Y-m-d', 'to' => ['nullable', 'date_format:Y-m-d', Rule::when($request->filled('from'), 'after_or_equal:from')], 'page' => 'nullable|integer|min:1|max:1000000']);
|
||||
}
|
||||
|
||||
/** @param array<string,mixed> $filters */
|
||||
private function query(User $actor, array $filters): Builder
|
||||
{
|
||||
return DB::table('audit_logs')->where('tenant_id', $actor->tenant_id)
|
||||
->when(! empty($filters['action']), fn ($q) => $q->where('action', $filters['action']))
|
||||
->when(! empty($filters['actor_id']), fn ($q) => $q->where('user_id', $filters['actor_id']))
|
||||
->when(! empty($filters['from']), fn ($q) => $q->whereDate('created_at', '>=', $filters['from']))
|
||||
->when(! empty($filters['to']), fn ($q) => $q->whereDate('created_at', '<=', $filters['to']))
|
||||
->select(['id', 'created_at', 'action', 'user_id']);
|
||||
}
|
||||
|
||||
public function index(Request $request): Response
|
||||
{
|
||||
$this->actor($request);
|
||||
$filters = $this->filters($request);
|
||||
|
||||
return DB::transaction(function () use ($request, $filters) {
|
||||
$this->readOnly();
|
||||
$actor = $this->actor($request);
|
||||
Inertia::encryptHistory();
|
||||
|
||||
$response = Inertia::render('audit/metadata', ['filters' => $filters,
|
||||
'events' => $this->query($actor, $filters)->orderBy('id')->paginate(25)->withQueryString()])->toResponse($request);
|
||||
$response->headers->set('Cache-Control', 'private, no-store, max-age=0');
|
||||
|
||||
return $response;
|
||||
});
|
||||
}
|
||||
|
||||
public function csv(Request $request): Response
|
||||
{
|
||||
$this->actor($request);
|
||||
$filters = $this->filters($request);
|
||||
|
||||
return response()->streamDownload(function () use ($request, $filters) {
|
||||
DB::transaction(function () use ($request, $filters) {
|
||||
$this->readOnly();
|
||||
$actor = $this->actor($request);
|
||||
$out = fopen('php://output', 'w');
|
||||
if ($out === false) {
|
||||
throw new \RuntimeException('Não foi possível preparar a exportação.');
|
||||
}
|
||||
$text = static function (mixed $value): string {
|
||||
$value = (string) ($value ?? '');
|
||||
|
||||
return preg_match('/^[\s\x00-\x1F]*[=+\-@]/u', $value) ? "'".$value : $value;
|
||||
};
|
||||
try {
|
||||
fwrite($out, "\xEF\xBB\xBF");
|
||||
fputcsv($out, ['Metadados de eventos da empresa', 'Consulta', now()->toIso8601String()], ';', '"', '');
|
||||
fputcsv($out, ['Registo', 'Data', 'Acção', 'Utilizador (id)'], ';', '"', '');
|
||||
foreach ($this->query($actor, $filters)->lazyById(100) as $event) {
|
||||
fputcsv($out, array_map($text, [$event->id, $event->created_at, $event->action, $event->user_id]), ';', '"', '');
|
||||
}
|
||||
} finally {
|
||||
fclose($out);
|
||||
}
|
||||
});
|
||||
}, 'eventos-metadados-'.now()->format('Ymd-His').'.csv', ['Content-Type' => 'text/csv; charset=UTF-8', 'Cache-Control' => 'private, no-store, max-age=0']);
|
||||
}
|
||||
|
||||
private function readOnly(): void
|
||||
{
|
||||
if (DB::getDriverName() === 'pgsql' && DB::transactionLevel() === 1) {
|
||||
DB::statement('SET TRANSACTION ISOLATION LEVEL REPEATABLE READ READ ONLY');
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user