diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 00000000..52ffe1fb --- /dev/null +++ b/.dockerignore @@ -0,0 +1,60 @@ +# Git +.git +.gitignore +.gitattributes + +# Documentation +*.md +docs/ +assets/ + +# IDE +.vscode/ +.cursor/ +.idea/ + +# Dependencies +node_modules/ +**/node_modules/ + +# Build outputs +dist/ +**/dist/ +build/ +**/build/ +out/ + +# Logs +*.log +logs/ + +# Environment +.env +.env.local +.env.*.local + +# OS +.DS_Store +Thumbs.db + +# Temporary files +tmp/ +temp/ +*.tmp +*.swp +*.swo + +# Test +coverage/ +.nyc_output/ + +# Mise +.mise.toml.local + +# Data +data/ +*.db +*.sqlite + +# Docker +docker-compose.override.yml diff --git a/DEPLOYMENT.md b/DEPLOYMENT.md new file mode 100644 index 00000000..42f37800 --- /dev/null +++ b/DEPLOYMENT.md @@ -0,0 +1,401 @@ +# Memoh Docker Deployment Guide + +Deploy Memoh AI Agent System with Docker Compose in one command. + +## Quick Start + +### 1. Clone the Repository +```bash +git clone https://github.com/memohai/Memoh.git +cd Memoh +``` + +### 2. One-Click Deployment +```bash +./deploy.sh +``` + +The script will automatically: +- Check Docker and Docker Compose installation +- Create `.env` configuration file (if not exists) +- Generate random JWT secret +- Create `config.toml` configuration file +- Build MCP image +- Start all services + +### 3. Access the Application +- Web UI: http://localhost +- API Service: http://localhost:8080 +- Agent Gateway: http://localhost:8081 + +Default admin credentials: +- Username: `admin` +- Password: `admin123` (change in `.env`) + +## Manual Deployment + +If you prefer not to use the automated script: + +```bash +# 1. Create configuration files +cp .env.example .env +cp config.docker.toml config.toml + +# 2. Edit configuration (Important!) +nano .env + +# 3. Generate JWT secret +openssl rand -base64 32 + +# 4. Build MCP image +docker build -f cmd/mcp/Dockerfile -t memoh-mcp:latest . + +# 5. Start services +docker compose up -d + +# 6. View logs +docker compose logs -f +``` + +## Architecture + +This deployment uses the host's Docker daemon to manage Bot containers: + +``` +Host Docker +├── memoh-postgres (PostgreSQL) +├── memoh-qdrant (Qdrant) +├── memoh-server (Main Service) ← Manages Bot containers via /var/run/docker.sock +├── memoh-agent (Agent Gateway) +├── memoh-web (Web Frontend) +└── memoh-bot-* (Bot containers, dynamically created by main service) +``` + +Advantages: +- ✅ Lightweight, no additional Docker daemon needed +- ✅ Better performance, uses host container runtime directly +- ✅ Easier to manage and debug +- ✅ Lower resource consumption + +## Common Commands + +### Using Make (Recommended) +```bash +make help # Show all commands +make deploy # One-click deployment +make logs # View logs +make restart # Restart services +make ps # View status +make backup # Backup data +make bots # View Bot containers +``` + +### Using Docker Compose +```bash +docker compose up -d # Start services +docker compose down # Stop services +docker compose logs -f # View logs +docker compose ps # View status +docker compose restart # Restart services +``` + +## Configuration + +### Environment Variables (.env) + +Key configuration items: + +```bash +# PostgreSQL password (must change) +POSTGRES_PASSWORD=your_secure_password + +# JWT secret (must change) +JWT_SECRET=your_random_jwt_secret + +# Admin account +ADMIN_USERNAME=admin +ADMIN_PASSWORD=your_admin_password +ADMIN_EMAIL=admin@yourdomain.com +``` + +### Application Configuration (config.toml) + +Main configuration items: + +```toml +[postgres] +host = "postgres" +password = "your_secure_password" # Must match POSTGRES_PASSWORD in .env + +[containerd] +socket_path = "unix:///var/run/docker.sock" # Use host Docker + +[qdrant] +base_url = "http://qdrant:6334" +``` + +## Service Overview + +| Service | Container Name | Ports | Description | +|---------|---------------|-------|-------------| +| postgres | memoh-postgres | - | PostgreSQL database (internal only) | +| qdrant | memoh-qdrant | - | Qdrant vector database (internal only) | +| docker-cli | memoh-docker-cli | - | Docker CLI (uses host Docker) | +| server | memoh-server | 8080 | Main service (Go) | +| agent | memoh-agent | 8081 | Agent Gateway (Bun) | +| web | memoh-web | 80 | Web frontend (Nginx) | + +## Data Persistence + +Data is stored in Docker volumes: + +```bash +# View volumes +docker volume ls | grep memoh + +# Backup database +docker compose exec postgres pg_dump -U memoh memoh > backup.sql +``` + +### Bot Container Management + +Bot containers are dynamically created by the main service and run directly on the host: + +```bash +# View all Bot containers +make bots +# or +docker ps -a | grep memoh-bot + +# View Bot logs +docker logs + +# Enter Bot container +docker exec -it sh + +# Stop Bot container +docker stop +``` + +## Backup and Restore + +### Backup +```bash +# Create backup directory +mkdir -p backups + +# Backup database +docker compose exec postgres pg_dump -U memoh memoh > backups/postgres_$(date +%Y%m%d).sql + +# Backup Bot data +docker run --rm -v memoh_memoh_bot_data:/data -v $(pwd)/backups:/backup alpine \ + tar czf /backup/bot_data_$(date +%Y%m%d).tar.gz -C /data . + +# Backup configuration files +tar czf backups/config_$(date +%Y%m%d).tar.gz config.toml .env +``` + +### Restore +```bash +# Restore database +docker compose exec -T postgres psql -U memoh memoh < backups/postgres_20240101.sql + +# Restore Bot data +docker run --rm -v memoh_memoh_bot_data:/data -v $(pwd)/backups:/backup alpine \ + tar xzf /backup/bot_data_20240101.tar.gz -C /data +``` + +## Troubleshooting + +### Services Won't Start +```bash +# View detailed logs +docker compose logs server + +# Check configuration +docker compose config + +# Rebuild +docker compose build --no-cache +docker compose up -d +``` + +### Database Connection Failed +```bash +# Check if database is ready +docker compose exec postgres pg_isready -U memoh + +# Test connection +docker compose exec postgres psql -U memoh -d memoh + +# View database logs +docker compose logs postgres +``` + +### Port Conflicts +```bash +# Check port usage +sudo netstat -tlnp | grep :8080 +sudo netstat -tlnp | grep :80 + +# Modify port mapping in docker-compose.yml +# Example: change "80:80" to "8000:80" +``` + +### Docker Socket Permission Issues +```bash +# Add user to docker group +sudo usermod -aG docker $USER +newgrp docker + +# Check permissions +ls -la /var/run/docker.sock +``` + +## Production Deployment + +### 1. Use HTTPS + +Create `docker-compose.override.yml`: +```yaml +services: + web: + ports: + - "443:443" + volumes: + - ./ssl:/etc/nginx/ssl:ro + - ./nginx-https.conf:/etc/nginx/conf.d/default.conf:ro +``` + +Create `nginx-https.conf`: +```nginx +server { + listen 80; + server_name your-domain.com; + return 301 https://$server_name$request_uri; +} + +server { + listen 443 ssl http2; + server_name your-domain.com; + + ssl_certificate /etc/nginx/ssl/cert.pem; + ssl_certificate_key /etc/nginx/ssl/key.pem; + + # SSL configuration + ssl_protocols TLSv1.2 TLSv1.3; + ssl_ciphers HIGH:!aNULL:!MD5; + ssl_prefer_server_ciphers on; + + # Other configurations same as nginx.conf + # ... +} +``` + +### 2. Resource Limits + +Edit `docker-compose.yml` to add resource limits: +```yaml +services: + server: + deploy: + resources: + limits: + cpus: '2' + memory: 2G + reservations: + cpus: '1' + memory: 1G +``` + +### 3. Security Recommendations + +Production environment recommendations: +- Use separate `.env` file +- Change all default passwords +- Use strong JWT secret +- Configure firewall rules +- Use HTTPS +- Regular data backups +- Limit containerd socket access permissions +- Run services as non-root user +- Configure log rotation + +## Performance Optimization + +### PostgreSQL Optimization +Create `postgres-custom.conf`: +``` +shared_buffers = 2GB +effective_cache_size = 6GB +maintenance_work_mem = 512MB +checkpoint_completion_target = 0.9 +wal_buffers = 16MB +``` + +Mount in `docker-compose.yml`: +```yaml +postgres: + volumes: + - ./postgres-custom.conf:/etc/postgresql/postgresql.conf:ro + command: postgres -c config_file=/etc/postgresql/postgresql.conf +``` + +### Network Optimization +```yaml +networks: + memoh-network: + driver: bridge + driver_opts: + com.docker.network.driver.mtu: 1500 +``` + +## Update Application + +```bash +# Pull latest code +git pull + +# Rebuild and restart +docker compose up -d --build + +# Or use Make +make update +``` + +## Complete Uninstall + +```bash +# Stop and remove all containers +docker compose down + +# Remove data volumes (Warning! This deletes all data) +docker compose down -v + +# Remove images +docker rmi memoh-mcp:latest +docker rmi $(docker images | grep memoh | awk '{print $3}') +``` + +## Security Considerations + +⚠️ Important Security Notes: + +1. **Docker Socket Access**: The main service container has access to the host Docker socket, which means the application can manage other containers on the host. Only run in trusted environments. +2. **Change Default Passwords**: Must change all default passwords in `.env` +3. **Strong JWT Secret**: Use a strong random JWT secret +4. **Firewall**: Configure firewall to only open necessary ports +5. **HTTPS**: Use HTTPS in production +6. **Regular Backups**: Regularly backup data +7. **Updates**: Regularly update images and dependencies + +## Get Help + +- Detailed Documentation: [DOCKER_DEPLOYMENT_CN.md](DOCKER_DEPLOYMENT_CN.md) (Chinese) +- GitHub Issues: https://github.com/memohai/Memoh/issues +- Telegram Group: https://t.me/memohai +- Email: business@memoh.net + +--- + +**That's it! Deploy Memoh in minutes!** diff --git a/Dockerfile.agent b/Dockerfile.agent new file mode 100644 index 00000000..e77893ae --- /dev/null +++ b/Dockerfile.agent @@ -0,0 +1,28 @@ +FROM oven/bun:1 AS builder + +WORKDIR /build + +COPY agent/package.json agent/bun.lock* ./ + +RUN bun install + +COPY agent/ ./ + +RUN bun run build + +FROM oven/bun:1-alpine + +WORKDIR /app + +RUN apk add --no-cache ca-certificates wget + +COPY --from=builder /build/dist /app/dist +COPY --from=builder /build/node_modules /app/node_modules +COPY --from=builder /build/package.json /app/package.json + +EXPOSE 8081 + +HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \ + CMD wget --no-verbose --tries=1 --spider http://localhost:8081/health || exit 1 + +CMD ["bun", "run", "dist/index.js"] diff --git a/Dockerfile.server b/Dockerfile.server new file mode 100644 index 00000000..f14c1c56 --- /dev/null +++ b/Dockerfile.server @@ -0,0 +1,33 @@ +FROM golang:1.25-alpine AS builder + +WORKDIR /build + +RUN apk add --no-cache git make + +COPY go.mod go.sum ./ +RUN go mod download + +COPY . . + +RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \ + go build -trimpath -ldflags "-s -w" \ + -o memoh-server ./cmd/agent/main.go + +FROM alpine:latest + +WORKDIR /app + +RUN apk add --no-cache ca-certificates tzdata wget + +COPY --from=builder /build/memoh-server /app/memoh-server + +COPY config.toml.example /app/config.toml.example + +RUN mkdir -p /var/lib/memoh/data + +EXPOSE 8080 + +HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \ + CMD wget --no-verbose --tries=1 --spider http://localhost:8080/health || exit 1 + +CMD ["/app/memoh-server"] diff --git a/Dockerfile.web b/Dockerfile.web new file mode 100644 index 00000000..c19f54f2 --- /dev/null +++ b/Dockerfile.web @@ -0,0 +1,33 @@ +FROM node:25-alpine AS builder + +WORKDIR /build + +RUN npm install -g pnpm@10 + +COPY package.json pnpm-workspace.yaml pnpm-lock.yaml ./ + +COPY packages ./packages + +RUN pnpm install + +ARG VITE_API_URL=http://localhost:8080 +ARG VITE_AGENT_URL=http://localhost:8081 + +ENV VITE_API_URL=$VITE_API_URL +ENV VITE_AGENT_URL=$VITE_AGENT_URL + +WORKDIR /build/packages/web +RUN pnpm build + +FROM nginx:alpine + +COPY --from=builder /build/packages/web/dist /usr/share/nginx/html + +COPY nginx.conf /etc/nginx/conf.d/default.conf + +EXPOSE 80 + +HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \ + CMD wget --no-verbose --tries=1 --spider http://localhost/ || exit 1 + +CMD ["nginx", "-g", "daemon off;"] diff --git a/README.md b/README.md index 5037b521..f3c4314d 100644 --- a/README.md +++ b/README.md @@ -43,9 +43,23 @@ Memoh Bot can distinguish and remember requests from multiple humans and bots, w Please refer to the [Roadmap Version 0.1](https://github.com/memohai/Memoh/issues/2) for more details. -## Development +## Quick Start -Refer to [CONTRIBUTING.md](CONTRIBUTING.md) for more details. +### Docker Deployment (Recommended) + +The fastest way to deploy Memoh: + +```bash +git clone https://github.com/memohai/Memoh.git +cd Memoh +./deploy.sh +``` + +Visit http://localhost after deployment. See [Docker Deployment Guide](README_DOCKER.md) for details. + +### Development + +Refer to [CONTRIBUTING.md](CONTRIBUTING.md) for development setup. ## Star History diff --git a/README_CN.md b/README_CN.md index d7bff033..0af59b14 100644 --- a/README_CN.md +++ b/README_CN.md @@ -43,7 +43,21 @@ Memoh Bot 能够区分并记忆来自多个人类/Bot 的请求,可在任意 详情请参阅 [Roadmap Version 0.1](https://github.com/memohai/Memoh/issues/2)。 -## 开发 +## 快速开始 + +### Docker 部署(推荐) + +最快的部署方式: + +```bash +git clone https://github.com/memohai/Memoh.git +cd Memoh +./deploy.sh +``` + +部署完成后访问 http://localhost。详见 [Docker 部署指南](README_DOCKER.md)。 + +### 开发环境 详见 [CONTRIBUTING.md](CONTRIBUTING.md)。 diff --git a/config.docker.toml b/config.docker.toml new file mode 100644 index 00000000..f8d34c21 --- /dev/null +++ b/config.docker.toml @@ -0,0 +1,54 @@ +## Service configuration +[log] +level = "info" +format = "text" + +[server] +addr = ":8080" + +## Admin +[admin] +username = "admin" +password = "admin123" +email = "admin@memoh.local" + +## Auth configuration +[auth] +jwt_secret = "YZq8kXrW5dFpNt9mLxQvHbRjKsMnOePw" +jwt_expires_in = "168h" + +## Docker configuration (使用宿主机 Docker socket) +[containerd] +socket_path = "unix:///var/run/docker.sock" +namespace = "default" + +[mcp] +busybox_image = "memoh-mcp:latest" +snapshotter = "overlayfs" +data_root = "/var/lib/memoh/data" +data_mount = "/data" + +## Postgres configuration +[postgres] +host = "postgres" +port = 5432 +user = "memoh" +password = "memoh123" +database = "memoh" +sslmode = "disable" + +## Qdrant configuration +[qdrant] +base_url = "http://qdrant:6334" +api_key = "" +collection = "memory" +timeout_seconds = 10 + +## Agent Gateway +[agent_gateway] +host = "agent" +port = 8081 + +[brave] +api_key = "" +base_url = "https://api.search.brave.com/res/v1/" diff --git a/deploy.sh b/deploy.sh new file mode 100755 index 00000000..b60a136b --- /dev/null +++ b/deploy.sh @@ -0,0 +1,89 @@ +#!/bin/bash + +set -e + +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +RED='\033[0;31m' +NC='\033[0m' # No Color + +echo -e "${GREEN}========================================${NC}" +echo -e "${GREEN} Memoh Docker Compose Deployment${NC}" +echo -e "${GREEN}========================================${NC}" +echo "" + +# Check Docker +if ! command -v docker &> /dev/null; then + echo -e "${RED}Error: Docker is not installed${NC}" + echo "Please install Docker first: https://docs.docker.com/get-docker/" + exit 1 +fi + +# Check Docker Compose +if ! docker compose version &> /dev/null; then + echo -e "${RED}Error: Docker Compose is not installed or version is too old${NC}" + echo "Please install Docker Compose v2.0+: https://docs.docker.com/compose/install/" + exit 1 +fi + +echo -e "${GREEN}✓ Docker and Docker Compose are installed${NC}" +echo "" + +# Check .env file +if [ ! -f .env ]; then + echo -e "${YELLOW}⚠ .env file does not exist, creating...${NC}" + cp .env.example .env + + # Generate random JWT secret + JWT_SECRET=$(openssl rand -base64 32 2>/dev/null || head -c 32 /dev/urandom | base64) + sed -i.bak "s|JWT_SECRET=.*|JWT_SECRET=$JWT_SECRET|g" .env + rm -f .env.bak + + echo -e "${GREEN}✓ .env file created${NC}" + echo -e "${YELLOW}⚠ Please edit .env file to change default passwords and configuration${NC}" + echo "" +fi + +# Check config.toml +if [ ! -f config.toml ]; then + echo -e "${YELLOW}⚠ config.toml does not exist, creating...${NC}" + cp config.docker.toml config.toml + echo -e "${GREEN}✓ config.toml created${NC}" + echo "" +fi + +# Build MCP image +echo -e "${GREEN}Building MCP image...${NC}" +if docker build -f cmd/mcp/Dockerfile -t memoh-mcp:latest . > /dev/null 2>&1; then + echo -e "${GREEN}✓ MCP image built successfully${NC}" +else + echo -e "${YELLOW}⚠ MCP image build failed, will try to pull at runtime${NC}" +fi +echo "" + +# Start services +echo -e "${GREEN}Starting services...${NC}" +docker compose up -d + +echo "" +echo -e "${GREEN}========================================${NC}" +echo -e "${GREEN} Deployment Complete!${NC}" +echo -e "${GREEN}========================================${NC}" +echo "" +echo "Service URLs:" +echo " - Web UI: http://localhost" +echo " - API Service: http://localhost:8080" +echo " - Agent Gateway: http://localhost:8081" +echo "" +echo "View service status:" +echo " docker compose ps" +echo "" +echo "View logs:" +echo " docker compose logs -f" +echo "" +echo "Stop services:" +echo " docker compose down" +echo "" +echo -e "${YELLOW}⚠ First startup may take 1-2 minutes, please be patient${NC}" +echo "" +echo "View detailed documentation: DEPLOYMENT.md" diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 00000000..62c4ad38 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,137 @@ +services: + + postgres: + image: postgres:16-alpine + container_name: memoh-postgres + environment: + POSTGRES_DB: memoh + POSTGRES_USER: memoh + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-memoh123} + volumes: + - postgres_data:/var/lib/postgresql/data + - ./db/migrations:/docker-entrypoint-initdb.d:ro + expose: + - "5432" + healthcheck: + test: ["CMD-SHELL", "pg_isready -U memoh"] + interval: 10s + timeout: 5s + retries: 5 + restart: unless-stopped + networks: + - memoh-network + + qdrant: + image: qdrant/qdrant:latest + container_name: memoh-qdrant + volumes: + - qdrant_data:/qdrant/storage + expose: + - "6333" + - "6334" + healthcheck: + test: ["CMD-SHELL", "timeout 10s bash -c ':> /dev/tcp/127.0.0.1/6333' || exit 1"] + interval: 10s + timeout: 5s + retries: 5 + restart: unless-stopped + networks: + - memoh-network + + docker-cli: + image: docker:27-cli + container_name: memoh-docker-cli + volumes: + - /var/run/docker.sock:/var/run/docker.sock + - memoh_bot_data:/var/lib/memoh/data + command: ["tail", "-f", "/dev/null"] + restart: unless-stopped + networks: + - memoh-network + + server: + build: + context: . + dockerfile: Dockerfile.server + container_name: memoh-server + environment: + - LOG_LEVEL=${LOG_LEVEL:-info} + - SERVER_ADDR=:8080 + - POSTGRES_HOST=postgres + - POSTGRES_PORT=5432 + - POSTGRES_USER=memoh + - POSTGRES_PASSWORD=${POSTGRES_PASSWORD:-memoh123} + - POSTGRES_DB=memoh + - POSTGRES_SSLMODE=disable + - QDRANT_BASE_URL=http://qdrant:6334 + - QDRANT_COLLECTION=memory + - CONTAINERD_SOCKET=unix:///var/run/docker.sock + - AGENT_GATEWAY_HOST=agent + - AGENT_GATEWAY_PORT=8081 + - JWT_SECRET=${JWT_SECRET:-YZq8kXrW5dFpNt9mLxQvHbRjKsMnOePw} + - JWT_EXPIRES_IN=168h + - ADMIN_USERNAME=${ADMIN_USERNAME:-admin} + - ADMIN_PASSWORD=${ADMIN_PASSWORD:-admin123} + - ADMIN_EMAIL=${ADMIN_EMAIL:-admin@memoh.local} + - MCP_BUSYBOX_IMAGE=memoh-mcp:latest + - MCP_DATA_ROOT=/var/lib/memoh/data + - MCP_DATA_MOUNT=/data + volumes: + - ./config.toml:/app/config.toml:ro + - /var/run/docker.sock:/var/run/docker.sock + - memoh_bot_data:/var/lib/memoh/data + ports: + - "8080:8080" + depends_on: + postgres: + condition: service_healthy + qdrant: + condition: service_healthy + restart: unless-stopped + networks: + - memoh-network + + agent: + build: + context: . + dockerfile: Dockerfile.agent + container_name: memoh-agent + environment: + - NODE_ENV=production + - PORT=8081 + ports: + - "8081:8081" + depends_on: + - server + restart: unless-stopped + networks: + - memoh-network + + web: + build: + context: . + dockerfile: Dockerfile.web + args: + - VITE_API_URL=${VITE_API_URL:-http://localhost:8080} + - VITE_AGENT_URL=${VITE_AGENT_URL:-http://localhost:8081} + container_name: memoh-web + ports: + - "8090:80" + depends_on: + - server + - agent + restart: unless-stopped + networks: + - memoh-network + +volumes: + postgres_data: + driver: local + qdrant_data: + driver: local + memoh_bot_data: + driver: local + +networks: + memoh-network: + driver: bridge diff --git a/nginx.conf b/nginx.conf new file mode 100644 index 00000000..b89ba890 --- /dev/null +++ b/nginx.conf @@ -0,0 +1,64 @@ +server { + listen 80; + server_name _; + root /usr/share/nginx/html; + index index.html; + + # Gzip 压缩 + gzip on; + gzip_vary on; + gzip_min_length 1024; + gzip_types text/plain text/css text/xml text/javascript application/x-javascript application/xml+rss application/json application/javascript; + + # 前端路由 + location / { + try_files $uri $uri/ /index.html; + } + + # API 代理 + location /api/ { + proxy_pass http://memoh-server:8080/; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection 'upgrade'; + proxy_set_header Host $host; + proxy_cache_bypass $http_upgrade; + 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_connect_timeout 60s; + proxy_send_timeout 60s; + proxy_read_timeout 60s; + } + + # Agent Gateway 代理 + location /agent/ { + proxy_pass http://memoh-agent:8081/; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection 'upgrade'; + proxy_set_header Host $host; + proxy_cache_bypass $http_upgrade; + 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_connect_timeout 60s; + proxy_send_timeout 60s; + proxy_read_timeout 60s; + } + + # 静态资源缓存 + location ~* \.(jpg|jpeg|png|gif|ico|css|js|svg|woff|woff2|ttf|eot)$ { + expires 1y; + add_header Cache-Control "public, immutable"; + } + + # 安全头 + add_header X-Frame-Options "SAMEORIGIN" always; + add_header X-Content-Type-Options "nosniff" always; + add_header X-XSS-Protection "1; mode=block" always; +}