accessibleBy($request->user()) ->withCount('targets') ->latest() ->get(); return view('investigations.index', compact('investigations')); } public function create(): View { $this->authorize('create', Investigation::class); return view('investigations.create'); } public function store(Request $request): RedirectResponse { $this->authorize('create', Investigation::class); $data = $request->validate([ 'name' => ['required', 'string', 'max:255'], 'description' => ['nullable', 'string', 'max:5000'], ]); $investigation = Investigation::create([ ...$data, 'owner_id' => $request->user()->id, 'status' => 'open', ]); return redirect()->route('investigations.show', $investigation); } public function show(Request $request, Investigation $investigation, RunnerRegistry $registry): View { $this->authorize('view', $investigation); $investigation->load(['owner', 'targets.creator', 'targets.findings.source']); $sources = Source::where('enabled', true)->orderBy('name')->get(); return view('investigations.show', [ 'investigation' => $investigation, 'canEdit' => $investigation->isEditableBy($request->user()), 'canManageAccess' => $request->user()->can('manageAccess', $investigation), 'sources' => $sources, 'runnableSourceKeys' => $sources->filter(fn (Source $s) => $registry->has($s->key))->pluck('key'), ]); } public function destroy(Investigation $investigation): RedirectResponse { $this->authorize('delete', $investigation); $investigation->delete(); return redirect()->route('investigations.index'); } public function runAll(Request $request, Investigation $investigation, FindingDispatcher $dispatcher): RedirectResponse { $this->authorize('update', $investigation); $queued = $investigation->targets->sum( fn ($target) => $dispatcher->dispatchAllApplicable($target, $request->user()) ); return redirect()->route('investigations.show', $investigation) ->with('status', $queued > 0 ? "Queued {$queued} lookup(s) across all targets..." : 'Nothing to queue — already up to date.'); } }