Wanderung friert das Nachziehen selbst ein, statt die Modellmethode zu rufen
up() rief bisher Seat::linkToInstanceAdmin() auf. Ein migrate:fresh auf frischer Datenbank haette damit die dann aktuelle Fassung dieser Methode mitgefahren, nicht die von heute. Die Nachbefuellung ist jetzt eine eigene, dokumentiert duplizierte Fassung ueber den Query Builder. nc_admin_ref ist am Modell verschluesselt gecastet - roher Spaltenzugriff haette Chiffretext statt Klartext geliefert. Crypt::decryptString() macht denselben Aufruf wie der Modell-Cast, ohne eine Modellklasse zu laden. Kleinere Nachbesserung: fill() statt forceFill() in linkToInstanceAdmin() (alle drei Felder stehen in $fillable), und die Gruppen-Zuordnungspruefung verifiziert jetzt die konkreten Namen 'mitarbeiter'/'nur-lesen', nicht nur "irgendein Wert".feat/versandtakt
parent
43d4c02353
commit
80974108b1
|
|
@ -73,7 +73,7 @@ class Seat extends Model
|
|||
return;
|
||||
}
|
||||
|
||||
$this->forceFill([
|
||||
$this->fill([
|
||||
'nc_username' => $ref,
|
||||
'nc_state' => self::STATE_SYNCED,
|
||||
'nc_synced_at' => now(),
|
||||
|
|
|
|||
|
|
@ -1,8 +1,10 @@
|
|||
<?php
|
||||
|
||||
use App\Models\Seat;
|
||||
use Illuminate\Contracts\Encryption\DecryptException;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Crypt;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
/**
|
||||
|
|
@ -15,6 +17,26 @@ use Illuminate\Support\Facades\Schema;
|
|||
* verschweigt dem Inhaber, dass sein Klick angekommen ist.
|
||||
*
|
||||
* Nur so laesst sich "fehlgeschlagen" ueberhaupt sagen.
|
||||
*
|
||||
* ## Warum das Nachziehen unten den Query Builder benutzt, nicht Seat::linkToInstanceAdmin()
|
||||
*
|
||||
* Eine Wanderung soll einfrieren, was zum Zeitpunkt ihres Schreibens galt.
|
||||
* Riefe up() stattdessen die Modellmethode auf, wuerde jeder kuenftige Lauf
|
||||
* gegen eine frische Datenbank — CI, ein neuer Entwickler, `migrate:fresh` —
|
||||
* die DANN aktuelle Fassung dieser Methode ausfuehren, nicht die von heute.
|
||||
* Heute ist sie harmlos; bekommt sie spaeter eine Nebenwirkung (einen
|
||||
* API-Aufruf, einen Warteschlangen-Auftrag), faehrt jede Wanderung, die je
|
||||
* ueber diese Zeile lief, sie rueckwirkend mit — ohne dass es jemand so
|
||||
* wollte. Der Block unten ist deshalb eine eigene, bewusst duplizierte
|
||||
* Fassung derselben Auswahl (Instanz mit status in active/
|
||||
* cancellation_scheduled, juengste zuerst, nur bei gesetztem nc_admin_ref).
|
||||
* BITTE NICHT „aufraeumen" und durch den Modellaufruf ersetzen — das ist
|
||||
* genau die Kopplung, die hier vermieden wird.
|
||||
*
|
||||
* `nc_admin_ref` ist am Instance-Modell 'encrypted' gecastet; roher
|
||||
* DB::table()-Zugriff liefert den Chiffretext. Crypt::decryptString() ist
|
||||
* exakt dieselbe Entschluesselung, die der Cast im Hintergrund aufruft —
|
||||
* ohne dafuer eine Modellklasse zu laden.
|
||||
*/
|
||||
return new class extends Migration
|
||||
{
|
||||
|
|
@ -32,14 +54,36 @@ return new class extends Migration
|
|||
// Bestandssitze sind ehrlich beschrieben: angelegt, nie eingeladen —
|
||||
// denn eingeladen hat sie nie jemand, resend() war eine Attrappe.
|
||||
// EINE Ausnahme: der Inhaber-Sitz IST das Admin-Konto der Instanz.
|
||||
//
|
||||
// with('customer') vor chunkById: linkToInstanceAdmin() liest
|
||||
// $this->customer, und ohne Eager Load waere das eine Anfrage pro
|
||||
// Sitz statt einer pro Bloecken von 100 — bei owner-Sitzen (einer
|
||||
// je Kunde) ist der Bestand hier so gross wie der Kundenstamm.
|
||||
Seat::query()->where('role', 'owner')->with('customer')->chunkById(100, function ($sitze) {
|
||||
DB::table('seats')->where('role', 'owner')->chunkById(100, function ($sitze) {
|
||||
foreach ($sitze as $sitz) {
|
||||
$sitz->linkToInstanceAdmin();
|
||||
$roh = DB::table('instances')
|
||||
->where('customer_id', $sitz->customer_id)
|
||||
->whereIn('status', ['active', 'cancellation_scheduled'])
|
||||
->orderByDesc('id')
|
||||
->value('nc_admin_ref');
|
||||
|
||||
if ($roh === null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
$ref = Crypt::decryptString($roh);
|
||||
} catch (DecryptException) {
|
||||
// Nicht entschluesselbar (z. B. ein Schluesselwechsel ohne
|
||||
// Migration der Altwerte) wird wie „kein Konto" behandelt —
|
||||
// ein Wanderungslauf darf daran nicht scheitern.
|
||||
continue;
|
||||
}
|
||||
|
||||
if (blank($ref)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
DB::table('seats')->where('id', $sitz->id)->update([
|
||||
'nc_username' => $ref,
|
||||
'nc_state' => 'synced',
|
||||
'nc_synced_at' => now(),
|
||||
]);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,6 +15,12 @@ it('bildet jede Rolle auf genau eine Nextcloud-Gruppe ab', function () {
|
|||
expect(Seat::GROUPS)->toHaveKey($rolle)
|
||||
->and(Seat::GROUPS[$rolle])->not->toBe('');
|
||||
}
|
||||
|
||||
// Nicht nur "irgendein Wert" — die Namen selbst, denn sie wandern spaeter
|
||||
// woertlich in `occ group:adduser`. Ein Vertipper darin faellt sonst
|
||||
// erst auf einer echten Instanz auf, nicht hier.
|
||||
expect(Seat::GROUPS['member'])->toBe('mitarbeiter')
|
||||
->and(Seat::GROUPS['readonly'])->toBe('nur-lesen');
|
||||
});
|
||||
|
||||
it('fuehrt owner und admin in die Admin-Gruppe', function () {
|
||||
|
|
|
|||
Loading…
Reference in New Issue