diff --git a/nginx/.dockerignore b/nginx/.dockerignore new file mode 100644 index 0000000..73c1852 --- /dev/null +++ b/nginx/.dockerignore @@ -0,0 +1,6 @@ +node_modules +npm-debug.log +.git +.gitignore +README.md +*.md diff --git a/nginx/README.md b/nginx/README.md new file mode 100644 index 0000000..29b4965 --- /dev/null +++ b/nginx/README.md @@ -0,0 +1,170 @@ +# Nginx 配置说明 + +## 配置文件 + +- `nginx.conf` - 主配置文件 + +## 功能特性 + +1. **前端静态文件服务** - 服务 React 构建后的静态文件 +2. **API 代理** - 将 `/api` 请求代理到后端服务器 (localhost:3001) +3. **SPA 路由支持** - 支持 React Router 的客户端路由 +4. **静态资源缓存** - 优化静态资源加载性能 +5. **健康检查** - `/health` 端点用于健康检查 + +## 部署步骤 + +### 1. 构建前端 + +```bash +cd frontend +npm run build +``` + +构建输出在 `frontend/dist/` 目录 + +### 2. 配置 Nginx + +编辑 `nginx.conf` 文件,修改以下配置: + +- `server_name`: 改为您的域名 +- `root`: 改为前端构建文件的绝对路径 +- `upstream backend`: 确保后端服务器地址正确 + +### 3. 安装配置文件 + +```bash +# 复制配置文件到 nginx 配置目录 +sudo cp nginx/nginx.conf /etc/nginx/sites-available/ai-learning-platform + +# 创建符号链接 +sudo ln -s /etc/nginx/sites-available/ai-learning-platform /etc/nginx/sites-enabled/ + +# 测试配置 +sudo nginx -t + +# 重新加载 nginx +sudo systemctl reload nginx +``` + +### 4. 启动后端服务 + +确保后端服务在 3001 端口运行: + +```bash +cd backend +npm run build +npm start +``` + +或者使用 PM2 管理: + +```bash +pm2 start backend/dist/index.js --name ai-learning-backend +``` + +## 配置说明 + +### 前端路径 + +默认配置假设前端构建文件在: +``` +/var/www/ai-learning-platform/frontend/dist +``` + +请根据实际部署路径修改 `root` 指令。 + +### 后端代理 + +API 请求会被代理到: +``` +http://localhost:3001 +``` + +如果需要修改,编辑 `upstream backend` 部分。 + +### 静态资源缓存 + +静态资源(JS、CSS、图片等)会缓存 1 年,提高性能。 + +### SPA 路由支持 + +`try_files $uri $uri/ /index.html;` 确保所有路由都返回 `index.html`,支持 React Router。 + +## HTTPS 配置(可选) + +如果需要 HTTPS,取消注释配置文件中的 HTTPS 部分,并: + +1. 获取 SSL 证书(Let's Encrypt 免费证书) +2. 修改证书路径 +3. 重新加载 nginx + +### 使用 Let's Encrypt + +```bash +# 安装 certbot +sudo apt-get install certbot python3-certbot-nginx + +# 获取证书 +sudo certbot --nginx -d yourdomain.com +``` + +## 性能优化 + +### Gzip 压缩 + +在 nginx 主配置文件中添加: + +```nginx +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; +``` + +### 缓存优化 + +静态资源已配置长期缓存,生产环境建议使用 CDN。 + +## 监控和日志 + +- 访问日志:`/var/log/nginx/ai-learning-access.log` +- 错误日志:`/var/log/nginx/ai-learning-error.log` + +## 故障排查 + +### 检查 nginx 状态 + +```bash +sudo systemctl status nginx +``` + +### 查看错误日志 + +```bash +sudo tail -f /var/log/nginx/ai-learning-error.log +``` + +### 测试配置 + +```bash +sudo nginx -t +``` + +### 重新加载配置 + +```bash +sudo systemctl reload nginx +``` + +## Docker 部署(可选) + +如果使用 Docker,可以创建 `Dockerfile.nginx`: + +```dockerfile +FROM nginx:alpine +COPY nginx/nginx.conf /etc/nginx/conf.d/default.conf +COPY frontend/dist /usr/share/nginx/html +EXPOSE 80 +CMD ["nginx", "-g", "daemon off;"] +``` diff --git a/nginx/docker-compose.yml b/nginx/docker-compose.yml new file mode 100644 index 0000000..cef8d88 --- /dev/null +++ b/nginx/docker-compose.yml @@ -0,0 +1,41 @@ +version: '3.8' + +services: + # 前端 Nginx 服务 + nginx: + image: nginx:alpine + container_name: ai-learning-nginx + ports: + - "80:80" + - "443:443" + volumes: + - ./nginx.conf:/etc/nginx/conf.d/default.conf:ro + - ../frontend/dist:/usr/share/nginx/html:ro + - ./logs:/var/log/nginx + depends_on: + - backend + restart: unless-stopped + networks: + - ai-learning-network + + # 后端服务 + backend: + build: + context: ../backend + dockerfile: Dockerfile + container_name: ai-learning-backend + ports: + - "3001:3001" + environment: + - NODE_ENV=production + - PORT=3001 + - DATABASE_URL=file:./prisma/dev.db + volumes: + - ../backend/prisma:/app/prisma + restart: unless-stopped + networks: + - ai-learning-network + +networks: + ai-learning-network: + driver: bridge diff --git a/nginx/nginx.conf b/nginx/nginx.conf new file mode 100644 index 0000000..47c7c82 --- /dev/null +++ b/nginx/nginx.conf @@ -0,0 +1,127 @@ +# Nginx 配置文件 - AI学习平台 + +# 上游后端服务器 +upstream backend { + server localhost:3001; + keepalive 64; +} + +# HTTP 服务器配置 +server { + listen 80; + server_name localhost; # 修改为您的域名 + + # 客户端最大请求体大小 + client_max_body_size 10M; + + # 日志配置 + access_log /var/log/nginx/ai-learning-access.log; + error_log /var/log/nginx/ai-learning-error.log; + + # 前端静态文件 + location / { + root /var/www/ai-learning-platform/frontend/dist; + index index.html; + try_files $uri $uri/ /index.html; + + # 静态资源缓存 + location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ { + expires 1y; + add_header Cache-Control "public, immutable"; + } + } + + # API 代理 + location /api { + proxy_pass http://backend; + 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; + + # 超时设置 + proxy_connect_timeout 60s; + proxy_send_timeout 60s; + proxy_read_timeout 60s; + } + + # 健康检查 + location /health { + access_log off; + return 200 "healthy\n"; + add_header Content-Type text/plain; + } +} + +# HTTPS 服务器配置(可选,需要 SSL 证书) +# server { +# listen 443 ssl http2; +# server_name localhost; # 修改为您的域名 +# +# # SSL 证书配置 +# ssl_certificate /path/to/your/certificate.crt; +# ssl_certificate_key /path/to/your/private.key; +# +# # SSL 优化配置 +# ssl_protocols TLSv1.2 TLSv1.3; +# ssl_ciphers HIGH:!aNULL:!MD5; +# ssl_prefer_server_ciphers on; +# ssl_session_cache shared:SSL:10m; +# ssl_session_timeout 10m; +# +# # 客户端最大请求体大小 +# client_max_body_size 10M; +# +# # 日志配置 +# access_log /var/log/nginx/ai-learning-ssl-access.log; +# error_log /var/log/nginx/ai-learning-ssl-error.log; +# +# # 前端静态文件 +# location / { +# root /var/www/ai-learning-platform/frontend/dist; +# index index.html; +# try_files $uri $uri/ /index.html; +# +# # 静态资源缓存 +# location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ { +# expires 1y; +# add_header Cache-Control "public, immutable"; +# } +# } +# +# # API 代理 +# location /api { +# proxy_pass http://backend; +# 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; +# +# # 超时设置 +# proxy_connect_timeout 60s; +# proxy_send_timeout 60s; +# proxy_read_timeout 60s; +# } +# +# # 健康检查 +# location /health { +# access_log off; +# return 200 "healthy\n"; +# add_header Content-Type text/plain; +# } +# } +# +# # HTTP 重定向到 HTTPS +# server { +# listen 80; +# server_name localhost; # 修改为您的域名 +# return 301 https://$server_name$request_uri; +# } diff --git a/nginx/nginx.conf.example b/nginx/nginx.conf.example new file mode 100644 index 0000000..f1b7111 --- /dev/null +++ b/nginx/nginx.conf.example @@ -0,0 +1,58 @@ +# Nginx 配置示例文件 +# 复制此文件并根据您的环境修改配置 + +# 上游后端服务器 +upstream backend { + server localhost:3001; # 修改为您的后端服务器地址 + keepalive 64; +} + +server { + listen 80; + server_name your-domain.com; # 修改为您的域名或 IP + + # 客户端最大请求体大小 + client_max_body_size 10M; + + # 日志配置(可选) + # access_log /var/log/nginx/ai-learning-access.log; + # error_log /var/log/nginx/ai-learning-error.log; + + # 前端静态文件路径(修改为实际路径) + location / { + root /path/to/frontend/dist; # 修改为前端构建文件的实际路径 + index index.html; + try_files $uri $uri/ /index.html; + + # 静态资源缓存 + location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ { + expires 1y; + add_header Cache-Control "public, immutable"; + } + } + + # API 代理 + location /api { + proxy_pass http://backend; + 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; + + # 超时设置 + proxy_connect_timeout 60s; + proxy_send_timeout 60s; + proxy_read_timeout 60s; + } + + # 健康检查 + location /health { + access_log off; + return 200 "healthy\n"; + add_header Content-Type text/plain; + } +}