fix(web): Session-ID pro Nutzer statt globalem web-main
Die feste Session "web-main" gehoerte dem ersten Nutzer (lokal: anonymous) -> SSO-Nutzer bekam "Session gehoert einem anderen Nutzer" (403). Die Web-UI leitet die Session jetzt aus /api/me ab (web-<user_id>); ensureSession() wartet vor dem ersten Turn auf /api/me. Asset-Version v4. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
881a5ac2de
commit
6073ba1133
2 changed files with 41 additions and 22 deletions
|
|
@ -1,6 +1,5 @@
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
const SESSION_ID = "web-main";
|
|
||||||
const $ = (sel) => document.querySelector(sel);
|
const $ = (sel) => document.querySelector(sel);
|
||||||
|
|
||||||
const messagesEl = $("#messages");
|
const messagesEl = $("#messages");
|
||||||
|
|
@ -10,28 +9,46 @@ const formEl = $("#prompt-form");
|
||||||
const micBtn = $("#mic");
|
const micBtn = $("#mic");
|
||||||
|
|
||||||
let busy = false;
|
let busy = false;
|
||||||
|
// Session-ID pro Nutzer (sonst "gehoert einem anderen Nutzer"-Konflikt). Wird aus
|
||||||
|
// /api/me abgeleitet; bis dahin null -> ensureSession() wartet auf loadMe().
|
||||||
|
let sessionId = null;
|
||||||
|
let mePromise = null;
|
||||||
|
|
||||||
// ---------- Identitaet / Menue ----------
|
// ---------- Identitaet / Menue ----------
|
||||||
async function loadMe() {
|
function loadMe() {
|
||||||
try {
|
mePromise = (async () => {
|
||||||
const res = await fetch("/api/me", { headers: { Accept: "application/json" } });
|
try {
|
||||||
if (!res.ok) {
|
const res = await fetch("/api/me", { headers: { Accept: "application/json" } });
|
||||||
$("#identity").textContent = "Nicht angemeldet";
|
if (!res.ok) {
|
||||||
return;
|
$("#identity").textContent = "Nicht angemeldet";
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const me = await res.json();
|
||||||
|
sessionId = "web-" + me.user_id;
|
||||||
|
$("#identity").textContent = "Angemeldet als " + (me.display_name || me.external_id || "Gast");
|
||||||
|
if (me.sso_logout_url) {
|
||||||
|
const logout = $("#logout");
|
||||||
|
logout.href = me.sso_logout_url;
|
||||||
|
logout.classList.remove("hidden");
|
||||||
|
}
|
||||||
|
if (me.is_admin) {
|
||||||
|
$("#admin").classList.remove("hidden");
|
||||||
|
}
|
||||||
|
return me;
|
||||||
|
} catch (e) {
|
||||||
|
$("#identity").textContent = "Verbindung fehlgeschlagen";
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
const me = await res.json();
|
})();
|
||||||
$("#identity").textContent = "Angemeldet als " + (me.display_name || me.external_id || "Gast");
|
return mePromise;
|
||||||
if (me.sso_logout_url) {
|
}
|
||||||
const logout = $("#logout");
|
|
||||||
logout.href = me.sso_logout_url;
|
// Stellt sicher, dass eine nutzerspezifische Session-ID feststeht, bevor ein Turn startet.
|
||||||
logout.classList.remove("hidden");
|
async function ensureSession() {
|
||||||
}
|
if (!sessionId && mePromise) {
|
||||||
if (me.is_admin) {
|
try { await mePromise; } catch (e) { /* ignore */ }
|
||||||
$("#admin").classList.remove("hidden");
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
$("#identity").textContent = "Verbindung fehlgeschlagen";
|
|
||||||
}
|
}
|
||||||
|
if (!sessionId) sessionId = "web-anon";
|
||||||
}
|
}
|
||||||
|
|
||||||
async function loadUsers() {
|
async function loadUsers() {
|
||||||
|
|
@ -91,7 +108,7 @@ function playPcm(chunks, sampleRate) {
|
||||||
// ---------- WS-Turn (Text und Sprache teilen die Event-Logik) ----------
|
// ---------- WS-Turn (Text und Sprache teilen die Event-Logik) ----------
|
||||||
function wsUrl(path) {
|
function wsUrl(path) {
|
||||||
const proto = location.protocol === "https:" ? "wss" : "ws";
|
const proto = location.protocol === "https:" ? "wss" : "ws";
|
||||||
return `${proto}://${location.host}${path}?session_id=${encodeURIComponent(SESSION_ID)}`;
|
return `${proto}://${location.host}${path}?session_id=${encodeURIComponent(sessionId)}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
// onopen: (ws) => sendet die Eingabe. Liefert ein Promise, das beim done-Event endet.
|
// onopen: (ws) => sendet die Eingabe. Liefert ein Promise, das beim done-Event endet.
|
||||||
|
|
@ -148,6 +165,7 @@ function runTurn(path, onopen) {
|
||||||
async function sendText(text) {
|
async function sendText(text) {
|
||||||
if (busy || !text.trim()) return;
|
if (busy || !text.trim()) return;
|
||||||
busy = true;
|
busy = true;
|
||||||
|
await ensureSession();
|
||||||
addMessage("user", text);
|
addMessage("user", text);
|
||||||
statusEl.textContent = "denkt …";
|
statusEl.textContent = "denkt …";
|
||||||
await runTurn("/ws/chat", (ws) => {
|
await runTurn("/ws/chat", (ws) => {
|
||||||
|
|
@ -198,6 +216,7 @@ function stopRecording() {
|
||||||
async function sendVoice(bytes) {
|
async function sendVoice(bytes) {
|
||||||
if (busy) return;
|
if (busy) return;
|
||||||
busy = true;
|
busy = true;
|
||||||
|
await ensureSession();
|
||||||
statusEl.textContent = "verarbeite Sprache …";
|
statusEl.textContent = "verarbeite Sprache …";
|
||||||
await runTurn("/ws/voice", (ws) => {
|
await runTurn("/ws/voice", (ws) => {
|
||||||
ws.send(JSON.stringify({ type: "start", format: "webm", stream: true, audio_stream: true }));
|
ws.send(JSON.stringify({ type: "start", format: "webm", stream: true, audio_stream: true }));
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@
|
||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
<title>Voice Assistant</title>
|
<title>Voice Assistant</title>
|
||||||
<link rel="stylesheet" href="/style.css?v=3" />
|
<link rel="stylesheet" href="/style.css?v=4" />
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="layout">
|
<div id="layout">
|
||||||
|
|
@ -31,6 +31,6 @@
|
||||||
<div id="status" class="muted"></div>
|
<div id="status" class="muted"></div>
|
||||||
</main>
|
</main>
|
||||||
</div>
|
</div>
|
||||||
<script src="/app.js?v=3"></script>
|
<script src="/app.js?v=4"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue