feat: add implementation plan and monorepo guide for DreamChat project

This commit is contained in:
GW_MC
2026-02-23 19:14:45 +08:00
parent fbd5c84eca
commit ab02758382
8 changed files with 4286 additions and 0 deletions

725
doc/deployment.md Normal file
View File

@@ -0,0 +1,725 @@
# DreamChat Deployment Guide
## Overview
This document covers the deployment configuration including Docker Compose setup, DevContainer configuration, and production deployment procedures.
## DevContainer Setup
### .devcontainer/devcontainer.json
```json
{
"name": "DreamChat Development",
"dockerComposeFile": "docker-compose.yml",
"service": "app",
"workspaceFolder": "/workspace",
"features": {
"ghcr.io/devcontainers/features/node:1": {
"version": "20"
},
"ghcr.io/devcontainers/features/docker-in-docker:2": {}
},
"customizations": {
"vscode": {
"extensions": [
"dbaeumer.vscode-eslint",
"esbenp.prettier-vscode",
"bradlc.vscode-tailwindcss",
"ms-vscode.vscode-typescript-next",
"nestjs.vscode-nestjs",
"prisma.prisma"
],
"settings": {
"editor.formatOnSave": true,
"editor.defaultFormatter": "esbenp.prettier-vscode",
"typescript.preferences.importModuleSpecifier": "relative"
}
}
},
"forwardPorts": [3000, 5173, 5432, 8080],
"portsAttributes": {
"3000": {
"label": "Backend API",
"onAutoForward": "notify"
},
"5173": {
"label": "Frontend Dev Server",
"onAutoForward": "notify"
},
"5432": {
"label": "PostgreSQL",
"onAutoForward": "silent"
},
"8080": {
"label": "Keycloak",
"onAutoForward": "notify"
}
},
"postCreateCommand": "bash .devcontainer/post-create.sh",
"remoteUser": "node",
"mounts": [
"source=${localWorkspaceFolderBasename}-node_modules,target=${containerWorkspaceFolder}/node_modules,type=volume",
"source=${localWorkspaceFolderBasename}-pnpm-store,target=/home/node/.local/share/pnpm/store,type=volume"
]
}
```
### .devcontainer/docker-compose.yml
```yaml
version: '3.8'
services:
app:
build:
context: ..
dockerfile: .devcontainer/Dockerfile
volumes:
- ..:/workspace:cached
- /var/run/docker.sock:/var/run/docker.sock
command: sleep infinity
environment:
- DATABASE_URL=postgresql://postgres:postgres@db:5432/dreamchat
- REDIS_URL=redis://redis:6379
- KEYCLOAK_URL=http://keycloak:8080
depends_on:
- db
- redis
- keycloak
networks:
- dreamchat-network
db:
image: ankane/pgvector:latest
restart: unless-stopped
environment:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
POSTGRES_DB: dreamchat
volumes:
- postgres-data:/var/lib/postgresql/data
ports:
- "5432:5432"
networks:
- dreamchat-network
redis:
image: redis:7-alpine
restart: unless-stopped
ports:
- "6379:6379"
networks:
- dreamchat-network
keycloak:
image: quay.io/keycloak/keycloak:23.0
restart: unless-stopped
command: start-dev
environment:
KEYCLOAK_ADMIN: admin
KEYCLOAK_ADMIN_PASSWORD: admin
KC_DB: postgres
KC_DB_URL: jdbc:postgresql://db:5432/keycloak
KC_DB_USERNAME: postgres
KC_DB_PASSWORD: postgres
ports:
- "8080:8080"
depends_on:
- db
networks:
- dreamchat-network
volumes:
postgres-data:
networks:
dreamchat-network:
driver: bridge
```
### .devcontainer/Dockerfile
```dockerfile
FROM mcr.microsoft.com/devcontainers/typescript-node:20
# Install additional tools
RUN apt-get update && apt-get install -y \
postgresql-client \
redis-tools \
&& rm -rf /var/lib/apt/lists/*
# Install pnpm
RUN npm install -g pnpm@8
# Set working directory
WORKDIR /workspace
# Install global packages
RUN npm install -g @nestjs/cli@latest
# Create non-root user
USER node
```
### .devcontainer/post-create.sh
```bash
#!/bin/bash
set -e
echo "🚀 Setting up DreamChat monorepo development environment..."
# Install pnpm globally
npm install -g pnpm@8
# Install all dependencies (uses pnpm workspaces)
echo "📦 Installing dependencies..."
cd /workspace
pnpm install
# Build shared packages first
echo "📦 Building shared packages..."
pnpm --filter @dreamchat/shared build
# Generate Prisma client
pnpm db:generate
# Copy environment files if they don't exist
if [ ! -f /workspace/apps/backend/.env ]; then
echo "⚙️ Creating backend .env file..."
cat > /workspace/apps/backend/.env << EOF
NODE_ENV=development
PORT=3000
DATABASE_URL=postgresql://postgres:postgres@db:5432/dreamchat
JWT_SECRET=dev-jwt-secret-change-in-production
JWT_EXPIRES_IN=1h
JWT_REFRESH_EXPIRES_IN=7d
LLM_PROVIDER=openrouter
LLM_API_KEY=your-openrouter-api-key
LLM_MODEL=openai/gpt-4o
KEYCLOAK_URL=http://localhost:8080
KEYCLOAK_REALM=dreamchat
KEYCLOAK_CLIENT_ID=dreamchat-backend
EOF
fi
if [ ! -f /workspace/apps/frontend/.env ]; then
echo "⚙️ Creating frontend .env file..."
cat > /workspace/apps/frontend/.env << EOF
VITE_API_URL=http://localhost:3000/api
VITE_WS_URL=ws://localhost:3000
VITE_KEYCLOAK_URL=http://localhost:8080
VITE_KEYCLOAK_REALM=dreamchat
VITE_KEYCLOAK_CLIENT_ID=dreamchat-frontend
EOF
fi
echo "✅ Development environment setup complete!"
echo ""
echo "Next steps:"
echo "1. Start all apps: pnpm dev"
echo "2. Or start individually:"
echo " - Backend: pnpm --filter @dreamchat/backend dev"
echo " - Frontend: pnpm --filter @dreamchat/frontend dev"
echo "3. Access Keycloak admin at http://localhost:8080 (admin/admin)"
```
## Docker Compose Production
### docker-compose.yml (Root)
```yaml
version: '3.8'
services:
# Backend API
backend:
build:
context: .
dockerfile: apps/backend/Dockerfile
restart: unless-stopped
environment:
- NODE_ENV=production
- PORT=3000
- DATABASE_URL=postgresql://postgres:${POSTGRES_PASSWORD}@db:5432/dreamchat
- JWT_SECRET=${JWT_SECRET}
- JWT_EXPIRES_IN=${JWT_EXPIRES_IN:-1h}
- JWT_REFRESH_EXPIRES_IN=${JWT_REFRESH_EXPIRES_IN:-7d}
- LLM_PROVIDER=${LLM_PROVIDER}
- LLM_API_KEY=${LLM_API_KEY}
- LLM_MODEL=${LLM_MODEL}
- EMBEDDING_PROVIDER=${EMBEDDING_PROVIDER:-local}
- EMBEDDING_MODEL=${EMBEDDING_MODEL:-Xenova/all-MiniLM-L6-v2}
- EMBEDDING_DIMENSION=${EMBEDDING_DIMENSION:-384}
- EMBEDDING_DEVICE=${EMBEDDING_DEVICE:-cpu}
- HUGGINGFACE_API_KEY=${HUGGINGFACE_API_KEY}
- KEYCLOAK_URL=${KEYCLOAK_URL}
- KEYCLOAK_REALM=${KEYCLOAK_REALM}
- KEYCLOAK_CLIENT_ID=${KEYCLOAK_CLIENT_ID}
- KEYCLOAK_CLIENT_SECRET=${KEYCLOAK_CLIENT_SECRET}
ports:
- "3000:3000"
depends_on:
db:
condition: service_healthy
volumes:
- backend-logs:/app/logs
- model-cache:/app/models
networks:
- dreamchat-network
healthcheck:
test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://localhost:3000/health"]
interval: 30s
timeout: 10s
retries: 3
# Frontend
frontend:
build:
context: .
dockerfile: apps/frontend/Dockerfile
restart: unless-stopped
ports:
- "80:80"
- "443:443"
environment:
- VITE_API_URL=/api
- VITE_WS_URL=/ws
depends_on:
- backend
networks:
- dreamchat-network
# Database
db:
image: ankane/pgvector:latest
restart: unless-stopped
environment:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
POSTGRES_DB: dreamchat
volumes:
- postgres-data:/var/lib/postgresql/data
- ./init-scripts:/docker-entrypoint-initdb.d
networks:
- dreamchat-network
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 10s
timeout: 5s
retries: 5
# Redis (optional, for session storage and caching)
redis:
image: redis:7-alpine
restart: unless-stopped
volumes:
- redis-data:/data
networks:
- dreamchat-network
# Nginx Reverse Proxy (optional)
nginx:
image: nginx:alpine
restart: unless-stopped
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
- ./nginx/ssl:/etc/nginx/ssl:ro
- model-cache:/model-cache:ro
depends_on:
- backend
- frontend
networks:
- dreamchat-network
volumes:
postgres-data:
redis-data:
backend-logs:
model-cache: # Persist downloaded embedding models
networks:
dreamchat-network:
driver: bridge
```
### Embedding Model Cache (Optional)
To avoid re-downloading models on every restart, models are cached in a Docker volume:
```yaml
# Add to docker-compose volumes
volumes:
model-cache:
```
Models are downloaded on first use and cached at `/app/models` in the backend container.
Common models and their sizes:
- `Xenova/all-MiniLM-L6-v2`: ~80MB (384 dimensions)
- `Xenova/all-mpnet-base-v2`: ~420MB (768 dimensions)
- `BAAI/bge-small-en`: ~130MB (384 dimensions)
### .env.example
```bash
# Database
POSTGRES_PASSWORD=your_secure_password_here
# JWT
JWT_SECRET=your_jwt_secret_key_min_32_chars
JWT_EXPIRES_IN=1h
JWT_REFRESH_EXPIRES_IN=7d
# LLM Configuration
LLM_PROVIDER=openrouter
LLM_API_KEY=sk-or-v1-...
LLM_MODEL=openai/gpt-4o
# Embedding Configuration (Local HuggingFace by default)
EMBEDDING_PROVIDER=local
EMBEDDING_MODEL=Xenova/all-MiniLM-L6-v2
EMBEDDING_DIMENSION=384
EMBEDDING_DEVICE=cpu
# HuggingFace API (optional - if not using local embeddings)
# HUGGINGFACE_API_KEY=hf_...
# Keycloak (optional - for external auth)
KEYCLOAK_URL=http://keycloak:8080
KEYCLOAK_REALM=dreamchat
KEYCLOAK_CLIENT_ID=dreamchat-backend
KEYCLOAK_CLIENT_SECRET=your_keycloak_secret
```
## Backend Dockerfile
```dockerfile
# apps/backend/Dockerfile
FROM node:20-alpine AS base
RUN npm install -g pnpm@8
FROM base AS dependencies
WORKDIR /app
# Copy workspace configuration
COPY pnpm-workspace.yaml package.json ./
COPY apps/backend/package.json ./apps/backend/
COPY packages/shared/package.json ./packages/shared/
# Install dependencies
RUN pnpm install --frozen-lockfile
FROM base AS build
WORKDIR /app
COPY --from=dependencies /app/node_modules ./node_modules
COPY --from=dependencies /app/apps/backend/node_modules ./apps/backend/node_modules
COPY --from=dependencies /app/packages/shared/node_modules ./packages/shared/node_modules
# Copy source code
COPY packages/shared ./packages/shared
COPY apps/backend ./apps/backend
# Build shared packages first
RUN pnpm --filter @dreamchat/shared build
# Build backend
RUN pnpm --filter @dreamchat/backend build
FROM base AS production
WORKDIR /app
# Copy only production dependencies
COPY --from=dependencies /app/node_modules ./node_modules
COPY --from=dependencies /app/apps/backend/node_modules ./apps/backend/node_modules
COPY --from=build /app/apps/backend/dist ./dist
COPY --from=build /app/packages/shared/dist ./node_modules/@dreamchat/shared/dist
# Create logs directory
RUN mkdir -p /app/logs
# Non-root user
RUN addgroup -g 1001 -S nodejs
RUN adduser -S nodejs -u 1001
USER nodejs
EXPOSE 3000
CMD ["node", "dist/main.js"]
```
## Frontend Dockerfile
```dockerfile
# apps/frontend/Dockerfile
FROM node:20-alpine AS base
RUN npm install -g pnpm@8
FROM base AS dependencies
WORKDIR /app
# Copy workspace configuration
COPY pnpm-workspace.yaml package.json ./
COPY apps/frontend/package.json ./apps/frontend/
COPY packages/shared/package.json ./packages/shared/
# Install dependencies
RUN pnpm install --frozen-lockfile
FROM base AS build
WORKDIR /app
COPY --from=dependencies /app/node_modules ./node_modules
COPY --from=dependencies /app/apps/frontend/node_modules ./apps/frontend/node_modules
COPY --from=dependencies /app/packages/shared/node_modules ./packages/shared/node_modules
# Copy source code
COPY packages/shared ./packages/shared
COPY apps/frontend ./apps/frontend
# Build shared packages first
RUN pnpm --filter @dreamchat/shared build
# Build frontend
RUN pnpm --filter @dreamchat/frontend build
# Production with Nginx
FROM nginx:alpine
# Copy built assets
COPY --from=build /app/apps/frontend/dist /usr/share/nginx/html
# Copy nginx config
COPY apps/frontend/nginx.conf /etc/nginx/conf.d/default.conf
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
```
### frontend/nginx.conf
```nginx
server {
listen 80;
server_name localhost;
root /usr/share/nginx/html;
index index.html;
# Gzip compression
gzip on;
gzip_vary on;
gzip_min_length 1024;
gzip_types text/plain text/css text/xml text/javascript application/json application/javascript application/xml+rss application/rss+xml font/truetype font/opentype application/vnd.ms-fontobject image/svg+xml;
# API proxy
location /api {
proxy_pass http://backend:3000/api;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_cache_bypass $http_upgrade;
}
# WebSocket proxy
location /ws {
proxy_pass http://backend:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
# Static files
location / {
try_files $uri $uri/ /index.html;
add_header Cache-Control "public, max-age=31536000, immutable";
}
# Health check
location /health {
access_log off;
return 200 "healthy\n";
add_header Content-Type text/plain;
}
}
```
## Keycloak Configuration
### Initial Setup
1. Access Keycloak admin console: `http://localhost:8080/admin`
2. Login with admin credentials
3. Create new realm: `dreamchat`
4. Create client: `dreamchat-backend`
- Client authentication: ON
- Authorization: ON
- Valid redirect URIs: `http://localhost:3000/*`
- Web origins: `http://localhost:3000`
5. Create client: `dreamchat-frontend`
- Client authentication: OFF
- Valid redirect URIs: `http://localhost:5173/*`
- Web origins: `http://localhost:5173`
### realm-export.json (Optional)
```json
{
"realm": "dreamchat",
"enabled": true,
"clients": [
{
"clientId": "dreamchat-backend",
"enabled": true,
"clientAuthenticatorType": "client-secret",
"secret": "**********",
"redirectUris": ["http://localhost:3000/*"],
"webOrigins": ["http://localhost:3000"],
"protocol": "openid-connect"
},
{
"clientId": "dreamchat-frontend",
"enabled": true,
"publicClient": true,
"redirectUris": ["http://localhost:5173/*"],
"webOrigins": ["http://localhost:5173"],
"protocol": "openid-connect"
}
]
}
```
## Deployment Procedures
### Local Development
```bash
# Using DevContainer
1. Open project in VS Code
2. Click "Reopen in Container"
3. Wait for setup to complete
4. Start services:
- Terminal 1: pnpm --filter @dreamchat/backend dev
- Terminal 2: pnpm --filter @dreamchat/frontend dev
- Or run all: pnpm dev
```
### Production Deployment
```bash
# 1. Clone repository
git clone https://github.com/yourusername/dreamchat.git
cd dreamchat
# 2. Create environment file
cp .env.example .env
# Edit .env with production values
# 3. Build and start
docker-compose up -d --build
# 4. Run database migrations
docker-compose exec backend pnpm db:migrate
# 5. Check health
curl http://localhost:3000/health
# 6. View logs
docker-compose logs -f backend
```
### Backup and Restore
```bash
# Backup database
docker-compose exec db pg_dump -U postgres -Fc dreamchat > backup_$(date +%Y%m%d).dump
# Restore database
docker-compose exec -T db pg_restore -U postgres -d dreamchat < backup_20240223.dump
# Backup volumes
docker run --rm -v dreamchat_postgres-data:/data -v $(pwd):/backup alpine tar czf /backup/postgres-backup.tar.gz -C /data .
```
### Updates
```bash
# Pull latest changes
git pull origin main
# Rebuild and restart
docker-compose down
docker-compose up -d --build
# Run migrations
docker-compose exec backend npx prisma migrate deploy
```
## Monitoring and Logging
### Health Checks
```bash
# Backend health
curl http://localhost:3000/health
# Database health
docker-compose exec db pg_isready -U postgres
# Full stack
docker-compose ps
```
### Log Management
```bash
# View all logs
docker-compose logs
# View specific service
docker-compose logs -f backend
# View last 100 lines
docker-compose logs --tail=100 backend
```
## Security Considerations
1. **Change default passwords** in production
2. **Use HTTPS** with valid SSL certificates
3. **Enable firewall** rules for required ports only
4. **Regular updates** of base images and dependencies
5. **Secrets management** - use Docker secrets or external vault
6. **Network isolation** - separate networks for different services
## Troubleshooting
### Common Issues
1. **Database connection failed**
- Check DATABASE_URL environment variable
- Ensure db service is healthy: `docker-compose ps`
2. **WebSocket not connecting**
- Verify VITE_WS_URL matches your domain
- Check Nginx proxy configuration
3. **Keycloak authentication issues**
- Verify client configuration in Keycloak admin
- Check redirect URIs match exactly
4. **File upload fails**
- Check file size limits in Nginx config
- Verify disk space in backend container