diff options
| author | Christophe Besson <cbesson@gmail.com> | 2026-08-11 16:16:32 +0200 |
|---|---|---|
| committer | Christophe Besson <cbesson@gmail.com> | 2026-08-11 16:16:32 +0200 |
| commit | f74e2c0d527354072a5064d2dac8f73494ec8b49 (patch) | |
| tree | d93d835b50f4513af30268f9d2c2570acd57e9bd /packages/meshbay-hub | |
| parent | b50750466622dd6cda0fc084d39dfce000ad0081 (diff) | |
| download | meshbay-f74e2c0d527354072a5064d2dac8f73494ec8b49.tar.gz | |
feat: chat notifications via hub, file delete, upload fix, cached display, inline thumbnails
Backend:
- Chat store persists sender_name (SQLite migration, no more UUID display)
- FILE_DELETE / FILE_DELETE_ACK MNP types — node admin can delete files
- Node sends chat_notify to hub WS — hub creates notifications for offline members
- Hub revocation.py handles chat_notify, creates per-member notifications
Frontend:
- Upload chunk size 64KB (was 1MB) — fixes WebRTC DataChannel max-message-size
- Show cached files immediately while WebRTC connects
- ChatImage component — inline image thumbnails in chat (download+decrypt)
- File delete action in menu (group admin, with confirm dialog)
- Member panel: "Owner" label instead of confusing "Group admin"
- Create group page: hint about needing a node
- Refresh index after chat attachment upload
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Diffstat (limited to 'packages/meshbay-hub')
| -rw-r--r-- | packages/meshbay-hub/src/meshbay_hub/api/revocation.py | 36 |
1 files changed, 36 insertions, 0 deletions
diff --git a/packages/meshbay-hub/src/meshbay_hub/api/revocation.py b/packages/meshbay-hub/src/meshbay_hub/api/revocation.py index 8f65f89..00258c1 100644 --- a/packages/meshbay-hub/src/meshbay_hub/api/revocation.py +++ b/packages/meshbay-hub/src/meshbay_hub/api/revocation.py @@ -99,6 +99,36 @@ def _sign_revocation(target: str, target_id: str, reason: str) -> str: # ── WebSocket endpoint ──────────────────────────────────────────────────────── @router.websocket("/v1/nodes/ws") +async def _handle_chat_notify(group_id: str, sender_name: str, sender_user_id: str) -> None: + """Node informs hub that a chat message was posted — create notifications for offline members.""" + if not group_id: + return + try: + from meshbay_hub.db.engine import get_session_factory + from meshbay_hub.db.models import GroupMember, Group + from meshbay_hub.api.notifications import create_notification + + async with get_session_factory()() as db: + group = await db.get(Group, group_id) + if not group: + return + result = await db.execute( + select(GroupMember.user_id).where(GroupMember.group_id == group_id) + ) + member_ids = [r[0] for r in result.all()] + for uid in member_ids: + if uid == sender_user_id: + continue + await create_notification( + db, uid, "chat_message", + f"{sender_name or 'Someone'} posted in {group.name}", + link=f"#/group/{group_id}", + ) + await db.commit() + except Exception as e: + log.warning("Chat notify failed: %s", e) + + async def node_websocket(ws: WebSocket): """ Persistent WebSocket connection for nodes. @@ -145,6 +175,12 @@ async def node_websocket(ws: WebSocket): elif msg.get("type") == "webrtc_answer": from meshbay_hub.api.signaling import handle_webrtc_answer handle_webrtc_answer(msg) + elif msg.get("type") == "chat_notify": + asyncio.ensure_future(_handle_chat_notify( + msg.get("group_id", ""), + msg.get("sender_name", ""), + decoded.get("sub", ""), + )) except WebSocketDisconnect: log.info("Node WS disconnected: %s", (node_id or "unknown")[:8]) |