fix: Session persistence - respect user choice across container restarts

- sessionFromFile flag prevents auto-pick after first start
- Atomic write (temp + rename) with loud error logging
- Auto-pick filters out aria-bridge/aria-diagnostic when user sessions exist
- handleSetActiveSession reports persistence failures to client

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-18 11:03:26 +02:00
parent acc13aef6b
commit 213edac3a7
+53 -18
View File
@@ -37,15 +37,41 @@ const state = {
}; };
const SESSION_KEY_FILE = "/data/active-session"; const SESSION_KEY_FILE = "/data/active-session";
// /data Verzeichnis sicherstellen (Volume Mount) // /data Verzeichnis sicherstellen (Volume Mount)
try { fs.mkdirSync("/data", { recursive: true }); } catch {} try { fs.mkdirSync("/data", { recursive: true }); } catch (e) {
console.error(`[startup] /data mkdir fehlgeschlagen: ${e.message}`);
}
// sessionFromFile zeigt an, ob der aktive Key aus der Datei kam.
// Wenn true, darf resolveActiveSession NICHT mehr auto-picken (Wahl respektieren).
let sessionFromFile = false;
let activeSessionKey = (() => { let activeSessionKey = (() => {
try { try {
const saved = fs.readFileSync(SESSION_KEY_FILE, "utf-8").trim(); const saved = fs.readFileSync(SESSION_KEY_FILE, "utf-8").trim();
if (saved) { console.log(`[startup] Gespeicherte Session geladen: '${saved}'`); return saved; } if (saved) {
} catch {} console.log(`[startup] Gespeicherte Session geladen: '${saved}'`);
sessionFromFile = true;
return saved;
}
} catch (e) {
console.error(`[startup] SESSION_KEY_FILE read: ${e.code || e.message}`);
}
console.log("[startup] Keine gespeicherte Session — Fallback 'main'"); console.log("[startup] Keine gespeicherte Session — Fallback 'main'");
return "main"; return "main";
})(); })();
// Atomic write: temp-file + rename, laute Logs bei Fehler.
function persistActiveSession(key) {
try {
const tmp = SESSION_KEY_FILE + ".tmp";
fs.writeFileSync(tmp, key);
fs.renameSync(tmp, SESSION_KEY_FILE);
sessionFromFile = true;
console.log(`[session] Aktive Session persistiert: '${key}'`);
return true;
} catch (e) {
console.error(`[session] FEHLER beim Persistieren von '${key}': ${e.message}`);
return false;
}
}
const logs = []; const logs = [];
let gatewayWs = null; let gatewayWs = null;
let rvsWs = null; let rvsWs = null;
@@ -1662,13 +1688,11 @@ async function handleDeleteSession(clientWs, sessionPath) {
} }
// ── Session-Aufloesung: letzte aktive Session finden ──── // ── Session-Aufloesung: letzte aktive Session finden ────
// Wird nach Gateway-(Re-)Connect aufgerufen. Darf die explizit gewaehlte
// Session NIE ueberschreiben — nur beim absoluten Erststart auto-picken.
async function resolveActiveSession() { async function resolveActiveSession() {
// Nur bei Fallback-Key "main" automatisch aufloesen — gespeicherte Wahl respektieren if (sessionFromFile) {
const hasSavedSession = (() => { log("info", "server", `Session '${activeSessionKey}' aus /data — keine Auto-Wahl`);
try { return !!fs.readFileSync(SESSION_KEY_FILE, "utf-8").trim(); } catch { return false; }
})();
if (hasSavedSession && activeSessionKey !== "main") {
log("info", "server", `Gespeicherte Session '${activeSessionKey}' wird beibehalten`);
return; return;
} }
@@ -1687,10 +1711,19 @@ async function resolveActiveSession() {
const keys = entries.map(e => (e.key || e.sessionKey || e.name || "?").replace(/^agent:main:/, "")); const keys = entries.map(e => (e.key || e.sessionKey || e.name || "?").replace(/^agent:main:/, ""));
log("info", "server", `Verfuegbare Sessions: [${keys.join(", ")}]`); log("info", "server", `Verfuegbare Sessions: [${keys.join(", ")}]`);
// Neueste Session nehmen // Neueste Session nehmen — aber user-definierte bevorzugen.
// aria-bridge / aria-diagnostic werden von den Services auto-erstellt;
// bei erstem Start soll lieber eine "echte" Session gewaehlt werden,
// falls vorhanden.
const AUTO_KEYS = new Set(["aria-bridge", "aria-diagnostic"]);
const normalise = (e) => (e.key || e.sessionKey || e.name || "").replace(/^agent:main:/, "");
const userEntries = entries.filter(e => !AUTO_KEYS.has(normalise(e)));
const pool = userEntries.length > 0 ? userEntries : entries;
let newest = null; let newest = null;
let newestTime = 0; let newestTime = 0;
for (const entry of entries) { for (const entry of pool) {
const t = entry.updatedAt || entry.createdAt || 0; const t = entry.updatedAt || entry.createdAt || 0;
if (t >= newestTime) { if (t >= newestTime) {
newestTime = t; newestTime = t;
@@ -1699,12 +1732,11 @@ async function resolveActiveSession() {
} }
if (newest) { if (newest) {
const rawKey = newest.key || newest.sessionKey || newest.name || ""; const key = normalise(newest);
const key = rawKey.replace(/^agent:main:/, "");
if (key) { if (key) {
activeSessionKey = key; activeSessionKey = key;
try { fs.writeFileSync(SESSION_KEY_FILE, activeSessionKey); } catch {} persistActiveSession(activeSessionKey);
log("info", "server", `Aktive Session auf neueste gewechselt: '${activeSessionKey}'`); log("info", "server", `Auto-Wahl Erststart: '${activeSessionKey}'`);
for (const c of browserClients) { for (const c of browserClients) {
c.send(JSON.stringify({ type: "active_session", sessionKey: activeSessionKey })); c.send(JSON.stringify({ type: "active_session", sessionKey: activeSessionKey }));
} }
@@ -1793,8 +1825,11 @@ function handleSetActiveSession(clientWs, sessionKey) {
return; return;
} }
activeSessionKey = sessionKey; activeSessionKey = sessionKey;
try { fs.writeFileSync(SESSION_KEY_FILE, activeSessionKey); } catch {} const ok = persistActiveSession(activeSessionKey);
log("info", "server", `Aktive Session: ${activeSessionKey}`); log("info", "server", `Aktive Session: ${activeSessionKey}${ok ? "" : " (WARN: nicht persistiert!)"}`);
if (!ok) {
clientWs.send(JSON.stringify({ type: "active_session", ok: false, sessionKey: activeSessionKey, error: "Persistierung fehlgeschlagen — /data Volume pruefen" }));
}
// Allen Clients mitteilen // Allen Clients mitteilen
for (const c of browserClients) { for (const c of browserClients) {
c.send(JSON.stringify({ type: "active_session", sessionKey: activeSessionKey })); c.send(JSON.stringify({ type: "active_session", sessionKey: activeSessionKey }));
@@ -1810,7 +1845,7 @@ async function handleCreateSession(clientWs, sessionName) {
try { try {
// Session wird automatisch erstellt wenn man die erste Nachricht sendet // Session wird automatisch erstellt wenn man die erste Nachricht sendet
activeSessionKey = sessionName; activeSessionKey = sessionName;
try { fs.writeFileSync(SESSION_KEY_FILE, activeSessionKey); } catch {} persistActiveSession(activeSessionKey);
log("info", "server", `Neue Session erstellt und aktiviert: ${sessionName}`); log("info", "server", `Neue Session erstellt und aktiviert: ${sessionName}`);
// Allen Clients mitteilen // Allen Clients mitteilen
for (const c of browserClients) { for (const c of browserClients) {