Skip to content

carols12352/chat

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

61 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Chat WebSocket Demo

A minimal chat example built with FastAPI + WebSocket + Postgres. The frontend uses a Next.js + React chat panel UI.

Structure

  • backend/main.py: FastAPI app bootstrap (CORS enabled)
  • backend/routes.py: Aggregates HTTP + WebSocket routers
  • backend/http_routes.py: Auth + chat HTTP routes
  • backend/ws_routes.py: Chat WebSocket routes
  • backend/ws_state.py: Shared WebSocket connection state/helpers
  • backend/auth.py: JWT helpers and auth endpoints
  • backend/emailer.py: SMTP email sender for verification codes
  • backend/db.py: Postgres storage for users, conversations, messages
  • backend/db_postgres.py: Postgres facade (exports storage helpers)
  • backend/db_postgres_core.py: Postgres pool + config
  • backend/db_postgres_schema.py: Postgres schema/bootstrap
  • backend/db_postgres_users.py: User + verification storage
  • backend/db_postgres_messages.py: Message storage
  • backend/db_postgres_conversations.py: Conversation + membership storage
  • frontend/app/login/page.tsx: Login/sign up UI
  • frontend/app/chat/page.tsx: Chat workspace UI
  • frontend/app/page.tsx: Root redirect to login/chat
  • frontend/app/globals.css: styling

Setup

Must be Python3.10

On windows:

python -m venv venv
venv\Scripts\activate
pip install -r requirements.txt

On macOS/Linux:

python -m venv venv
source venv/bin/activate
pip install -r requirements.txt

Running the backend

uvicorn backend.main:app --host 0.0.0.0 --port 8000 --reload

Default base URL: http://localhost:8000

Environment variables

  • Backend reads from root .env (loaded by backend/main.py).
  • Frontend reads from frontend/.env.local.
  • .env_eg is a template only and is not loaded automatically.

Rate limiting is enabled by default (in-memory, per-process). Toggle with:

CHAT_RATE_LIMIT_ENABLED=1

HTTP endpoints (all require Authorization: Bearer <token>):

POST /auth/signup
POST /auth/login
GET /auth/me
GET /bootstrap
POST /auth/verify
POST /auth/resend-verification
PATCH /users/me
GET /messages/{conversation_id}?before_id=123&limit=30
GET /messages/{conversation_id}?since_id=123
GET /conversations
PATCH /conversations/{conversation_id}
GET /conversations/{conversation_id}/members
POST /conversations/{conversation_id}/members
DELETE /conversations/{conversation_id}/members/{member_id}
POST /conversations/{conversation_id}/read
POST /conversations
GET /invites
POST /invites/{invite_id}/accept
POST /invites/{invite_id}/decline

WebSocket endpoint (token required as query param or first auth message):

ws://localhost:8000/ws/chat/{user_id}

After connect, send:

{"type":"auth","token":"YOUR_JWT"}

Legacy query param still works:

ws://localhost:8000/ws/chat/{user_id}?token=YOUR_JWT

Running the frontend

cd frontend
npm install
npm run dev

Open http://localhost:3000 in your browser.

Routes:

  • /login for sign in / sign up
  • /chat for the chat workspace (requires verified email)

Optional: override the API base URL (defaults to http://localhost:8000):

NEXT_PUBLIC_API_BASE=http://localhost:8000 npm run dev

You can also point to a different host (LAN/VPN); the frontend will honor the explicit env value even if it differs from the page host.

If WebSocket access is blocked on port 8000, the frontend dev server proxies /ws to the backend. Set the backend target before starting:

NEXT_PUBLIC_API_BASE=http://172.25.223.62:8000 BACKEND_WS=ws://172.25.223.62:8000 npm run dev

If you need to allow non-localhost dev origins, set a comma-separated list:

NEXT_DEV_ORIGINS=http://192.168.1.10:3000,http://my-host:3000 npm run dev

Automated tests

Backend (pytest):

pip install -r requirements.txt
pip install pytest
pytest backend/tests

Frontend (Playwright):

cd frontend
npm install
npx playwright install
npm run test:e2e

Note: the Playwright test expects the frontend to be running at PLAYWRIGHT_BASE_URL (defaults to http://localhost:3000).

Database

Local dev uses Postgres via DATABASE_URL:

DATABASE_URL=postgresql://USER:PASSWORD@HOST:5432/postgres

Use a schema name if you want to isolate test data:

CHAT_DB_SCHEMA=test

Reset Postgres (drops all chat tables):

python scripts/reset_postgres.py

Local Postgres for tests (Docker)

Run a local Postgres just for pytest:

docker compose -f docker-compose.test.yml up -d

Then run tests:

pytest backend/tests

Stop the test database:

docker compose -f docker-compose.test.yml down

Email verification (SMTP)

Set the following environment variables for SMTP:

SMTP_HOST=smtp.gmail.com
SMTP_PORT=587
SMTP_USER=your-email@gmail.com
SMTP_PASS=your-app-password
SMTP_FROM="Chat Support <your-email@gmail.com>"
SMTP_USE_TLS=true

Verification rules:

  • 6-digit code, expires in 10 minutes
  • Resend allowed every 60 seconds
  • Unverified users can log in but cannot access chat or WebSocket
  • Invite endpoints require verified accounts

Usage

  1. Sign up or log in to get a token (the frontend stores it automatically).
  2. Click Connect to open the WebSocket.
  3. Click a conversation to load history via GET /messages/{conversation_id}.
  4. Type a message and click Send.
  5. Other connected users receive messages in real-time via WebSocket.

Display names

  • PATCH /users/me updates the display name (must be non-empty).
  • Other participants see the updated display name; your own messages still show You.

Notes

  • This is a minimal example without auth or a message queue.
  • Conversations support optional names via PATCH /conversations/{conversation_id}.
  • The chat UI supports local display names, draft persistence, failed-send retry, typing/active indicators, and local message search with highlights (in Settings).
  • Self-message recall is supported via right-click (desktop) or long-press (mobile); recall cancels the local message only.
  • Quotes use a structured bubble (similar to WeChat/Instagram) and do not pollute message text.
  • WebSocket connections are limited to 3 per user; excess connections are rejected with a reason.
  • New members join via chat invites; acceptance is required before membership is added.
  • UI errors show a short message plus an error code; see docs/error-codes.md for mappings.
  • Disabled endpoints return E_ENDPOINT_DISABLED (see error codes doc).
  • Backend logs HTTP and unhandled errors to stdout; set LOG_LEVEL to control verbosity.
  • WebSocket rejects non-membership actions (close code 4003) without logging users out.

About

chat practice

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Contributors 2

  •  
  •