ccd92787d6f3e1fbe226b19c356b697bdee1f25a
Root cause (found earlier): the server only pushes to users it believes are "connected" to a room, but that was a raw WebSocket- connection check with no concept of whether the tab was actually foregrounded -- a backgrounded-but-still-connected tab looked exactly like someone actively watching, so the push got suppressed even though nothing could surface on a hidden page. No backend change needed: the server's presence tracking (and the push-suppression logic built on it) was already correct for "not joined to this room's channel" -- the gap was purely that the client never told it about backgrounding. useChatSocket.ts now tracks document.visibilityState and sends "leave" for every desired room when hidden (without forgetting the app still wants them joined), and "join" again when visible -- reusing the exact join/leave path a real room switch already goes through, no new WS message type or backend logic required. Rejoining also triggers a message-history refetch in ChatPane (keyed off the server's existing "joined" ack), so anything sent while backgrounded gets backfilled instead of silently missing -- as a side effect, this also fixes reconnect-after-a-dropped-connection never backfilling either, which had the same gap. Verified end-to-end: simulated backgrounding in the browser and confirmed via direct Redis inspection that the room's presence hash (what push-suppression actually reads) goes empty on hide and repopulates with a message resync on show. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
DS Chat
A web-based team chat service (Mattermost-style, no threaded conversations), invite-only. See ARCHITECTURE.md for the full system design and phased build plan.
Phase 1: auth, open-room CRUD, and single-instance WebSocket chat, backend
- a minimal frontend. Phase 2: private rooms, room roles (owner/admin/ member), and room invites — backend only, see below. Later phases (push notifications, Redis fan-out, the admin portal, the bot/extension system, and production deployment) are tracked as issues in the repo's issue tracker, prioritized.
Structure
backend/— FastAPI + SQLAlchemy 2.0 (async) + PostgreSQL. Seebackend/README.mdfor local setup, migrations, how to create a user (site registration is invite-only — no public sign-up endpoint), and the Phase 2 room-roles/invites API.frontend/— React + Vite PWA (login, room list, chat view). Still Phase-1-only: it doesn't yet call any of the Phase 2 endpoints. A UI redesign is happening separately; frontend work resumes once that lands.
Quickstart
# 1. Postgres (see backend/README.md for details)
docker run -d --name ds-chat-postgres \
-e POSTGRES_USER=ds_chat -e POSTGRES_PASSWORD=ds_chat -e POSTGRES_DB=ds_chat \
-p 5432:5432 postgres:16-alpine
# 2. Backend
cd backend
python3 -m venv .venv
.venv/bin/pip install -e ".[dev]"
cp .env.example .env # then set SESSION_SECRET
.venv/bin/alembic upgrade head
.venv/bin/python -m app.cli create-user alice alice@example.com "some-password"
.venv/bin/uvicorn app.main:app --reload &
# 3. Frontend (in another shell)
cd frontend
npm install
npm run dev
Then open http://localhost:5173 and log in with the account created above.
The Vite dev server proxies /api and /ws to the backend on :8000, so no
CORS configuration is needed in development.
Deployment
See DEPLOYMENT.md for the full production runbook — two Debian 13 servers, no containers, matching ARCHITECTURE.md §9.