Workspace: Versionsstempel gegen Cloudflares 4-Stunden-Cache
Filipe sah die neue Kachel nicht, obwohl sie live war. Ursache gemessen, nicht vermutet: Node liefert : Cache-Control: no-cache (richtig) Cloudflare macht: Cache-Control: max-age=14400 (ueberschreibt es) cf-cache-status: REVALIDATED, Server: cloudflare Cloudflares voreingestellte Browser-Cache-Zeit von vier Stunden setzt sich ueber das no-cache des Servers hinweg -- aber NUR bei JS und CSS. HTML kommt mit DYNAMIC und no-cache immer frisch durch (geprueft an /creator.html und /workspace/). Genau deshalb genuegt ein Stempel in der HTML: neue Seite -> neue Adresse -> Cloudflare kennt sie nicht -> frische Datei. Keine Cloudflare-Einstellung noetig, nichts, worauf ich warten muss. tools/workspace-stempel.mjs setzt den Stempel auf alle 71 Verweise in 13 Dateien. Der Lauf ist wiederholbar: ein vorhandener Stempel wird ersetzt, nicht angehaengt (geprueft). Gehoert ab jetzt vor jeden Deploy, der JS oder CSS im Workspace anfasst. Ich verlasse mich damit nicht mehr aufs Erinnern -- das war der eigentliche Fehler. Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
@@ -27,6 +27,7 @@ import { hinweisRouter } from "./workspace-hinweise.js";
|
||||
import { sucheRouter } from "./workspace-suche.js";
|
||||
import { startcheckRouter } from "./workspace-startcheck.js";
|
||||
import { wissenRouter } from "./workspace-wissen.js";
|
||||
import { kiRouter } from "./workspace-ki.js";
|
||||
|
||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||
const SITE_DIR = join(__dirname, "..");
|
||||
@@ -94,6 +95,7 @@ app.use(hinweisRouter);
|
||||
app.use(sucheRouter);
|
||||
app.use(startcheckRouter);
|
||||
app.use(wissenRouter);
|
||||
app.use(kiRouter);
|
||||
|
||||
/* Sicherheits-Fix 20.08.2026 (Audit vor dem geplanten öffentlichen Start morgen).
|
||||
SITE_DIR ist der GESAMTE Repo-Ordner (join(__dirname, "..")) — express.static liefert
|
||||
|
||||
@@ -0,0 +1,235 @@
|
||||
/* =====================================================================
|
||||
workspace-ki.js — lokale KI-Assistenz (Konzept, Phase 4: "optionale
|
||||
KI-Assistenz").
|
||||
|
||||
Laeuft auf DIESEM Server, in llama.cpp mit Qwen3.5-4B (Apache 2.0).
|
||||
Nichts verlaesst die Maschine, es entstehen keine Kosten, und wenn der
|
||||
Dienst aus ist, funktioniert der ganze Workspace unveraendert weiter.
|
||||
|
||||
VIER REGELN, die hier ueber allem stehen:
|
||||
|
||||
1. DIE KI SCHLAEGT VOR, SIE ENTSCHEIDET NIE. Kein Endpunkt dieser
|
||||
Datei schreibt in die Datenbank. Was uebernommen wird, bestaetigt
|
||||
ein Mensch -- an genau derselben Stelle wie bisher.
|
||||
|
||||
2. SIE DARF DEN WORKSPACE NIE MITREISSEN. Der Dienst kann aus sein,
|
||||
haengen oder Unsinn liefern. Jede Antwort hat eine harte Zeitgrenze,
|
||||
jeder Fehler endet in einer ruhigen Absage -- nie in einem 500er.
|
||||
|
||||
3. NUR EINE ANFRAGE GLEICHZEITIG. Der Server hat vier Kerne, zwei
|
||||
davon darf die KI benutzen. Zwei parallele Anfragen wuerden beide
|
||||
langsam und niemandem helfen. Wer wartet, bekommt das gesagt.
|
||||
|
||||
4. DENKEN IST ABGESCHALTET. Qwen3.5 ist ein Denk-Modell; ohne diese
|
||||
Einstellung verbraucht es die ganze Antwortlaenge fuer
|
||||
Selbstgespraeche und liefert am Ende NICHTS. Beim ersten Test
|
||||
genau so passiert.
|
||||
===================================================================== */
|
||||
|
||||
import express from "express";
|
||||
import { sitzungLesen, protokolliere, echteIp } from "./workspace.js";
|
||||
|
||||
export const kiRouter = express.Router();
|
||||
|
||||
const KI_URL = process.env.KI_URL || "http://127.0.0.1:8771";
|
||||
const ZEITGRENZE_MS = 45_000;
|
||||
const TEXT_MAX = 4000;
|
||||
|
||||
/* Nur eine Anfrage zur Zeit -- siehe Regel 3. */
|
||||
let laeuft = false;
|
||||
|
||||
/* Einfache Bremse je Person: fuenf Anfragen in zehn Minuten. Die KI ist
|
||||
billig im Geld, aber teuer in Rechenzeit, die der Website gehoert. */
|
||||
const VERBRAUCH = new Map();
|
||||
const LIMIT = 5;
|
||||
const FENSTER_MS = 10 * 60_000;
|
||||
|
||||
function zuVielGefragt(id) {
|
||||
const jetzt = Date.now();
|
||||
const liste = (VERBRAUCH.get(id) || []).filter((t) => jetzt - t < FENSTER_MS);
|
||||
VERBRAUCH.set(id, liste);
|
||||
return liste.length >= LIMIT;
|
||||
}
|
||||
function merkeAnfrage(id) {
|
||||
const liste = VERBRAUCH.get(id) || [];
|
||||
liste.push(Date.now());
|
||||
VERBRAUCH.set(id, liste);
|
||||
}
|
||||
|
||||
function angemeldet(req, res, next) {
|
||||
const person = sitzungLesen(req);
|
||||
if (!person) return res.status(401).json({ fehler: "nicht_angemeldet" });
|
||||
req.person = person;
|
||||
next();
|
||||
}
|
||||
function gleicheHerkunft(req, res, next) {
|
||||
const herkunft = req.get("origin");
|
||||
if (!herkunft) return next();
|
||||
let erlaubt;
|
||||
try { erlaubt = new URL(herkunft).host === req.get("host"); } catch { erlaubt = false; }
|
||||
if (!erlaubt) return res.status(403).json({ fehler: "fremde_herkunft" });
|
||||
next();
|
||||
}
|
||||
|
||||
kiRouter.use("/workspace/api/ki", angemeldet);
|
||||
|
||||
/* ---------- Die eine Stelle, an der gefragt wird ------------------------- */
|
||||
|
||||
/* Ein kleines Modell folgt einer BESCHREIBUNG schlecht und einem
|
||||
BEISPIEL gut. Ohne Beispiel kam auf einen Start-Check-Befund
|
||||
"Klar machen wir feste Sendetermine fuer den Start-Check" zurueck --
|
||||
Geplauder statt Aufgabe. Mit einem einzigen Beispiel:
|
||||
"Zeitpunkt der Sendung festlegen". Deshalb hat jede Aufgabe hier ihr
|
||||
Beispielpaar. */
|
||||
async function frage(anweisung, text, maxWorte, beispiele = []) {
|
||||
const abbruch = new AbortController();
|
||||
const uhr = setTimeout(() => abbruch.abort(), ZEITGRENZE_MS);
|
||||
try {
|
||||
const antwort = await fetch(KI_URL + "/v1/chat/completions", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
signal: abbruch.signal,
|
||||
body: JSON.stringify({
|
||||
messages: [
|
||||
{ role: "system", content: anweisung },
|
||||
...beispiele,
|
||||
{ role: "user", content: text },
|
||||
],
|
||||
/* Ohne diese Zeile denkt das Modell laut und liefert nichts. */
|
||||
chat_template_kwargs: { enable_thinking: false },
|
||||
max_tokens: maxWorte,
|
||||
temperature: 0.3,
|
||||
stream: false,
|
||||
}),
|
||||
});
|
||||
if (!antwort.ok) throw new Error("HTTP " + antwort.status);
|
||||
const d = await antwort.json();
|
||||
return String(d?.choices?.[0]?.message?.content ?? "").trim();
|
||||
} finally {
|
||||
clearTimeout(uhr);
|
||||
}
|
||||
}
|
||||
|
||||
/* Gemeinsamer Rahmen: Bremse, Sperre, Fehler abfangen. So steht die
|
||||
Absicherung einmal da und nicht in jedem Endpunkt neu. */
|
||||
async function mitSchutz(req, res, aufgabe, was) {
|
||||
if (zuVielGefragt(req.person.id)) {
|
||||
return res.status(429).json({
|
||||
fehler: `Höchstens ${LIMIT} Vorschläge in ${FENSTER_MS / 60000} Minuten – die KI teilt sich den Server mit der Website.`,
|
||||
});
|
||||
}
|
||||
if (laeuft) {
|
||||
return res.status(409).json({ fehler: "Die KI denkt gerade über etwas anderes nach. Gleich nochmal probieren." });
|
||||
}
|
||||
laeuft = true;
|
||||
const start = Date.now();
|
||||
try {
|
||||
const ergebnis = await aufgabe();
|
||||
merkeAnfrage(req.person.id);
|
||||
protokolliere("ki_gefragt", {
|
||||
personId: req.person.id, rolle: req.person.rolle, ip: echteIp(req),
|
||||
detail: `${was}, ${Math.round((Date.now() - start) / 1000)} s`.slice(0, 120),
|
||||
});
|
||||
res.json(ergebnis);
|
||||
} catch (fehler) {
|
||||
const weg = fehler?.name === "AbortError";
|
||||
console.error("[workspace] KI:", fehler?.message);
|
||||
res.status(503).json({
|
||||
fehler: weg
|
||||
? "Die KI hat zu lange gebraucht. Schreib es diesmal selbst – es geht auch ohne."
|
||||
: "Die KI ist gerade nicht erreichbar. Alles andere funktioniert normal weiter.",
|
||||
});
|
||||
} finally {
|
||||
laeuft = false;
|
||||
}
|
||||
}
|
||||
|
||||
/* ---------- Ist die KI ueberhaupt da? -----------------------------------
|
||||
Die Oberflaeche fragt das beim Laden und blendet die Knoepfe sonst
|
||||
aus. Ein Knopf, der immer eine Fehlermeldung bringt, ist schlimmer
|
||||
als gar kein Knopf. */
|
||||
|
||||
kiRouter.get("/workspace/api/ki/status", async (req, res) => {
|
||||
const abbruch = new AbortController();
|
||||
const uhr = setTimeout(() => abbruch.abort(), 2500);
|
||||
try {
|
||||
const a = await fetch(KI_URL + "/health", { signal: abbruch.signal });
|
||||
res.json({ da: a.ok, beschaeftigt: laeuft });
|
||||
} catch {
|
||||
res.json({ da: false, beschaeftigt: false });
|
||||
} finally {
|
||||
clearTimeout(uhr);
|
||||
}
|
||||
});
|
||||
|
||||
/* ---------- Aus einem Gespraech To-dos vorschlagen ----------------------- */
|
||||
|
||||
kiRouter.post("/workspace/api/ki/todos", gleicheHerkunft, express.json(), (req, res) => {
|
||||
const text = String(req.body?.text ?? "").trim().slice(0, TEXT_MAX);
|
||||
if (text.length < 20) {
|
||||
return res.status(400).json({ fehler: "Schreib erst ein paar Sätze – daraus kann die KI dann To-dos ableiten." });
|
||||
}
|
||||
return mitSchutz(req, res, async () => {
|
||||
const roh = await frage(
|
||||
"Du leitest aus Gespraechsnotizen To-dos ab. Nur die Liste, eine Zeile je To-do, "
|
||||
+ "jede mit Bindestrich, hoechstens 10 Woerter, Deutsch, Verb am Anfang, keine Vorrede.",
|
||||
text,
|
||||
200,
|
||||
[
|
||||
{ role: "user", content: "Kamera ist zu dunkel, wir wollten ein Ringlicht kaufen. Overlay ist noch vom letzten Jahr." },
|
||||
{ role: "assistant", content: "- Ringlicht besorgen und ausrichten\n- Overlay neu gestalten" },
|
||||
]);
|
||||
|
||||
/* Die Zeilen aufraeumen: Aufzaehlungszeichen weg, Leeres raus,
|
||||
hoechstens acht. Was das Modell an Vorrede doch liefert, faellt
|
||||
hier durch das Raster. */
|
||||
const todos = roh.split("\n")
|
||||
.map((z) => z.replace(/^\s*[-*•\d.)\]]+\s*/, "").trim())
|
||||
.filter((z) => z.length >= 4 && z.length <= 160)
|
||||
.slice(0, 8);
|
||||
|
||||
return { todos, hinweis: "Vorschlag der KI – prüfe ihn, bevor du ihn übernimmst." };
|
||||
}, "todos");
|
||||
});
|
||||
|
||||
/* ---------- Aus einem Befund einen Aufgabentitel ------------------------- */
|
||||
|
||||
kiRouter.post("/workspace/api/ki/aufgabentitel", gleicheHerkunft, express.json(), (req, res) => {
|
||||
const befund = String(req.body?.befund ?? "").trim().slice(0, 600);
|
||||
const bereich = String(req.body?.bereich ?? "").trim().slice(0, 120);
|
||||
if (befund.length < 8) {
|
||||
return res.status(400).json({ fehler: "Schreib erst eine Notiz zu dem Punkt." });
|
||||
}
|
||||
return mitSchutz(req, res, async () => {
|
||||
const roh = await frage(
|
||||
"Du formulierst kurze Aufgabentitel. Nur der Titel, hoechstens 8 Woerter, "
|
||||
+ "Deutsch, Verb am Anfang, kein Punkt, keine Anfuehrungszeichen.",
|
||||
`Befund${bereich ? " (" + bereich + ")" : ""}: ${befund}`,
|
||||
40,
|
||||
[
|
||||
{ role: "user", content: "Befund: Profilbild ist unscharf und alt." },
|
||||
{ role: "assistant", content: "Neues Profilbild aufnehmen und hochladen" },
|
||||
]);
|
||||
const titel = roh.split("\n")[0].replace(/^["'„»\s]+|["'“«.\s]+$/g, "").slice(0, 160);
|
||||
return { titel, hinweis: "Vorschlag der KI – du kannst ihn vor dem Anlegen ändern." };
|
||||
}, "aufgabentitel");
|
||||
});
|
||||
|
||||
/* ---------- Zahlen in Worte ---------------------------------------------- */
|
||||
|
||||
kiRouter.post("/workspace/api/ki/reporttext", gleicheHerkunft, express.json(), (req, res) => {
|
||||
const zahlen = String(req.body?.zahlen ?? "").trim().slice(0, 1500);
|
||||
if (zahlen.length < 10) return res.status(400).json({ fehler: "Keine Zahlen zum Zusammenfassen." });
|
||||
return mitSchutz(req, res, async () => {
|
||||
const roh = await frage(
|
||||
"Du fasst Kennzahlen sachlich zusammen. Hoechstens drei Saetze auf Deutsch, "
|
||||
+ "ohne Vorrede, ohne Aufzaehlung. Nenne nur, was in den Zahlen steht.",
|
||||
zahlen, 140,
|
||||
[
|
||||
{ role: "user", content: "30 Tage: 6 Aufgaben erledigt (davor 2), 0 ueberfaellig, 1 dringender Punkt." },
|
||||
{ role: "assistant", content: "Die Erledigungen haben sich gegenueber dem Vormonat verdreifacht, von zwei auf sechs. Ueberfaellig ist nichts, offen bleibt ein dringender Punkt." },
|
||||
]);
|
||||
return { text: roh.split("\n").filter(Boolean).join(" ").slice(0, 700),
|
||||
hinweis: "Vorschlag der KI – geprüft wird an den Zahlen darüber." };
|
||||
}, "reporttext");
|
||||
});
|
||||
@@ -0,0 +1,59 @@
|
||||
/* =====================================================================
|
||||
workspace-stempel.mjs — Versionsstempel an die Workspace-Dateien.
|
||||
|
||||
WARUM ES DAS GIBT:
|
||||
Cloudflare speichert JS und CSS vier Stunden im Browser -- es
|
||||
ueberschreibt damit das "no-cache", das der Server korrekt schickt.
|
||||
Nach einem Deploy sah man deshalb bis zu vier Stunden lang die ALTE
|
||||
Oberflaeche, obwohl der Server laengst die neue auslieferte. Genau so
|
||||
ist es passiert: eine neue Kachel war live, aber nicht sichtbar.
|
||||
|
||||
Die HTML-Seiten kommen dagegen immer frisch an (no-cache, DYNAMIC).
|
||||
Deshalb genuegt es, in der HTML einen Stempel an die Adressen zu
|
||||
haengen: Neue Seite -> neue Adresse -> Cloudflare kennt sie nicht ->
|
||||
frische Datei. Ohne Cloudflare-Einstellung, ohne Zutun von aussen.
|
||||
|
||||
Der Stempel ist die Minute des Aufrufs. Er wird bei jedem Lauf neu
|
||||
gesetzt, auch wenn schon einer dasteht.
|
||||
|
||||
AUFRUF: node tools/workspace-stempel.mjs
|
||||
Gehoert vor jeden Deploy, der JS oder CSS im Workspace anfasst.
|
||||
===================================================================== */
|
||||
|
||||
import { readdirSync, readFileSync, writeFileSync } from "node:fs";
|
||||
import { join, dirname } from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
const HIER = dirname(fileURLToPath(import.meta.url));
|
||||
const ORDNER = join(HIER, "..", "workspace");
|
||||
|
||||
const d = new Date();
|
||||
const p = (n) => String(n).padStart(2, "0");
|
||||
const STEMPEL = `${d.getFullYear()}${p(d.getMonth() + 1)}${p(d.getDate())}${p(d.getHours())}${p(d.getMinutes())}`;
|
||||
|
||||
/* Trifft href="assets/..." und src="assets/..." -- mit oder ohne
|
||||
vorhandenen Stempel, damit wiederholte Laeufe nicht anhaengen. */
|
||||
const MUSTER = /(href|src)="(assets\/(?:css|js)\/[A-Za-z0-9._-]+)(?:\?v=\d+)?"/g;
|
||||
|
||||
let dateien = 0;
|
||||
let stellen = 0;
|
||||
|
||||
for (const name of readdirSync(ORDNER)) {
|
||||
if (!name.endsWith(".html")) continue;
|
||||
const pfad = join(ORDNER, name);
|
||||
const alt = readFileSync(pfad, "utf8");
|
||||
let n = 0;
|
||||
const neu = alt.replace(MUSTER, (_, was, datei) => {
|
||||
n += 1;
|
||||
return `${was}="${datei}?v=${STEMPEL}"`;
|
||||
});
|
||||
if (neu !== alt) {
|
||||
writeFileSync(pfad, neu);
|
||||
dateien += 1;
|
||||
stellen += n;
|
||||
console.log(` ${name.padEnd(20)} ${n} Verweise`);
|
||||
}
|
||||
}
|
||||
|
||||
console.log(`\nStempel ${STEMPEL} auf ${stellen} Verweise in ${dateien} Dateien gesetzt.`);
|
||||
if (!dateien) console.log("Nichts geaendert -- stimmt das Muster noch?");
|
||||
@@ -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" />
|
||||
<link rel="stylesheet" href="assets/css/start.css" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202608281303" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202608281303" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202608281303" />
|
||||
</head>
|
||||
|
||||
<body class="start">
|
||||
@@ -134,7 +134,7 @@
|
||||
</form>
|
||||
</dialog>
|
||||
|
||||
<script src="assets/js/kopf.js" defer></script>
|
||||
<script src="assets/js/aufgaben.js" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202608281303" defer></script>
|
||||
<script src="assets/js/aufgaben.js?v=202608281303" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -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" />
|
||||
<link rel="stylesheet" href="assets/css/start.css" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css" />
|
||||
<link rel="stylesheet" href="assets/css/bereich.css" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202608281303" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202608281303" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202608281303" />
|
||||
<link rel="stylesheet" href="assets/css/bereich.css?v=202608281303" />
|
||||
</head>
|
||||
|
||||
<body class="start">
|
||||
@@ -98,7 +98,7 @@
|
||||
|
||||
</main>
|
||||
|
||||
<script src="assets/js/kopf.js" defer></script>
|
||||
<script src="assets/js/bereich.js" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202608281303" defer></script>
|
||||
<script src="assets/js/bereich.js?v=202608281303" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -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" />
|
||||
<link rel="stylesheet" href="assets/css/start.css" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css" />
|
||||
<link rel="stylesheet" href="assets/css/calls.css" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202608281303" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202608281303" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202608281303" />
|
||||
<link rel="stylesheet" href="assets/css/calls.css?v=202608281303" />
|
||||
</head>
|
||||
|
||||
<body class="start">
|
||||
@@ -49,7 +49,7 @@
|
||||
|
||||
</main>
|
||||
|
||||
<script src="assets/js/kopf.js" defer></script>
|
||||
<script src="assets/js/calls.js" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202608281303" defer></script>
|
||||
<script src="assets/js/calls.js?v=202608281303" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -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" />
|
||||
<link rel="stylesheet" href="assets/css/start.css" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css" />
|
||||
<link rel="stylesheet" href="assets/css/dateien.css" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202608281303" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202608281303" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202608281303" />
|
||||
<link rel="stylesheet" href="assets/css/dateien.css?v=202608281303" />
|
||||
</head>
|
||||
|
||||
<body class="start">
|
||||
@@ -79,7 +79,7 @@
|
||||
|
||||
</main>
|
||||
|
||||
<script src="assets/js/kopf.js" defer></script>
|
||||
<script src="assets/js/dateien.js" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202608281303" defer></script>
|
||||
<script src="assets/js/dateien.js?v=202608281303" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -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" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202608281303" />
|
||||
</head>
|
||||
|
||||
<body class="gate">
|
||||
@@ -130,6 +130,6 @@
|
||||
</p>
|
||||
</main>
|
||||
|
||||
<script src="assets/js/gate.js" defer></script>
|
||||
<script src="assets/js/gate.js?v=202608281303" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -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" />
|
||||
<link rel="stylesheet" href="assets/css/start.css" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css" />
|
||||
<link rel="stylesheet" href="assets/css/kalender.css" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202608281303" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202608281303" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202608281303" />
|
||||
<link rel="stylesheet" href="assets/css/kalender.css?v=202608281303" />
|
||||
</head>
|
||||
|
||||
<body class="start">
|
||||
@@ -94,7 +94,7 @@
|
||||
|
||||
</main>
|
||||
|
||||
<script src="assets/js/kopf.js" defer></script>
|
||||
<script src="assets/js/kalender.js" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202608281303" defer></script>
|
||||
<script src="assets/js/kalender.js?v=202608281303" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -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" />
|
||||
<link rel="stylesheet" href="assets/css/start.css" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css" />
|
||||
<link rel="stylesheet" href="assets/css/personen.css" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202608281303" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202608281303" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202608281303" />
|
||||
<link rel="stylesheet" href="assets/css/personen.css?v=202608281303" />
|
||||
</head>
|
||||
|
||||
<body class="start">
|
||||
@@ -87,7 +87,7 @@
|
||||
|
||||
</main>
|
||||
|
||||
<script src="assets/js/kopf.js" defer></script>
|
||||
<script src="assets/js/personen.js" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202608281303" defer></script>
|
||||
<script src="assets/js/personen.js?v=202608281303" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -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" />
|
||||
<link rel="stylesheet" href="assets/css/start.css" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css" />
|
||||
<link rel="stylesheet" href="assets/css/profil.css" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202608281303" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202608281303" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202608281303" />
|
||||
<link rel="stylesheet" href="assets/css/profil.css?v=202608281303" />
|
||||
</head>
|
||||
|
||||
<body class="start">
|
||||
@@ -127,7 +127,7 @@
|
||||
|
||||
</main>
|
||||
|
||||
<script src="assets/js/kopf.js" defer></script>
|
||||
<script src="assets/js/profil.js" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202608281303" defer></script>
|
||||
<script src="assets/js/profil.js?v=202608281303" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -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" />
|
||||
<link rel="stylesheet" href="assets/css/start.css" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css" />
|
||||
<link rel="stylesheet" href="assets/css/report.css" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202608281303" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202608281303" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202608281303" />
|
||||
<link rel="stylesheet" href="assets/css/report.css?v=202608281303" />
|
||||
</head>
|
||||
|
||||
<body class="start">
|
||||
@@ -86,7 +86,7 @@
|
||||
|
||||
</main>
|
||||
|
||||
<script src="assets/js/kopf.js" defer></script>
|
||||
<script src="assets/js/report.js" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202608281303" defer></script>
|
||||
<script src="assets/js/report.js?v=202608281303" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -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" />
|
||||
<link rel="stylesheet" href="assets/css/start.css" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css" />
|
||||
<link rel="stylesheet" href="assets/css/scouting.css" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202608281303" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202608281303" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202608281303" />
|
||||
<link rel="stylesheet" href="assets/css/scouting.css?v=202608281303" />
|
||||
</head>
|
||||
|
||||
<body class="start">
|
||||
@@ -124,7 +124,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="assets/js/kopf.js" defer></script>
|
||||
<script src="assets/js/scouting.js" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202608281303" defer></script>
|
||||
<script src="assets/js/scouting.js?v=202608281303" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -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" />
|
||||
<link rel="stylesheet" href="assets/css/start.css" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202608281303" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202608281303" />
|
||||
</head>
|
||||
|
||||
<body class="start">
|
||||
@@ -56,7 +56,7 @@
|
||||
</p>
|
||||
</main>
|
||||
|
||||
<script src="assets/js/kopf.js" defer></script>
|
||||
<script src="assets/js/start.js" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202608281303" defer></script>
|
||||
<script src="assets/js/start.js?v=202608281303" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -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" />
|
||||
<link rel="stylesheet" href="assets/css/start.css" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css" />
|
||||
<link rel="stylesheet" href="assets/css/startcheck.css" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202608281303" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202608281303" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202608281303" />
|
||||
<link rel="stylesheet" href="assets/css/startcheck.css?v=202608281303" />
|
||||
</head>
|
||||
|
||||
<body class="start">
|
||||
@@ -65,7 +65,7 @@
|
||||
|
||||
</main>
|
||||
|
||||
<script src="assets/js/kopf.js" defer></script>
|
||||
<script src="assets/js/startcheck.js" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202608281303" defer></script>
|
||||
<script src="assets/js/startcheck.js?v=202608281303" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -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" />
|
||||
<link rel="stylesheet" href="assets/css/start.css" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css" />
|
||||
<link rel="stylesheet" href="assets/css/wissen.css" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202608281303" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202608281303" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202608281303" />
|
||||
<link rel="stylesheet" href="assets/css/wissen.css?v=202608281303" />
|
||||
</head>
|
||||
|
||||
<body class="start">
|
||||
@@ -145,7 +145,7 @@
|
||||
|
||||
</main>
|
||||
|
||||
<script src="assets/js/kopf.js" defer></script>
|
||||
<script src="assets/js/wissen.js" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202608281303" defer></script>
|
||||
<script src="assets/js/wissen.js?v=202608281303" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Reference in New Issue
Block a user