Add session version handling

This commit is contained in:
2026-07-02 14:23:22 +02:00
parent 65e4162651
commit 20b5129f57
12 changed files with 272 additions and 8 deletions
+4
View File
@@ -21,6 +21,8 @@ const loginUsernameInput = $("loginUsername");
const loginPasswordInput = $("loginPassword");
const registerUsernameInput = $("registerUsername");
const registerPasswordInput = $("registerPassword");
const registerInviteField = $("registerInviteField");
const registerInviteCodeInput = $("registerInviteCode");
const loginSubmit = $("loginSubmit");
const registerSubmit = $("registerSubmit");
const chatList = $("chatList");
@@ -190,6 +192,8 @@ export {
popupModal,
popupTitle,
registerForm,
registerInviteCodeInput,
registerInviteField,
registerPasswordInput,
registerSubmit,
registerUsernameInput,
+4
View File
@@ -52,6 +52,10 @@
<span>Create password</span>
<input id="registerPassword" type="password" placeholder="Create a password" autocomplete="new-password"/>
</label>
<label id="registerInviteField" class="field hidden">
<span>Invite code</span>
<input id="registerInviteCode" placeholder="Enter invite code" autocomplete="off"/>
</label>
<div class="consent-container">
<input type="checkbox" id="consentCheckbox" required/>
<label for="consentCheckbox">
+7 -1
View File
@@ -78,6 +78,8 @@ import {
popupModal,
popupTitle,
registerForm,
registerInviteCodeInput,
registerInviteField,
registerPasswordInput,
registerSubmit,
registerUsernameInput,
@@ -1643,6 +1645,7 @@ async function displayModels() {
async function checkSession() {
const res = await get("/session");
if (registerInviteField) registerInviteField.classList.toggle("hidden", !res.inviteOnlyRegistration);
if (!res.user) return false;
currentUsername = res.user; authDiv.style.display = "none"; chatDiv.style.display = "block";
await Promise.all([displayModels(), loadSummary(), loadChatSessions(), loadPersonas()]);
@@ -1654,7 +1657,10 @@ async function handleAuth(endpoint, credentials) {
const {username, password} = credentials, submitBtn = endpoint === "login" ? loginSubmit : registerSubmit;
if (!username || !password) return setAuthMessage("Please enter both username and password.", "error");
submitBtn.disabled = true; setAuthMessage("Processing...");
const res = await post(`/${endpoint}`, {username, password});
const payload = endpoint === "register"
? {username, password, inviteCode: registerInviteCodeInput?.value.trim() || ""}
: {username, password};
const res = await post(`/${endpoint}`, payload);
submitBtn.disabled = false;
if (res.error) return setAuthMessage(res.error, "error");
if (endpoint === "login") {
+27
View File
@@ -81,6 +81,33 @@
<button id="savePassword" type="submit">Save password</button>
</form>
</div>
<div class="settings-panel">
<div class="settings-section-header">
<div>
<p class="eyebrow">Account data</p>
<h2>Export and sessions</h2>
<p class="subtitle">Download your account data or sign out every active browser session.</p>
</div>
</div>
<div class="persona-actions">
<button id="exportAccount" type="button" class="secondary-action">Export account</button>
<button id="logoutAllSessions" type="button" class="secondary-action">Logout all sessions</button>
</div>
</div>
<div class="settings-panel">
<div class="settings-section-header">
<div>
<p class="eyebrow">Danger zone</p>
<h2>Delete account</h2>
<p class="subtitle">This permanently removes your account, chats, personas, templates, and market activity.</p>
</div>
</div>
<div class="persona-actions">
<button id="deleteAccount" type="button" class="secondary-action danger">Delete account</button>
</div>
</div>
</section>
<section class="settings-view" role="tabpanel" aria-hidden="true" data-settings-panel="my-roleplay-companions">
+56
View File
@@ -21,6 +21,9 @@ const usernamePasswordInput = $("usernamePassword");
const currentPasswordInput = $("currentPassword");
const newPasswordInput = $("newPassword");
const repeatPasswordInput = $("repeatPassword");
const exportAccountBtn = $("exportAccount");
const logoutAllSessionsBtn = $("logoutAllSessions");
const deleteAccountBtn = $("deleteAccount");
const usernameForm = $("usernameForm");
const passwordForm = $("passwordForm");
const settingsNavItems = document.querySelectorAll("[data-settings-view]");
@@ -458,6 +461,56 @@ async function unpublishPersona(id) {
setNotice("Persona unpublished.", "success");
}
function downloadJson(filename, payload) {
const blob = new Blob([JSON.stringify(payload, null, 2)], {type: "application/json;charset=utf-8"});
const url = URL.createObjectURL(blob);
const link = document.createElement("a");
link.href = url;
link.download = filename;
document.body.appendChild(link);
link.click();
link.remove();
URL.revokeObjectURL(url);
}
async function exportAccount() {
const res = await get("/settings/export");
if (res.error) return setNotice(res.error, "error");
const username = currentUsernameInput.value || "account";
downloadJson(`account-export-${username}-${new Date().toISOString().slice(0, 10)}.json`, res.account);
setNotice("Account export downloaded.", "success");
}
async function logoutAllSessions() {
const confirmed = await promptSettingsPopup({
eyebrow: "Security",
title: "Logout all sessions",
description: "Type logout to sign out this browser and every other active session.",
label: "Confirmation",
placeholder: "logout",
confirmLabel: "Logout all"
});
if (String(confirmed || "").trim().toLowerCase() !== "logout") return;
const res = await post("/settings/logout-all-sessions", {});
if (res.error) return setNotice(res.error, "error");
window.location.href = "/";
}
async function deleteAccount() {
const currentPassword = await promptSettingsPopup({
eyebrow: "Danger zone",
title: "Delete account",
description: "Enter your current password to permanently delete your account and all account data.",
label: "Current password",
placeholder: "Password",
confirmLabel: "Delete account"
});
if (currentPassword === null) return;
const res = await request("/settings/account", {currentPassword}, "DELETE");
if (res.error) return setNotice(res.error, "error");
window.location.href = "/";
}
usernameForm.addEventListener("submit", async (event) => {
event.preventDefault();
const username = newUsernameInput.value.trim();
@@ -514,6 +567,9 @@ workspaceModeSelect.addEventListener("change", (event) => {
localStorage.setItem("krishd-workspace-mode", mode);
setNotice(`Workspace mode set to ${mode}.`, "success");
});
exportAccountBtn.addEventListener("click", () => { void exportAccount(); });
logoutAllSessionsBtn.addEventListener("click", () => { void logoutAllSessions(); });
deleteAccountBtn.addEventListener("click", () => { void deleteAccount(); });
refreshDiagnosticsBtn.addEventListener("click", () => { void loadDiagnostics(); });
settingsNavItems.forEach((item) => item.addEventListener("click", () => setSettingsView(item.dataset.settingsView)));