aboutsummaryrefslogtreecommitdiffstats
path: root/packages/meshbay-hub/src/meshbay_hub/api/groups.py
diff options
context:
space:
mode:
Diffstat (limited to 'packages/meshbay-hub/src/meshbay_hub/api/groups.py')
-rw-r--r--packages/meshbay-hub/src/meshbay_hub/api/groups.py34
1 files changed, 32 insertions, 2 deletions
diff --git a/packages/meshbay-hub/src/meshbay_hub/api/groups.py b/packages/meshbay-hub/src/meshbay_hub/api/groups.py
index e78d950..91aa9c4 100644
--- a/packages/meshbay-hub/src/meshbay_hub/api/groups.py
+++ b/packages/meshbay-hub/src/meshbay_hub/api/groups.py
@@ -6,6 +6,7 @@ from datetime import datetime, timezone
from sqlalchemy import func, or_, select, update
from sqlalchemy.ext.asyncio import AsyncSession
+from meshbay_hub import hub_settings
from meshbay_hub.api.deps import get_current_user, require_user_scope
from meshbay_hub.api.netutil import client_ip
from meshbay_hub.db.engine import get_db
@@ -107,7 +108,17 @@ async def group_online_nodes(
if not group:
raise HTTPException(status_code=404, detail="Group not found")
if group.status != "active":
- raise HTTPException(status_code=403, detail="Group is suspended")
+ # Name the real state: "suspended" is reversible, "revoked" is a signed
+ # instruction every node enforces. The client shows this string as-is.
+ raise HTTPException(status_code=403, detail=f"Group is {group.status}")
+
+ # A public group is normally readable by anyone — that is the point of it.
+ # But when the hub has public groups switched off, an existing one keeps
+ # working only for the people already in it: no non-member gets handed a
+ # node to connect to. Members always have a row here, so they are unaffected.
+ if group.visibility == "public" and not await hub_settings.public_groups_allowed(db):
+ if not await db.get(GroupMember, (group_id, current_user.id)):
+ raise HTTPException(status_code=403, detail="Not a member of this group")
node_ids = get_online_nodes_for_group(group_id)
nodes = []
@@ -127,6 +138,12 @@ async def list_public_groups(
include_federated: bool = True,
):
"""List/search public groups — local and optionally federated. No auth required."""
+ # The hub admin can switch public groups off for the whole instance. When
+ # they have, there is no directory at all — not the local groups that predate
+ # the switch, and not the federated ones a peer still advertises. The switch
+ # is read live, so flipping it back brings the directory straight back.
+ if not await hub_settings.public_groups_allowed(db):
+ return {"groups": [], "total": 0}
# Unhosted groups are absent from the directory: until a node announces it,
# a group has no files, no key and nothing to connect to, so listing it only
# produces a dead end. Its owner still sees it in /mine while they set it up.
@@ -272,9 +289,14 @@ async def join_group(
if not group:
raise HTTPException(status_code=404, detail="Group not found")
if group.status != "active":
- raise HTTPException(status_code=403, detail="Group is not active")
+ raise HTTPException(status_code=403, detail=f"Group is {group.status}")
if group.join_policy != "open":
raise HTTPException(status_code=403, detail="Group does not allow open joining")
+ if group.visibility == "public" and not await hub_settings.public_groups_allowed(db):
+ # The group predates the switch; open joining is off with it. Existing
+ # members keep their row and their access.
+ raise HTTPException(
+ status_code=403, detail="This hub does not allow joining public groups.")
existing = await db.get(GroupMember, (group_id, current_user.id))
if existing:
@@ -365,6 +387,14 @@ async def create_group(
detail="A private group is invite-only. Make it public if you want "
"anyone to be able to join.")
if body.visibility == "public":
+ if not await hub_settings.public_groups_allowed(db):
+ # Instance policy, set by a hub admin in the panel. Absolute — staff
+ # included — because the way back is to re-enable it, not to slip
+ # past it. A client that still shows the "open" choice lands here.
+ raise HTTPException(
+ status_code=403,
+ detail="This hub does not allow public groups. Create the group "
+ "as private — you can still invite people to it.")
await _check_public_group_quota(db, current_user)
desc = (body.description or "")[:512] if body.description else None