Add the central site registry and remote updates
Every installation now reports address and version to sites.kant-it.de every 15 minutes and picks up update orders there (ADR 123). Reports and answers are HMAC-signed with a per-installation key sent on first contact; an order can only install a published release with checksum. The next request after the file swap runs the migrations, so a remote update finishes without anyone in the admin. The platform itself lives in zentrale/ (plain PHP + SQLite, not part of the release archive): overview with outdated, silent and unassigned installations, single and bulk updates, per-site detail and history. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
32cb77b72e
commit
723680b7de
39 files changed
+2355
-27
No files matched your search
@@ -0,0 +1,73 @@
|
||||
<?php
|
||||
|
||||
namespace App\EventSubscriber;
|
||||
|
||||
use App\Ops\ArchiveUpdate;
|
||||
use App\Ops\Funktionspruefung;
|
||||
use App\Ops\Installation;
|
||||
use App\Ops\Updater;
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
use Symfony\Component\HttpKernel\Event\RequestEvent;
|
||||
use Symfony\Component\HttpKernel\KernelEvents;
|
||||
|
||||
/**
|
||||
* Schließt ein Update ab, das die Zentrale angestoßen hat (ADR 123).
|
||||
*
|
||||
* Aus der Verwaltung ruft die Systemseite den zweiten Schritt selbst auf. Bei einem Auftrag der
|
||||
* Zentrale sitzt niemand davor. Dann übernimmt die nächste Anfrage: Sie startet mit dem neuen
|
||||
* Code - genau das, was die Migrationen brauchen -, führt sie aus, prüft die Startseite und gibt
|
||||
* die Website frei oder schaltet zurück. Der Besucher, der sie auslöst, wartet so lange und
|
||||
* bekommt danach die Seite in der neuen Fassung.
|
||||
*
|
||||
* Vor der Wartungsseite (Priorität 40), weil die sonst jede Anfrage mit 503 beendet. Nur bei
|
||||
* einer Marke mit `fern`: Ein Update aus der Verwaltung schließt die Verwaltung ab.
|
||||
*
|
||||
* Der Prozess, der die Dateien getauscht hat, kommt hier nicht mehr vorbei: Seine Anfrage ist zu
|
||||
* dem Zeitpunkt längst über diesen Punkt hinaus.
|
||||
*/
|
||||
final class RemoteUpdateFinishSubscriber implements EventSubscriberInterface
|
||||
{
|
||||
public function __construct(
|
||||
private ArchiveUpdate $archive,
|
||||
private Installation $installation,
|
||||
private Updater $updater,
|
||||
private Funktionspruefung $pruefung,
|
||||
private string $projectDir,
|
||||
) {
|
||||
}
|
||||
|
||||
public static function getSubscribedEvents(): array
|
||||
{
|
||||
return [KernelEvents::REQUEST => ['onRequest', 48]];
|
||||
}
|
||||
|
||||
public function onRequest(RequestEvent $event): void
|
||||
{
|
||||
// Der übliche Fall kostet eine Dateiprüfung.
|
||||
if (!$event->isMainRequest() || !is_file($this->projectDir.'/'.ArchiveUpdate::MARKE)) {
|
||||
return;
|
||||
}
|
||||
$offen = $this->archive->pending();
|
||||
if (true !== ($offen['fern'] ?? null)) {
|
||||
return;
|
||||
}
|
||||
// Gleichzeitige Anfragen: Eine schließt ab, die anderen sehen die Wartungsseite.
|
||||
$sperre = $this->updater->lock();
|
||||
if (null === $sperre) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
if (true !== ($this->archive->pending()['fern'] ?? null)) {
|
||||
return;
|
||||
}
|
||||
@ignore_user_abort(true);
|
||||
@set_time_limit(0);
|
||||
$this->archive->finish($this->installation, $this->pruefung);
|
||||
} catch (\Throwable) {
|
||||
// Was schiefging, steht im Updateprotokoll; die Wartungsseite bleibt dann an.
|
||||
} finally {
|
||||
flock($sperre, LOCK_UN);
|
||||
fclose($sperre);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user