91 lines
5.4 KiB
Markdown
91 lines
5.4 KiB
Markdown
# 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 `<script setup>`, Pinia stores, Axios API modules.
|
|
|
|
- `frontend/src/main.ts` — App entry, globally registers Naive UI components (MUST use `N`-prefixed names: `NButton` not `Button`)
|
|
- `frontend/src/layouts/UnifiedLayout.vue` — Main authenticated layout: 64px icon sidebar + 300px secondary panel + main content
|
|
- `frontend/src/router/index.ts` — All auth routes under `UnifiedLayout`, uses named `secondary` + `default` router-views
|
|
- `frontend/src/api/client.ts` — Axios with JWT interceptors and auto-refresh on 401
|
|
- `frontend/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:
|
|
1. Manual SQL: `docker compose exec postgres psql -U qingye -d qingye -c "ALTER TABLE ..."`
|
|
2. 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`.
|