diff options
Diffstat (limited to 'packages/meshbay-hub/src/meshbay_hub/static')
4 files changed, 40 insertions, 49 deletions
diff --git a/packages/meshbay-hub/src/meshbay_hub/static/app.js b/packages/meshbay-hub/src/meshbay_hub/static/app.js index 92dcb28..fbfb942 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/app.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/app.js @@ -10,7 +10,7 @@ import * as downloads from './downloads.js'; import { Icon } from './icon.js'; import { formatSize } from './file-utils.js'; import { - HUB, navigate, session, getCachedGroupIndex, + HUB, navigate, session, purgeGroupIndexCache, _storeBundleKey, _loadBundleKey, _clearKeyDB, loadAuth, saveAuth, setAuth, setAuthChangeListener, ensureFreshToken, hubFetch, refreshAccessToken, logoutOnHub, @@ -1252,7 +1252,23 @@ const trayLabels = () => ({ // Catalogues are fetched, so the first render waits for one: mounting earlier // would paint the interface in English and then swap every string. initLocale() // falls back to English rather than rejecting, so this cannot strand the page. +// One sweep, once per browser, to remove what the cross-group search of 2026-08 +// left behind: a cleartext copy of every group's file listing that nothing has +// read since, and that no sign-out removed. Guarded by a flag so it costs one +// transaction ever rather than one per load; a browser that refuses storage +// simply does it again, which is harmless. +const PURGED_KEY = 'meshbay.indexcache.purged'; +const purgeOnce = () => { + try { + if (localStorage.getItem(PURGED_KEY)) return; + } catch { /* no storage: purge anyway, it is idempotent */ } + purgeGroupIndexCache().then(() => { + try { localStorage.setItem(PURGED_KEY, '1'); } catch { /* nothing to remember with */ } + }); +}; + const mount = () => { + purgeOnce(); render(html`<${App} />`, document.getElementById('app')); // Get the download worker registered and this page under its control now, // rather than inside the first click on Download. On Firefox and Safari it is diff --git a/packages/meshbay-hub/src/meshbay_hub/static/group-page.js b/packages/meshbay-hub/src/meshbay_hub/static/group-page.js index 9a86f2a..26519da 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/group-page.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/group-page.js @@ -6,7 +6,7 @@ import { Icon } from './icon.js'; import { transfers } from './transfers.js'; import { downloadEntry } from './file-utils.js'; import { - HUB, session, cacheGroupIndex, hubFetch, ensureFreshToken, + HUB, session, hubFetch, ensureFreshToken, _loadBundleKey, _loadRecoveryKey, _storeBundleKey, } from './hub-client.js'; import { APPS, visibleApps } from './apps.js'; @@ -265,10 +265,6 @@ function GroupPage({ groupId, group, token, username, userId, userPrefs, setEntries(fresh); if (indexMsg.dirs) setNodeDirs(indexMsg.dirs); if (indexMsg.roots) setNodeRoots(indexMsg.roots); - cacheGroupIndex(groupId, group ? group.name : groupId, - group ? group.owner_username : null, fresh, - { video: appDirs('video'), music: appDirs('music'), - photo: appDirs('photo') }); }, [groupId, group, appDirs]); // additions/deletions/updates (daemon.py _broadcast_index_change, once @@ -296,10 +292,6 @@ function GroupPage({ groupId, group, token, username, userId, userPrefs, const keptIds = new Set(updated.map((e) => e.id)); const additions = (deltaMsg.additions || []).filter((e) => !keptIds.has(e.id)); const fresh = updated.concat(additions); - cacheGroupIndex(groupId, group ? group.name : groupId, - group ? group.owner_username : null, fresh, - { video: appDirs('video'), music: appDirs('music'), - photo: appDirs('photo') }); return fresh; }); }, [groupId, group, appDirs]); diff --git a/packages/meshbay-hub/src/meshbay_hub/static/hub-client.js b/packages/meshbay-hub/src/meshbay_hub/static/hub-client.js index f467720..ba91f00 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/hub-client.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/hub-client.js @@ -81,42 +81,26 @@ function openDB() { }); } -async function cacheGroupIndex(groupId, groupName, groupOwner, entries, roots) { - try { - const db = await openDB(); - const tx = db.transaction(IDB_STORE, 'readwrite'); - tx.objectStore(IDB_STORE).put({ - groupId, groupName, groupOwner, entries, roots: roots || {}, - cachedAt: Date.now(), - }); - await new Promise((r, rej) => { tx.oncomplete = r; tx.onerror = rej; }); - db.close(); - } catch { /* best-effort */ } -} - -async function getCachedGroupIndex(groupId) { - try { - const db = await openDB(); - const tx = db.transaction(IDB_STORE, 'readonly'); - const req = tx.objectStore(IDB_STORE).get(groupId); - const result = await new Promise((r, rej) => { req.onsuccess = () => r(req.result); req.onerror = rej; }); - db.close(); - return result || null; - } catch { return null; } -} - -async function getAllCachedIndexes() { - try { - const db = await openDB(); - const tx = db.transaction(IDB_STORE, 'readonly'); - const req = tx.objectStore(IDB_STORE).getAll(); - const result = await new Promise((r, rej) => { req.onsuccess = () => r(req.result); req.onerror = rej; }); - db.close(); - return result || []; - } catch { return []; } -} - -async function clearAllCachedIndexes() { +// `group_indexes` held a decrypted copy of every group's index — each file's +// name, path, size, hash and uploader — written on every index and every delta, +// and read by the cross-group search of the time, which searched those records +// instead of dialling anything. +// +// Search has dialled the nodes since 2026-08-28. The reader went with that +// change and the writers stayed, so for weeks the browser kept building a +// cleartext file listing that nothing consulted and no sign-out removed: the key +// database is a different one. It is **L7** — code nothing calls does not sit +// still, it accumulates. +// +// Showing a group's files while its node is unreachable was the only use left +// for such a cache, and it is not wanted: a listing you cannot open is worse +// than an honest absence. +// +// The store itself is left in the schema. Dropping it means a version bump, and +// a version bump means an upgrade another tab can block — which would take +// playlists down with it, since they share this database. Emptying it costs +// nothing and leaves nothing behind. +async function purgeGroupIndexCache() { try { const db = await openDB(); const tx = db.transaction(IDB_STORE, 'readwrite'); @@ -378,7 +362,7 @@ async function hubFetch(path, { method = 'GET', body, token, _retried } = {}) { export { HUB, navigate, session, openDB, IDB_PLAYLISTS, - cacheGroupIndex, getCachedGroupIndex, getAllCachedIndexes, clearAllCachedIndexes, + purgeGroupIndexCache, _storeBundleKey, _loadBundleKey, _storeRecoveryKey, _loadRecoveryKey, _clearKeyDB, loadAuth, saveAuth, setAuth, setAuthChangeListener, tokenLifeLeft, refreshAccessToken, ensureFreshToken, logoutOnHub, hubFetch, diff --git a/packages/meshbay-hub/src/meshbay_hub/static/search-page.js b/packages/meshbay-hub/src/meshbay_hub/static/search-page.js index a096414..0a96237 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/search-page.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/search-page.js @@ -5,7 +5,7 @@ import { t } from './i18n.js'; import { Icon } from './icon.js'; import { canPreview, downloadEntry } from './file-utils.js'; import { - HUB, session, cacheGroupIndex, hubFetch, ensureFreshToken, _loadBundleKey, + HUB, session, hubFetch, ensureFreshToken, _loadBundleKey, } from './hub-client.js'; import { FilesPanel, FilePreview } from './files-app.js'; import { VideoApp, groupVideoEntries } from './video-app.js'; @@ -295,7 +295,6 @@ async function fetchAllIndexes(groups, token, username, userId, onProgress, onRe groupName: g.name, groupOwner: g.owner_username, }); - cacheGroupIndex(g.id, g.name, g.owner_username, result.entries, result.roots); // Drawn now, not when this group's neighbours are done. Its index is // already in hand; holding it back until a group that is not answering // has finished not answering is ten seconds of blank page for work that |