summaryrefslogtreecommitdiffstats
path: root/packages/meshbay-hub
diff options
context:
space:
mode:
authorChristophe Besson <cbesson@gmail.com>2026-08-11 16:16:32 +0200
committerChristophe Besson <cbesson@gmail.com>2026-08-11 16:16:32 +0200
commitf74e2c0d527354072a5064d2dac8f73494ec8b49 (patch)
treed93d835b50f4513af30268f9d2c2570acd57e9bd /packages/meshbay-hub
parentb50750466622dd6cda0fc084d39dfce000ad0081 (diff)
downloadmeshbay-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.py36
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])