aboutsummaryrefslogtreecommitdiffstats
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.py37
1 files changed, 36 insertions, 1 deletions
diff --git a/packages/meshbay-node/src/meshbay_node/ops.py b/packages/meshbay-node/src/meshbay_node/ops.py
index b680d43..e6b2d1f 100644
--- a/packages/meshbay-node/src/meshbay_node/ops.py
+++ b/packages/meshbay-node/src/meshbay_node/ops.py
@@ -41,7 +41,7 @@ from meshbay_common.join import ROLE_MEMBER, ROLE_OPERATOR
from meshbay_node.config import DEFAULT_CONFIG_PATH
from meshbay_node.roots import RootError, RootSet, off_disk
-from meshbay_node.roster import Roster
+from meshbay_node.roster import LinkInviteLimit, Roster
log = logging.getLogger(__name__)
@@ -236,6 +236,41 @@ async def create_invite(state: dict, group_id: str, username: str, *,
"username": username, "user_id": user_id}
+async def create_link_invite(state: dict, group_id: str, *,
+ created_by: str = "local-cli") -> dict:
+ """
+ Issue a code bound to no account, for an invitation link.
+
+ Nothing is registered on the hub here, unlike `create_invite`: there is no
+ account to register yet. The hub half is a ticket the inviter's client asks
+ the hub for, bound to the invitee's address (docs/invite-links.md §3.5).
+ """
+ roster = _roster(state)
+ _group_ctx(state, group_id)
+ config = state.get("config")
+ ttl = (config.node.invite_ttl_hours if config else 168) * 3600
+ try:
+ code, invite_id, expires = await roster.create_link_invite(
+ group_id, created_by, ttl=ttl)
+ except LinkInviteLimit as e:
+ raise OpError(str(e), status=429) from e
+ log.info("Invitation link issued: group=%s invite=%s", group_id[:8], invite_id[:8])
+ return {"code": code, "invite_id": invite_id, "expires_at": expires,
+ "group_id": group_id}
+
+
+async def cancel_invite(state: dict, group_id: str, invite_id: str) -> dict:
+ """Take back an unredeemed invitation link. Unknown or spent is a refusal,
+ so a mistyped handle does not read as success."""
+ roster = _roster(state)
+ _group_ctx(state, group_id)
+ if not await roster.cancel_invite(group_id, invite_id):
+ raise OpError("No unredeemed invitation link with that id in this group",
+ status=404)
+ log.info("Invitation link cancelled: group=%s invite=%s", group_id[:8], invite_id[:8])
+ return {"cancelled": True, "invite_id": invite_id, "group_id": group_id}
+
+
async def revoke_member(state: dict, user_id: str, group_id: str) -> dict:
"""
Stop serving the group key to someone.