aboutsummaryrefslogtreecommitdiffstats
path: root/packages/meshbay-hub/src/meshbay_hub
diff options
context:
space:
mode:
authorChristophe Besson <cbesson@gmail.com>2026-09-10 17:49:58 +0200
committerChristophe Besson <cbesson@gmail.com>2026-09-10 17:49:58 +0200
commit07ff8b4f6143039fcc74b8cf7c423282bce093c1 (patch)
tree89e095aeef9f5ad0bcbe7b3e6cfdc67d36bcbba8 /packages/meshbay-hub/src/meshbay_hub
parent1e6f3861a1570029897a30b42121836fd03565c1 (diff)
downloadmeshbay-07ff8b4f6143039fcc74b8cf7c423282bce093c1.tar.gz
refactor(mnp)!: one operation for an app's folders, not one per app
`video_root`, `audio_root` and `photo_roots` are gone — the messages, the signed operations, the handlers, the `ops` wrappers, the three scalars on the handshake ack, and the client's handlers for their acks. `app_directories` does the same thing for every application, keyed by the app's own registry name, and it is what the SPA has been sending. The three were the same instruction three times, differing only in the key they wrote and whether they carried a string or a list. That shape is what made adding an application mean adding a message type, an op, a handler and a widget; it also meant three validation paths, and the older ones validated nothing — a typo was stored and then quietly matched no entry, an app showing an empty tab with no way to tell "misconfigured" from "no files yet". **What stays, and why.** `Roster.LEGACY_DIR_KEYS` still reads `video_root` and friends out of `group_settings`: that is a key on an operator's disk, not on the wire, and a node upgraded into this must find its own configuration. The Search page still reads its own older cache keys, for the same reason — the cache outlives a deploy. `CTX_ALIASES` keeps only `chat`, which is the one app whose second name something still reads. The two per-app policy test files go with the messages. What only they held — the real challenge/response path from message to database, which no other test exercises — is retargeted at `app_directories` in `test_app_directories_signed.py`, and the handler's own refusals (unknown app, malformed `directories`, nobody to authorize it) join `test_app_directories.py`. Node and common suites 1368 passed. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AsoWC3GmhNdwVFomW3QjH3
Diffstat (limited to 'packages/meshbay-hub/src/meshbay_hub')
-rw-r--r--packages/meshbay-hub/src/meshbay_hub/static/group-page.js17
-rw-r--r--packages/meshbay-hub/src/meshbay_hub/static/search-page.js6
-rw-r--r--packages/meshbay-hub/src/meshbay_hub/static/transport.js27
3 files changed, 10 insertions, 40 deletions
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 341d37e..fec685b 100644
--- a/packages/meshbay-hub/src/meshbay_hub/static/group-page.js
+++ b/packages/meshbay-hub/src/meshbay_hub/static/group-page.js
@@ -355,11 +355,9 @@ function GroupPage({ groupId, group, token, username, userId, userPrefs,
// missing `video_directories` as "nothing configured" would empty a
// working Videos tab.
setAppDirectories({
- video: ack.video_directories
- || (ack.video_root ? [ack.video_root] : []),
- music: ack.music_directories
- || (ack.audio_root ? [ack.audio_root] : []),
- photo: ack.photo_directories || ack.photo_roots || [],
+ video: ack.video_directories || [],
+ music: ack.music_directories || [],
+ photo: ack.photo_directories || [],
});
setChatDirectory(ack.chat_directory || '');
setChatLinkPreview(ack.chat_link_preview !== false);
@@ -374,16 +372,9 @@ function GroupPage({ groupId, group, token, username, userId, userPrefs,
// recent value.
transport.onTmdbConfig = (cfg) => setTmdbConfig((prev) => ({ ...(prev || {}), ...cfg }));
transport.onTmdbEnabled = (enabled) => setTmdbConfig((prev) => ({ ...(prev || {}), enabled }));
- // One handler for every app's directories, plus the three older
- // per-app messages a node that predates the generic op still sends.
+ // One handler for every app's directories, keyed by the app's name.
transport.onAppDirectories = (app, dirs) =>
setAppDirectories((prev) => ({ ...prev, [app]: dirs }));
- transport.onVideoRoot = (path) => setAppDirectories(
- (prev) => ({ ...prev, video: path ? [path] : [] }));
- transport.onAudioRoot = (path) => setAppDirectories(
- (prev) => ({ ...prev, music: path ? [path] : [] }));
- transport.onPhotoRoots = (roots) => setAppDirectories(
- (prev) => ({ ...prev, photo: roots || [] }));
transport.onChatDirectory = (path) => setChatDirectory(path);
transport.onChatLinkPreview = (on) => setChatLinkPreview(on);
transport.onMusicbrainzEnabled = (enabled) =>
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 81d4ac2..ee99ccc 100644
--- a/packages/meshbay-hub/src/meshbay_hub/static/search-page.js
+++ b/packages/meshbay-hub/src/meshbay_hub/static/search-page.js
@@ -151,9 +151,9 @@ async function fetchGroupIndex(groupId, token, bundleKey, username, userId) {
// Plural, with the old scalars as the fallback for a node still speaking
// MNP 1.0 — the same reading group-page.js does on its own handshake.
const roots = {
- video: ack.video_directories || (ack.video_root ? [ack.video_root] : []),
- music: ack.music_directories || (ack.audio_root ? [ack.audio_root] : []),
- photo: ack.photo_directories || ack.photo_roots || [],
+ video: ack.video_directories || [],
+ music: ack.music_directories || [],
+ photo: ack.photo_directories || [],
};
// Which of the reader's groups sit on their own node — the tie-breaker
// when the same file is announced by several of them
diff --git a/packages/meshbay-hub/src/meshbay_hub/static/transport.js b/packages/meshbay-hub/src/meshbay_hub/static/transport.js
index 2e3712c..32005b7 100644
--- a/packages/meshbay-hub/src/meshbay_hub/static/transport.js
+++ b/packages/meshbay-hub/src/meshbay_hub/static/transport.js
@@ -75,8 +75,8 @@ function _aborted() {
// back-to-back sequence) fired two of these within milliseconds of each
// other. Both admin_challenge replies, and both domain acks afterward,
// were routed by nothing more than "whichever request happens to be
-// oldest pending" — apps_enabled's challenge stole audio_root's slot, then
-// audio_root's own request just sat there until its 30s timeout, having
+// oldest pending" — apps_enabled's challenge stole app_directories' slot,
+// then that request just sat there until its 30s timeout, having
// never received a challenge to answer at all. Keying both hops by op name
// (below, in _key and in _dispatch) fixes this without needing the node to
// change anything — `op` is already on every admin_challenge, and this
@@ -532,9 +532,6 @@ class MeshBayTransport {
set onChatEpoch(fn) { this._onChatEpoch = fn; }
set onTmdbConfig(fn) { this._onTmdbConfig = fn; }
set onTmdbEnabled(fn) { this._onTmdbEnabled = fn; }
- set onVideoRoot(fn) { this._onVideoRoot = fn; }
- set onAudioRoot(fn) { this._onAudioRoot = fn; }
- set onPhotoRoots(fn) { this._onPhotoRoots = fn; }
set onMusicbrainzEnabled(fn) { this._onMusicbrainzEnabled = fn; }
set onIndexProgress(fn) { this._onIndexProgress = fn; }
// Fired when a message that must open under the group key does not —
@@ -1961,7 +1958,7 @@ class MeshBayTransport {
// signature) — carried so _sendAndWait can key this reply by op, the
// same way the admin_challenge that preceded it was keyed. Without
// it, two admin_response replies in flight together (e.g. one op's
- // audio_root_ack arriving while another's apps_enabled_ack is still
+ // app_directories_ack arriving while another's apps_enabled_ack is still
// pending) are matched by nothing more than arrival order.
op: challenge.op,
});
@@ -3133,24 +3130,6 @@ class MeshBayTransport {
this._onTmdbOverride({ fileId: msg.file_id || '', tmdbId: '', mediaType: '' });
}
- // Same shape: the operator changed which folder is the Videos app's
- // entry point for this group.
- if (msg.type === 'video_root_ack' && this._onVideoRoot) {
- this._onVideoRoot(msg.path || '');
- }
-
- // Same shape: the operator changed which folder is the Music app's
- // entry point for this group.
- if (msg.type === 'audio_root_ack' && this._onAudioRoot) {
- this._onAudioRoot(msg.path || '');
- }
-
- // Same shape: the operator replaced the Photos app's whole root set
- // for this group (docs/photos.md §2.1).
- if (msg.type === 'photo_roots_ack' && this._onPhotoRoots) {
- this._onPhotoRoots(msg.roots || []);
- }
-
// Per-group, like tmdb_enabled_ack above.
if (msg.type === 'musicbrainz_enabled_ack' && this._onMusicbrainzEnabled) {
this._onMusicbrainzEnabled(Boolean(msg.enabled));