improve vault manager, refactor vault operations into shared service.

This commit is contained in:
Nystik 2026-03-18 18:52:38 +01:00
parent 408eb7fb54
commit e1d484fd28
5 changed files with 159 additions and 68 deletions

118
services/vault-service.js Normal file
View file

@ -0,0 +1,118 @@
const API_BASE = "/api/vault";
async function fetchJson(url, options) {
const res = await fetch(url, options);
if (!res.ok) {
const data = await res.json().catch(() => ({ error: res.statusText }));
throw new Error(data.error || "Request failed");
}
return res.json();
}
export const vaultService = {
getCurrentVaultId() {
return window.__currentVaultId || "";
},
async listVaults() {
const list = await fetchJson(API_BASE + "/list");
window.__vaultList = list;
return list;
},
listVaultsSync() {
const xhr = new XMLHttpRequest();
xhr.open("GET", API_BASE + "/list", false);
xhr.send();
if (xhr.status === 200) {
const list = JSON.parse(xhr.responseText);
window.__vaultList = list;
return list;
}
return [];
},
async createVault(name) {
await fetchJson(API_BASE + "/create", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ name }),
});
return this.listVaults();
},
createVaultSync(name) {
const xhr = new XMLHttpRequest();
xhr.open("POST", API_BASE + "/create", false);
xhr.setRequestHeader("Content-Type", "application/json");
xhr.send(JSON.stringify({ name }));
if (xhr.status >= 400) {
return null;
}
return true;
},
async renameVault(id, newName) {
await fetchJson(API_BASE + "/rename", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ vault: id, name: newName }),
});
if (id === this.getCurrentVaultId()) {
window.__currentVaultId = newName;
if (window.__vaultConfig) {
window.__vaultConfig.id = newName;
}
}
return this.listVaults();
},
async deleteVault(id) {
await fetchJson(
API_BASE + "/remove?vault=" + encodeURIComponent(id),
{ method: "DELETE" },
);
const wasCurrentVault = id === this.getCurrentVaultId();
await this.listVaults();
return { wasCurrentVault };
},
deleteVaultSync(id) {
const xhr = new XMLHttpRequest();
xhr.open(
"DELETE",
API_BASE + "/remove?vault=" + encodeURIComponent(id),
false,
);
xhr.send();
return xhr.status < 400;
},
openVault(id) {
const target = window.parent !== window ? window.parent : window;
target.location.href = "/?vault=" + encodeURIComponent(id);
},
};

View file

@ -1,4 +1,5 @@
import { showVaultManager } from "../ui/vault-manager.js"; import { showVaultManager } from "../../ui/bootstrap.js";
import { vaultService } from "../../services/vault-service.js";
const listeners = new Map(); const listeners = new Map();
@ -44,7 +45,7 @@ const syncHandlers = {
result[v.id] = { result[v.id] = {
path: "/" + v.id, path: "/" + v.id,
ts: Date.now(), ts: Date.now(),
open: v.id === (window.__currentVaultId || ""), open: v.id === vaultService.getCurrentVaultId(),
}; };
} }
@ -56,36 +57,20 @@ const syncHandlers = {
const vault = (window.__vaultList || []).find((v) => v.id === id); const vault = (window.__vaultList || []).find((v) => v.id === id);
if (!vault && id) { if (!vault && id) {
const xhr = new XMLHttpRequest(); if (!vaultService.createVaultSync(id)) {
xhr.open("POST", "/api/vault/create", false);
xhr.setRequestHeader("Content-Type", "application/json");
xhr.send(JSON.stringify({ name: id }));
if (xhr.status >= 400) {
return "Failed to create vault"; return "Failed to create vault";
} }
} }
const target = window.parent !== window ? window.parent : window; vaultService.openVault(id);
target.location.href = "/?vault=" + encodeURIComponent(id);
return true; return true;
}, },
"vault-remove": (vaultPath) => { "vault-remove": (vaultPath) => {
const id = (vaultPath || "").replace(/^\/+/, ""); const id = (vaultPath || "").replace(/^\/+/, "");
const xhr = new XMLHttpRequest();
xhr.open( return vaultService.deleteVaultSync(id);
"DELETE",
"/api/vault/remove?vault=" + encodeURIComponent(id),
false,
);
xhr.send();
return xhr.status < 400;
}, },
"vault-move": (oldPath, newPath) => { "vault-move": (oldPath, newPath) => {

View file

@ -15,6 +15,7 @@ import * as eventsShim from "./node/events.js";
import * as osShim from "./node/os.js"; import * as osShim from "./node/os.js";
import * as netShim from "./node/net.js"; import * as netShim from "./node/net.js";
import * as httpShim from "./node/http.js"; import * as httpShim from "./node/http.js";
import { vaultService } from "../services/vault-service.js";
const DEBUG = true; const DEBUG = true;
const _accessLog = new Map(); // "module.property" -> count const _accessLog = new Map(); // "module.property" -> count
@ -217,14 +218,7 @@ window.__currentVaultId = _urlParams.get("vault") || "";
(function initVaultList() { (function initVaultList() {
try { try {
const xhr = new XMLHttpRequest(); vaultService.listVaultsSync();
xhr.open("GET", "/api/vault/list", false);
xhr.send();
if (xhr.status === 200) {
window.__vaultList = JSON.parse(xhr.responseText);
}
} catch (e) { } catch (e) {
window.__vaultList = []; window.__vaultList = [];
} }

View file

@ -1,8 +1,11 @@
import { vaultService } from "../services/vault-service.js";
export function showVaultManager() { export function showVaultManager() {
if (!document.querySelector(".workspace")) return; if (!document.querySelector(".workspace")) return;
if (document.querySelector(".vault-manager-overlay")) return; if (document.querySelector(".vault-manager-overlay")) return;
new window.IgnisUI.VaultManager({ new window.IgnisUI.VaultManager({
target: document.body, target: document.body,
props: { vaultService },
}); });
} }

View file

@ -17,6 +17,8 @@
import ListItem from "../components/display/ListItem.svelte"; import ListItem from "../components/display/ListItem.svelte";
import PopoverMenu from "../components/menu/PopoverMenu.svelte"; import PopoverMenu from "../components/menu/PopoverMenu.svelte";
export let vaultService;
let vaults = []; let vaults = [];
let searchQuery = ""; let searchQuery = "";
let openMenuId = null; let openMenuId = null;
@ -31,7 +33,8 @@
{ id: "delete", label: "Delete", danger: true }, { id: "delete", label: "Delete", danger: true },
]; ];
$: currentVaultId = window.__currentVaultId || ""; let currentVaultId = vaultService.getCurrentVaultId();
$: deleteMessage = targetVault $: deleteMessage = targetVault
? 'Are you sure you want to delete "' + targetVault.name + '"?' ? 'Are you sure you want to delete "' + targetVault.name + '"?'
: ""; : "";
@ -41,10 +44,9 @@
) )
: vaults; : vaults;
async function fetchVaults() { async function refreshVaults() {
try { try {
const res = await fetch("/api/vault/list"); vaults = await vaultService.listVaults();
vaults = res.ok ? await res.json() : [];
} catch { } catch {
vaults = []; vaults = [];
} }
@ -55,7 +57,7 @@
modalRef.dismiss(); modalRef.dismiss();
return; return;
} }
window.location.href = "/?vault=" + encodeURIComponent(vault.id); vaultService.openVault(vault.id);
} }
function toggleMenu(vaultId) { function toggleMenu(vaultId) {
@ -104,21 +106,14 @@
return; return;
} }
const res = await fetch("/api/vault/create", { try {
method: "POST", vaults = await vaultService.createVault(name);
headers: { "Content-Type": "application/json" }, } catch (err) {
body: JSON.stringify({ name }), alert("Failed to create vault: " + err.message);
});
if (!res.ok) {
const data = await res.json();
alert("Failed to create vault: " + (data.error || "Unknown error"));
return; return;
} }
closeDialog(); closeDialog();
window.location.href = "/?vault=" + encodeURIComponent(name);
} }
async function onRenameConfirm(e) { async function onRenameConfirm(e) {
@ -129,41 +124,37 @@
return; return;
} }
const res = await fetch("/api/vault/rename", { const wasCurrentVault = targetVault.id === currentVaultId;
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ vault: targetVault.id, name: trimmed }),
});
if (!res.ok) { try {
const data = await res.json(); vaults = await vaultService.renameVault(targetVault.id, trimmed);
alert("Failed to rename vault: " + (data.error || "Unknown error")); } catch (err) {
alert("Failed to rename vault: " + err.message);
return; return;
} }
closeDialog(); closeDialog();
if (targetVault.id === currentVaultId) { if (wasCurrentVault) {
window.location.href = "/?vault=" + encodeURIComponent(trimmed); currentVaultId = vaultService.getCurrentVaultId();
} else {
await fetchVaults();
} }
} }
async function onDeleteConfirm() { async function onDeleteConfirm() {
const wasCurrentVault = targetVault.id === currentVaultId; try {
const { wasCurrentVault } = await vaultService.deleteVault(
targetVault.id,
);
await fetch( closeDialog();
"/api/vault/remove?vault=" + encodeURIComponent(targetVault.id),
{ method: "DELETE" },
);
closeDialog(); vaults = await vaultService.listVaults();
await fetchVaults(); if (wasCurrentVault) {
vaultService.openVault("");
if (wasCurrentVault) { }
window.location.href = "/"; } catch (err) {
alert("Failed to delete vault: " + err.message);
} }
} }
@ -176,7 +167,7 @@
} }
onMount(() => { onMount(() => {
fetchVaults(); refreshVaults();
}); });
</script> </script>