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.py53
1 files changed, 52 insertions, 1 deletions
diff --git a/packages/meshbay-hub/src/meshbay_hub/api/groups.py b/packages/meshbay-hub/src/meshbay_hub/api/groups.py
index 49902de..fcf0360 100644
--- a/packages/meshbay-hub/src/meshbay_hub/api/groups.py
+++ b/packages/meshbay-hub/src/meshbay_hub/api/groups.py
@@ -7,7 +7,8 @@ from sqlalchemy import func, or_, select, update
from sqlalchemy.exc import IntegrityError
from sqlalchemy.ext.asyncio import AsyncSession
-from meshbay_hub import hub_settings
+from meshbay_hub import hub_settings, mail
+from meshbay_hub.auth import decrypt_email
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
@@ -671,3 +672,53 @@ async def delete_group(
return {"status": "deleted", "group_id": group_id}
+class InviteNotifyRequest(BaseModel):
+ username: str
+ code: str
+ group_name: str
+
+
+@router.post("/{group_id}/invite-notify")
+async def invite_notify(
+ group_id: str,
+ body: InviteNotifyRequest,
+ current_user: User = Depends(require_user_scope),
+ db: AsyncSession = Depends(get_db),
+):
+ """Send an invitation email to a member who was just invited.
+
+ The invite code was created on the node — the hub only knows about it
+ because the inviter's browser sends it here. The hub looks up the
+ invitee's encrypted email, decrypts it, and sends the notification.
+ The inviter never sees the email address.
+ """
+ group = await db.get(Group, group_id)
+ if not group:
+ raise HTTPException(status_code=404, detail="Group not found")
+ if group.admin_id != current_user.id:
+ raise HTTPException(status_code=403,
+ detail="Only the group owner can send invitations")
+
+ target = (await db.execute(
+ select(User).where(User.username == body.username))).scalar_one_or_none()
+ if not target:
+ raise HTTPException(status_code=404, detail="User not found")
+
+ email = ""
+ try:
+ email = decrypt_email(target.email) if target.email else ""
+ except Exception:
+ pass
+
+ if not email:
+ return {"status": "no_email"}
+
+ try:
+ mail.send_invite_notification(
+ email, body.code, current_user.username, body.group_name)
+ except Exception:
+ return {"status": "send_failed"}
+
+ return {"status": "sent"}
+
+