# CLAUDE.md This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. ## Project Overview **青叶 (QingYe)** — a social chat application with teal-green theme (#009688 primary). Monorepo with `frontend/` (Vue 3) and `backend/` (Python FastAPI), all orchestrated via Docker Compose. ## Development Commands All services run in Docker. No host dependencies needed. ```bash docker compose up --build # Start all 4 services docker compose up -d # Start detached docker compose down # Stop docker compose logs -f backend # View logs docker compose restart backend # Restart a service ``` **Backend** runs on `localhost:8000`, **Frontend** on `localhost:5173`. Both have hot-reload via volume mounts. Database changes requiring new columns need manual `ALTER TABLE` or `docker compose restart backend` (which triggers `Base.metadata.create_all`). ### Production (dev/prod separation) Two **independent, self-contained** Compose files: - `docker-compose.yml` — **dev** (hot-reload, `docker compose up`). Leave untouched for local dev. - `docker-compose.prod.yml` — **prod**. Run with: ```bash docker compose --env-file .env.prod -f docker-compose.prod.yml up -d --build ``` Secrets come from `.env.prod` (gitignored; copy from `.env.prod.example`). **Never run bare `docker compose up` on the server.** CRITICAL prod invariant: the backend **must run single-process**. The WebSocket `ConnectionManager` (`backend/app/websocket/manager.py`) is an in-memory singleton — `--workers N` / gunicorn multi-worker splits connections and silently breaks real-time messaging. `backend/Dockerfile.prod` is pinned to single-process; do not change. The frontend prod build is domain-agnostic (same-origin `/api/v1`, auto `wss://`). See [DEPLOYMENT.md](DEPLOYMENT.md) for the full VPS (Ubuntu 20.04 + host Nginx + certbot) walkthrough. ## Architecture ### Backend (FastAPI + SQLAlchemy 2.0 async) **Pattern**: Router → Service → Model, with Pydantic v2 schemas for request/response. - `backend/app/main.py` — App entry, lifespan (creates DB tables, seeds config), router registration at `/api/v1/{module}` - `backend/app/config.py` — Pydantic Settings from env vars - `backend/app/database.py` — Async engine + session factory + declarative Base - `backend/app/dependencies.py` — `get_db()`, `get_current_user()`, `get_admin_user()` - `backend/app/utils/security.py` — JWT (HS256, 30min access / 7day refresh), bcrypt hashing **API routes**: auth, users, conversations, messages, friends, admin, uploads, **moments** (friend circle) **Models** (7 + 3 new): User, Conversation, ConversationMember, Message, Friend, FriendRequest, SystemConfig, **Moment, MomentLike, MomentComment** **Key convention**: All IDs are `str(uuid.uuid4())`, not auto-increment. Services return dicts, not ORM objects. Timestamps use `datetime.utcnow()` (NOT timezone-aware — PostgreSQL columns are `TIMESTAMP WITHOUT TIME ZONE`). ### Frontend (Vue 3 + Vite + Naive UI) **Pattern**: Composition API with `