CluPilotCloud/app/Provisioning/Jobs/SyncSeatToNextcloud.php

102 lines
3.6 KiB
PHP

<?php
namespace App\Provisioning\Jobs;
use App\Models\Seat;
use App\Services\Nextcloud\NextcloudUsers;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Throwable;
/**
* Spiegelt einen Sitz in die Nextcloud des Kunden.
*
* Laeuft auf der `provisioning`-Warteschlange, weil nur dieser Arbeiter den
* Tunnel und die Proxmox-Zugangsdaten hat — das Portal hat weder das eine noch
* das andere und darf es auch gar nicht versuchen.
*
* Daraus folgt: der Knopf wirkt nicht sofort. Deshalb schreibt dieser Auftrag
* zurueck, was WIRKLICH passiert ist (`nc_state`, `nc_error`) — sonst drueckt
* der Inhaber wieder und wieder, weil nichts sichtbar geschieht. Genau die
* Beschwerde, die dieses Projekt schon zweimal hatte.
*
* `tries = 1`: ein blinder Wiederholungslauf gegen einen nicht erreichbaren
* Gast bringt nichts, und der Inhaber hat einen Knopf dafuer.
*/
class SyncSeatToNextcloud implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
public int $tries = 1;
public function __construct(
public string $seatUuid,
/** invite | role | disable | enable */
public string $action,
) {
$this->onConnection('provisioning');
$this->onQueue('provisioning');
}
/** Auch was an handle() vorbeifliegt, muss am Sitz sichtbar werden. */
public function failed(?Throwable $e): void
{
Seat::query()->where('uuid', $this->seatUuid)->update([
'nc_state' => Seat::STATE_FAILED,
'nc_error' => 'unexpected',
]);
}
public function handle(NextcloudUsers $users): void
{
$seat = Seat::query()->with('customer')->where('uuid', $this->seatUuid)->first();
if ($seat === null) {
return;
}
// Erneut geprueft, nicht der Seite geglaubt: eine geschlossene Instanz
// kann ihre VMID auf demselben Host weiterverliehen haben — dieselbe
// Falle, die in IssueInstanceAdminAccess schon beschrieben steht.
$instance = $seat->customer?->instances()
->whereIn('status', ['active', 'cancellation_scheduled'])
->latest('id')->first();
if ($instance === null || $instance->host === null || blank($instance->vmid)) {
$this->record($seat, false, 'no_instance');
return;
}
$ok = match ($this->action) {
// Einladen setzt in einem Zug auch die Gruppe: ein Benutzer, der
// angelegt ist und in keiner Rolle steckt, sieht in seiner neuen
// Cloud nichts und meldet sich am ersten Tag beim Inhaber.
'invite' => $users->invite($instance, $seat) && $users->applyRole($instance, $seat),
'role' => $users->applyRole($instance, $seat),
'disable' => $users->disable($instance, $seat),
'enable' => $users->enable($instance, $seat),
default => false,
};
$this->record($seat, $ok, $ok ? null : 'guest_failed');
}
/**
* `nc_synced_at` bleibt beim Fehlschlag stehen, statt geleert zu werden:
* wann dieser Sitz zuletzt WIRKLICH stimmte, ist genau die Angabe, die
* einen Fehlschlag einordnet — heute Morgen oder vor drei Wochen.
*/
private function record(Seat $seat, bool $ok, ?string $grund): void
{
$seat->forceFill([
'nc_state' => $ok ? Seat::STATE_SYNCED : Seat::STATE_FAILED,
'nc_error' => $grund,
'nc_synced_at' => $ok ? now() : $seat->nc_synced_at,
])->save();
}
}