applications()->pluck('id'); $environmentIds = Environment::query() ->whereIn('application_id', $applicationIds) ->pluck('id'); $serverIds = $organisation->servers()->pluck('id'); $serviceIds = $organisation->services()->pluck('id'); $replicaIds = ServiceReplica::query() ->whereIn('service_id', $serviceIds) ->pluck('id'); $sliceIds = ServiceSlice::query() ->whereIn('service_id', $serviceIds) ->pluck('id'); $operations = Operation::query() ->with(['target', 'parent', 'children.target']) ->withCount('steps', 'children') ->where(function (Builder $query) use ($applicationIds, $environmentIds, $serverIds, $serviceIds, $replicaIds, $sliceIds): void { $query ->where(function (Builder $query) use ($applicationIds): void { $query->where('target_type', (new Application)->getMorphClass()) ->whereIn('target_id', $applicationIds); }) ->orWhere(function (Builder $query) use ($environmentIds): void { $query->where('target_type', (new Environment)->getMorphClass()) ->whereIn('target_id', $environmentIds); }) ->orWhere(function (Builder $query) use ($serverIds): void { $query->where('target_type', (new Server)->getMorphClass()) ->whereIn('target_id', $serverIds); }) ->orWhere(function (Builder $query) use ($serviceIds): void { $query->where('target_type', (new Service)->getMorphClass()) ->whereIn('target_id', $serviceIds); }) ->orWhere(function (Builder $query) use ($replicaIds): void { $query->where('target_type', (new ServiceReplica)->getMorphClass()) ->whereIn('target_id', $replicaIds); }) ->orWhere(function (Builder $query) use ($sliceIds): void { $query->where('target_type', (new ServiceSlice)->getMorphClass()) ->whereIn('target_id', $sliceIds); }); }) ->when($request->filled('kind'), fn (Builder $query) => $query->where('kind', $request->string('kind')->toString())) ->when($request->filled('status'), fn (Builder $query) => $query->where('status', $request->string('status')->toString())) ->latest() ->paginate(30) ->withQueryString(); return inertia('operations/Index', [ 'operations' => $operations, 'filters' => $request->only(['kind', 'status']), 'operationKinds' => OperationKind::toArray(), 'operationStatuses' => OperationStatus::toArray(), ]); } public function show(Organisation $organisation, Operation $operation): Response { abort_unless($this->operationBelongsToOrganisation($operation, $organisation), 404); $operation->load([ 'target', 'parent.target', 'children.target', 'children.steps', 'steps', ]); return inertia('operations/Show', [ 'operation' => $operation, ]); } public function retry(Organisation $organisation, Operation $operation): RedirectResponse { abort_unless($this->operationBelongsToOrganisation($operation, $organisation), 404); $operation->update([ 'status' => OperationStatus::PENDING, 'started_at' => null, 'finished_at' => null, ]); return redirect() ->route('operations.show', [ 'organisation' => $organisation->id, 'operation' => $operation->id, ]) ->with('success', 'Operation queued to run again.'); } public function cancel(Organisation $organisation, Operation $operation): RedirectResponse { abort_unless($this->operationBelongsToOrganisation($operation, $organisation), 404); $operation->update([ 'status' => OperationStatus::CANCELLED, 'finished_at' => now(), ]); return redirect() ->route('operations.show', [ 'organisation' => $organisation->id, 'operation' => $operation->id, ]) ->with('success', 'Operation cancelled.'); } public function downloadLogs(Organisation $organisation, Operation $operation): StreamedResponse { abort_unless($this->operationBelongsToOrganisation($operation, $organisation), 404); $operation->load('steps'); return response()->streamDownload(function () use ($operation): void { foreach ($operation->steps as $step) { echo "# {$step->name}\n\n"; if ($step->logs) { echo "## Logs\n{$step->logs}\n\n"; } if ($step->error_logs) { echo "## Error Logs\n{$step->error_logs}\n\n"; } } }, "operation-{$operation->hash}.log", [ 'Content-Type' => 'text/plain', ]); } private function operationBelongsToOrganisation(Operation $operation, Organisation $organisation): bool { $target = $operation->target; return match (true) { $target instanceof Application => $target->organisation_id === $organisation->id, $target instanceof Environment => $target->application()->where('organisation_id', $organisation->id)->exists(), $target instanceof Server => $target->organisation_id === $organisation->id, $target instanceof Service => $target->organisation_id === $organisation->id, $target instanceof ServiceReplica => $target->service()->where('organisation_id', $organisation->id)->exists(), $target instanceof ServiceSlice => $target->service()->where('organisation_id', $organisation->id)->exists(), default => false, }; } }