aboutsummaryrefslogtreecommitdiffstats
path: root/packages/meshbay-hub/src/meshbay_hub/static/app.js
diff options
context:
space:
mode:
authorChristophe Besson <cbesson@gmail.com>2026-10-07 21:25:47 +0200
committerChristophe Besson <cbesson@gmail.com>2026-10-07 22:22:51 +0200
commite833fe1bfc8eb6f66cc5dc53997cc4158bab583f (patch)
tree6874dfd09cb210eaf61e4f5761d0bad23c9cd0a3 /packages/meshbay-hub/src/meshbay_hub/static/app.js
parent92e25ffcc5edf5d1a9996bfb921b5a95b826134b (diff)
downloadmeshbay-e833fe1bfc8eb6f66cc5dc53997cc4158bab583f.tar.gz
fix: set the Windows node up at sign-in, and stop it for real
Found by the first Windows beta tester, then reproduced on a clean install. After a service-mode install nothing set the node up for the account that signed in: the boot task started a node that quit ("hub.username not set"), and the sidebar showed Node / Create group only once the hub held a node key. The only way to the wizard that provisions was the home page's welcome card, which an account already in a group never sees. The way out was `meshbay-node init` and the key pasted on the profile page -- which is also what PACKAGING-GUIDE.md told people to do. - main.js `node:ensure`, called by app.js at sign-in: provisions, starts and links the node this build ships (Windows, bundled node only). A node set up for another account, or an account linked to another node, is left alone. node:start waits for it, so the two never race. - The sidebar shows the Node section when a node exists on this machine. - The Node page's status is the node's: its control API and the process list, not the service task's state (a node started from a terminal ran while the page said Stopped). Stop says Stopped only once no meshbay-node.exe is left, and stays offered for a process that answers nothing. - CLI stop kills the pid that answered when a graceful stop does not finish, and fails with the reason when a node process is still there. - The daemon ends its process 3s after _shutdown(): Python's exit waited for a busy indexer thread, with the control API already closed. Armed by main() only, never by a daemon run inside a test. - node.toml is read as utf-8-sig (PowerShell 5.1 writes a BOM), and a config that cannot be read is logged instead of dying silently in service mode. - "Pair this browser" queues the code for the next group of this node to open instead of saying "Paired successfully"; no banner before a group. - test_e2e_windows_app.py (opt-in, MESHBAY_WIN_E2E=1) drives the installed app against a throwaway hub: fresh account to linked node, Stop, Start, Restart, checked against the real processes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Diffstat (limited to 'packages/meshbay-hub/src/meshbay_hub/static/app.js')
-rw-r--r--packages/meshbay-hub/src/meshbay_hub/static/app.js30
1 files changed, 26 insertions, 4 deletions
diff --git a/packages/meshbay-hub/src/meshbay_hub/static/app.js b/packages/meshbay-hub/src/meshbay_hub/static/app.js
index c1913c0..b9466d0 100644
--- a/packages/meshbay-hub/src/meshbay_hub/static/app.js
+++ b/packages/meshbay-hub/src/meshbay_hub/static/app.js
@@ -429,7 +429,7 @@ function Nav({ user, theme, onThemeChange, onLogout, onMenuToggle, unreadCount,
// ── Sidebar ──────────────────────────────────────────────────────────────────
-function Sidebar({ groups, presence, indexProgressPct, route, menuOpen, role, hasNodeKey,
+function Sidebar({ groups, presence, indexProgressPct, route, menuOpen, role, showNode,
allowPublicGroups = true }) {
const isStaff = role === 'moderator' || role === 'admin';
return html`
@@ -449,7 +449,7 @@ function Sidebar({ groups, presence, indexProgressPct, route, menuOpen, role, ha
<a class="sidebar-item ${route === '/search' ? 'active' : ''}"
href="#/search"><${Icon} name="search" /> ${t('sidebar.search')}</a>
</div>
- ${platform.capabilities.nodeAdmin && hasNodeKey && html`
+ ${platform.capabilities.nodeAdmin && showNode && html`
<div class="sidebar-section">
<div class="sidebar-heading">${t('sidebar.node')}</div>
<a class="sidebar-item ${route === '/node' ? 'active' : ''}"
@@ -781,6 +781,9 @@ function App() {
const [notifDisabled, setNotifDisabled] = useState(false);
const [userPrefs, setUserPrefs] = useState({});
const [hasNodeKey, setHasNodeKey] = useState(false);
+ // A node on this machine, linked to the account or not: the Node section is
+ // where it is looked after, so it cannot wait for a link to show up.
+ const [localNode, setLocalNode] = useState(false);
// Instance policy, fetched once, unauthenticated. `null` until it answers;
// treat unknown as "allowed" so a slow hub never blocks a legitimate private
// group — the hub refuses a public one server-side regardless.
@@ -1041,6 +1044,25 @@ function App() {
fetchNotifications();
}, [user]);
+ // The node this application ships, set up, started and linked for whoever
+ // signs in (main.js `ensureNode`): installing it is all it should take.
+ // Keyed on the account, not the session object -- the token rotates, and a
+ // renewal is not a sign-in.
+ const signedInAs = user ? user.username : null;
+ useEffect(() => {
+ setLocalNode(false);
+ if (!signedInAs || !platform.capabilities.nodeAdmin || !platform.node.available) return;
+ let cancelled = false;
+ platform.node.installed()
+ .then((r) => { if (!cancelled) setLocalNode(Boolean(r && r.installed)); })
+ .catch(() => {});
+ const live = userRef.current;
+ platform.node.ensure({ username: live.username, token: live.token })
+ .then(() => { if (!cancelled) refreshNodeKey(); })
+ .catch(() => {});
+ return () => { cancelled = true; };
+ }, [signedInAs]);
+
// After an invitation is accepted: the group is ours now, and only the hub
// knows its row the way `/mine` answers it.
const reloadGroups = useCallback(() => {
@@ -1316,7 +1338,7 @@ function App() {
.catch(() => {});
refreshNodeKey();
}} />`;
- } else if (route === '/node' && platform.capabilities.nodeAdmin && hasNodeKey) {
+ } else if (route === '/node' && platform.capabilities.nodeAdmin && (hasNodeKey || localNode)) {
page = html`<${LazyNodePage} groups=${groups} token=${user.token} username=${user.username} />`;
} else if (groupId) {
page = html`<${GroupPage}
@@ -1380,7 +1402,7 @@ function App() {
menuOpen=${menuOpen}
role=${user.role}
allowPublicGroups=${allowPublicGroups}
- hasNodeKey=${hasNodeKey} />`}
+ showNode=${hasNodeKey || localNode} />`}
${menuOpen && html`<div class="overlay visible"
onClick=${() => setMenuOpen(false)} />`}
<main class="main">