# API 路径配置说明 ## 📋 概述 本项目使用统一的 API 路径配置机制,通过环境变量和辅助函数确保开发环境和生产环境的 API 调用一致性。 --- ## 🔧 配置方式 ### 1. **环境变量配置** #### **开发环境** (`.env`) ```env REACT_APP_API_BASE_URL=http://localhost:5001 ``` #### **生产环境** (`.env.production`) ```env REACT_APP_API_BASE_URL=https://d1kt.cn/api ``` ### 2. **代码中的使用** 所有 API 调用统一使用 `api` 对象(来自 `src/api/apiClient.ts`): ```typescript import { api } from './api/apiClient'; // ✅ 正确用法 const users = await api.get('/admin/users'); // ❌ 不要直接使用 apiClient // const response = await apiClient.get('/admin/users'); ``` --- ## 🌐 URL 构造流程 ### **开发环境** ``` 1. 代码调用:api.get('/admin/users') ↓ 2. getFullApiPath('/admin/users') → '/api/admin/users' ↓ 3. axios.get(API_BASE_URL + '/api/admin/users') → axios.get('http://localhost:5001/api/admin/users') ↓ 4. 最终请求:http://localhost:5001/api/admin/users ↓ 5. 后端接收:/api/admin/users ✅ ``` ### **生产环境** ``` 1. 代码调用:api.get('/admin/users') ↓ 2. getFullApiPath('/admin/users') → '/api/admin/users' ↓ 3. axios.get(API_BASE_URL + '/api/admin/users') → axios.get('https://d1kt.cn/api/api/admin/users') ↓ 4. 浏览器请求:https://d1kt.cn/api/api/admin/users ↓ 5. Nginx 处理: location /api/ { proxy_pass http://localhost:5001/; } → 去掉第一个 /api,转发到后端 ↓ 6. 后端接收:http://localhost:5001/api/admin/users ✅ ``` --- ## 📊 关键点说明 ### ✅ **为什么生产环境需要两个 `/api`?** 1. **第一个 `/api`**:Nginx 反向代理的路径匹配 ```nginx location /api/ { proxy_pass http://localhost:5001/; } ``` - 浏览器请求 `https://d1kt.cn/api/xxx` - Nginx 匹配到 `/api/` 后,去掉这部分,转发到后端 - 后端收到 `http://localhost:5001/xxx` 2. **第二个 `/api`**:后端路由的前缀 - 后端所有路由都以 `/api` 开头 - 例如:`app.use('/api/admin', adminRoutes)` - 所以需要请求 `/api/admin/users` 3. **组合结果**: ``` 浏览器 → https://d1kt.cn/api/api/admin/users Nginx → 转发 http://localhost:5001/api/admin/users 后端 → 路由匹配 /api/admin/users ✅ ``` --- ## 🔍 统一的调用方式 ### **所有页面都使用相同的逻辑** | 功能模块 | API 调用示例 | 最终 URL(开发) | 最终 URL(生产) | |---------|------------|---------------|---------------| | 打字练习 | `api.get('/practice-records/statistics')` | `http://localhost:5001/api/practice-records/statistics` | `https://d1kt.cn/api/api/practice-records/statistics` | | 用户管理 | `api.get('/admin/users')` | `http://localhost:5001/api/admin/users` | `https://d1kt.cn/api/api/admin/users` | | 词汇学习 | `api.get('/vocabulary/word-sets')` | `http://localhost:5001/api/vocabulary/word-sets` | `https://d1kt.cn/api/api/vocabulary/word-sets` | | 扫雷游戏 | `api.get('/minesweeper/leaderboard/beginner')` | `http://localhost:5001/api/minesweeper/leaderboard/beginner` | `https://d1kt.cn/api/api/minesweeper/leaderboard/beginner` | --- ## 🚀 部署步骤 ### **生产环境部署** 1. **在服务器上创建 `.env.production` 文件** ```bash cd /path/to/typing_practiceweb cp .env.production.example .env.production nano .env.production ``` 2. **配置生产环境变量** ```env REACT_APP_API_BASE_URL=https://d1kt.cn/api ``` 3. **构建前端** ```bash npm run build ``` 这会自动读取 `.env.production` 文件 4. **确认 Nginx 配置** ```nginx location /api/ { proxy_pass http://localhost:5001/; } ``` 5. **重启服务** ```bash sudo nginx -s reload pm2 restart typing-backend ``` --- ## ⚠️ 常见错误 ### ❌ **错误配置示例** ```env # ❌ 生产环境配置为空字符串 REACT_APP_API_BASE_URL= # 结果:请求变成 /api/admin/users(相对路径) # 问题:Nginx 无法正确代理 ``` ```env # ❌ 开发环境包含 /api REACT_APP_API_BASE_URL=http://localhost:5001/api # 结果:http://localhost:5001/api/api/admin/users # 问题:多了一层 /api,后端无法匹配路由 ``` ### ✅ **正确配置** ```env # ✅ 开发环境 REACT_APP_API_BASE_URL=http://localhost:5001 # ✅ 生产环境 REACT_APP_API_BASE_URL=https://d1kt.cn/api ``` --- ## 🧪 验证方法 ### **开发环境验证** ```bash # 1. 启动服务 npm run start # 2. 浏览器访问 http://localhost:3001/admin # 3. 打开 Network 面板,查看请求 # 应该看到:http://localhost:5001/api/admin/users ✅ ``` ### **生产环境验证** ```bash # 1. 构建并部署 npm run build pm2 restart typing-backend # 2. 浏览器访问 https://d1kt.cn/admin # 3. 打开 Network 面板,查看请求 # 应该看到:https://d1kt.cn/api/api/admin/users ✅ ``` --- ## 📝 总结 - ✅ **统一使用 `api` 对象**进行所有 API 调用 - ✅ **通过环境变量**控制不同环境的基础 URL - ✅ **`getFullApiPath` 函数**自动添加 `/api` 前缀 - ✅ **生产环境需要两个 `/api`**:第一个给 Nginx,第二个给后端路由 - ✅ **开发环境和生产环境使用完全相同的代码逻辑** --- ## 🌐 WebSocket 连接配置 ### **WebSocket 路径配置** 扫雷游戏的 WebSocket 连接需要正确处理路径前缀,以匹配 Nginx 代理配置: #### **客户端配置** - `REACT_APP_API_BASE_URL`: API 基础地址(如 `/api` 或 `https://d1kt.cn/api`) - `REACT_APP_CLIENT_URL`: 客户端域名(如 `https://d1kt.cn`) #### **服务器端配置** - `REACT_APP_API_BASE_URL`: 与客户端相同的环境变量,用于确定 WebSocket 路径前缀 ### **配置示例** #### **开发环境** (`.env`) ```env REACT_APP_API_BASE_URL=http://localhost:5001 REACT_APP_CLIENT_URL=http://localhost:3001 ``` #### **生产环境** (`.env.production`) ```env REACT_APP_API_BASE_URL=/api REACT_APP_CLIENT_URL=https://d1kt.cn ``` 或使用完整 URL: ```env REACT_APP_API_BASE_URL=https://d1kt.cn/api REACT_APP_CLIENT_URL=https://d1kt.cn ``` ### **WebSocket 连接流程** ``` 开发环境: 1. 客户端连接: ws://localhost:5001/socket.io 2. 服务器监听: /socket.io 生产环境: 1. 客户端连接: wss://d1kt.cn/api/socket.io 2. Nginx 代理: location /api/ { proxy_pass http://localhost:5001/; } 3. 服务器监听: /socket.io ``` ### **Nginx WebSocket 代理配置** 确保 Nginx 配置包含 WebSocket 代理头: ```nginx location /api/ { proxy_pass http://localhost:5001/; proxy_http_version 1.1; # WebSocket 代理头 proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; proxy_set_header Host $host; # 超时设置 proxy_read_timeout 86400; proxy_send_timeout 86400; } ``` ### **故障排查** #### **WebSocket 连接失败** 1. **检查 Nginx 配置**:确认包含 `Upgrade` 和 `Connection` 头 2. **检查服务器日志**:查看 Socket.IO 服务器是否启动 3. **验证环境变量**:确保 `REACT_APP_API_BASE_URL` 正确设置 #### **路径不匹配** - 客户端日志显示连接地址:`wss://d1kt.cn/api/socket.io` - 服务器日志显示连接日志:`用户连接: [socket-id]` #### **生产环境验证命令** ```bash # 测试 HTTP 握手 curl "https://d1kt.cn/api/socket.io/?EIO=4&transport=polling" # 预期响应: # 0{"sid":"xxx","upgrades":["websocket"],"pingInterval":25000,"pingTimeout":20000} ``` ### **已完成的修复** 1. ✅ 客户端 WebSocket 路径配置 (`getWebSocketPath()`) 2. ✅ 服务器端 Socket.IO 路径配置 (`getSocketIoPath()`) 3. ✅ Nginx WebSocket 代理配置示例 --- *最后更新:2025-10-30*