Einmal anmelden reicht -- und beim Zurueckgehen bleibt die Seite stehen
Filipe: "kannst du bitte machen dass die leute sich nur einmal anmelden
muessen und dan nur noch abgemeldet werden wenn sie sich selbst
abmelden. damit sie auch die benarichtigungen sofort kriegen ... und
auch sofort die anrufe annehmen koennen."
---- 1. DIE ANMELDUNG BLEIBT ----------------------------------------
VORHER: zwoelf Stunden, feste Frist ab dem Anmelden. Wer morgens um
acht anfing, flog abends um acht raus -- mitten im Betrieb. Und wer
abgemeldet ist, hat die Seite nicht offen; ein Anruf erreicht ihn dann
nur noch ueber die Benachrichtigung, und bis er sich wieder angemeldet
hat, ist das Klingeln vorbei. Genau das beschreibt Filipe.
JETZT: ein gleitendes Fenster von 180 Tagen, das sich bei jeder Nutzung
verlaengert. Wer die Seite benutzt, bleibt angemeldet -- ohne Ende.
NICHT UNENDLICH, und das ist Absicht: In diesem Haus liegen
vertrauliche Meldungen ueber Menschen. Ein Zugang, der nie ablaeuft,
ist auf einem verlorenen Handy fuer immer offen. Ein halbes Jahr ohne
Besuch schliesst das Geraet und ist niemandem zu viel zugemutet.
An EINER Stelle gebaut: `sitzungLesen` -- durch die gehen alle 26
Fachmodule. Ein Parameter mehr haette 26 Aufrufe geaendert und beim 27.
Modul vergessen werden koennen; `req.res` haengt ohnehin an der
Anfrage. Verlaengert wird erst, wenn weniger als die Haelfte des
Fensters uebrig ist -- sonst waere das ein Schreibzugriff bei jedem
Bild und jedem Herzschlag des Ereignisstroms.
Drei Texte, die noch "zwoelf Stunden" behaupteten, sagen es jetzt
richtig -- inklusive der Abmelde-Nachfrage, die jetzt dazusagt, dass
man angemeldet bleiben SOLLTE, um Anrufe zu bekommen.
---- 2. BEIM ZURUECKGEHEN BLEIBT DIE STELLE -------------------------
Filipe, zum dritten Mal und in Grossbuchstaben. Also erst gemessen:
gescrollt auf: 900
gemerkt: 900
gelandet: 1054 <- 154 px daneben, zuverlaessig
Die Wiederherstellung LIEF also -- sie traf nur nicht. Ursache ist
Chromes Scroll-Verankerung: Waechst Inhalt OBERHALB der Stelle,
verschiebt der Browser den Bildlauf mit, damit das Sichtbare stehen
bleibt. Im Alltag genau richtig; beim Wiederherstellen das Gegenteil.
Sie wird jetzt fuer die Dauer des Wiederherstellens abgeschaltet und
danach wieder eingeschaltet -- nicht dauerhaft, sonst spraenge einem im
Chat der Text unter dem Finger weg. Dazu wird die Stelle nachgesetzt,
solange die Seite noch waechst, und aufgehoert, sobald sie 400 ms lang
ruhig ist. Ergebnis: 900 -> 900, und es bleibt dort.
DAZU, und das ist der groessere Teil: Der Ereignisstrom in kopf.js
laeuft auf 32 Seiten und wird jetzt beim Weggehen geschlossen. Eine
offene EventSource sperrt den Vor-/Zurueck-Speicher des Browsers aus --
deshalb wurde bisher JEDE Rueckkehr ein vollstaendiger Neuaufbau.
Filipe hat genau das beschrieben ("OHNE DASS DIE SEITE ... NEU LAEDT").
EHRLICH DAZU: Playwright schaltet diesen Speicher fuer Tests ab, und er
liess sich hier nicht einschalten. Ich kann also NICHT messen, dass er
jetzt greift -- die Aenderung ist trotzdem richtig (eine offene
Verbindung ist die dokumentierte Sperre, und ein Strom, der beim
Weggehen offen bleibt, ist ohnehin ein Zuhoerer, den niemand mehr
liest). Gemessen und abgesichert ist der Weg OHNE diesen Speicher --
also der schlechteste Fall.
---- 3. DABEI GEFUNDEN: pruef-schranke mass seit neun Tagen nichts --
Sie suchte `const GESCHUETZT = {` per Textmuster in workspace.js. Diese
Tabelle ist am 11.09.2026 nach rechte.js umgezogen -- seitdem fand das
Muster nichts, und die Pruefung meldete "0 geschuetzte Seiten", "alle 0
Seiten leiten um", "0 Schreibweisen ausprobiert". Drei rote Zeilen, die
nach einem Zaehlfehler aussahen und in Wahrheit hiessen: hier wird
nichts mehr geprueft.
Jetzt wird die Tabelle IMPORTIERT. Ein Textmuster auf fremden
Quelltext reisst beim naechsten Umzug still; ein import reisst laut.
Ergebnis: 33 geschuetzte Seiten, 363 Schreibweisen -- alles gruen.
GEPRUEFT: pruef-rollstelle 8/0 (neu, mit Spur ueber die Zeit und zwei
Gegenproben), pruef-schranke (war rot), pruef-code 17/0,
pruef-haerte 20/0.
Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
@@ -0,0 +1,179 @@
|
||||
/* =====================================================================
|
||||
pruef-rollstelle.mjs — Beim Zurückgehen dort bleiben, wo man war
|
||||
|
||||
Filipe, zum dritten Mal und in Großbuchstaben (20.09.2026):
|
||||
„WENN ICH IN EINE KATEGORIE REIN GEHE UND DAN ZURÜCK, GEHT DIE
|
||||
HAUPTSEITE IMMER WIEDER GANZ HOCH; DAS NERVT ... SO DASS WENN MAN
|
||||
ZURÜCK GEHT MAN DAN GENAU DA IST UND BLEIBT WO MAN VORHER WAR."
|
||||
|
||||
---------------------------------------------------------------------
|
||||
WAS GEMESSEN WURDE, BEVOR ETWAS GEBAUT WURDE
|
||||
|
||||
Scrollen auf 900, eine Kachel anklicken, zurück:
|
||||
|
||||
gemerkt: 900
|
||||
gelandet: 1054 <- 154 px daneben, zuverlässig
|
||||
|
||||
Die Wiederherstellung LIEF also — sie traf nur nicht. Die Erklärung
|
||||
ist Chromes Scroll-Verankerung: Wächst Inhalt OBERHALB der Stelle,
|
||||
verschiebt der Browser den Bildlauf mit, damit das Sichtbare stehen
|
||||
bleibt. Im Alltag richtig, beim Wiederherstellen das Gegenteil: Wir
|
||||
setzen 900, danach lädt der Inhalt darüber nach, und aus 900 wird
|
||||
1054.
|
||||
|
||||
---------------------------------------------------------------------
|
||||
ZWEI FALLEN BEIM MESSEN, IN BEIDE BIN ICH GELAUFEN
|
||||
|
||||
1. `scrollIntoViewIfNeeded()` vor dem Klick scrollt SELBST. Gemerkt
|
||||
wurde dann die neue Stelle, und die Prüfung meldete einen Fehler,
|
||||
den es nicht gab. Deshalb wird hier ein Link genommen, der schon
|
||||
im Bild steht.
|
||||
|
||||
2. Playwright schaltet den Vor-/Zurück-Speicher des Browsers ab.
|
||||
Diese Prüfung misst deshalb ausdrücklich den Weg OHNE ihn — also
|
||||
den schlechtesten Fall. Ist er im echten Browser aktiv, ist es
|
||||
ohnehin sofort und exakt.
|
||||
|
||||
AUFRUF: node server/pruef-rollstelle.mjs
|
||||
===================================================================== */
|
||||
|
||||
import { mkdtempSync, rmSync } from "node:fs";
|
||||
import { tmpdir } from "node:os";
|
||||
import { join } from "node:path";
|
||||
import { portMussFreiSein } from "./helfer-port.mjs";
|
||||
import { notbremse } from "./helfer-notbremse.mjs";
|
||||
|
||||
const PORT = await portMussFreiSein(4432, "pruef-rollstelle");
|
||||
const ordner = mkdtempSync(join(tmpdir(), "ws-rollstelle-"));
|
||||
process.env.WORKSPACE_DB = join(ordner, "workspace.db");
|
||||
process.env.PORT = String(PORT);
|
||||
process.env.SITE_ACCESS_SECRET = "lokaler-test";
|
||||
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 }); };
|
||||
await import("./index.js");
|
||||
notbremse(240_000, "pruef-rollstelle");
|
||||
await new Promise((r) => setTimeout(r, 900));
|
||||
const BASIS = `http://127.0.0.1:${PORT}`;
|
||||
|
||||
process.on("uncaughtException", (f) => { console.error("ABSTURZ:", f); process.exit(7); });
|
||||
process.on("unhandledRejection", (f) => { console.error("ABSTURZ (Versprechen):", f); process.exit(7); });
|
||||
|
||||
let fehler = 0, geprueft = 0;
|
||||
const ok = (b, t) => { geprueft++; console.log((b ? " ok " : " FEHL ") + t); if (!b) fehler++; };
|
||||
|
||||
console.log("\n=== Beim Zurueckgehen dort bleiben, wo man war ===\n");
|
||||
|
||||
const { DatabaseSync } = await import("node:sqlite");
|
||||
const { scryptSync, randomBytes } = await import("node:crypto");
|
||||
const d = new DatabaseSync(process.env.WORKSPACE_DB);
|
||||
const salt = randomBytes(16).toString("hex");
|
||||
const hash = scryptSync("CODE-DOGI-0001", 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, aktiv, erstellt)"
|
||||
+ " VALUES (?,?,?,?,?,1,?)").run("Filipe", "admin", hash, salt, 32768, new Date().toISOString());
|
||||
d.close();
|
||||
|
||||
const pw = await import("file:///C:/Users/qciga/Documents/Obelix/Analyse/node_modules/playwright/index.mjs");
|
||||
const browser = await pw.chromium.launch();
|
||||
try {
|
||||
const ctx = await browser.newContext({ viewport: { width: 1280, height: 800 } });
|
||||
const s = await ctx.newPage();
|
||||
await s.goto(BASIS + "/workspace/", { waitUntil: "networkidle" });
|
||||
await s.click('.rolle[data-rolle="admin"]');
|
||||
await s.fill("#code", "CODE-DOGI-0001");
|
||||
await s.click("button[type=submit]");
|
||||
await s.waitForURL("**/start.html", { timeout: 25000 });
|
||||
await s.waitForTimeout(1800);
|
||||
|
||||
const hoch = await s.evaluate(() => document.documentElement.scrollHeight);
|
||||
ok(hoch > 1600, `die Startseite ist hoch genug zum Messen (${hoch} px)`);
|
||||
|
||||
const ZIEL = 900;
|
||||
await s.evaluate((y) => window.scrollTo(0, y), ZIEL);
|
||||
await s.waitForTimeout(400);
|
||||
const vorKlick = await s.evaluate(() => Math.round(window.scrollY));
|
||||
ok(vorKlick === ZIEL, `gescrollt auf ${vorKlick}`);
|
||||
|
||||
/* EINEN LINK NEHMEN, DER SCHON IM BILD STEHT -- siehe Falle 1 oben. */
|
||||
const ziel = await s.evaluate(() => {
|
||||
const mitte = window.scrollY + window.innerHeight / 2;
|
||||
let best = null, dist = 1e9;
|
||||
for (const a of document.querySelectorAll("a.kachel__link")) {
|
||||
const r = a.getBoundingClientRect();
|
||||
if (r.top < 0 || r.bottom > window.innerHeight) continue;
|
||||
const y = window.scrollY + r.top + r.height / 2;
|
||||
if (Math.abs(y - mitte) < dist) { dist = Math.abs(y - mitte); best = a; }
|
||||
}
|
||||
if (!best) return null;
|
||||
best.id = "pruef-ziel";
|
||||
return best.getAttribute("href");
|
||||
});
|
||||
ok(!!ziel, `eine Kachel im Bild gefunden (${ziel})`);
|
||||
|
||||
await s.click("#pruef-ziel");
|
||||
await s.waitForLoadState("networkidle");
|
||||
await s.waitForTimeout(1200);
|
||||
ok((await s.url()).includes(String(ziel).split("?")[0]),
|
||||
"die Kachel hat wirklich geöffnet");
|
||||
|
||||
/* MITSCHREIBEN, WAS NACH DEM ZURUECKGEHEN PASSIERT. Ein Endwert sagt
|
||||
nicht, ob die Seite erst richtig stand und dann verschoben wurde. */
|
||||
await s.addInitScript(() => {
|
||||
window.__spur = [];
|
||||
setInterval(() => window.__spur.push(Math.round(window.scrollY)), 120);
|
||||
});
|
||||
await s.click("#zurueck");
|
||||
await s.waitForLoadState("networkidle");
|
||||
await s.waitForTimeout(2600);
|
||||
|
||||
const nachher = await s.evaluate(() => Math.round(window.scrollY));
|
||||
const spur = await s.evaluate(() => (window.__spur || []).slice(0, 25));
|
||||
ok(Math.abs(nachher - ZIEL) <= 8,
|
||||
`nach dem Zurückgehen wieder bei ${nachher} (war ${ZIEL})`);
|
||||
|
||||
/* UND SIE BLEIBT DORT. Der erste Anlauf traf 900 und wurde danach von
|
||||
der Scroll-Verankerung auf 1054 geschoben -- ein Endwert allein
|
||||
hätte das nicht gezeigt. */
|
||||
const spaet = spur.slice(-8);
|
||||
ok(spaet.length >= 4 && spaet.every((y) => Math.abs(y - ZIEL) <= 8),
|
||||
`und bleibt dort (letzte Messwerte: ${spaet.join(", ")})`);
|
||||
|
||||
/* ---- GEGENPROBE 1: ohne gemerkte Stelle darf nichts springen ----
|
||||
|
||||
OHNE KLICK. Zweimal bin ich in dieselbe Falle gelaufen: Playwright
|
||||
scrollt von selbst zu einem Element ausserhalb des Bildes, die
|
||||
Seite merkt sich diese Stelle, und die Gegenprobe meldet einen
|
||||
Fehler, den es nicht gibt. Hier wird deshalb direkt navigiert.
|
||||
|
||||
ganz oben (unter 80 px) LOESCHT die gemerkte Stelle sogar -- so
|
||||
steht es in kopf.js: Wer zehn Pixel gescrollt hat, merkt vom
|
||||
Wiederherstellen nichts. Genau das wird hier ausgenutzt. */
|
||||
await s.evaluate(() => window.scrollTo(0, 0));
|
||||
await s.waitForTimeout(300);
|
||||
await s.goto(`${BASIS}/workspace/uebersicht.html`, { waitUntil: "networkidle" });
|
||||
await s.waitForTimeout(700);
|
||||
await s.goBack({ waitUntil: "networkidle" });
|
||||
await s.waitForTimeout(2000);
|
||||
const ohne = await s.evaluate(() => Math.round(window.scrollY));
|
||||
ok(ohne < 80,
|
||||
`Gegenprobe: ohne gemerkte Stelle beginnt die Seite oben (${ohne})`);
|
||||
|
||||
/* ---- GEGENPROBE 2: die Verankerung ist danach wieder an --------
|
||||
Sie wird nur waehrend des Wiederherstellens abgeschaltet. Bliebe
|
||||
sie aus, spraenge einem im Chat der Text unter dem Finger weg --
|
||||
eine Reparatur, die woanders etwas kaputt macht. */
|
||||
const anker = await s.evaluate(() =>
|
||||
getComputedStyle(document.documentElement).overflowAnchor);
|
||||
ok(anker !== "none",
|
||||
`die Scroll-Verankerung ist danach wieder an (${anker})`);
|
||||
|
||||
await ctx.close();
|
||||
} finally {
|
||||
await browser.close();
|
||||
}
|
||||
|
||||
console.log(`\n${fehler ? fehler + " FEHLER" : "Alles in Ordnung"} — ${geprueft} Prüfungen\n`);
|
||||
try { rmSync(ordner, { recursive: true, force: true }); } catch { /* egal */ }
|
||||
process.exit(fehler ? 1 : 0);
|
||||
@@ -108,15 +108,25 @@ const hol = (pfad, keks) => fetch(BASIS + pfad, {
|
||||
======================================================================= */
|
||||
console.log("\n=== Ohne Anmeldung ===");
|
||||
|
||||
/* Die Liste aus workspace.js, damit hier keine zweite entsteht, die
|
||||
irgendwann abweicht. */
|
||||
const { readFileSync } = await import("node:fs");
|
||||
const quelle = readFileSync(new URL("./workspace.js", import.meta.url), "utf8");
|
||||
const block = quelle.slice(quelle.indexOf("const GESCHUETZT = {"));
|
||||
const SEITEN = [...block.slice(0, block.indexOf("\n};")).matchAll(/"(\/workspace\/[a-z]+\.html)"/g)]
|
||||
.map((m) => m[1]);
|
||||
/* DIE LISTE WIRD IMPORTIERT, NICHT AUS DEM QUELLTEXT GEKRATZT
|
||||
(berichtigt 20.09.2026).
|
||||
|
||||
ok(SEITEN.length >= 15, `${SEITEN.length} geschuetzte Seiten aus workspace.js gelesen`);
|
||||
Hier stand ein Textmuster auf "const GESCHUETZT = {" in
|
||||
workspace.js. Diese Tabelle ist am 11.09.2026 nach rechte.js
|
||||
UMGEZOGEN -- und seitdem fand das Muster nichts. Die Pruefung lief
|
||||
weiter, meldete "0 geschuetzte Seiten" und pruefte damit neun Tage
|
||||
lang NICHTS: keine Umleitung, keine Schreibweise, keine Schranke.
|
||||
Drei rote Zeilen, die niemand las, weil sie nach einem kleinen
|
||||
Zaehlfehler aussahen.
|
||||
|
||||
Gefunden beim Umbau der Anmeldedauer -- also zufaellig. Ein
|
||||
Textmuster auf fremden Quelltext ist genau die Art Verbindung, die
|
||||
beim naechsten Umzug still reisst. Ein import reisst laut. */
|
||||
const { SEITEN: RECHTE_SEITEN } = await import("./rechte.js");
|
||||
const SEITEN = Object.keys(RECHTE_SEITEN)
|
||||
.filter((x) => /workspace/.test(x) && x.endsWith(".html"));
|
||||
|
||||
ok(SEITEN.length >= 15, `${SEITEN.length} geschuetzte Seiten aus rechte.js gelesen`);
|
||||
|
||||
let geprueft = 0;
|
||||
for (const seite of SEITEN) {
|
||||
|
||||
+65
-3
@@ -65,7 +65,37 @@ export const DB_PFAD = process.env.WORKSPACE_DB
|
||||
export const DATEN_ORDNER = dirname(DB_PFAD);
|
||||
|
||||
const COOKIE = "dfw_sitzung";
|
||||
const SITZUNG_STUNDEN = 12;
|
||||
/* ===== WIE LANGE GILT EINE ANMELDUNG? (20.09.2026) ==================
|
||||
|
||||
Filipe: "kannst du bitte machen dass die leute sich nur einmal
|
||||
anmelden muessen und dan nur noch abgemeldet werden wenn sie sich
|
||||
selbst abmelden. damit sie auch die benarichtigungen sofort kriegen
|
||||
... und auch sofort die anrufe annehmen koennen."
|
||||
|
||||
VORHER: zwoelf Stunden, feste Frist ab dem Anmelden. Wer morgens um
|
||||
acht anfing, flog abends um acht raus -- mitten im Betrieb. Und wer
|
||||
abgemeldet ist, hat die Seite nicht offen; ein Anruf erreicht ihn
|
||||
dann nur noch ueber die Benachrichtigung, und bis er sich wieder
|
||||
angemeldet hat, ist das Klingeln vorbei.
|
||||
|
||||
JETZT: ein GLEITENDES Fenster. Wer die Seite benutzt, bleibt
|
||||
angemeldet -- ohne Ende, ohne Nachfrage. Abgemeldet wird nur, wer
|
||||
sich selbst abmeldet.
|
||||
|
||||
WARUM NICHT "unendlich": Ein Zugang, der nie ablaeuft, ist auf einem
|
||||
verlorenen Handy fuer immer offen -- und in diesem Haus liegen
|
||||
vertrauliche Meldungen ueber Menschen. Wer ein halbes Jahr nicht da
|
||||
war, meldet sich neu an; das ist niemandem zuzumuten zu viel und
|
||||
schliesst das verlorene Geraet.
|
||||
|
||||
WARUM NICHT BEI JEDEM AUFRUF NEU SCHREIBEN: Das waere ein
|
||||
Schreibzugriff auf die Datenbank bei jedem Bild, jeder Abfrage,
|
||||
jedem Herzschlag des Ereignisstroms. Verlaengert wird erst, wenn
|
||||
weniger als die Haelfte des Fensters uebrig ist -- also hoechstens
|
||||
einmal alle drei Monate je Geraet.
|
||||
==================================================================== */
|
||||
const SITZUNG_TAGE = 180;
|
||||
const SITZUNG_MS = SITZUNG_TAGE * 86400_000;
|
||||
const VERSUCHE_MAX = 8; // pro IP
|
||||
const VERSUCHE_FENSTER_MIN = 10;
|
||||
const ROLLEN = new Set(["spicy", "admin", "manager", "scout", "creator", "hand", "modi", "gast"]);
|
||||
@@ -4448,6 +4478,38 @@ export function sitzungLesen(req) {
|
||||
db().prepare("DELETE FROM sitzungen WHERE token_hash = ?").run(tokenHash(token));
|
||||
return null;
|
||||
}
|
||||
|
||||
/* ==== DAS FENSTER GLEITET MIT (20.09.2026) =======================
|
||||
|
||||
Wer die Seite benutzt, bleibt angemeldet. Verlaengert wird erst,
|
||||
wenn weniger als die HAELFTE des Fensters uebrig ist -- sonst
|
||||
waere hier ein Schreibzugriff bei jedem einzelnen Aufruf, auch
|
||||
bei jedem Herzschlag des Ereignisstroms.
|
||||
|
||||
DER KEKS MUSS MIT. Ohne ihn liefe die Zeile in der Datenbank
|
||||
weiter und der Browser wuerfe den Keks trotzdem weg -- die
|
||||
Person waere abgemeldet, obwohl der Server sie noch kennt. Das
|
||||
ist die Sorte Fehler, die niemand findet, weil beide Seiten fuer
|
||||
sich richtig aussehen.
|
||||
|
||||
`req.res` STATT EINES ZWEITEN ARGUMENTS: Diese Funktion wird an
|
||||
26 Stellen aufgerufen -- in jedem Fachmodul einmal. Einen
|
||||
Parameter zu ergaenzen hiesse, 26 Aufrufe zu aendern und beim
|
||||
27. Modul daran zu denken. Express haengt die Antwort ohnehin an
|
||||
die Anfrage; wo sie fehlt (etwa in einer Pruefung), wird eben
|
||||
nur die Datenbank verlaengert. */
|
||||
const restMs = new Date(reihe.gueltig_bis).getTime() - Date.now();
|
||||
if (restMs < SITZUNG_MS / 2) {
|
||||
const neuBis = new Date(Date.now() + SITZUNG_MS).toISOString();
|
||||
db().prepare("UPDATE sitzungen SET gueltig_bis = ? WHERE token_hash = ?")
|
||||
.run(neuBis, tokenHash(token));
|
||||
try {
|
||||
req.res?.cookie?.(COOKIE, token, {
|
||||
httpOnly: true, secure: !!req.secure, sameSite: "lax",
|
||||
path: "/workspace", maxAge: SITZUNG_MS,
|
||||
});
|
||||
} catch { /* kein res (z. B. in einer Pruefung) -- die Zeile reicht */ }
|
||||
}
|
||||
/* DIE ADRESSE ENTSCHEIDET MIT (10.09.2026).
|
||||
|
||||
Seit die Modi-App unter crew.dogfather-universe.com laeuft, gehoert
|
||||
@@ -4527,7 +4589,7 @@ export function sitzungLesen(req) {
|
||||
|
||||
function sitzungSetzen(res, person, req) {
|
||||
const token = randomBytes(32).toString("hex");
|
||||
const bis = new Date(Date.now() + SITZUNG_STUNDEN * 3600_000).toISOString();
|
||||
const bis = new Date(Date.now() + SITZUNG_MS).toISOString();
|
||||
db().prepare(
|
||||
"INSERT INTO sitzungen (token_hash, person_id, erstellt, gueltig_bis, ip, browser) VALUES (?,?,?,?,?,?)"
|
||||
).run(tokenHash(token), person.id, jetzt(), bis, echteIp(req),
|
||||
@@ -4542,7 +4604,7 @@ function sitzungSetzen(res, person, req) {
|
||||
secure: req.secure,
|
||||
sameSite: "lax", // schützt vor fremden Formularen (CSRF)
|
||||
path: "/workspace", // gilt nur hier, nicht auf der ganzen Domain
|
||||
maxAge: SITZUNG_STUNDEN * 3600_000,
|
||||
maxAge: SITZUNG_MS,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user