Die Modi-App bekommt ihre eigene Adresse: crew.dogfather-universe.com
Filipe: "ich will dass es eine eigene app wird" -- und dann "subdomain
machen jetzt sofort". Name von ihm gewaehlt: crew. (unauffaellig).
Auf einem Ursprung laesst sich genau EINE App installieren (w3c/manifest
Nr. 1180). Derselbe Grund, aus dem der Workspace am 06.09. umgezogen ist.
Getrennt wird ueber den HOSTNAMEN: ein Dienst, eine Datenbank, ein
Verzeichnis wie bisher.
DIE REGEL STEHT AN EINER STELLE
crew-adresse.js beantwortet: Darf diese Rolle auf dieser Adresse
angemeldet sein? Eingehaengt in sitzungLesen() -- durch die Funktion geht
jedes der 26 Fachmodule und jede Seitenschranke. Eine Middleware daneben
kann man in einem neuen Modul vergessen, und ein vergessener Rechteschutz
faellt nicht auf, weil dann alles geht.
crew. nur Modis; jeder andere bekommt 401 wie bei einem Tippfehler
workspace. keine Modis mehr; sie bekommen den Weg zur neuen Adresse
localhost UNVERAENDERT
Die dritte Zeile ist der Kern: Die Regel ist eine AUFZAEHLUNG der drei
echten Adressen, nicht "alles ausser crew". Sonst wuerden sieben andere
Pruefdateien ab sofort messen, dass ein Modi nirgends hereinkommt -- gruen,
weil sie nichts mehr finden.
ZWEI APPS, ZWEI NAMEN
Beide Adressen liefern dieselben HTML-Dateien. Auf crew. wird
/workspace/app.webmanifest serverseitig auf crew.webmanifest umgebogen --
so braucht keine der 30 Seiten eine zweite Zeile, die man bei der 31.
vergisst. "Team Dogi" statt "Creator Workspace", eigenes Zeichen:
derselbe Husky, aber ohne Chili (die gehoert zu Spicy Media, nicht zu
ihnen) und im Modi-Ton #5f8a9f.
tools/crew-symbol.mjs erzeugt die sechs Symbole und bricht ab, wenn sie
sich zu weniger als 10 % vom Workspace-Symbol unterscheiden. Gemessen:
43 bis 53 %.
GEMESSEN
pruef-crew-adresse 74 Pruefungen, 0 Fehler -- mit Gegenprobe: der Modi
bekommt in der Datenbank die Rolle 'manager', danach
MUSS dieselbe Sitzung auf crew. ins Leere laufen
pruef-rollen 245, unveraendert (die Regel ist lokal wirkungslos)
pruef-workspace-umzug 31 Faelle
pruef-zwischenspeicher 21 -- ein Stempel im Haus, jetzt ueber 23 Dateien
Der Server kennt die Adresse damit. DNS und Caddy bleiben Filipes Schritt.
Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
Binary file not shown.
|
After Width: | Height: | Size: 30 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 31 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 34 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 1.8 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 171 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 187 KiB |
@@ -157,6 +157,28 @@ export function crewWeiche(req, res, next) {
|
||||
|
||||
res.setHeader("X-Robots-Tag", "noindex, nofollow");
|
||||
|
||||
/* DAS MANIFEST MACHT DIE APP (10.09.2026).
|
||||
|
||||
Beide Adressen liefern dieselben HTML-Dateien aus -- in jeder steht
|
||||
<link rel="manifest" href="/workspace/app.webmanifest">. Auf der
|
||||
Modi-Adresse soll dahinter aber ein anderes Manifest stehen: eigener
|
||||
Name ("Team Dogi"), eigenes Symbol. Sonst hiesse die App auf dem
|
||||
Startbildschirm eines Modis "Creator Workspace" und traege das
|
||||
Zeichen mit der Chili -- die zu Spicy Media gehoert, nicht zu ihnen.
|
||||
|
||||
WARUM DIE ADRESSE UMGEBOGEN WIRD, STATT IN 30 HTML-DATEIEN EINE
|
||||
ZWEITE ZEILE ZU SETZEN: Eine zweite Zeile muesste jemand bei jeder
|
||||
neuen Seite mitschreiben, und wer sie vergisst, bekommt eine Seite,
|
||||
die still zur falschen App gehoert. Hier steht es einmal.
|
||||
|
||||
ZWISCHENSPEICHER: Cloudflare nimmt den Hostnamen in den Schluessel
|
||||
auf, die beiden Antworten koennen sich also nicht vermischen. Der
|
||||
Browser ohnehin nicht -- fuer ihn sind es zwei Ursprunge. */
|
||||
if (req.path === "/workspace/app.webmanifest") {
|
||||
req.url = "/workspace/crew.webmanifest";
|
||||
return next();
|
||||
}
|
||||
|
||||
if (istWorkspacePfad(req.path) || req.path.startsWith("/assets/")) return next();
|
||||
return res.redirect(302, "/workspace/");
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ import { fileURLToPath } from "node:url";
|
||||
|
||||
import { gateMiddleware } from "./gate.js";
|
||||
import { workspaceUmzug } from "./workspace-umzug.js";
|
||||
import { crewWeiche } from "./crew-adresse.js";
|
||||
import { webdesignGate } from "./webdesign-gate.js";
|
||||
import { inhaltsrichtlinie } from "./inhaltsrichtlinie.js";
|
||||
import { workspaceRouter, sichtSetzen } from "./workspace.js";
|
||||
@@ -112,6 +113,20 @@ app.use((req, res, next) => {
|
||||
unter der alten Adresse nicht mehr erscheint. */
|
||||
app.use(workspaceUmzug);
|
||||
|
||||
/* Die eigene Adresse der Modi-App (10.09.2026): crew.dogfather-universe.com.
|
||||
Begruendung und die drei Fallstricke stehen in crew-adresse.js -- sie sind
|
||||
dort als eigene Funktionen pruefbar (pruef-crew-adresse.mjs).
|
||||
|
||||
Steht direkt neben dem Workspace-Umzug und aus demselben Grund FRUEH: Der
|
||||
Kopf "nicht indizieren" soll auch fuer Manifest, Symbole und die
|
||||
Zugangswand gelten, nicht erst ab der ersten Schranke.
|
||||
|
||||
WICHTIG: Diese Weiche vergibt KEINE Rechte und nimmt keine. Wer auf
|
||||
welcher Adresse angemeldet sein darf, entscheidet sitzungLesen() in
|
||||
workspace.js -- an genau einer Stelle, durch die alle 26 Fachmodule
|
||||
gehen. Zwei Orte fuer dieselbe Regel waeren einer zu viel. */
|
||||
app.use(crewWeiche);
|
||||
|
||||
/* Inhaltsrichtlinie (26.08.2026). Steht bewusst HIER -- vor jeder
|
||||
Schranke und vor express.static: Auch die Zugangswand und die
|
||||
404-Seite sollen sie bekommen, denn gerade dort werden Eingaben
|
||||
|
||||
@@ -0,0 +1,335 @@
|
||||
/* =====================================================================
|
||||
DIE EIGENE ADRESSE DER MODI-APP
|
||||
|
||||
Geprueft wird nicht "die neue Adresse geht", sondern die vier Fragen,
|
||||
an denen dieser Umbau scheitern kann:
|
||||
|
||||
1. KOMMT EIN MODI AUF crew. HEREIN -- und sonst niemand?
|
||||
2. IST DER WORKSPACE UNVERAENDERT? Derselbe Dienst bedient beide
|
||||
Adressen. Wer nicht auf den Hostnamen prueft, sperrt mit einer
|
||||
einzigen Zeile alle Creator, Scouts und Manager aus, sofort nach
|
||||
dem Neustart und ohne Vorwarnung.
|
||||
3. BLEIBEN DIE PRUEFADRESSEN UNBERUEHRT? Waere die Regel "alles
|
||||
ausser crew", dann wuerden ab sofort sieben andere Pruefdateien
|
||||
messen, dass ein Modi nirgends hereinkommt -- gruen, weil sie
|
||||
nichts mehr finden. Ein Lauf, der nichts mehr prueft, ist
|
||||
schlimmer als ein roter.
|
||||
4. HEISST DIE APP AUF DEM HANDY DES MODIS ANDERS? Sonst war der
|
||||
ganze Umzug umsonst.
|
||||
|
||||
MIT GEGENPROBE. Ganz unten bekommt der Modi in der Datenbank eine
|
||||
andere Rolle -- danach MUSS ihn dieselbe Sitzung auf crew. nicht mehr
|
||||
hereinlassen. Ohne diesen Teil waere nicht bewiesen, dass die
|
||||
Pruefung ueberhaupt anschlagen kann.
|
||||
===================================================================== */
|
||||
import { mkdtempSync, rmSync } from "node:fs";
|
||||
import { tmpdir } from "node:os";
|
||||
import { join } from "node:path";
|
||||
import { request } from "node:http";
|
||||
|
||||
const ordner = mkdtempSync(join(tmpdir(), "ws-crew-"));
|
||||
process.env.WORKSPACE_DB = join(ordner, "workspace.db");
|
||||
process.env.PORT = "4371";
|
||||
process.env.SITE_ACCESS_SECRET = "lokaler-test";
|
||||
process.env.SITE_ACCESS_CODE_DOGI = "DOGI-TEST-1";
|
||||
process.env.SITE_PUBLIC_LAUNCH_AT = "2020-01-01T00:00:00+01:00";
|
||||
|
||||
const express = (await import("express")).default;
|
||||
const ec = express.response.cookie;
|
||||
express.response.cookie = function (n, w, o) { return ec.call(this, n, w, { ...(o || {}), secure: false }); };
|
||||
import { notbremse } from "./helfer-notbremse.mjs";
|
||||
await import("./index.js");
|
||||
notbremse(180_000, "pruef-crew-adresse");
|
||||
await new Promise((r) => setTimeout(r, 700));
|
||||
|
||||
let fehler = 0;
|
||||
let geprueft = 0;
|
||||
const ok = (b, t) => { geprueft++; console.log((b ? " ok " : " FEHL ") + t); if (!b) fehler++; };
|
||||
let offen = 0;
|
||||
const unklar = (t) => { offen++; console.log(" -- " + t); };
|
||||
|
||||
/* ---------------------------------------------------------------------
|
||||
Ein Aufruf mit frei waehlbarem Host-Kopf.
|
||||
|
||||
Bewusst node:http und nicht fetch: `Host` gilt in der Fetch-Norm als
|
||||
verbotener Kopf, und ob undici das durchlaesst, ist eine Annahme, auf
|
||||
die diese ganze Pruefung nicht gebaut sein soll. Hier wird der Kopf
|
||||
nachweislich gesetzt.
|
||||
------------------------------------------------------------------ */
|
||||
function hole(pfad, { host, methode = "GET", rumpf = null, keks = "" } = {}) {
|
||||
return new Promise((fertig, schief) => {
|
||||
const kopf = { Host: host };
|
||||
if (rumpf) { kopf["Content-Type"] = "application/json"; kopf["Content-Length"] = Buffer.byteLength(rumpf); }
|
||||
if (keks) kopf.Cookie = keks;
|
||||
const a = request({ host: "127.0.0.1", port: 4371, path: pfad, method: methode, headers: kopf },
|
||||
(antwort) => {
|
||||
let text = "";
|
||||
antwort.setEncoding("utf8");
|
||||
antwort.on("data", (t) => { text += t; });
|
||||
antwort.on("end", () => fertig({
|
||||
status: antwort.statusCode,
|
||||
kopf: antwort.headers,
|
||||
text,
|
||||
keks: (antwort.headers["set-cookie"] || [])
|
||||
.map((z) => z.split(";")[0]).join("; "),
|
||||
}));
|
||||
});
|
||||
a.on("error", schief);
|
||||
a.setTimeout(15_000, () => { a.destroy(new Error("Zeitueberschreitung")); });
|
||||
if (rumpf) a.write(rumpf);
|
||||
a.end();
|
||||
});
|
||||
}
|
||||
|
||||
const anmelden = (host, rolle, code) =>
|
||||
hole("/workspace/api/anmelden", { host, methode: "POST", rumpf: JSON.stringify({ rolle, code }) });
|
||||
|
||||
/* ---------- Personen anlegen ---------------------------------------- */
|
||||
const { DatabaseSync } = await import("node:sqlite");
|
||||
const { scryptSync, randomBytes, createHmac } = await import("node:crypto");
|
||||
const d = new DatabaseSync(process.env.WORKSPACE_DB);
|
||||
const jetzt = new Date().toISOString();
|
||||
|
||||
const schluessel = randomBytes(32).toString("hex");
|
||||
d.prepare("INSERT INTO einstellungen (schluessel, wert, geaendert, von)"
|
||||
+ " VALUES (?,?,?,NULL) ON CONFLICT(schluessel) DO NOTHING")
|
||||
.run("code_kennung_schluessel", schluessel, jetzt);
|
||||
const kennung = (code) => createHmac("sha256", schluessel).update(code).digest("hex");
|
||||
|
||||
function anlegen(name, rolle, code) {
|
||||
const salt = randomBytes(16).toString("hex");
|
||||
const hash = scryptSync(code, salt, 64,
|
||||
{ N: 32768, r: 8, p: 1, maxmem: 96 * 1024 * 1024 }).toString("hex");
|
||||
d.prepare("INSERT INTO personen"
|
||||
+ " (name, rolle, code_hash, code_salt, code_n, code_kennung, aktiv, erstellt)"
|
||||
+ " VALUES (?,?,?,?,?,?,1,?)")
|
||||
.run(name, rolle, hash, salt, 32768, kennung(code), jetzt);
|
||||
return d.prepare("SELECT last_insert_rowid() AS id").get().id;
|
||||
}
|
||||
|
||||
anlegen("Filipe", "admin", "CODE-DOGI-0001");
|
||||
anlegen("Schulle", "manager", "CODE-MAN-0001");
|
||||
const idModi = anlegen("Marina", "modi", "CODE-MODI-0001");
|
||||
|
||||
const CREW = "crew.dogfather-universe.com";
|
||||
const WS = "workspace.dogfather-universe.com";
|
||||
const HAUPT = "dogfather-universe.com";
|
||||
const LOKAL = "127.0.0.1:4371";
|
||||
|
||||
/* =====================================================================
|
||||
TEIL A -- DIE REGELN, OHNE SERVER
|
||||
|
||||
Sie stehen als eigene Funktionen in crew-adresse.js, damit genau das
|
||||
hier moeglich ist: die Entscheidung pruefen, nicht ihre Auswirkung.
|
||||
===================================================================== */
|
||||
console.log("\n--- Die Regeln selbst ---");
|
||||
const { istCrewAdresse, istOhneModiAdresse, istWorkspacePfad, sitzungPasstZurAdresse, CREW_ADRESSE }
|
||||
= await import("./crew-adresse.js");
|
||||
|
||||
ok(istCrewAdresse(CREW) === true, "crew.dogfather-universe.com ist die Modi-Adresse");
|
||||
ok(istCrewAdresse("CREW.Dogfather-Universe.COM") === true, "Gross/klein macht keinen Unterschied");
|
||||
ok(istCrewAdresse(CREW + ":443") === true, "ein Port angehaengt macht keinen Unterschied");
|
||||
ok(istCrewAdresse(WS) === false, "workspace. ist NICHT die Modi-Adresse");
|
||||
ok(istCrewAdresse(HAUPT) === false, "die Hauptseite ist NICHT die Modi-Adresse");
|
||||
ok(istCrewAdresse("crew.dogfather-universe.com.boese.de") === false,
|
||||
"eine fremde Domain mit dem Namen darin zaehlt nicht");
|
||||
ok(istCrewAdresse("") === false && istCrewAdresse(null) === false,
|
||||
"leer und fehlend sind nicht die Modi-Adresse");
|
||||
|
||||
ok(istOhneModiAdresse(WS) === true, "auf workspace. haben Modis nichts verloren");
|
||||
ok(istOhneModiAdresse(HAUPT) === true, "auf der Hauptseite ebenfalls nicht");
|
||||
ok(istOhneModiAdresse("www." + HAUPT) === true, "auf www. ebenfalls nicht");
|
||||
ok(istOhneModiAdresse(CREW) === false, "crew. gehoert NICHT dazu (Falle 1)");
|
||||
ok(istOhneModiAdresse(LOKAL) === false, "127.0.0.1 gehoert NICHT dazu (Falle 2)");
|
||||
ok(istOhneModiAdresse("localhost:4100") === false, "localhost gehoert NICHT dazu (Falle 2)");
|
||||
|
||||
/* Die beiden Mengen duerfen sich nirgends beruehren. Ein Muster statt
|
||||
einer Liste haette hier sofort angeschlagen. */
|
||||
const alleAdressen = [CREW, WS, HAUPT, "www." + HAUPT, LOKAL, "localhost:4100", "git." + HAUPT];
|
||||
ok(alleAdressen.every((h) => !(istCrewAdresse(h) && istOhneModiAdresse(h))),
|
||||
"keine Adresse liegt in beiden Mengen");
|
||||
|
||||
ok(istWorkspacePfad("/workspace") === true, "/workspace gehoert dazu");
|
||||
ok(istWorkspacePfad("/workspace/start.html") === true, "/workspace/start.html gehoert dazu");
|
||||
ok(istWorkspacePfad("/workspaceXYZ") === false, "/workspaceXYZ gehoert NICHT dazu (Falle 3)");
|
||||
ok(istWorkspacePfad("/workspace-alt") === false, "/workspace-alt gehoert NICHT dazu (Falle 3)");
|
||||
ok(istWorkspacePfad("/assets/img/x.png") === false, "/assets liegt ausserhalb");
|
||||
|
||||
const ROLLEN = ["admin", "spicy", "manager", "scout", "creator", "modi"];
|
||||
let passtWahr = 0, passtFalsch = 0;
|
||||
for (const r of ROLLEN) {
|
||||
const aufCrew = sitzungPasstZurAdresse(r, CREW);
|
||||
const aufWs = sitzungPasstZurAdresse(r, WS);
|
||||
const aufLokal = sitzungPasstZurAdresse(r, LOKAL);
|
||||
ok(aufCrew === (r === "modi"), `crew.: ${r} ${r === "modi" ? "darf" : "darf nicht"}`);
|
||||
ok(aufWs === (r !== "modi"), `workspace.: ${r} ${r !== "modi" ? "darf" : "darf nicht"}`);
|
||||
ok(aufLokal === true, `127.0.0.1: ${r} unveraendert erlaubt`);
|
||||
for (const w of [aufCrew, aufWs, aufLokal]) { if (w) passtWahr++; else passtFalsch++; }
|
||||
}
|
||||
/* Die Gegenprobe zur Regel selbst: Eine Funktion, die immer `true`
|
||||
liefert, haette alle 18 Zeilen darueber ebenfalls teilweise bestanden.
|
||||
Beide Antworten muessen vorkommen. */
|
||||
ok(passtWahr > 0 && passtFalsch > 0,
|
||||
`die Regel unterscheidet wirklich (${passtWahr}x ja, ${passtFalsch}x nein)`);
|
||||
|
||||
/* =====================================================================
|
||||
TEIL B -- AM LAUFENDEN SERVER
|
||||
===================================================================== */
|
||||
console.log("\n--- Anmeldung je Adresse ---");
|
||||
|
||||
const modiAufCrew = await anmelden(CREW, "modi", "CODE-MODI-0001");
|
||||
ok(modiAufCrew.status === 200, "crew.: der Modi kommt herein");
|
||||
let zielModi = {};
|
||||
try { zielModi = JSON.parse(modiAufCrew.text); } catch { /* bleibt leer */ }
|
||||
ok(zielModi.weiter === "/workspace/start.html", "crew.: er landet auf seiner Startseite");
|
||||
ok(modiAufCrew.keks.includes("dfw_sitzung"), "crew.: er bekommt eine Sitzung");
|
||||
|
||||
/* Der Code des Modis wird bewusst mit der Kachel 'creator' geschickt --
|
||||
genau so kommt er auch aus der Zugangswand, denn eine Modi-Kachel
|
||||
gibt es nicht. */
|
||||
const modiOhneKachel = await anmelden(CREW, "creator", "CODE-MODI-0001");
|
||||
ok(modiOhneKachel.status === 200, "crew.: die gewaehlte Kachel ist egal (stiller Zugang)");
|
||||
|
||||
const managerAufCrew = await anmelden(CREW, "manager", "CODE-MAN-0001");
|
||||
ok(managerAufCrew.status === 401, "crew.: ein Manager mit gueltigem Code kommt NICHT herein");
|
||||
ok(/ungueltig/.test(managerAufCrew.text),
|
||||
"crew.: er bekommt dieselbe Antwort wie bei einem Tippfehler");
|
||||
ok(!managerAufCrew.keks.includes("dfw_sitzung"), "crew.: und keine Sitzung");
|
||||
|
||||
const adminAufCrew = await anmelden(CREW, "admin", "CODE-DOGI-0001");
|
||||
ok(adminAufCrew.status === 401, "crew.: auch DogFather selbst kommt dort nicht herein");
|
||||
|
||||
const modiAufWs = await anmelden(WS, "creator", "CODE-MODI-0001");
|
||||
ok(modiAufWs.status === 200, "workspace.: der Modi bekommt eine Antwort");
|
||||
let zielWs = {};
|
||||
try { zielWs = JSON.parse(modiAufWs.text); } catch { /* bleibt leer */ }
|
||||
ok(zielWs.weiter === `${CREW_ADRESSE}/workspace/start.html`,
|
||||
"workspace.: sie zeigt ihm die neue Adresse");
|
||||
ok(!modiAufWs.keks.includes("dfw_sitzung"),
|
||||
"workspace.: aber KEINE Sitzung -- er meldet sich nur noch auf crew. an");
|
||||
|
||||
/* DER WICHTIGSTE PUNKT DIESER DATEI. Derselbe Dienst bedient beide
|
||||
Adressen; ein Denkfehler hier sperrt das ganze Team aus. */
|
||||
const managerAufWs = await anmelden(WS, "manager", "CODE-MAN-0001");
|
||||
ok(managerAufWs.status === 200 && managerAufWs.keks.includes("dfw_sitzung"),
|
||||
"workspace.: der Manager kommt herein wie immer -- der Workspace ist unberuehrt");
|
||||
|
||||
const managerLokal = await anmelden(LOKAL, "manager", "CODE-MAN-0001");
|
||||
const modiLokal = await anmelden(LOKAL, "creator", "CODE-MODI-0001");
|
||||
ok(managerLokal.status === 200 && managerLokal.keks.includes("dfw_sitzung"),
|
||||
"127.0.0.1: der Manager kommt herein (Pruefadressen unberuehrt)");
|
||||
ok(modiLokal.status === 200 && modiLokal.keks.includes("dfw_sitzung"),
|
||||
"127.0.0.1: der Modi ebenfalls -- die sieben anderen Modi-Pruefungen messen weiter");
|
||||
|
||||
/* ---------------------------------------------------------------------
|
||||
Eine Sitzung gehoert zu genau einer Adresse.
|
||||
------------------------------------------------------------------ */
|
||||
console.log("\n--- Eine Sitzung, eine Adresse ---");
|
||||
const keksModi = modiAufCrew.keks;
|
||||
const keksManager = managerAufWs.keks;
|
||||
|
||||
const modiDaheim = await hole("/workspace/api/ich", { host: CREW, keks: keksModi });
|
||||
ok(modiDaheim.status === 200, "crew.: der angemeldete Modi wird erkannt");
|
||||
ok(/Marina/.test(modiDaheim.text), "crew.: und zwar als er selbst");
|
||||
|
||||
const modiFremd = await hole("/workspace/api/ich", { host: WS, keks: keksModi });
|
||||
ok(modiFremd.status === 401, "workspace.: dieselbe Sitzung zaehlt dort nicht");
|
||||
|
||||
const managerDaheim = await hole("/workspace/api/ich", { host: WS, keks: keksManager });
|
||||
ok(managerDaheim.status === 200, "workspace.: der Manager wird erkannt");
|
||||
|
||||
const managerFremd = await hole("/workspace/api/ich", { host: CREW, keks: keksManager });
|
||||
ok(managerFremd.status === 401, "crew.: seine Sitzung zaehlt dort nicht");
|
||||
|
||||
/* Auch die Seitenschranke, nicht nur die Schnittstelle: Eine Seite, die
|
||||
ausgeliefert wird, waere ein Loch, selbst wenn die Daten dahinter
|
||||
leer blieben. */
|
||||
const seiteFremd = await hole("/workspace/start.html", { host: CREW, keks: keksManager });
|
||||
ok(seiteFremd.status === 302, "crew.: die Startseite schickt den Manager zur Zugangswand");
|
||||
const seiteDaheim = await hole("/workspace/start.html", { host: CREW, keks: keksModi });
|
||||
ok(seiteDaheim.status === 200, "crew.: dem Modi liefert sie die Seite");
|
||||
|
||||
/* ---------------------------------------------------------------------
|
||||
Die Weiche: was es auf crew. ueberhaupt gibt.
|
||||
------------------------------------------------------------------ */
|
||||
console.log("\n--- Die Weiche ---");
|
||||
const wurzel = await hole("/", { host: CREW });
|
||||
ok(wurzel.status === 302 && wurzel.kopf.location === "/workspace/",
|
||||
"crew.: die Hauptseite gibt es dort nicht, es geht zur Zugangswand");
|
||||
const wurzelWs = await hole("/", { host: HAUPT });
|
||||
ok(wurzelWs.status === 200, "die Hauptseite selbst ist unveraendert erreichbar");
|
||||
|
||||
const fastWorkspace = await hole("/workspaceXYZ", { host: CREW });
|
||||
ok(fastWorkspace.status === 302, "crew.: /workspaceXYZ gehoert nicht dazu (Falle 3)");
|
||||
|
||||
const robots = await hole("/workspace/", { host: CREW });
|
||||
ok(String(robots.kopf["x-robots-tag"] || "").includes("noindex"),
|
||||
"crew.: nicht indizieren -- die Adresse soll in keiner Suchmaschine stehen");
|
||||
const robotsWs = await hole("/workspace/", { host: WS });
|
||||
ok(!robotsWs.kopf["x-robots-tag"],
|
||||
"workspace.: dort steht der Kopf NICHT (Gegenprobe zur Weiche)");
|
||||
|
||||
/* ---------------------------------------------------------------------
|
||||
Die App heisst anders.
|
||||
------------------------------------------------------------------ */
|
||||
console.log("\n--- Zwei Apps, zwei Namen ---");
|
||||
const manifestCrew = await hole("/workspace/app.webmanifest", { host: CREW });
|
||||
const manifestWs = await hole("/workspace/app.webmanifest", { host: WS });
|
||||
let mc = {}, mw = {};
|
||||
try { mc = JSON.parse(manifestCrew.text); } catch { /* bleibt leer */ }
|
||||
try { mw = JSON.parse(manifestWs.text); } catch { /* bleibt leer */ }
|
||||
ok(manifestCrew.status === 200 && manifestWs.status === 200,
|
||||
"beide Adressen liefern ein Manifest");
|
||||
ok(mc.name === "Team Dogi", `crew.: die App heisst "Team Dogi" (gelesen: ${mc.name || "?"})`);
|
||||
ok(mw.name === "Creator Workspace — Spicy & Dogi",
|
||||
`workspace.: dort heisst sie unveraendert (gelesen: ${mw.name || "?"})`);
|
||||
ok(Array.isArray(mc.icons) && mc.icons.length > 0
|
||||
&& mc.icons.every((s) => /crew-/.test(s.src)),
|
||||
`crew.: alle ${(mc.icons || []).length} Symbole sind die eigenen`);
|
||||
ok(Array.isArray(mw.icons) && mw.icons.length > 0
|
||||
&& mw.icons.every((s) => /workspace-/.test(s.src)),
|
||||
"workspace.: dort weiterhin die alten");
|
||||
ok(mc.start_url === "/workspace/start.html" && mc.scope === "/workspace/",
|
||||
"crew.: Bereich und Startseite stimmen");
|
||||
|
||||
/* Die Symboldateien muessen es auch wirklich geben -- ein Manifest, das
|
||||
auf nichts zeigt, faellt sonst erst auf dem Startbildschirm auf. */
|
||||
let symboleDa = 0;
|
||||
for (const s of mc.icons || []) {
|
||||
const pfad = String(s.src).split("?")[0];
|
||||
const a = await hole(pfad, { host: CREW });
|
||||
if (a.status === 200) symboleDa++;
|
||||
}
|
||||
ok((mc.icons || []).length > 0 && symboleDa === (mc.icons || []).length,
|
||||
`crew.: alle ${symboleDa} Symbole werden auch ausgeliefert`);
|
||||
|
||||
/* =====================================================================
|
||||
DIE GEGENPROBE
|
||||
|
||||
Bis hierhin koennte alles auch dann gruen sein, wenn die Regel gar
|
||||
nicht auf die Rolle sieht. Deshalb wird sie jetzt ausgehebelt: Der
|
||||
Modi bekommt in der Datenbank die Rolle 'manager'. Seine Sitzung
|
||||
bleibt dieselbe -- aber auf crew. darf sie ab jetzt NICHT mehr
|
||||
gelten.
|
||||
===================================================================== */
|
||||
console.log("\n--- Gegenprobe: die Regel ausgehebelt ---");
|
||||
d.prepare("UPDATE personen SET rolle = 'manager' WHERE id = ?").run(idModi);
|
||||
|
||||
const nachher = await hole("/workspace/api/ich", { host: CREW, keks: keksModi });
|
||||
ok(nachher.status === 401,
|
||||
"aus dem Modi wurde ein Manager -- seine Sitzung gilt auf crew. nicht mehr");
|
||||
const nachherWs = await hole("/workspace/api/ich", { host: WS, keks: keksModi });
|
||||
ok(nachherWs.status === 200,
|
||||
"und dieselbe Sitzung gilt jetzt auf workspace. -- die Regel sieht wirklich auf die Rolle");
|
||||
|
||||
d.prepare("UPDATE personen SET rolle = 'modi' WHERE id = ?").run(idModi);
|
||||
const zurueck = await hole("/workspace/api/ich", { host: CREW, keks: keksModi });
|
||||
ok(zurueck.status === 200, "zurueckgedreht gilt sie wieder");
|
||||
|
||||
/* ---------- Ende ------------------------------------------------------ */
|
||||
d.close();
|
||||
try { rmSync(ordner, { recursive: true, force: true }); } catch { /* Windows haelt die Datei manchmal */ }
|
||||
|
||||
console.log(`\n${fehler === 0 ? "BESTANDEN" : "FEHLGESCHLAGEN"}`
|
||||
+ ` -- ${geprueft} geprueft, ${fehler} Fehler, ${offen} nicht nachsehbar`);
|
||||
process.exit(fehler === 0 ? 0 : 1);
|
||||
+65
-1
@@ -27,6 +27,8 @@ import { dirname, join } from "node:path";
|
||||
import { fileURLToPath, pathToFileURL } from "node:url";
|
||||
import { mkdirSync } from "node:fs";
|
||||
import { createRequire } from "node:module";
|
||||
import { sitzungPasstZurAdresse, CREW_ADRESSE, istOhneModiAdresse, istCrewAdresse }
|
||||
from "./crew-adresse.js";
|
||||
|
||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||
|
||||
@@ -2397,6 +2399,24 @@ export function sitzungLesen(req) {
|
||||
db().prepare("DELETE FROM sitzungen WHERE token_hash = ?").run(tokenHash(token));
|
||||
return null;
|
||||
}
|
||||
/* DIE ADRESSE ENTSCHEIDET MIT (10.09.2026).
|
||||
|
||||
Seit die Modi-App unter crew.dogfather-universe.com laeuft, gehoert
|
||||
jede Sitzung zu genau EINER Adresse: Modis dorthin, alle anderen auf
|
||||
den Workspace. Die Regel steht in crew-adresse.js und ist dort ohne
|
||||
laufenden Server pruefbar.
|
||||
|
||||
WARUM HIER UND NICHT IN DEN MODULEN: Durch diese Funktion geht jedes
|
||||
der 26 Fachmodule und jede Seitenschranke. Eine Middleware daneben
|
||||
koennte man in einem neuen Modul vergessen -- und ein vergessener
|
||||
Rechteschutz faellt nicht auf, weil dann alles geht.
|
||||
|
||||
`null` heisst hier dasselbe wie bei einer abgelaufenen Sitzung:
|
||||
nicht angemeldet. Die Seitenschranke schickt zur Zugangswand, die
|
||||
Schnittstellen antworten mit 401. Beides ist genau richtig -- auf
|
||||
dieser Adresse ist diese Person schlicht niemand. */
|
||||
if (!sitzungPasstZurAdresse(reihe.rolle, req.get?.("host"))) return null;
|
||||
|
||||
return { id: reihe.id, name: reihe.name, rolle: reihe.rolle };
|
||||
} catch { return null; }
|
||||
}
|
||||
@@ -2697,6 +2717,33 @@ workspaceRouter.post("/workspace/api/anmelden", (req, res) => {
|
||||
WEN man pruefen soll -- danach entscheidet scrypt wie ueberall. */
|
||||
const still = codeBrauchbar ? stillerZugang(code) : null;
|
||||
if (still) {
|
||||
/* AUF DER ALTEN ADRESSE GIBT ES KEINE SITZUNG MEHR (10.09.2026).
|
||||
|
||||
Die Modi-App hat seit heute eine eigene Adresse. Wer seinen Code
|
||||
noch auf workspace.dogfather-universe.com eintippt, bekommt hier
|
||||
den Weg dorthin -- und ausdruecklich KEINE Anmeldung: zwei
|
||||
Sitzungen auf zwei Adressen waeren zwei Apps auf dem
|
||||
Startbildschirm und eine zweite Spur genau dort, wo alle anderen
|
||||
arbeiten.
|
||||
|
||||
DASS DIE ANTWORT DIE NEUE ADRESSE NENNT, IST KEIN LECK. Sie
|
||||
kommt nur nach Eingabe eines gueltigen Modi-Codes; wer den hat,
|
||||
ist Modi. Fuer jeden anderen -- auch fuer einen Manager mit
|
||||
gueltigem eigenen Code -- veraendert sich hier gar nichts.
|
||||
|
||||
Kein Eintrag in `versuche`: Der Code war richtig. Ein Modi, der
|
||||
die alte Verknuepfung auf dem Handy hat, wuerde sich sonst mit
|
||||
jedem Versuch selbst aussperren. */
|
||||
if (istOhneModiAdresse(req.get("host"))) {
|
||||
protokolliere("anmeldung_alte_adresse", {
|
||||
personId: still.id, rolle: still.rolle, ip, detail: still.name,
|
||||
});
|
||||
return res.json({
|
||||
weiter: `${CREW_ADRESSE}/workspace/start.html`,
|
||||
name: still.name, rolle: still.rolle, umgezogen: true,
|
||||
});
|
||||
}
|
||||
|
||||
db().prepare("DELETE FROM versuche WHERE ip = ?").run(ip);
|
||||
db().prepare("UPDATE personen SET letzter_login = ? WHERE id = ?").run(jetzt(), still.id);
|
||||
sitzungSetzen(res, still, req);
|
||||
@@ -2711,7 +2758,24 @@ workspaceRouter.post("/workspace/api/anmelden", (req, res) => {
|
||||
/* Ab hier der gewohnte Weg -- unveraendert. 'modi' ist keine
|
||||
Kachel-Rolle und faellt deshalb in dieselbe Antwort wie eine
|
||||
erfundene. */
|
||||
if (!ROLLEN_KACHEL.has(rolle) || !codeBrauchbar) {
|
||||
/* AUF DER MODI-ADRESSE ENDET DER GEWOHNTE WEG HIER (10.09.2026).
|
||||
|
||||
crew.dogfather-universe.com ist nicht die zweite Tuer in den
|
||||
Workspace. Wer sie errät und seinen eigenen Code eintippt, soll
|
||||
genau das erleben, was er bei einem Tippfehler erlebt: Die
|
||||
Zugangswand sieht aus wie immer, der Code stimmt nicht. Wuerde er
|
||||
stattdessen hereinkommen, wuesste er im selben Moment, dass es
|
||||
hier noch etwas gibt.
|
||||
|
||||
BEWUSST IN DIESELBE BEDINGUNG GESCHRIEBEN und nicht als eigener
|
||||
Block davor: So ist es nicht nur dieselbe ANTWORT, sondern
|
||||
derselbe Weg -- ein Eintrag in `versuche`, kein scrypt-Durchlauf,
|
||||
401 "ungueltig". Ein eigener Block waere messbar schneller oder
|
||||
langsamer gewesen als der Weg daneben, und genau daran erkennt
|
||||
man eine Sonderbehandlung. */
|
||||
const fremdAufCrew = istCrewAdresse(req.get("host"));
|
||||
|
||||
if (!ROLLEN_KACHEL.has(rolle) || !codeBrauchbar || fremdAufCrew) {
|
||||
db().prepare("INSERT INTO versuche (ip, zeitpunkt) VALUES (?,?)").run(ip, jetzt());
|
||||
return res.status(401).json({ fehler: "ungueltig" });
|
||||
}
|
||||
|
||||
@@ -0,0 +1,260 @@
|
||||
/* ===================================================================
|
||||
Das App-Symbol der Modi-App ("Team Dogi", 10.09.2026).
|
||||
|
||||
DER HUSKY IST DERSELBE. Filipe am 09.09.2026 zum Rollenbild:
|
||||
"das modi symbol soll das gleiche sein wie bei dogfather." Das gilt
|
||||
hier genauso -- es ist dieselbe Vorlage, dieselbe Technik.
|
||||
|
||||
DIE CHILI IST ES NICHT, UND ZWAR ABSICHTLICH. Sie steht fuer Spicy
|
||||
Media. Die Modis gehoeren nicht dorthin; ihre Marke heisst "Team
|
||||
Dogi". Eine Chili auf ihrem Startbildschirm waere kein Schmuck,
|
||||
sondern eine falsche Aussage. An ihre Stelle tritt der Ton, der auch
|
||||
ihre Kacheln traegt: das gedeckte Stahlblau #5f8a9f.
|
||||
|
||||
WARUM EINE EIGENE DATEI UND KEIN SCHALTER IN workspace-symbol.mjs:
|
||||
Jenes Werkzeug PRUEFT, dass die Chili in jeder Fassung sichtbar ist.
|
||||
Ein Schalter haette diese Pruefung fuer den halben Aufruf abschalten
|
||||
muessen -- und eine Pruefung, die man abschalten kann, ist bald
|
||||
abgeschaltet. Zwei Bilder mit zwei Regeln bekommen zwei Werkzeuge,
|
||||
die jeweils ihre eigenen Regeln durchsetzen.
|
||||
|
||||
DIE DRITTE GEGENPROBE, DIE ES DORT NICHT GIBT: Am Ende wird das
|
||||
erzeugte Symbol gegen das des Workspace gehalten. Waeren beide
|
||||
gleich, haette der ganze Umzug seinen Sinn verloren -- zwei Apps mit
|
||||
demselben Zeichen sind auf einem Startbildschirm dasselbe Symbol
|
||||
zweimal. Gemessen wird das, nicht behauptet.
|
||||
|
||||
Aufrufen mit: node tools/crew-symbol.mjs
|
||||
=================================================================== */
|
||||
|
||||
import { readFileSync, writeFileSync, existsSync, mkdirSync } from "node:fs";
|
||||
import { join, dirname } from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
const WURZEL = join(dirname(fileURLToPath(import.meta.url)), "..");
|
||||
const HUSKY = join(WURZEL, "workspace", "assets", "img", "marke-husky.webp");
|
||||
/* Umlenkbar wie beim Schwesterwerkzeug: Ein Symbol beurteilt man, indem
|
||||
man es ansieht -- und dafuer darf man die echten Dateien nicht bei
|
||||
jedem Versuch ueberschreiben. */
|
||||
const ZIEL = process.env.SYMBOL_ZIEL || join(WURZEL, "assets", "img", "app-symbole");
|
||||
/* Zum Vergleichen -- immer die echten Symbole, auch wenn das Ziel
|
||||
umgelenkt ist. Fehlt eines, ist die dritte Gegenprobe NICHT
|
||||
durchfuehrbar, und das ist ein Fehler, kein Ueberspringen. */
|
||||
const VERGLEICH = join(WURZEL, "assets", "img", "app-symbole");
|
||||
|
||||
if (!existsSync(HUSKY)) { console.error(`Fehlt: ${HUSKY}`); process.exit(1); }
|
||||
const alsDaten = (p) => "data:image/webp;base64," + readFileSync(p).toString("base64");
|
||||
|
||||
const GROESSEN = [
|
||||
{ n: 512, endung: "512" },
|
||||
{ n: 192, endung: "192" },
|
||||
{ n: 512, endung: "512-maskable", maskable: true },
|
||||
{ n: 192, endung: "192-maskable", maskable: true },
|
||||
{ n: 180, endung: "180" },
|
||||
{ n: 32, endung: "32" },
|
||||
];
|
||||
|
||||
function html(n, maskable, husky, weglassen = "") {
|
||||
/* KLEIN = alles unter 64 px. Dort faellt die Gesichtszeichnung weg:
|
||||
achtfach vergroessert verglichen ist der Kopf mit Zeichnung bei
|
||||
32 px ein dunkler Fleck, als reine Silhouette ein Hund. Dieselbe
|
||||
Messung wie beim Workspace-Symbol, dasselbe Ergebnis. */
|
||||
const klein = n < 64;
|
||||
const rand = Math.round(n * 0.22);
|
||||
/* ALLES IN EINER GRUPPE, damit fuer die maskable-Fassung genau EINE
|
||||
Zahl kleiner wird und nicht mehrere auseinanderlaufen. 0,74 haelt
|
||||
alles im 80-%-Sicherheitskreis, den Android als einziges garantiert.
|
||||
Ohne Chili darf der Kopf etwas groesser stehen -- er ist hier allein
|
||||
und muss die Flaeche selbst tragen. */
|
||||
const g = Math.round(n * (maskable ? 0.74 : 0.96));
|
||||
const ecke = maskable ? "" : `border-radius:${rand}px;`;
|
||||
|
||||
return `<!doctype html><html><head><meta charset="utf-8"><style>
|
||||
html,body{margin:0;width:${n}px;height:${n}px;overflow:hidden}
|
||||
.feld{position:relative;width:${n}px;height:${n}px;${ecke}
|
||||
/* DER GRUND. Dieselbe Nacht wie beim Workspace -- es ist dasselbe
|
||||
Haus --, aber das warme Licht unten ist durch den Modi-Ton
|
||||
ersetzt. Beide Lichter sind damit kalt, das Zeichen wirkt
|
||||
ruhiger und ist trotzdem sofort ein anderes.
|
||||
Gedeckt, nicht leuchtend: augenschonend gilt auch fuer ein
|
||||
Symbol, das auf einem hellen Startbildschirm liegt. */
|
||||
background:
|
||||
radial-gradient(76% 56% at 50% 24%, rgba(95,189,255,.20) 0%, transparent 68%),
|
||||
radial-gradient(90% 56% at 50% 104%, rgba(95,138,159,.46) 0%, transparent 70%),
|
||||
linear-gradient(180deg,#1a2f47 0%,#0d1a29 52%,#060c14 100%);
|
||||
box-shadow:inset 0 ${Math.max(1, Math.round(n * .004))}px 0 rgba(255,255,255,.24),
|
||||
inset 0 0 ${Math.round(n * .06)}px rgba(0,0,0,.38);
|
||||
display:grid;place-items:center}
|
||||
.gruppe{position:relative;width:${g}px;height:${g}px}
|
||||
.gruppe > i{position:absolute;left:50%;transform:translate(-50%,-50%)}
|
||||
|
||||
/* DER KOPF -- zwei Lagen, und der Grund dafuer ist teuer bezahlt:
|
||||
|
||||
Als blosse MASKE zaehlt nur der Alphakanal, und die Vorlage ist
|
||||
rundum freigestellt. Uebrig bliebe eine geschlossene Flaeche in
|
||||
Hundeform -- ohne Auge, ohne Schnauze, ohne Ohrinneres. Genau das
|
||||
war am 09.09.2026 der Fehler beim Workspace-Symbol.
|
||||
|
||||
Jetzt unten die Silhouette, darueber die Originalzeichnung im
|
||||
Mischmodus "screen": Screen laesst Schwarz stehen (das Fell
|
||||
bleibt) und hebt Weiss heraus (Gesicht, Ohrinneres, Auge).
|
||||
"mask-mode: luminance" kann Chromium zwar, aber eine
|
||||
Kurzschreibweise in einer spezifischeren Regel setzt es
|
||||
stillschweigend auf "alpha" zurueck -- der Mischmodus hat diese
|
||||
Falle nicht.
|
||||
|
||||
DIE ZWEITE MASKE blendet den Hals nach unten aus. Die Vorlage ist
|
||||
dort angeschnitten; als Symbol sah die gerade Kante nach Fehler
|
||||
aus. Bei 32 px faellt der Auslauf weg, weil er dann gerade die
|
||||
Schnauze frisst -- die Kante ist dort drei Bildpunkte hoch. */
|
||||
.gruppe > i.kopf{width:${klein ? 78 : 90}%;height:${klein ? 78 : 90}%;
|
||||
top:${klein ? 46 : 45}%;
|
||||
z-index:2;isolation:isolate;
|
||||
background:linear-gradient(162deg,#6f9fbe 0%,#3a6a91 44%,#14344f 100%);
|
||||
${klein
|
||||
? `-webkit-mask:url("${husky}") center/contain no-repeat;
|
||||
mask:url("${husky}") center/contain no-repeat;`
|
||||
: `-webkit-mask:url("${husky}") center/contain no-repeat,
|
||||
linear-gradient(180deg,#000 58%, transparent 86%);
|
||||
-webkit-mask-composite:source-in;
|
||||
mask:url("${husky}") center/contain no-repeat,
|
||||
linear-gradient(180deg,#000 58%, transparent 86%);
|
||||
mask-composite:intersect;`}
|
||||
filter:drop-shadow(0 ${Math.round(n * .006)}px ${Math.round(n * .016)}px rgba(0,0,0,.7))
|
||||
drop-shadow(0 0 ${Math.round(n * .024)}px rgba(95,138,159,.44))}
|
||||
.gruppe > i.kopf .zeichnung{position:absolute;inset:0;display:block;
|
||||
background:url("${husky}") center/contain no-repeat;
|
||||
mix-blend-mode:screen;
|
||||
/* Ohne Daempfer wird das Weiss der Vorlage zu reinem Weiss und
|
||||
blendet auf dunklem Grund. Hell genug zum Erkennen, nicht
|
||||
heller. */
|
||||
filter:brightness(.88)}
|
||||
.gruppe > i.kopf.flach{background:linear-gradient(168deg,#ffffff 4%,#e2edf5 38%,#a8bfcd 100%)}
|
||||
.gruppe > i.kopf.flach .zeichnung{display:none}
|
||||
${weglassen === "zeichnung" ? ".gruppe > i.kopf .zeichnung{display:none}" : ""}
|
||||
</style></head><body><div class="feld"><div class="gruppe">
|
||||
<i class="kopf${klein ? " flach" : ""}"><b class="zeichnung"></b></i>
|
||||
</div></div></body></html>`;
|
||||
}
|
||||
|
||||
const { chromium } = await import(
|
||||
"file:///C:/Users/qciga/Documents/Obelix/Analyse/node_modules/playwright/index.mjs");
|
||||
const browser = await chromium.launch();
|
||||
const seite = await (await browser.newContext({ deviceScaleFactor: 1 })).newPage();
|
||||
if (!existsSync(ZIEL)) mkdirSync(ZIEL, { recursive: true });
|
||||
|
||||
const husky = alsDaten(HUSKY);
|
||||
let gebaut = 0;
|
||||
const maengel = [];
|
||||
|
||||
async function aufnehmen(n, maskable, weglassen = "") {
|
||||
await seite.setViewportSize({ width: n, height: n });
|
||||
await seite.setContent(html(n, maskable, husky, weglassen), { waitUntil: "networkidle" });
|
||||
return await seite.screenshot({ omitBackground: false });
|
||||
}
|
||||
|
||||
/* Streuung der Helligkeit. Ein leeres Feld hat keine -- das beweist,
|
||||
dass ueberhaupt etwas draufsteht, mehr nicht. Die Bytezahl beweist
|
||||
auch das nicht: ein einfarbiges PNG ist genauso gross wie ein volles. */
|
||||
async function streuungVon(bild) {
|
||||
return await seite.evaluate(async (b64) => {
|
||||
const b = new Image(); b.src = "data:image/png;base64," + b64; await b.decode();
|
||||
const c = document.createElement("canvas");
|
||||
c.width = b.width; c.height = b.height;
|
||||
const g2 = c.getContext("2d"); g2.drawImage(b, 0, 0);
|
||||
const d = g2.getImageData(0, 0, c.width, c.height).data;
|
||||
let s = 0, s2 = 0, m = 0;
|
||||
for (let i = 0; i < d.length; i += 4) {
|
||||
const h = 0.2126 * d[i] + 0.7152 * d[i + 1] + 0.0722 * d[i + 2];
|
||||
s += h; s2 += h * h; m++;
|
||||
}
|
||||
return Math.sqrt(s2 / m - (s / m) ** 2);
|
||||
}, bild.toString("base64"));
|
||||
}
|
||||
|
||||
/* Anteil der Bildpunkte, die sich sichtbar unterscheiden. Acht Stufen
|
||||
Abstand, damit Kantenglaettung allein den Wert nicht hochtreibt.
|
||||
-1 heisst "nicht vergleichbar" (verschiedene Groessen) und ist
|
||||
ausdruecklich etwas anderes als 0. */
|
||||
async function unterschied(a, b) {
|
||||
return await seite.evaluate(async ([x, y]) => {
|
||||
const laden = async (b64) => {
|
||||
const b = new Image(); b.src = "data:image/png;base64," + b64; await b.decode();
|
||||
const c = document.createElement("canvas");
|
||||
c.width = b.width; c.height = b.height;
|
||||
c.getContext("2d").drawImage(b, 0, 0);
|
||||
return c.getContext("2d").getImageData(0, 0, c.width, c.height).data;
|
||||
};
|
||||
const [p, q] = [await laden(x), await laden(y)];
|
||||
if (p.length !== q.length) return -1;
|
||||
let anders = 0, m = 0;
|
||||
for (let i = 0; i < p.length; i += 4) {
|
||||
const w = Math.max(Math.abs(p[i] - q[i]), Math.abs(p[i + 1] - q[i + 1]),
|
||||
Math.abs(p[i + 2] - q[i + 2]), Math.abs(p[i + 3] - q[i + 3]));
|
||||
if (w > 8) anders++;
|
||||
m++;
|
||||
}
|
||||
return anders / m;
|
||||
}, [a.toString("base64"), b.toString("base64")]);
|
||||
}
|
||||
|
||||
let verglichen = 0;
|
||||
|
||||
for (const g of GROESSEN) {
|
||||
const bild = await aufnehmen(g.n, !!g.maskable);
|
||||
writeFileSync(join(ZIEL, `crew-${g.endung}.png`), bild);
|
||||
gebaut++;
|
||||
|
||||
const streuung = await streuungVon(bild);
|
||||
if (streuung < 12) maengel.push(`${g.endung}: kaum Zeichnung (Streuung ${streuung.toFixed(1)})`);
|
||||
|
||||
const klein = g.n < 64;
|
||||
const ohneZeichnung = await unterschied(bild, await aufnehmen(g.n, !!g.maskable, "zeichnung"));
|
||||
|
||||
if (klein) {
|
||||
/* Unter 64 px ist die schlichte Silhouette Absicht. Geprueft wird,
|
||||
dass sie es auch bleibt -- sonst waere die Regel bloss ein
|
||||
Kommentar, der beim naechsten Umbau stillschweigend kippt. */
|
||||
if (ohneZeichnung > 0.001) maengel.push(`${g.endung}: klein soll ohne Gesichtszeichnung sein`);
|
||||
} else if (ohneZeichnung < 0.04) {
|
||||
maengel.push(`${g.endung}: das Gesicht kommt nicht durch (${(ohneZeichnung * 100).toFixed(1)} %)`);
|
||||
}
|
||||
|
||||
/* DRITTE GEGENPROBE: anders als das Workspace-Symbol. */
|
||||
const gegen = join(VERGLEICH, `workspace-${g.endung}.png`);
|
||||
let anders = -2;
|
||||
if (!existsSync(gegen)) {
|
||||
maengel.push(`${g.endung}: Vergleichsbild workspace-${g.endung}.png fehlt `
|
||||
+ `-- nicht nachsehbar, ob sich die Symbole unterscheiden`);
|
||||
} else {
|
||||
anders = await unterschied(bild, readFileSync(gegen));
|
||||
verglichen++;
|
||||
if (anders < 0) {
|
||||
maengel.push(`${g.endung}: Vergleichsbild hat eine andere Groesse -- nicht vergleichbar`);
|
||||
} else if (anders < 0.10) {
|
||||
maengel.push(`${g.endung}: kaum vom Workspace-Symbol zu unterscheiden `
|
||||
+ `(${(anders * 100).toFixed(1)} %)`);
|
||||
}
|
||||
}
|
||||
|
||||
console.log(` crew-${g.endung}.png`.padEnd(30)
|
||||
+ `${(bild.length / 1024).toFixed(1)} KB · Streuung ${streuung.toFixed(1)}`
|
||||
+ ` · Gesicht ${(ohneZeichnung * 100).toFixed(1)} %`
|
||||
+ ` · anders als Workspace ${anders < 0 ? "?" : (anders * 100).toFixed(1) + " %"}`);
|
||||
}
|
||||
await browser.close();
|
||||
|
||||
/* Die Zahlen gehoeren in die Bedingung, nicht nur in den Bericht. */
|
||||
if (gebaut !== GROESSEN.length) {
|
||||
console.error(`\nFEHLER: ${gebaut} von ${GROESSEN.length} erzeugt.`); process.exit(1);
|
||||
}
|
||||
if (verglichen !== GROESSEN.length) {
|
||||
console.error(`\nFEHLER: nur ${verglichen} von ${GROESSEN.length} gegen das `
|
||||
+ `Workspace-Symbol gehalten.`); process.exit(1);
|
||||
}
|
||||
if (maengel.length) {
|
||||
console.error("\nFEHLER:\n " + maengel.join("\n ")); process.exit(1);
|
||||
}
|
||||
console.log(`\n${gebaut} Symbole erzeugt. Der Husky ist derselbe wie bei DogFather,`
|
||||
+ ` das Gesicht ist in jeder grossen Fassung nachweisbar sichtbar, und alle`
|
||||
+ ` ${verglichen} unterscheiden sich messbar vom Workspace-Symbol.`);
|
||||
@@ -20,7 +20,7 @@
|
||||
Gehoert vor jeden Deploy, der JS oder CSS im Workspace anfasst.
|
||||
===================================================================== */
|
||||
|
||||
import { readdirSync, readFileSync, writeFileSync } from "node:fs";
|
||||
import { readdirSync, readFileSync, writeFileSync, existsSync } from "node:fs";
|
||||
import { join, dirname } from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
@@ -88,11 +88,24 @@ for (const name of readdirSync(ORDNER)) {
|
||||
Derselbe Stempel wie in den Seiten -- `pruef-zwischenspeicher`
|
||||
verlangt im ganzen Haus genau eine Zahl und wuerde eine zweite sofort
|
||||
melden. */
|
||||
const MANIFEST = join(ORDNER, "app.webmanifest");
|
||||
/* ES SIND ZWEI MANIFESTE (10.09.2026). Seit die Modi-App unter ihrer
|
||||
eigenen Adresse laeuft, gibt es neben app.webmanifest auch
|
||||
crew.webmanifest -- dasselbe Haus, anderer Name, andere Symbole.
|
||||
|
||||
ALS LISTE UND NICHT ALS ZWEITER BLOCK: Ein kopierter Block waere beim
|
||||
dritten Manifest ein dritter, und irgendwann bleibt einer stehen. Ein
|
||||
fehlendes Manifest ist hier ausserdem ein FEHLER und kein
|
||||
Ueberspringen -- sonst faellt das Ausbleiben nicht auf. */
|
||||
const MANIFESTE = ["app.webmanifest", "crew.webmanifest"];
|
||||
const MUSTER_MANIFEST =
|
||||
/("src":\s*")(\/assets\/img\/app-symbole\/[A-Za-z0-9._-]+)(?:\?v=\d+)?(")/g;
|
||||
{
|
||||
const alt = readFileSync(MANIFEST, "utf8");
|
||||
for (const name of MANIFESTE) {
|
||||
const pfad = join(ORDNER, name);
|
||||
if (!existsSync(pfad)) {
|
||||
console.error(`FEHLER: ${name} fehlt -- erwartet in ${ORDNER}.`);
|
||||
process.exit(1);
|
||||
}
|
||||
const alt = readFileSync(pfad, "utf8");
|
||||
let n = 0;
|
||||
const neu = alt.replace(MUSTER_MANIFEST, (_, vor, datei, nach) => {
|
||||
n += 1;
|
||||
@@ -102,11 +115,11 @@ const MUSTER_MANIFEST =
|
||||
Dann hat jemand das Manifest umgebaut, und ab da laeuft das Symbol
|
||||
still auf dem alten Stand weiter. */
|
||||
if (n === 0) {
|
||||
console.error("FEHLER: im Manifest keine Symboladresse gefunden -- Muster pruefen.");
|
||||
console.error(`FEHLER: in ${name} keine Symboladresse gefunden -- Muster pruefen.`);
|
||||
process.exit(1);
|
||||
}
|
||||
if (neu !== alt) { writeFileSync(MANIFEST, neu); dateien += 1; stellen += n; }
|
||||
console.log(` app.webmanifest ${n} Verweise`);
|
||||
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.`);
|
||||
|
||||
@@ -13,25 +13,25 @@
|
||||
"theme_color": "#06090f",
|
||||
"icons": [
|
||||
{
|
||||
"src": "/assets/img/app-symbole/workspace-192.png?v=202609100327",
|
||||
"src": "/assets/img/app-symbole/workspace-192.png?v=202609101301",
|
||||
"sizes": "192x192",
|
||||
"type": "image/png",
|
||||
"purpose": "any"
|
||||
},
|
||||
{
|
||||
"src": "/assets/img/app-symbole/workspace-512.png?v=202609100327",
|
||||
"src": "/assets/img/app-symbole/workspace-512.png?v=202609101301",
|
||||
"sizes": "512x512",
|
||||
"type": "image/png",
|
||||
"purpose": "any"
|
||||
},
|
||||
{
|
||||
"src": "/assets/img/app-symbole/workspace-192-maskable.png?v=202609100327",
|
||||
"src": "/assets/img/app-symbole/workspace-192-maskable.png?v=202609101301",
|
||||
"sizes": "192x192",
|
||||
"type": "image/png",
|
||||
"purpose": "maskable"
|
||||
},
|
||||
{
|
||||
"src": "/assets/img/app-symbole/workspace-512-maskable.png?v=202609100327",
|
||||
"src": "/assets/img/app-symbole/workspace-512-maskable.png?v=202609101301",
|
||||
"sizes": "512x512",
|
||||
"type": "image/png",
|
||||
"purpose": "maskable"
|
||||
|
||||
+11
-11
@@ -12,12 +12,12 @@
|
||||
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
|
||||
eine Flaeche sind statt zweier. -->
|
||||
<meta name="theme-color" content="#06090f" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609100327" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609100327" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609100327" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609100327" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609100327" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609100327" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609101301" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609101301" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609101301" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609101301" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609101301" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609101301" />
|
||||
</head>
|
||||
|
||||
<body class="start">
|
||||
@@ -245,10 +245,10 @@
|
||||
</form>
|
||||
</dialog>
|
||||
|
||||
<script src="assets/js/wahl.js?v=202609100327" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609100327" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609100327" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609100327" defer></script>
|
||||
<script src="assets/js/aufgaben.js?v=202609100327" defer></script>
|
||||
<script src="assets/js/wahl.js?v=202609101301" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609101301" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609101301" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609101301" defer></script>
|
||||
<script src="assets/js/aufgaben.js?v=202609101301" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+12
-12
@@ -12,13 +12,13 @@
|
||||
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
|
||||
eine Flaeche sind statt zweier. -->
|
||||
<meta name="theme-color" content="#06090f" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609100327" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609100327" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609100327" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609100327" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609100327" />
|
||||
<link rel="stylesheet" href="assets/css/automation.css?v=202609100327" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609100327" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609101301" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609101301" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609101301" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609101301" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609101301" />
|
||||
<link rel="stylesheet" href="assets/css/automation.css?v=202609101301" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609101301" />
|
||||
</head>
|
||||
|
||||
<body class="start">
|
||||
@@ -140,10 +140,10 @@
|
||||
|
||||
</main>
|
||||
|
||||
<script src="assets/js/wahl.js?v=202609100327" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609100327" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609100327" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609100327" defer></script>
|
||||
<script src="assets/js/automation.js?v=202609100327" defer></script>
|
||||
<script src="assets/js/wahl.js?v=202609101301" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609101301" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609101301" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609101301" defer></script>
|
||||
<script src="assets/js/automation.js?v=202609101301" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+16
-16
@@ -12,13 +12,13 @@
|
||||
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
|
||||
eine Flaeche sind statt zweier. -->
|
||||
<meta name="theme-color" content="#06090f" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609100327" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609100327" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609100327" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609100327" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609100327" />
|
||||
<link rel="stylesheet" href="assets/css/bereich.css?v=202609100327" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609100327" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609101301" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609101301" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609101301" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609101301" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609101301" />
|
||||
<link rel="stylesheet" href="assets/css/bereich.css?v=202609101301" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609101301" />
|
||||
</head>
|
||||
|
||||
<body class="start">
|
||||
@@ -157,14 +157,14 @@
|
||||
|
||||
</main>
|
||||
|
||||
<script src="assets/js/wahl.js?v=202609100327" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609100327" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609100327" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609100327" defer></script>
|
||||
<script src="assets/js/checkliste.js?v=202609100327" defer></script>
|
||||
<script src="assets/js/ampel.js?v=202609100327" defer></script>
|
||||
<script src="assets/js/schulung.js?v=202609100327" defer></script>
|
||||
<script src="assets/js/vorlagen.js?v=202609100327" defer></script>
|
||||
<script src="assets/js/bereich.js?v=202609100327" defer></script>
|
||||
<script src="assets/js/wahl.js?v=202609101301" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609101301" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609101301" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609101301" defer></script>
|
||||
<script src="assets/js/checkliste.js?v=202609101301" defer></script>
|
||||
<script src="assets/js/ampel.js?v=202609101301" defer></script>
|
||||
<script src="assets/js/schulung.js?v=202609101301" defer></script>
|
||||
<script src="assets/js/vorlagen.js?v=202609101301" defer></script>
|
||||
<script src="assets/js/bereich.js?v=202609101301" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+13
-13
@@ -12,13 +12,13 @@
|
||||
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
|
||||
eine Flaeche sind statt zweier. -->
|
||||
<meta name="theme-color" content="#06090f" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609100327" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609100327" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609100327" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609100327" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609100327" />
|
||||
<link rel="stylesheet" href="assets/css/calls.css?v=202609100327" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609100327" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609101301" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609101301" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609101301" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609101301" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609101301" />
|
||||
<link rel="stylesheet" href="assets/css/calls.css?v=202609101301" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609101301" />
|
||||
</head>
|
||||
|
||||
<body class="start">
|
||||
@@ -82,11 +82,11 @@
|
||||
|
||||
</main>
|
||||
|
||||
<script src="assets/js/ki.js?v=202609100327" defer></script>
|
||||
<script src="assets/js/wahl.js?v=202609100327" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609100327" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609100327" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609100327" defer></script>
|
||||
<script src="assets/js/calls.js?v=202609100327" defer></script>
|
||||
<script src="assets/js/ki.js?v=202609101301" defer></script>
|
||||
<script src="assets/js/wahl.js?v=202609101301" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609101301" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609101301" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609101301" defer></script>
|
||||
<script src="assets/js/calls.js?v=202609101301" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+12
-12
@@ -12,19 +12,19 @@
|
||||
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
|
||||
eine Flaeche sind statt zweier. -->
|
||||
<meta name="theme-color" content="#06090f" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609100327" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609100327" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609100327" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609100327" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609101301" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609101301" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609101301" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609101301" />
|
||||
<!-- aufgaben.css bringt die Dialog- und Formularstile mit (.dialog,
|
||||
.neu__zeile, .dialog__knoepfe). Sie stehen dort seit dem
|
||||
Aufgabenbrett; sie hierher zu kopieren hiesse, sie an zwei Stellen
|
||||
zu pflegen. Dieselbe Lösung nutzen kalender.html und content.html.
|
||||
Gemeldet von pruef-css-klassen.mjs, bevor jemand einen ungestylten
|
||||
Dialog zu sehen bekam. -->
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609100327" />
|
||||
<link rel="stylesheet" href="assets/css/chat.css?v=202609100327" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609100327" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609101301" />
|
||||
<link rel="stylesheet" href="assets/css/chat.css?v=202609101301" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609101301" />
|
||||
</head>
|
||||
|
||||
<body class="start">
|
||||
@@ -247,10 +247,10 @@
|
||||
</form>
|
||||
</dialog>
|
||||
|
||||
<script src="assets/js/bereiche.js?v=202609100327"></script>
|
||||
<script src="assets/js/wahl.js?v=202609100327" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609100327"></script>
|
||||
<script src="assets/js/glocke.js?v=202609100327"></script>
|
||||
<script src="assets/js/chat.js?v=202609100327"></script>
|
||||
<script src="assets/js/bereiche.js?v=202609101301"></script>
|
||||
<script src="assets/js/wahl.js?v=202609101301" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609101301"></script>
|
||||
<script src="assets/js/glocke.js?v=202609101301"></script>
|
||||
<script src="assets/js/chat.js?v=202609101301"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+14
-14
@@ -12,13 +12,13 @@
|
||||
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
|
||||
eine Flaeche sind statt zweier. -->
|
||||
<meta name="theme-color" content="#06090f" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609100327" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609100327" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609100327" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609100327" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609100327" />
|
||||
<link rel="stylesheet" href="assets/css/content.css?v=202609100327" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609100327" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609101301" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609101301" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609101301" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609101301" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609101301" />
|
||||
<link rel="stylesheet" href="assets/css/content.css?v=202609101301" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609101301" />
|
||||
</head>
|
||||
|
||||
<body class="start">
|
||||
@@ -172,12 +172,12 @@
|
||||
|
||||
</main>
|
||||
|
||||
<script src="assets/js/wahl.js?v=202609100327" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609100327" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609100327" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609100327" defer></script>
|
||||
<script src="assets/js/checkliste.js?v=202609100327" defer></script>
|
||||
<script src="assets/js/vorlagen.js?v=202609100327" defer></script>
|
||||
<script src="assets/js/content.js?v=202609100327" defer></script>
|
||||
<script src="assets/js/wahl.js?v=202609101301" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609101301" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609101301" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609101301" defer></script>
|
||||
<script src="assets/js/checkliste.js?v=202609101301" defer></script>
|
||||
<script src="assets/js/vorlagen.js?v=202609101301" defer></script>
|
||||
<script src="assets/js/content.js?v=202609101301" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
{
|
||||
"name": "Team Dogi",
|
||||
"short_name": "Team Dogi",
|
||||
"description": "Aufgaben, Termine, Ideen und Angebote an einem Ort. Nur mit persönlichem Zugangscode.",
|
||||
"start_url": "/workspace/start.html",
|
||||
"id": "/workspace/start.html",
|
||||
"scope": "/workspace/",
|
||||
"display": "standalone",
|
||||
"orientation": "any",
|
||||
"lang": "de",
|
||||
"dir": "ltr",
|
||||
"background_color": "#0a121e",
|
||||
"theme_color": "#06090f",
|
||||
"icons": [
|
||||
{
|
||||
"src": "/assets/img/app-symbole/crew-192.png?v=202609101301",
|
||||
"sizes": "192x192",
|
||||
"type": "image/png",
|
||||
"purpose": "any"
|
||||
},
|
||||
{
|
||||
"src": "/assets/img/app-symbole/crew-512.png?v=202609101301",
|
||||
"sizes": "512x512",
|
||||
"type": "image/png",
|
||||
"purpose": "any"
|
||||
},
|
||||
{
|
||||
"src": "/assets/img/app-symbole/crew-192-maskable.png?v=202609101301",
|
||||
"sizes": "192x192",
|
||||
"type": "image/png",
|
||||
"purpose": "maskable"
|
||||
},
|
||||
{
|
||||
"src": "/assets/img/app-symbole/crew-512-maskable.png?v=202609101301",
|
||||
"sizes": "512x512",
|
||||
"type": "image/png",
|
||||
"purpose": "maskable"
|
||||
}
|
||||
]
|
||||
}
|
||||
+12
-12
@@ -12,13 +12,13 @@
|
||||
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
|
||||
eine Flaeche sind statt zweier. -->
|
||||
<meta name="theme-color" content="#06090f" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609100327" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609100327" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609100327" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609100327" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609100327" />
|
||||
<link rel="stylesheet" href="assets/css/dateien.css?v=202609100327" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609100327" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609101301" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609101301" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609101301" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609101301" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609101301" />
|
||||
<link rel="stylesheet" href="assets/css/dateien.css?v=202609101301" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609101301" />
|
||||
</head>
|
||||
|
||||
<body class="start">
|
||||
@@ -101,10 +101,10 @@
|
||||
|
||||
</main>
|
||||
|
||||
<script src="assets/js/wahl.js?v=202609100327" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609100327" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609100327" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609100327" defer></script>
|
||||
<script src="assets/js/dateien.js?v=202609100327" defer></script>
|
||||
<script src="assets/js/wahl.js?v=202609101301" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609101301" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609101301" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609101301" defer></script>
|
||||
<script src="assets/js/dateien.js?v=202609101301" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -30,9 +30,9 @@
|
||||
<link rel="preload" as="image" href="assets/img/buehne/gate-hoch-580.webp"
|
||||
type="image/webp" media="(max-width: 899px)" />
|
||||
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609100327" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609100327" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609100327" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609101301" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609101301" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609101301" />
|
||||
</head>
|
||||
|
||||
<body class="gate">
|
||||
@@ -401,6 +401,6 @@
|
||||
</main>
|
||||
</div>
|
||||
|
||||
<script src="assets/js/gate.js?v=202609100327" defer></script>
|
||||
<script src="assets/js/gate.js?v=202609101301" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+13
-13
@@ -12,13 +12,13 @@
|
||||
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
|
||||
eine Flaeche sind statt zweier. -->
|
||||
<meta name="theme-color" content="#06090f" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609100327" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609100327" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609100327" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609100327" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609100327" />
|
||||
<link rel="stylesheet" href="assets/css/kalender.css?v=202609100327" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609100327" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609101301" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609101301" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609101301" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609101301" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609101301" />
|
||||
<link rel="stylesheet" href="assets/css/kalender.css?v=202609101301" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609101301" />
|
||||
</head>
|
||||
|
||||
<body class="start">
|
||||
@@ -370,11 +370,11 @@
|
||||
|
||||
</main>
|
||||
|
||||
<script src="assets/js/wahl.js?v=202609100327" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609100327" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609100327" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609100327" defer></script>
|
||||
<script src="assets/js/kalender.js?v=202609100327" defer></script>
|
||||
<script src="assets/js/kalender-abo.js?v=202609100327" defer></script>
|
||||
<script src="assets/js/wahl.js?v=202609101301" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609101301" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609101301" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609101301" defer></script>
|
||||
<script src="assets/js/kalender.js?v=202609101301" defer></script>
|
||||
<script src="assets/js/kalender-abo.js?v=202609101301" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+12
-12
@@ -12,13 +12,13 @@
|
||||
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
|
||||
eine Flaeche sind statt zweier. -->
|
||||
<meta name="theme-color" content="#06090f" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609100327" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609100327" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609100327" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609100327" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609100327" />
|
||||
<link rel="stylesheet" href="assets/css/leistung.css?v=202609100327" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609100327" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609101301" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609101301" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609101301" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609101301" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609101301" />
|
||||
<link rel="stylesheet" href="assets/css/leistung.css?v=202609101301" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609101301" />
|
||||
</head>
|
||||
|
||||
<body class="start">
|
||||
@@ -217,10 +217,10 @@
|
||||
</form>
|
||||
</dialog>
|
||||
|
||||
<script src="assets/js/wahl.js?v=202609100327"></script>
|
||||
<script src="assets/js/bereiche.js?v=202609100327"></script>
|
||||
<script src="assets/js/kopf.js?v=202609100327"></script>
|
||||
<script src="assets/js/glocke.js?v=202609100327"></script>
|
||||
<script src="assets/js/leistung.js?v=202609100327"></script>
|
||||
<script src="assets/js/wahl.js?v=202609101301"></script>
|
||||
<script src="assets/js/bereiche.js?v=202609101301"></script>
|
||||
<script src="assets/js/kopf.js?v=202609101301"></script>
|
||||
<script src="assets/js/glocke.js?v=202609101301"></script>
|
||||
<script src="assets/js/leistung.js?v=202609101301"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+12
-12
@@ -12,13 +12,13 @@
|
||||
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
|
||||
eine Flaeche sind statt zweier. -->
|
||||
<meta name="theme-color" content="#06090f" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609100327" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609100327" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609100327" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609100327" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609100327" />
|
||||
<link rel="stylesheet" href="assets/css/personen.css?v=202609100327" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609100327" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609101301" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609101301" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609101301" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609101301" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609101301" />
|
||||
<link rel="stylesheet" href="assets/css/personen.css?v=202609101301" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609101301" />
|
||||
</head>
|
||||
|
||||
<body class="start">
|
||||
@@ -335,10 +335,10 @@
|
||||
|
||||
</main>
|
||||
|
||||
<script src="assets/js/wahl.js?v=202609100327" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609100327" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609100327" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609100327" defer></script>
|
||||
<script src="assets/js/personen.js?v=202609100327" defer></script>
|
||||
<script src="assets/js/wahl.js?v=202609101301" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609101301" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609101301" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609101301" defer></script>
|
||||
<script src="assets/js/personen.js?v=202609101301" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+13
-13
@@ -12,13 +12,13 @@
|
||||
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
|
||||
eine Flaeche sind statt zweier. -->
|
||||
<meta name="theme-color" content="#06090f" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609100327" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609100327" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609100327" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609100327" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609100327" />
|
||||
<link rel="stylesheet" href="assets/css/profil.css?v=202609100327" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609100327" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609101301" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609101301" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609101301" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609101301" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609101301" />
|
||||
<link rel="stylesheet" href="assets/css/profil.css?v=202609101301" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609101301" />
|
||||
</head>
|
||||
|
||||
<body class="start">
|
||||
@@ -207,11 +207,11 @@
|
||||
|
||||
</main>
|
||||
|
||||
<script src="assets/js/wahl.js?v=202609100327" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609100327" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609100327" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609100327" defer></script>
|
||||
<script src="assets/js/steckbrief.js?v=202609100327" defer></script>
|
||||
<script src="assets/js/profil.js?v=202609100327" defer></script>
|
||||
<script src="assets/js/wahl.js?v=202609101301" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609101301" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609101301" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609101301" defer></script>
|
||||
<script src="assets/js/steckbrief.js?v=202609101301" defer></script>
|
||||
<script src="assets/js/profil.js?v=202609101301" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+13
-13
@@ -12,13 +12,13 @@
|
||||
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
|
||||
eine Flaeche sind statt zweier. -->
|
||||
<meta name="theme-color" content="#06090f" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609100327" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609100327" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609100327" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609100327" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609100327" />
|
||||
<link rel="stylesheet" href="assets/css/report.css?v=202609100327" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609100327" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609101301" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609101301" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609101301" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609101301" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609101301" />
|
||||
<link rel="stylesheet" href="assets/css/report.css?v=202609101301" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609101301" />
|
||||
</head>
|
||||
|
||||
<body class="start">
|
||||
@@ -99,11 +99,11 @@
|
||||
|
||||
</main>
|
||||
|
||||
<script src="assets/js/ki.js?v=202609100327" defer></script>
|
||||
<script src="assets/js/wahl.js?v=202609100327" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609100327" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609100327" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609100327" defer></script>
|
||||
<script src="assets/js/report.js?v=202609100327" defer></script>
|
||||
<script src="assets/js/ki.js?v=202609101301" defer></script>
|
||||
<script src="assets/js/wahl.js?v=202609101301" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609101301" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609101301" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609101301" defer></script>
|
||||
<script src="assets/js/report.js?v=202609101301" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+12
-12
@@ -12,13 +12,13 @@
|
||||
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
|
||||
eine Flaeche sind statt zweier. -->
|
||||
<meta name="theme-color" content="#06090f" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609100327" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609100327" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609100327" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609100327" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609100327" />
|
||||
<link rel="stylesheet" href="assets/css/scouting.css?v=202609100327" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609100327" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609101301" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609101301" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609101301" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609101301" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609101301" />
|
||||
<link rel="stylesheet" href="assets/css/scouting.css?v=202609101301" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609101301" />
|
||||
</head>
|
||||
|
||||
<body class="start">
|
||||
@@ -132,10 +132,10 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="assets/js/wahl.js?v=202609100327" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609100327" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609100327" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609100327" defer></script>
|
||||
<script src="assets/js/scouting.js?v=202609100327" defer></script>
|
||||
<script src="assets/js/wahl.js?v=202609101301" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609101301" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609101301" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609101301" defer></script>
|
||||
<script src="assets/js/scouting.js?v=202609101301" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+11
-11
@@ -12,14 +12,14 @@
|
||||
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
|
||||
eine Flaeche sind statt zweier. -->
|
||||
<meta name="theme-color" content="#06090f" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609100327" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609100327" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609100327" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609100327" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609101301" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609101301" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609101301" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609101301" />
|
||||
<!-- Nur die Startseite: Begrüßungskachel, Uhr, Glockenmulde. Siehe
|
||||
den Hinweis in start.css -- dort stand es bis zum 07.09.2026. -->
|
||||
<link rel="stylesheet" href="assets/css/heim.css?v=202609100327" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609100327" />
|
||||
<link rel="stylesheet" href="assets/css/heim.css?v=202609101301" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609101301" />
|
||||
</head>
|
||||
|
||||
<body class="start">
|
||||
@@ -667,10 +667,10 @@
|
||||
</p>
|
||||
</main>
|
||||
|
||||
<script src="assets/js/wahl.js?v=202609100327" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609100327" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609100327" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609100327" defer></script>
|
||||
<script src="assets/js/start.js?v=202609100327" defer></script>
|
||||
<script src="assets/js/wahl.js?v=202609101301" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609101301" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609101301" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609101301" defer></script>
|
||||
<script src="assets/js/start.js?v=202609101301" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+13
-13
@@ -12,13 +12,13 @@
|
||||
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
|
||||
eine Flaeche sind statt zweier. -->
|
||||
<meta name="theme-color" content="#06090f" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609100327" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609100327" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609100327" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609100327" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609100327" />
|
||||
<link rel="stylesheet" href="assets/css/startcheck.css?v=202609100327" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609100327" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609101301" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609101301" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609101301" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609101301" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609101301" />
|
||||
<link rel="stylesheet" href="assets/css/startcheck.css?v=202609101301" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609101301" />
|
||||
</head>
|
||||
|
||||
<body class="start">
|
||||
@@ -78,11 +78,11 @@
|
||||
|
||||
</main>
|
||||
|
||||
<script src="assets/js/ki.js?v=202609100327" defer></script>
|
||||
<script src="assets/js/wahl.js?v=202609100327" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609100327" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609100327" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609100327" defer></script>
|
||||
<script src="assets/js/startcheck.js?v=202609100327" defer></script>
|
||||
<script src="assets/js/ki.js?v=202609101301" defer></script>
|
||||
<script src="assets/js/wahl.js?v=202609101301" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609101301" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609101301" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609101301" defer></script>
|
||||
<script src="assets/js/startcheck.js?v=202609101301" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+12
-12
@@ -12,13 +12,13 @@
|
||||
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
|
||||
eine Flaeche sind statt zweier. -->
|
||||
<meta name="theme-color" content="#06090f" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609100327" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609100327" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609100327" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609100327" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609100327" />
|
||||
<link rel="stylesheet" href="assets/css/profil.css?v=202609100327" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609100327" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609101301" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609101301" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609101301" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609101301" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609101301" />
|
||||
<link rel="stylesheet" href="assets/css/profil.css?v=202609101301" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609101301" />
|
||||
</head>
|
||||
|
||||
<body class="start">
|
||||
@@ -119,10 +119,10 @@
|
||||
|
||||
</main>
|
||||
|
||||
<script src="assets/js/wahl.js?v=202609100327" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609100327" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609100327" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609100327" defer></script>
|
||||
<script src="assets/js/steckbrief.js?v=202609100327" defer></script>
|
||||
<script src="assets/js/wahl.js?v=202609101301" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609101301" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609101301" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609101301" defer></script>
|
||||
<script src="assets/js/steckbrief.js?v=202609101301" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+12
-12
@@ -7,13 +7,13 @@
|
||||
<meta name="robots" content="noindex, nofollow" />
|
||||
<link rel="manifest" href="/workspace/app.webmanifest" />
|
||||
<meta name="theme-color" content="#06090f" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609100327" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609100327" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609100327" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609100327" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609100327" />
|
||||
<link rel="stylesheet" href="assets/css/team.css?v=202609100327" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609100327" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609101301" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609101301" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609101301" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609101301" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609101301" />
|
||||
<link rel="stylesheet" href="assets/css/team.css?v=202609101301" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609101301" />
|
||||
</head>
|
||||
|
||||
<body class="start">
|
||||
@@ -103,10 +103,10 @@
|
||||
|
||||
</main>
|
||||
|
||||
<script src="assets/js/wahl.js?v=202609100327" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609100327" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609100327" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609100327" defer></script>
|
||||
<script src="assets/js/team.js?v=202609100327" defer></script>
|
||||
<script src="assets/js/wahl.js?v=202609101301" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609101301" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609101301" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609101301" defer></script>
|
||||
<script src="assets/js/team.js?v=202609101301" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+10
-10
@@ -7,13 +7,13 @@
|
||||
<meta name="robots" content="noindex, nofollow" />
|
||||
<link rel="manifest" href="/workspace/app.webmanifest" />
|
||||
<meta name="theme-color" content="#06090f" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609100327" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609100327" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609100327" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609100327" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609100327" />
|
||||
<link rel="stylesheet" href="assets/css/team.css?v=202609100327" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609100327" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609101301" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609101301" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609101301" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609101301" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609101301" />
|
||||
<link rel="stylesheet" href="assets/css/team.css?v=202609101301" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609101301" />
|
||||
</head>
|
||||
|
||||
<body class="start">
|
||||
@@ -66,8 +66,8 @@
|
||||
|
||||
</main>
|
||||
|
||||
<script src="assets/js/bereiche.js?v=202609100327" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609100327" defer></script>
|
||||
<script src="assets/js/teamlage.js?v=202609100327" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609101301" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609101301" defer></script>
|
||||
<script src="assets/js/teamlage.js?v=202609101301" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+12
-12
@@ -12,15 +12,15 @@
|
||||
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
|
||||
eine Flaeche sind statt zweier. -->
|
||||
<meta name="theme-color" content="#06090f" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609100327" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609100327" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609100327" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609100327" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609101301" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609101301" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609101301" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609101301" />
|
||||
<!-- Formular- und Dialogstile: seit dem 07.09.2026 steht hier der
|
||||
Dialog "Creator anlegen". -->
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609100327" />
|
||||
<link rel="stylesheet" href="assets/css/uebersicht.css?v=202609100327" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609100327" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609101301" />
|
||||
<link rel="stylesheet" href="assets/css/uebersicht.css?v=202609101301" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609101301" />
|
||||
</head>
|
||||
|
||||
<body class="start">
|
||||
@@ -142,11 +142,11 @@
|
||||
|
||||
</main>
|
||||
|
||||
<script src="assets/js/bereiche.js?v=202609100327" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609101301" defer></script>
|
||||
|
||||
<script src="assets/js/wahl.js?v=202609100327" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609100327" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609100327" defer></script>
|
||||
<script src="assets/js/uebersicht.js?v=202609100327" defer></script>
|
||||
<script src="assets/js/wahl.js?v=202609101301" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609101301" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609101301" defer></script>
|
||||
<script src="assets/js/uebersicht.js?v=202609101301" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+12
-12
@@ -12,13 +12,13 @@
|
||||
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
|
||||
eine Flaeche sind statt zweier. -->
|
||||
<meta name="theme-color" content="#06090f" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609100327" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609100327" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609100327" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609100327" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609100327" />
|
||||
<link rel="stylesheet" href="assets/css/wissen.css?v=202609100327" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609100327" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609101301" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609101301" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609101301" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609101301" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609101301" />
|
||||
<link rel="stylesheet" href="assets/css/wissen.css?v=202609101301" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609101301" />
|
||||
</head>
|
||||
|
||||
<body class="start">
|
||||
@@ -183,10 +183,10 @@
|
||||
|
||||
</main>
|
||||
|
||||
<script src="assets/js/wahl.js?v=202609100327" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609100327" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609100327" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609100327" defer></script>
|
||||
<script src="assets/js/wissen.js?v=202609100327" defer></script>
|
||||
<script src="assets/js/wahl.js?v=202609101301" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609101301" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609101301" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609101301" defer></script>
|
||||
<script src="assets/js/wissen.js?v=202609101301" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Reference in New Issue
Block a user