Files
KantSites/zentrale/app/Datenbank.php
T
kantarciandClaude Opus 5.5 968d19b611
CMS quality / php (8.2) (push) Waiting to run
CMS quality / php (8.4) (push) Waiting to run
CMS quality / browser (push) Waiting to run
CMS quality / mysql (push) Waiting to run
Add a KantTresor tab to the central registry
KantTresor instances (from 1.82.0) report to /api/kanttresor/melden with the
same key-then-HMAC protocol. The new tab lists them with tenants, disk usage
and failing services; it never offers or accepts an update order, since
KantTresor is updated with ./update.sh on the server. See ADR 124.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-27 10:02:07 +01:00

107 lines
3.9 KiB
PHP

<?php
declare(strict_types=1);
namespace Zentrale;
/**
* SQLite in var/zentrale.db. Das Schema legt sich beim ersten Aufruf selbst an; spätere
* Änderungen kommen als nummerierte Schritte in MIGRATIONEN dazu, nie als Änderung eines alten.
*/
final class Datenbank
{
private const MIGRATIONEN = [
1 => [
'CREATE TABLE konto (id INTEGER PRIMARY KEY, name TEXT NOT NULL UNIQUE, passwort TEXT NOT NULL, erstellt TEXT NOT NULL)',
'CREATE TABLE anmeldeversuch (ip TEXT NOT NULL, zeit TEXT NOT NULL)',
'CREATE INDEX anmeldeversuch_ip ON anmeldeversuch (ip, zeit)',
'CREATE TABLE installation (
id TEXT PRIMARY KEY,
schluessel TEXT NOT NULL,
adresse TEXT NOT NULL,
domain TEXT NOT NULL,
domain_vorher TEXT,
fassung TEXT NOT NULL,
php TEXT NOT NULL DEFAULT \'\',
datenbank TEXT NOT NULL DEFAULT \'\',
art TEXT NOT NULL DEFAULT \'archiv\',
wartung INTEGER NOT NULL DEFAULT 0,
update_phase TEXT,
update_meldung TEXT,
update_zeit TEXT,
notiz TEXT NOT NULL DEFAULT \'\',
ip TEXT NOT NULL DEFAULT \'\',
erste_meldung TEXT NOT NULL,
letzte_meldung TEXT NOT NULL
)',
'CREATE TABLE auftrag (
id INTEGER PRIMARY KEY,
installation TEXT NOT NULL REFERENCES installation (id) ON DELETE CASCADE,
aktion TEXT NOT NULL,
fassung TEXT NOT NULL,
erstellt TEXT NOT NULL,
abgeholt TEXT,
erledigt TEXT,
ok INTEGER,
meldung TEXT
)',
'CREATE INDEX auftrag_installation ON auftrag (installation, id)',
],
// KantTresor meldet sich ebenfalls (ADR 124). Bestehende Zeilen sind KantSites.
2 => [
'ALTER TABLE installation ADD COLUMN produkt TEXT NOT NULL DEFAULT \'kantsites\'',
'ALTER TABLE installation ADD COLUMN laufzeit TEXT NOT NULL DEFAULT \'\'',
'ALTER TABLE installation ADD COLUMN mandanten INTEGER',
'ALTER TABLE installation ADD COLUMN stoerungen TEXT NOT NULL DEFAULT \'\'',
'ALTER TABLE installation ADD COLUMN speicher INTEGER',
'CREATE INDEX installation_produkt ON installation (produkt)',
],
];
public static function oeffnen(string $datei): \PDO
{
$neu = !is_file($datei);
if ($neu && !is_dir(dirname($datei)) && !@mkdir(dirname($datei), 0o775, true)) {
throw new \RuntimeException(dirname($datei).' lässt sich nicht anlegen.');
}
$pdo = new \PDO('sqlite:'.$datei, null, null, [
\PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION,
\PDO::ATTR_DEFAULT_FETCH_MODE => \PDO::FETCH_ASSOC,
]);
$pdo->exec('PRAGMA foreign_keys = ON');
$pdo->exec('PRAGMA busy_timeout = 5000');
if ($neu) {
$pdo->exec('PRAGMA journal_mode = WAL');
}
self::migrieren($pdo);
return $pdo;
}
private static function migrieren(\PDO $pdo): void
{
$stand = (int) $pdo->query('PRAGMA user_version')->fetchColumn();
foreach (self::MIGRATIONEN as $nummer => $anweisungen) {
if ($nummer <= $stand) {
continue;
}
$pdo->beginTransaction();
try {
foreach ($anweisungen as $sql) {
$pdo->exec($sql);
}
$pdo->exec('PRAGMA user_version = '.$nummer);
$pdo->commit();
} catch (\Throwable $fehler) {
$pdo->rollBack();
throw $fehler;
}
}
}
public static function jetzt(): string
{
return gmdate('Y-m-d\TH:i:s\Z');
}
}