Workspace: naechtliche Sicherung, die auch wirklich Daten enthaelt

Bisher wurde nur EINMAL gesichert: direkt vor einer Umstellung der
Datenbank. Faellt die Platte aus, ist alles weg -- die komplette
Betreuung, jede Aufgabe, jedes Protokoll. Das war das einzige Risiko im
System, das auf einen Schlag ALLES kostet.

Beim Nachsehen auf dem Server kam ein zweiter, schwerwiegenderer Befund
dazu:

    workspace.db          4.096 B
    workspace.db-wal  2.101.232 B

Im WAL-Betrieb landen Schreibvorgaenge zuerst im -wal und wandern erst
beim Checkpoint in die Hauptdatei. Der lief seit dem ersten Tag nicht.
Wer also workspace.db kopiert haette -- der naheliegendste Sicherungsweg
ueberhaupt -- haette eine LEERE Datenbank gesichert und es nicht gemerkt.
Der Test stellt genau das nach: aus der reinen Dateikopie waren 0 von 200
Aufgaben lesbar.

Deshalb:
- Nie eine Dateikopie. Immer VACUUM INTO -- SQLite schreibt selbst, liest
  das WAL mit, Ergebnis ist in sich stimmig auch waehrend Schreibzugriffen.
- Vor jeder Sicherung ein erzwungener Checkpoint (TRUNCATE). Haelt die
  Hauptdatei aktuell und das WAL klein.
- Jede frische Sicherung wird SOFORT wieder geoeffnet und geprueft: auf
  Unversehrtheit UND darauf, dass Personen darin stehen -- gegen genau den
  Fall einer technisch einwandfreien, aber leeren Datei. Faellt sie durch,
  wird sie geloescht statt behalten.
- Erst unter Zwischennamen schreiben, dann pruefen, dann umbenennen. Sonst
  stuende nach einem Abbruch eine halbe Datei unter dem richtigen Namen.
- Aufbewahrung 7 taeglich / 4 woechentlich / 6 monatlich, je Art getrennt
  aufgeraeumt, damit taegliche nie die monatlichen verdraengen.

Laeuft IM Prozess, nicht als Systemdienst: keine Aenderung an Systemdateien
noetig, die Datenbank ist ohnehin offen, und es kommt mit jedem git pull
mit. Statt setTimeout auf 24 h wird viertelstuendlich geprueft, ob fuer
HEUTE schon eine liegt -- das ueberlebt Neustarts und Ausfaelle.

Dazu eine Zustandsansicht auf der Automationen-Seite: wann zuletzt, wie
gross, wie viele Personen darin, wie viel wartet noch im WAL, wie viel
Platz ist frei. Plus "Jetzt sichern" von Hand. Nur Leitung, 404 statt 403
fuer alle anderen.

Geprueft (server/pruef-sicherung.mjs, 27 Pruefungen) einschliesslich des
Ernstfalls: Sicherung schreiben, Datenbank zerstoeren, zurueckspielen,
Vollstaendigkeit und Schreibbarkeit nachweisen. Eine ungetestete Sicherung
ist eine Vermutung.

Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
2026-08-31 18:20:35 +02:00
co-authored by Claude Opus 5
parent 1894395d8f
commit 7a393b9335
23 changed files with 966 additions and 100 deletions
+37
View File
@@ -53,6 +53,43 @@
text-transform: uppercase; color: var(--text-still); padding-top: 2px;
}
/* ---------- Sicherung & Zustand -------------------------------------------
Derselbe Kasten wie bei der KI, aber mit eigener Farbgebung fuer den
Zustand: Gruen gibt es hier bewusst nicht -- "es lief" ist der
Normalfall und soll nicht feiern. Auffaellig wird es nur, wenn eine
Nacht ausgefallen ist oder etwas schiefging.
-------------------------------------------------------------------------- */
.zustand {
margin-bottom: 34px; padding: 20px 22px;
border: 1px solid var(--rand); border-radius: var(--radius);
background: rgba(255, 255, 255, .028);
}
.zustand[data-zustand="alt"] {
border-color: rgba(232, 192, 125, .34); background: rgba(232, 192, 125, .05);
}
.zustand[data-zustand="fehler"] {
border-color: rgba(255, 154, 162, .34); background: rgba(255, 154, 162, .05);
}
.zustand__kopf {
display: flex; gap: 20px; align-items: flex-start;
justify-content: space-between; flex-wrap: wrap;
}
.zustand__titel { margin: 2px 0 6px; font-size: 1.1rem; font-weight: 700; }
.zustand[data-zustand="fehler"] .zustand__titel { color: var(--warn); }
.zustand__text { margin: 0; font-size: .88rem; color: var(--text-leise); max-width: 68ch; }
.zustand__daten {
display: grid; gap: 7px; margin: 18px 0 0; padding: 16px 0 0; list-style: none;
border-top: 1px solid var(--rand);
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
}
.zustand__daten li { display: flex; gap: 10px; font-size: .83rem; color: var(--text-leise); }
.zustand__daten .ki-kasten__schild { min-width: 128px; }
.zustand__listenkopf { margin-top: 18px; padding-top: 14px; border-top: 1px solid var(--rand); }
/* ---------- Hinweise ------------------------------------------------------- */
.hinweise { display: grid; gap: 7px; }
+109 -1
View File
@@ -46,6 +46,8 @@
text: 'Acht Fehlversuche je Anschluss in zehn Minuten, danach ist zu. Gezählt wird die echte Adresse, nicht die von Cloudflare.' },
{ name: 'Sicherung vor Umbauten',
text: 'Bevor die Datenbank umgestellt wird, schreibt der Server eine vollständige Sicherung. Ohne Sicherung wird nicht umgestellt.' },
{ name: 'Nächtliche Sicherung',
text: 'Jede Nacht eine vollständige Sicherung, die sofort wieder geöffnet und geprüft wird – auf Unversehrtheit und darauf, dass wirklich Daten drinstehen. Aufgehoben werden 7 tägliche, 4 wöchentliche und 6 monatliche. Geschrieben wird nie eine Dateikopie, sondern immer von SQLite selbst.' },
];
const ART = {
@@ -153,6 +155,112 @@
ziel.append(liste);
}
/* ---------- Sicherung & Zustand ------------------------------------------
Die einzige Automation, deren Ausfall ALLES auf einmal kostet -- und
die einzige, die man nicht bemerkt, wenn sie ausbleibt. Deshalb steht
hier nicht nur "läuft", sondern wann zuletzt, wie viel, wie viele
Personen darin standen und wie alt das ist.
------------------------------------------------------------------------ */
const bytes = (n) => {
if (n === null || n === undefined) return '–';
if (n < 1024) return n + ' B';
if (n < 1024 * 1024) return Math.round(n / 1024) + ' KB';
if (n < 1024 * 1024 * 1024) return (n / 1024 / 1024).toFixed(1) + ' MB';
return (n / 1024 / 1024 / 1024).toFixed(1) + ' GB';
};
function herZeit(iso) {
if (!iso) return null;
const min = Math.floor((Date.now() - Date.parse(iso)) / 60000);
if (!Number.isFinite(min)) return null;
if (min < 60) return `vor ${Math.max(min, 0)} Minuten`;
const std = Math.floor(min / 60);
if (std < 48) return `vor ${std} Stunden`;
return `vor ${Math.floor(std / 24)} Tagen`;
}
async function zustand() {
const kasten = $('zustand-kasten');
let z;
try {
const a = await hole('/workspace/api/zustand');
/* 404 heisst hier: diese Rolle soll davon gar nichts wissen. Dann
verschwindet der ganze Kasten, statt leer dazustehen. */
if (!a.ok) { kasten.hidden = true; return; }
z = await a.json();
} catch { kasten.hidden = true; return; }
const her = herZeit(z.letzte?.zeit);
/* Älter als 36 Stunden heisst: mindestens eine Nacht ist ausgefallen. */
const alt = z.letzte?.zeit
? (Date.now() - Date.parse(z.letzte.zeit)) > 36 * 3600_000 : true;
kasten.dataset.zustand = z.fehler ? 'fehler' : (alt ? 'alt' : 'gut');
$('zustand-titel').textContent = z.fehler
? 'Die letzte Sicherung ist fehlgeschlagen'
: z.letzte
? `Zuletzt gesichert ${her}`
: 'Noch keine Sicherung gelaufen';
$('zustand-text').textContent = z.fehler
? z.fehler.text
: z.letzte
? `${z.letzte.datei} · ${bytes(z.letzte.groesse)} · ${z.letzte.personen} Personen · `
+ `geschrieben und sofort wieder geprüft in ${z.letzte.dauer_ms} ms`
: 'Die erste läuft in der kommenden Nacht – oder jetzt gleich auf Knopfdruck.';
const daten = $('zustand-daten');
daten.textContent = '';
const zeilen = [
['Aufgehoben', `${z.anzahl} Sicherungen · ${bytes(z.belegt)} · `
+ `${z.behalten.taeglich} täglich, ${z.behalten.woechentlich} wöchentlich, ${z.behalten.monatlich} monatlich`],
['Datenbank', `${bytes(z.dateien?.db)} · Prüfung: ${z.heil || '–'} · Betrieb: ${z.journal || '–'}`],
/* Die WAL-Grösse steht hier, weil genau daran der Fehler vom
31.08.2026 aufgefallen wäre: Datenbank 4 KB, WAL 2,1 MB. */
['Wartende Schreibvorgänge', `${bytes(z.dateien?.wal)} (WAL – wird bei jeder Sicherung zusammengeführt)`],
['Platz', z.platte ? `${bytes(z.platte.frei)} frei von ${bytes(z.platte.gesamt)}` : '–'],
['Dienst läuft', `${Math.floor(z.seit / 3600)} h ${Math.floor((z.seit % 3600) / 60)} min`],
];
for (const [name, wert] of zeilen) {
const li = el('li');
li.append(el('span', 'ki-kasten__schild', name), el('span', null, wert));
daten.append(li);
}
const liste = $('sicherungen');
liste.textContent = '';
if (!z.sicherungen.length) {
liste.append(el('p', 'leise', 'Noch keine.'));
$('sicherungen-schalter').hidden = true;
return;
}
for (const s of z.sicherungen) {
const zeile = el('div', 'verlauf__zeile');
zeile.append(el('span', 'verlauf__zeit', s.name.replace(/\.db$/, '').split('-').slice(1).reverse().join('.')));
zeile.append(el('span', 'verlauf__was', s.name.split('-')[0]));
zeile.append(el('span', 'verlauf__wer', bytes(s.groesse)));
liste.append(zeile);
}
window.klappe?.(liste, $('sicherungen-schalter'), $('sicherungen-schalter-text'));
}
$('jetzt-sichern').addEventListener('click', async () => {
const knopf = $('jetzt-sichern');
knopf.disabled = true;
const vorher = knopf.textContent;
knopf.textContent = 'Sichert …';
try {
const a = await hole('/workspace/api/zustand/sichern', { method: 'POST' });
const d = await a.json().catch(() => ({}));
if (!a.ok) { melde(d.fehler || 'Die Sicherung ist fehlgeschlagen.'); return; }
melde('');
await zustand();
} catch { /* umgeleitet */ } finally {
knopf.disabled = false; knopf.textContent = vorher;
}
});
/* ---------- Verlauf ------------------------------------------------------ */
async function verlauf() {
@@ -204,6 +312,6 @@
ich = await a.json();
} catch { return; }
$('wer').textContent = ich.name + ' · ' + (ich.rolle_name || ich.rolle);
await Promise.all([kiZustand(), hinweise(), verlauf()]);
await Promise.all([kiZustand(), zustand(), hinweise(), verlauf()]);
})();
})();
+6 -6
View File
@@ -7,9 +7,9 @@
<meta name="robots" content="noindex, nofollow" />
<meta name="theme-color" content="#05070d" />
<link rel="icon" href="assets/img/favicon.png" />
<link rel="stylesheet" href="assets/css/gate.css?v=202608311327" />
<link rel="stylesheet" href="assets/css/start.css?v=202608311327" />
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202608311327" />
<link rel="stylesheet" href="assets/css/gate.css?v=202608311820" />
<link rel="stylesheet" href="assets/css/start.css?v=202608311820" />
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202608311820" />
</head>
<body class="start">
@@ -134,8 +134,8 @@
</form>
</dialog>
<script src="assets/js/wahl.js?v=202608311327" defer></script>
<script src="assets/js/kopf.js?v=202608311327" defer></script>
<script src="assets/js/aufgaben.js?v=202608311327" defer></script>
<script src="assets/js/wahl.js?v=202608311820" defer></script>
<script src="assets/js/kopf.js?v=202608311820" defer></script>
<script src="assets/js/aufgaben.js?v=202608311820" defer></script>
</body>
</html>
+32 -7
View File
@@ -7,10 +7,10 @@
<meta name="robots" content="noindex, nofollow" />
<meta name="theme-color" content="#05070d" />
<link rel="icon" href="assets/img/favicon.png" />
<link rel="stylesheet" href="assets/css/gate.css?v=202608311327" />
<link rel="stylesheet" href="assets/css/start.css?v=202608311327" />
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202608311327" />
<link rel="stylesheet" href="assets/css/automation.css?v=202608311327" />
<link rel="stylesheet" href="assets/css/gate.css?v=202608311820" />
<link rel="stylesheet" href="assets/css/start.css?v=202608311820" />
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202608311820" />
<link rel="stylesheet" href="assets/css/automation.css?v=202608311820" />
</head>
<body class="start">
@@ -76,6 +76,31 @@
<div id="hinweise"><p class="leise">Wird geprüft …</p></div>
</section>
<!-- Sicherung und Zustand. Steht bewusst weit oben: Es ist die einzige
Automation, deren Ausfall alles auf einmal kosten würde. -->
<section class="zustand" id="zustand-kasten">
<div class="zustand__kopf">
<div>
<p class="marke">Sicherung &amp; Zustand</p>
<h2 class="zustand__titel" id="zustand-titel">Wird geprüft …</h2>
<p class="zustand__text" id="zustand-text"></p>
</div>
<button type="button" class="knopf knopf--klein" id="jetzt-sichern">Jetzt sichern</button>
</div>
<ul class="zustand__daten" id="zustand-daten"></ul>
<div class="klapp-kopf zustand__listenkopf">
<p class="feldschild">Vorhandene Sicherungen</p>
<button type="button" class="knopf-still" id="sicherungen-schalter"
aria-expanded="false" aria-controls="sicherungen" hidden>
<span id="sicherungen-schalter-text">Alle zeigen</span>
<svg class="klapp-pfeil" viewBox="0 0 24 24" aria-hidden="true">
<path d="m6 9.5 6 6 6-6" />
</svg>
</button>
</div>
<div id="sicherungen" data-klapp="zu"></div>
</section>
<section class="block">
<div class="block__kopf">
<h2 class="block__titel">Läuft dauerhaft im Hintergrund</h2>
@@ -102,8 +127,8 @@
</main>
<script src="assets/js/wahl.js?v=202608311327" defer></script>
<script src="assets/js/kopf.js?v=202608311327" defer></script>
<script src="assets/js/automation.js?v=202608311327" defer></script>
<script src="assets/js/wahl.js?v=202608311820" defer></script>
<script src="assets/js/kopf.js?v=202608311820" defer></script>
<script src="assets/js/automation.js?v=202608311820" defer></script>
</body>
</html>
+7 -7
View File
@@ -7,10 +7,10 @@
<meta name="robots" content="noindex, nofollow" />
<meta name="theme-color" content="#05070d" />
<link rel="icon" href="assets/img/favicon.png" />
<link rel="stylesheet" href="assets/css/gate.css?v=202608311327" />
<link rel="stylesheet" href="assets/css/start.css?v=202608311327" />
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202608311327" />
<link rel="stylesheet" href="assets/css/bereich.css?v=202608311327" />
<link rel="stylesheet" href="assets/css/gate.css?v=202608311820" />
<link rel="stylesheet" href="assets/css/start.css?v=202608311820" />
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202608311820" />
<link rel="stylesheet" href="assets/css/bereich.css?v=202608311820" />
</head>
<body class="start">
@@ -98,8 +98,8 @@
</main>
<script src="assets/js/wahl.js?v=202608311327" defer></script>
<script src="assets/js/kopf.js?v=202608311327" defer></script>
<script src="assets/js/bereich.js?v=202608311327" defer></script>
<script src="assets/js/wahl.js?v=202608311820" defer></script>
<script src="assets/js/kopf.js?v=202608311820" defer></script>
<script src="assets/js/bereich.js?v=202608311820" defer></script>
</body>
</html>
+8 -8
View File
@@ -7,10 +7,10 @@
<meta name="robots" content="noindex, nofollow" />
<meta name="theme-color" content="#05070d" />
<link rel="icon" href="assets/img/favicon.png" />
<link rel="stylesheet" href="assets/css/gate.css?v=202608311327" />
<link rel="stylesheet" href="assets/css/start.css?v=202608311327" />
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202608311327" />
<link rel="stylesheet" href="assets/css/calls.css?v=202608311327" />
<link rel="stylesheet" href="assets/css/gate.css?v=202608311820" />
<link rel="stylesheet" href="assets/css/start.css?v=202608311820" />
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202608311820" />
<link rel="stylesheet" href="assets/css/calls.css?v=202608311820" />
</head>
<body class="start">
@@ -49,9 +49,9 @@
</main>
<script src="assets/js/ki.js?v=202608311327" defer></script>
<script src="assets/js/wahl.js?v=202608311327" defer></script>
<script src="assets/js/kopf.js?v=202608311327" defer></script>
<script src="assets/js/calls.js?v=202608311327" defer></script>
<script src="assets/js/ki.js?v=202608311820" defer></script>
<script src="assets/js/wahl.js?v=202608311820" defer></script>
<script src="assets/js/kopf.js?v=202608311820" defer></script>
<script src="assets/js/calls.js?v=202608311820" defer></script>
</body>
</html>
+7 -7
View File
@@ -7,10 +7,10 @@
<meta name="robots" content="noindex, nofollow" />
<meta name="theme-color" content="#05070d" />
<link rel="icon" href="assets/img/favicon.png" />
<link rel="stylesheet" href="assets/css/gate.css?v=202608311327" />
<link rel="stylesheet" href="assets/css/start.css?v=202608311327" />
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202608311327" />
<link rel="stylesheet" href="assets/css/dateien.css?v=202608311327" />
<link rel="stylesheet" href="assets/css/gate.css?v=202608311820" />
<link rel="stylesheet" href="assets/css/start.css?v=202608311820" />
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202608311820" />
<link rel="stylesheet" href="assets/css/dateien.css?v=202608311820" />
</head>
<body class="start">
@@ -88,8 +88,8 @@
</main>
<script src="assets/js/wahl.js?v=202608311327" defer></script>
<script src="assets/js/kopf.js?v=202608311327" defer></script>
<script src="assets/js/dateien.js?v=202608311327" defer></script>
<script src="assets/js/wahl.js?v=202608311820" defer></script>
<script src="assets/js/kopf.js?v=202608311820" defer></script>
<script src="assets/js/dateien.js?v=202608311820" defer></script>
</body>
</html>
+2 -2
View File
@@ -25,7 +25,7 @@
type="image/webp" media="(max-width: 899px)" />
<link rel="icon" href="assets/img/favicon.png" />
<link rel="stylesheet" href="assets/css/gate.css?v=202608311327" />
<link rel="stylesheet" href="assets/css/gate.css?v=202608311820" />
</head>
<body class="gate">
@@ -143,6 +143,6 @@
</p>
</main>
<script src="assets/js/gate.js?v=202608311327" defer></script>
<script src="assets/js/gate.js?v=202608311820" defer></script>
</body>
</html>
+7 -7
View File
@@ -7,10 +7,10 @@
<meta name="robots" content="noindex, nofollow" />
<meta name="theme-color" content="#05070d" />
<link rel="icon" href="assets/img/favicon.png" />
<link rel="stylesheet" href="assets/css/gate.css?v=202608311327" />
<link rel="stylesheet" href="assets/css/start.css?v=202608311327" />
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202608311327" />
<link rel="stylesheet" href="assets/css/kalender.css?v=202608311327" />
<link rel="stylesheet" href="assets/css/gate.css?v=202608311820" />
<link rel="stylesheet" href="assets/css/start.css?v=202608311820" />
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202608311820" />
<link rel="stylesheet" href="assets/css/kalender.css?v=202608311820" />
</head>
<body class="start">
@@ -94,8 +94,8 @@
</main>
<script src="assets/js/wahl.js?v=202608311327" defer></script>
<script src="assets/js/kopf.js?v=202608311327" defer></script>
<script src="assets/js/kalender.js?v=202608311327" defer></script>
<script src="assets/js/wahl.js?v=202608311820" defer></script>
<script src="assets/js/kopf.js?v=202608311820" defer></script>
<script src="assets/js/kalender.js?v=202608311820" defer></script>
</body>
</html>
+7 -7
View File
@@ -7,10 +7,10 @@
<meta name="robots" content="noindex, nofollow" />
<meta name="theme-color" content="#05070d" />
<link rel="icon" href="assets/img/favicon.png" />
<link rel="stylesheet" href="assets/css/gate.css?v=202608311327" />
<link rel="stylesheet" href="assets/css/start.css?v=202608311327" />
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202608311327" />
<link rel="stylesheet" href="assets/css/personen.css?v=202608311327" />
<link rel="stylesheet" href="assets/css/gate.css?v=202608311820" />
<link rel="stylesheet" href="assets/css/start.css?v=202608311820" />
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202608311820" />
<link rel="stylesheet" href="assets/css/personen.css?v=202608311820" />
</head>
<body class="start">
@@ -104,8 +104,8 @@
</main>
<script src="assets/js/wahl.js?v=202608311327" defer></script>
<script src="assets/js/kopf.js?v=202608311327" defer></script>
<script src="assets/js/personen.js?v=202608311327" defer></script>
<script src="assets/js/wahl.js?v=202608311820" defer></script>
<script src="assets/js/kopf.js?v=202608311820" defer></script>
<script src="assets/js/personen.js?v=202608311820" defer></script>
</body>
</html>
+7 -7
View File
@@ -7,10 +7,10 @@
<meta name="robots" content="noindex, nofollow" />
<meta name="theme-color" content="#05070d" />
<link rel="icon" href="assets/img/favicon.png" />
<link rel="stylesheet" href="assets/css/gate.css?v=202608311327" />
<link rel="stylesheet" href="assets/css/start.css?v=202608311327" />
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202608311327" />
<link rel="stylesheet" href="assets/css/profil.css?v=202608311327" />
<link rel="stylesheet" href="assets/css/gate.css?v=202608311820" />
<link rel="stylesheet" href="assets/css/start.css?v=202608311820" />
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202608311820" />
<link rel="stylesheet" href="assets/css/profil.css?v=202608311820" />
</head>
<body class="start">
@@ -127,8 +127,8 @@
</main>
<script src="assets/js/wahl.js?v=202608311327" defer></script>
<script src="assets/js/kopf.js?v=202608311327" defer></script>
<script src="assets/js/profil.js?v=202608311327" defer></script>
<script src="assets/js/wahl.js?v=202608311820" defer></script>
<script src="assets/js/kopf.js?v=202608311820" defer></script>
<script src="assets/js/profil.js?v=202608311820" defer></script>
</body>
</html>
+8 -8
View File
@@ -7,10 +7,10 @@
<meta name="robots" content="noindex, nofollow" />
<meta name="theme-color" content="#05070d" />
<link rel="icon" href="assets/img/favicon.png" />
<link rel="stylesheet" href="assets/css/gate.css?v=202608311327" />
<link rel="stylesheet" href="assets/css/start.css?v=202608311327" />
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202608311327" />
<link rel="stylesheet" href="assets/css/report.css?v=202608311327" />
<link rel="stylesheet" href="assets/css/gate.css?v=202608311820" />
<link rel="stylesheet" href="assets/css/start.css?v=202608311820" />
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202608311820" />
<link rel="stylesheet" href="assets/css/report.css?v=202608311820" />
</head>
<body class="start">
@@ -86,9 +86,9 @@
</main>
<script src="assets/js/ki.js?v=202608311327" defer></script>
<script src="assets/js/wahl.js?v=202608311327" defer></script>
<script src="assets/js/kopf.js?v=202608311327" defer></script>
<script src="assets/js/report.js?v=202608311327" defer></script>
<script src="assets/js/ki.js?v=202608311820" defer></script>
<script src="assets/js/wahl.js?v=202608311820" defer></script>
<script src="assets/js/kopf.js?v=202608311820" defer></script>
<script src="assets/js/report.js?v=202608311820" defer></script>
</body>
</html>
+7 -7
View File
@@ -7,10 +7,10 @@
<meta name="robots" content="noindex, nofollow" />
<meta name="theme-color" content="#05070d" />
<link rel="icon" href="assets/img/favicon.png" />
<link rel="stylesheet" href="assets/css/gate.css?v=202608311327" />
<link rel="stylesheet" href="assets/css/start.css?v=202608311327" />
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202608311327" />
<link rel="stylesheet" href="assets/css/scouting.css?v=202608311327" />
<link rel="stylesheet" href="assets/css/gate.css?v=202608311820" />
<link rel="stylesheet" href="assets/css/start.css?v=202608311820" />
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202608311820" />
<link rel="stylesheet" href="assets/css/scouting.css?v=202608311820" />
</head>
<body class="start">
@@ -124,8 +124,8 @@
</div>
</div>
<script src="assets/js/wahl.js?v=202608311327" defer></script>
<script src="assets/js/kopf.js?v=202608311327" defer></script>
<script src="assets/js/scouting.js?v=202608311327" defer></script>
<script src="assets/js/wahl.js?v=202608311820" defer></script>
<script src="assets/js/kopf.js?v=202608311820" defer></script>
<script src="assets/js/scouting.js?v=202608311820" defer></script>
</body>
</html>
+5 -5
View File
@@ -7,8 +7,8 @@
<meta name="robots" content="noindex, nofollow" />
<meta name="theme-color" content="#05070d" />
<link rel="icon" href="assets/img/favicon.png" />
<link rel="stylesheet" href="assets/css/gate.css?v=202608311327" />
<link rel="stylesheet" href="assets/css/start.css?v=202608311327" />
<link rel="stylesheet" href="assets/css/gate.css?v=202608311820" />
<link rel="stylesheet" href="assets/css/start.css?v=202608311820" />
</head>
<body class="start">
@@ -56,8 +56,8 @@
</p>
</main>
<script src="assets/js/wahl.js?v=202608311327" defer></script>
<script src="assets/js/kopf.js?v=202608311327" defer></script>
<script src="assets/js/start.js?v=202608311327" defer></script>
<script src="assets/js/wahl.js?v=202608311820" defer></script>
<script src="assets/js/kopf.js?v=202608311820" defer></script>
<script src="assets/js/start.js?v=202608311820" defer></script>
</body>
</html>
+8 -8
View File
@@ -7,10 +7,10 @@
<meta name="robots" content="noindex, nofollow" />
<meta name="theme-color" content="#05070d" />
<link rel="icon" href="assets/img/favicon.png" />
<link rel="stylesheet" href="assets/css/gate.css?v=202608311327" />
<link rel="stylesheet" href="assets/css/start.css?v=202608311327" />
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202608311327" />
<link rel="stylesheet" href="assets/css/startcheck.css?v=202608311327" />
<link rel="stylesheet" href="assets/css/gate.css?v=202608311820" />
<link rel="stylesheet" href="assets/css/start.css?v=202608311820" />
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202608311820" />
<link rel="stylesheet" href="assets/css/startcheck.css?v=202608311820" />
</head>
<body class="start">
@@ -65,9 +65,9 @@
</main>
<script src="assets/js/ki.js?v=202608311327" defer></script>
<script src="assets/js/wahl.js?v=202608311327" defer></script>
<script src="assets/js/kopf.js?v=202608311327" defer></script>
<script src="assets/js/startcheck.js?v=202608311327" defer></script>
<script src="assets/js/ki.js?v=202608311820" defer></script>
<script src="assets/js/wahl.js?v=202608311820" defer></script>
<script src="assets/js/kopf.js?v=202608311820" defer></script>
<script src="assets/js/startcheck.js?v=202608311820" defer></script>
</body>
</html>
+5 -5
View File
@@ -7,9 +7,9 @@
<meta name="robots" content="noindex, nofollow" />
<meta name="theme-color" content="#05070d" />
<link rel="icon" href="assets/img/favicon.png" />
<link rel="stylesheet" href="assets/css/gate.css?v=202608311327" />
<link rel="stylesheet" href="assets/css/start.css?v=202608311327" />
<link rel="stylesheet" href="assets/css/uebersicht.css?v=202608311327" />
<link rel="stylesheet" href="assets/css/gate.css?v=202608311820" />
<link rel="stylesheet" href="assets/css/start.css?v=202608311820" />
<link rel="stylesheet" href="assets/css/uebersicht.css?v=202608311820" />
</head>
<body class="start">
@@ -52,7 +52,7 @@
</main>
<script src="assets/js/kopf.js?v=202608311327" defer></script>
<script src="assets/js/uebersicht.js?v=202608311327" defer></script>
<script src="assets/js/kopf.js?v=202608311820" defer></script>
<script src="assets/js/uebersicht.js?v=202608311820" defer></script>
</body>
</html>
+7 -7
View File
@@ -7,10 +7,10 @@
<meta name="robots" content="noindex, nofollow" />
<meta name="theme-color" content="#05070d" />
<link rel="icon" href="assets/img/favicon.png" />
<link rel="stylesheet" href="assets/css/gate.css?v=202608311327" />
<link rel="stylesheet" href="assets/css/start.css?v=202608311327" />
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202608311327" />
<link rel="stylesheet" href="assets/css/wissen.css?v=202608311327" />
<link rel="stylesheet" href="assets/css/gate.css?v=202608311820" />
<link rel="stylesheet" href="assets/css/start.css?v=202608311820" />
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202608311820" />
<link rel="stylesheet" href="assets/css/wissen.css?v=202608311820" />
</head>
<body class="start">
@@ -170,8 +170,8 @@
</main>
<script src="assets/js/wahl.js?v=202608311327" defer></script>
<script src="assets/js/kopf.js?v=202608311327" defer></script>
<script src="assets/js/wissen.js?v=202608311327" defer></script>
<script src="assets/js/wahl.js?v=202608311820" defer></script>
<script src="assets/js/kopf.js?v=202608311820" defer></script>
<script src="assets/js/wissen.js?v=202608311820" defer></script>
</body>
</html>