summaryrefslogtreecommitdiffstats
path: root/packages/meshbay-node/src
diff options
context:
space:
mode:
Diffstat (limited to 'packages/meshbay-node/src')
-rw-r--r--packages/meshbay-node/src/meshbay_node/ops.py35
1 files changed, 28 insertions, 7 deletions
diff --git a/packages/meshbay-node/src/meshbay_node/ops.py b/packages/meshbay-node/src/meshbay_node/ops.py
index 6d53edb..7a8e8ac 100644
--- a/packages/meshbay-node/src/meshbay_node/ops.py
+++ b/packages/meshbay-node/src/meshbay_node/ops.py
@@ -180,6 +180,34 @@ async def create_invite(state: dict, group_id: str, username: str, *,
raise OpError(f"Unknown user {username!r}: {e}", status=404) from e
user_id = account["user_id"]
+ # Hub membership first, and fatal if it fails.
+ #
+ # `/v1/groups/mine` joins `GroupMember`, so someone who was never registered
+ # does not see the group at all and can never redeem the code. Creating the
+ # invite first and tolerating a failed registration — which is what this did
+ # — hands the operator a code that cannot work, and says nothing. Worse, an
+ # unreachable hub raised *after* the roster write, leaving a valid code
+ # nobody was ever given; every retry left another.
+ #
+ # Registering before the roster write means a failure costs nothing: no code
+ # exists to be orphaned. A membership row without an invite is harmless —
+ # without the code there is still no group key.
+ #
+ # The endpoint is idempotent (`if not mem: db.add(...)`, no 409), so the SPA
+ # registering the same membership again right after `createInvite`
+ # (group-settings.js) costs nothing either.
+ #
+ # Skipped only when there is no username to register with: the MNP path
+ # allows an empty one (`username || ''` in transport.js), and there the SPA
+ # is the one that registers.
+ if username:
+ try:
+ await _hub(state).add_group_member(group_id, username)
+ except Exception as e:
+ raise OpError(
+ f"Could not register {username!r} on the hub, so the invite "
+ f"could not be redeemed: {e}", status=502) from e
+
config = state.get("config")
ttl = (config.node.invite_ttl_hours if config else 168) * 3600
code = await roster.create_invite(
@@ -191,13 +219,6 @@ async def create_invite(state: dict, group_id: str, username: str, *,
username=username,
)
- hub = _hub(state)
- try:
- await hub.add_group_member(group_id, username)
- except Exception:
- log.warning("Could not register %s as hub member of %s",
- username, group_id[:8])
-
invites = await roster.list_invites()
expires = next((i["expires_at"] for i in invites
if i["user_id"] == user_id