summaryrefslogtreecommitdiffstats
path: root/packages/meshbay-node/src/meshbay_node/ops.py
diff options
context:
space:
mode:
Diffstat (limited to 'packages/meshbay-node/src/meshbay_node/ops.py')
-rw-r--r--packages/meshbay-node/src/meshbay_node/ops.py47
1 files changed, 24 insertions, 23 deletions
diff --git a/packages/meshbay-node/src/meshbay_node/ops.py b/packages/meshbay-node/src/meshbay_node/ops.py
index c3a3f9c..13a7250 100644
--- a/packages/meshbay-node/src/meshbay_node/ops.py
+++ b/packages/meshbay-node/src/meshbay_node/ops.py
@@ -390,7 +390,7 @@ async def list_groups(state: dict) -> dict:
async def attach_group(state: dict, name: str, shared_dir: str,
- upload_dir: str = "") -> dict:
+ upload_dir: str = "", writable: bool = True) -> dict:
"""
Write a new [[groups]] block into node.toml.
@@ -448,7 +448,7 @@ async def attach_group(state: dict, name: str, shared_dir: str,
# Forward slashes: a Windows path in a TOML basic string is a
# parse error (`\U`, `\a`, ... are escapes). pathlib reads `/`.
f' path = "{path.as_posix()}"\n'
- f' writable = true\n')
+ f' writable = {"true" if writable else "false"}\n')
try:
with conf_path.open("a", encoding="utf-8", newline="\n") as f:
f.write(block)
@@ -457,6 +457,7 @@ async def attach_group(state: dict, name: str, shared_dir: str,
result = {"group_id": group["id"], "name": group["name"],
"shared_dir": str(path), "config": str(conf_path),
+ "writable": writable,
"note": "restart the node to pick it up"}
return result
@@ -629,7 +630,7 @@ def _remove_roots_block(conf_path: Path, group_id: str,
conf_path.write_text("\n".join(new_lines), encoding="utf-8", newline="\n")
return
- raise OpError(f"Root path not found in config", status=404)
+ raise OpError("Root path not found in config", status=404)
async def add_root(state: dict, group_id: str, path: str, *,
@@ -670,9 +671,9 @@ async def add_root(state: dict, group_id: str, path: str, *,
if kind != "generic":
root_block += f'\n kind = "{added.kind}"'
if writable:
- root_block += f'\n writable = true'
+ root_block += '\n writable = true'
if removable:
- root_block += f'\n removable = true'
+ root_block += '\n removable = true'
_insert_roots_block(conf_path, group_id, root_block)
from meshbay_node.config import RootSpec
@@ -822,18 +823,17 @@ async def eject_root(state: dict, group_id: str, root_name: str) -> dict:
return {"status": "already_ejected", "name": root_name,
"group_id": group_id, "roots": roots.describe()}
- root.ejected = True
- root.available = False
-
- roster = _roster(state)
- if roster:
- await roster.set_setting(
- group_id, f"root_ejected:{fold(root_name)}", "1",
- set_by=state.get("node_user_id", ""))
-
+ # The indexer stops its watchdog and freezes the entries; it holds the same
+ # RootSet object, but the flags are set here too so a context whose indexer
+ # was replaced by a retarget cannot be left disagreeing with the roster.
indexer = state.get("indexers", {}).get(group_id)
if indexer:
indexer.eject_root(root_name)
+ root.ejected = True
+ root.available = False
+
+ await _roster(state).set_root_ejected(
+ group_id, root_name, True, set_by=state.get("node_user_id", ""))
log.info("Root ejected: %s from group %s", root_name, group_id[:8])
return {"status": "ejected", "name": root_name, "group_id": group_id,
@@ -869,18 +869,17 @@ async def plug_root(state: dict, group_id: str, root_name: str) -> dict:
f"Directory not found: {root.path}. Is the device connected?",
status=409)
- root.ejected = False
- root.available = True
-
- roster = _roster(state)
- if roster:
- await roster.set_setting(
- group_id, f"root_ejected:{fold(root_name)}", "0",
- set_by=state.get("node_user_id", ""))
+ # Persisted before the rescan, which can take minutes on a large library:
+ # a crash halfway through must leave the root plugged, not ejected with
+ # entries half rebuilt.
+ await _roster(state).set_root_ejected(
+ group_id, root_name, False, set_by=state.get("node_user_id", ""))
indexer = state.get("indexers", {}).get(group_id)
if indexer:
await indexer.plug_root(root_name)
+ root.ejected = False
+ root.available = root.is_live()
log.info("Root plugged: %s in group %s", root_name, group_id[:8])
return {"status": "plugged", "name": root_name, "group_id": group_id,
@@ -947,7 +946,7 @@ def _update_root_field(conf_path: Path, group_id: str,
conf_path.write_text("\n".join(lines), encoding="utf-8", newline="\n")
return
- raise OpError(f"Root path not found in config", status=404)
+ raise OpError("Root path not found in config", status=404)
# ── Files ────────────────────────────────────────────────────────────────────
@@ -1125,6 +1124,8 @@ async def set_enabled_apps(state: dict, group_id: str, apps: list[str]) -> dict:
"""
roster = _roster(state)
ctx = _group_ctx(state, group_id)
+ # See the same guard in webrtc_server._do_apps_enabled: Files cannot be
+ # turned off, and both writers put it at the front so the two agree.
if "files" not in apps:
apps = ["files"] + list(apps)
await roster.set_enabled_apps(group_id, apps,