5.4 KiB
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.
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:Secrets come fromdocker compose --env-file .env.prod -f docker-compose.prod.yml up -d --build.env.prod(gitignored; copy from.env.prod.example). Never run baredocker compose upon 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 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 varsbackend/app/database.py— Async engine + session factory + declarative Basebackend/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 <script setup>, Pinia stores, Axios API modules.
frontend/src/main.ts— App entry, globally registers Naive UI components (MUST useN-prefixed names:NButtonnotButton)frontend/src/layouts/UnifiedLayout.vue— Main authenticated layout: 64px icon sidebar + 300px secondary panel + main contentfrontend/src/router/index.ts— All auth routes underUnifiedLayout, uses namedsecondary+defaultrouter-viewsfrontend/src/api/client.ts— Axios with JWT interceptors and auto-refresh on 401frontend/src/composables/useWebSocket.ts— WebSocket client with exponential backoff reconnect
CRITICAL: Naive UI components must be registered with N-prefixed names (e.g., app.component('NButton', NButton)) because Vue resolves <n-button> to NButton. The component.name property returns "Button" (no prefix) — do NOT use component.name for registration.
Database
PostgreSQL 16 with tables auto-created on startup. No Alembic migrations set up yet. Adding columns to existing tables requires:
- Manual SQL:
docker compose exec postgres psql -U qingye -d qingye -c "ALTER TABLE ..." - Then the model update will work
Key Files
| File | Purpose |
|---|---|
docker-compose.yml |
4 services: postgres, redis, backend, frontend |
frontend/src/layouts/UnifiedLayout.vue |
Main layout with fixed left icon sidebar |
frontend/src/views/chat/ConversationListPanel.vue |
Reusable conversation list with 3 display modes |
frontend/src/views/chat/GroupInfoPanel.vue |
Group management (members, roles) |
frontend/src/views/chat/CreateGroupModal.vue |
Create group with friend selection |
frontend/src/views/moments/MomentsFeedView.vue |
Friend circle / moments feed |
frontend/src/views/moments/MomentCard.vue |
Single moment card with like/comment |
frontend/src/views/settings/ |
Settings pages: profile, account (password+email), notifications, about |
backend/app/services/moment_service.py |
Moments business logic (feed, like, comment, visibility) |
backend/app/services/conversation_service.py |
Chat + group management (create, update, add/remove members) |
backend/app/services/friend_service.py |
Friend system (request, direct-add, remark) |
Environment Variables
Defined in .env.example: DATABASE_URL, REDIS_URL, JWT_SECRET_KEY, JWT_REFRESH_SECRET_KEY, CORS_ORIGINS, ADMIN_PASSWORD, VITE_API_BASE_URL, VITE_WS_BASE_URL.