172 lines
6.8 KiB
PHP
172 lines
6.8 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Controllers;
|
|
|
|
use App\Enums\OperationKind;
|
|
use App\Enums\OperationStatus;
|
|
use App\Models\Application;
|
|
use App\Models\Environment;
|
|
use App\Models\Operation;
|
|
use App\Models\Organisation;
|
|
use App\Models\Server;
|
|
use App\Models\Service;
|
|
use App\Models\ServiceReplica;
|
|
use App\Models\ServiceSlice;
|
|
use Illuminate\Database\Eloquent\Builder;
|
|
use Illuminate\Http\RedirectResponse;
|
|
use Illuminate\Http\Request;
|
|
use Inertia\Response;
|
|
use Symfony\Component\HttpFoundation\StreamedResponse;
|
|
|
|
class OperationController extends Controller
|
|
{
|
|
public function index(Request $request, Organisation $organisation): Response
|
|
{
|
|
$applicationIds = $organisation->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,
|
|
};
|
|
}
|
|
}
|