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:
+53
-18
@@ -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) {
|
||||||
|
|||||||
Reference in New Issue
Block a user