mirror of
https://github.com/OpenBMB/ChatDev.git
synced 2026-04-25 11:18:06 +00:00
fix: resolve runtime issues in Docker environment
This commit is contained in:
parent
d60bb77bf0
commit
83a7c36f3b
@ -20,7 +20,6 @@ WareHouse/
|
|||||||
data/
|
data/
|
||||||
temp/
|
temp/
|
||||||
logs
|
logs
|
||||||
.aider*
|
|
||||||
|
|
||||||
/frontend
|
/frontend
|
||||||
README*
|
README*
|
||||||
|
|||||||
@ -5,3 +5,6 @@ FRONTEND_PORT=5173
|
|||||||
|
|
||||||
# Frontend points to the backend service name and exposed port
|
# Frontend points to the backend service name and exposed port
|
||||||
VITE_API_BASE_URL=http://backend:6400
|
VITE_API_BASE_URL=http://backend:6400
|
||||||
|
|
||||||
|
# Explicit CORS origins for Docker-based dev (comma-separated)
|
||||||
|
CORS_ALLOW_ORIGINS=http://localhost:5173,http://127.0.0.1:5173
|
||||||
|
|||||||
17
README-zh.md
17
README-zh.md
@ -190,6 +190,23 @@ make dev
|
|||||||
|
|
||||||
> 服务在异常退出后会自动重启,本地文件的修改会同步映射到容器中,便于实时开发。
|
> 服务在异常退出后会自动重启,本地文件的修改会同步映射到容器中,便于实时开发。
|
||||||
|
|
||||||
|
#### 生产环境(Docker + nginx)
|
||||||
|
|
||||||
|
- 构建并运行:
|
||||||
|
```bash
|
||||||
|
docker compose -f compose.yml -f compose.prod.yml up -d --build
|
||||||
|
```
|
||||||
|
- 访问:
|
||||||
|
- 前端:http://localhost:8080
|
||||||
|
- 后端:不对外暴露,由 nginx 通过 /api 与 /ws 转发
|
||||||
|
- 停止:
|
||||||
|
```bash
|
||||||
|
docker compose -f compose.yml -f compose.prod.yml down
|
||||||
|
```
|
||||||
|
- 说明:
|
||||||
|
- .env 保存密钥(API Keys),.env.docker 保存容器相关配置。
|
||||||
|
- nginx 将 /api 与 /ws 代理到 backend:6400(见 frontend/nginx.conf)。
|
||||||
|
|
||||||
### 🔑 配置
|
### 🔑 配置
|
||||||
|
|
||||||
* **环境变量**:在项目根目录创建一个 `.env` 文件。
|
* **环境变量**:在项目根目录创建一个 `.env` 文件。
|
||||||
|
|||||||
44
compose.prod.yml
Normal file
44
compose.prod.yml
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
services:
|
||||||
|
backend:
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
dockerfile: Dockerfile
|
||||||
|
target: runtime
|
||||||
|
container_name: chatdev_backend
|
||||||
|
env_file:
|
||||||
|
- .env
|
||||||
|
- .env.docker
|
||||||
|
expose:
|
||||||
|
- "6400"
|
||||||
|
restart: unless-stopped
|
||||||
|
networks:
|
||||||
|
- appnet
|
||||||
|
# Optional healthcheck (adjust path if different)
|
||||||
|
# healthcheck:
|
||||||
|
# test: ["CMD-SHELL", "curl -fsS http://localhost:6400/api/health || exit 1"]
|
||||||
|
# interval: 10s
|
||||||
|
# timeout: 3s
|
||||||
|
# retries: 5
|
||||||
|
|
||||||
|
frontend:
|
||||||
|
build:
|
||||||
|
context: ./frontend
|
||||||
|
dockerfile: Dockerfile
|
||||||
|
target: prod
|
||||||
|
container_name: chatdev_frontend
|
||||||
|
ports:
|
||||||
|
- "8080:80"
|
||||||
|
depends_on:
|
||||||
|
- backend
|
||||||
|
restart: unless-stopped
|
||||||
|
networks:
|
||||||
|
- appnet
|
||||||
|
# Optional healthcheck
|
||||||
|
# healthcheck:
|
||||||
|
# test: ["CMD-SHELL", "wget -q -O - http://localhost/ >/dev/null 2>&1 || exit 1"]
|
||||||
|
# interval: 10s
|
||||||
|
# timeout: 3s
|
||||||
|
# retries: 5
|
||||||
|
|
||||||
|
networks:
|
||||||
|
appnet: {}
|
||||||
@ -18,7 +18,6 @@ venv/
|
|||||||
node_modules/
|
node_modules/
|
||||||
temp/
|
temp/
|
||||||
logs
|
logs
|
||||||
.aider*
|
|
||||||
|
|
||||||
README*
|
README*
|
||||||
compose.yml
|
compose.yml
|
||||||
|
|||||||
2
frontend/.gitignore
vendored
2
frontend/.gitignore
vendored
@ -7,7 +7,7 @@ yarn-error.log*
|
|||||||
pnpm-debug.log*
|
pnpm-debug.log*
|
||||||
lerna-debug.log*
|
lerna-debug.log*
|
||||||
|
|
||||||
node_modules/
|
node_modules
|
||||||
dist
|
dist
|
||||||
dist-ssr
|
dist-ssr
|
||||||
*.local
|
*.local
|
||||||
|
|||||||
@ -27,7 +27,7 @@ RUN npm run build
|
|||||||
# ---- Prod runtime: serve static dist with nginx ----
|
# ---- Prod runtime: serve static dist with nginx ----
|
||||||
FROM nginx:alpine AS prod
|
FROM nginx:alpine AS prod
|
||||||
# For SPA deep links you might add a custom nginx.conf with index.html fallback.
|
# For SPA deep links you might add a custom nginx.conf with index.html fallback.
|
||||||
# COPY frontend/nginx.conf /etc/nginx/conf.d/default.conf
|
COPY nginx.conf /etc/nginx/conf.d/default.conf
|
||||||
COPY --from=build /app/dist /usr/share/nginx/html
|
COPY --from=build /app/dist /usr/share/nginx/html
|
||||||
EXPOSE 80
|
EXPOSE 80
|
||||||
CMD ["nginx", "-g", "daemon off;"]
|
CMD ["nginx", "-g", "daemon off;"]
|
||||||
|
|||||||
32
frontend/nginx.conf
Normal file
32
frontend/nginx.conf
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
server_name _;
|
||||||
|
|
||||||
|
# Serve built assets
|
||||||
|
root /usr/share/nginx/html;
|
||||||
|
index index.html;
|
||||||
|
|
||||||
|
# Proxy API
|
||||||
|
location /api/ {
|
||||||
|
proxy_pass http://backend:6400/api/;
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
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 WebSocket
|
||||||
|
location /ws {
|
||||||
|
proxy_pass http://backend:6400/ws;
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
proxy_set_header Upgrade $http_upgrade;
|
||||||
|
proxy_set_header Connection "upgrade";
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
}
|
||||||
|
|
||||||
|
# SPA fallback
|
||||||
|
location / {
|
||||||
|
try_files $uri $uri/ /index.html;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1363,10 +1363,24 @@ const establishWebSocketConnection = () => {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const baseUrl = import.meta.env.VITE_API_BASE_URL || 'http://localhost:8000'
|
const apiBase = import.meta.env.VITE_API_BASE_URL || ''
|
||||||
const wsProtocol = baseUrl.startsWith('https') ? 'wss:' : 'ws:'
|
// Defaults: same-origin (works with Vite dev proxy)
|
||||||
const urlObj = new URL(baseUrl)
|
const defaultScheme = window.location.protocol === 'https:' ? 'wss:' : 'ws:'
|
||||||
const wsUrl = `${wsProtocol}//${urlObj.host}/ws`
|
let scheme = defaultScheme
|
||||||
|
let host = window.location.host
|
||||||
|
|
||||||
|
// In production, prefer explicit API base if provided
|
||||||
|
if (!import.meta.env.DEV && apiBase) {
|
||||||
|
try {
|
||||||
|
const api = new URL(apiBase, window.location.origin)
|
||||||
|
scheme = api.protocol === 'https:' ? 'wss:' : 'ws:'
|
||||||
|
host = api.host
|
||||||
|
} catch {
|
||||||
|
// keep defaults
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const wsUrl = `${scheme}//${host}/ws`
|
||||||
const socket = new WebSocket(wsUrl)
|
const socket = new WebSocket(wsUrl)
|
||||||
ws = socket
|
ws = socket
|
||||||
|
|
||||||
|
|||||||
@ -1,7 +1,6 @@
|
|||||||
import yaml from 'js-yaml'
|
import yaml from 'js-yaml'
|
||||||
|
|
||||||
const API_BASE_URL = import.meta.env.VITE_API_BASE_URL || 'http://localhost:8000'
|
const apiUrl = (path) => path
|
||||||
const apiUrl = (path) => `${API_BASE_URL}${path}`
|
|
||||||
|
|
||||||
const addYamlSuffix = (filename) => {
|
const addYamlSuffix = (filename) => {
|
||||||
const trimmed = (filename || '').trim()
|
const trimmed = (filename || '').trim()
|
||||||
|
|||||||
@ -4,15 +4,21 @@ import vue from '@vitejs/plugin-vue'
|
|||||||
// https://vite.dev/config/
|
// https://vite.dev/config/
|
||||||
export default defineConfig(({ mode }) => {
|
export default defineConfig(({ mode }) => {
|
||||||
const env = loadEnv(mode, process.cwd(), '')
|
const env = loadEnv(mode, process.cwd(), '')
|
||||||
const target = env.VITE_API_BASE_URL || 'http://localhost:8000'
|
const target = env.VITE_API_BASE_URL || 'http://localhost:6400'
|
||||||
|
|
||||||
return {
|
return {
|
||||||
plugins: [vue()],
|
plugins: [vue()],
|
||||||
server: {
|
server: {
|
||||||
|
host: true,
|
||||||
proxy: {
|
proxy: {
|
||||||
'/api': {
|
'/api': {
|
||||||
target: target,
|
target: target,
|
||||||
changeOrigin: true,
|
changeOrigin: true,
|
||||||
|
},
|
||||||
|
'/ws': {
|
||||||
|
target: target,
|
||||||
|
ws: true,
|
||||||
|
changeOrigin: true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,10 +2,12 @@
|
|||||||
|
|
||||||
import uuid
|
import uuid
|
||||||
from typing import Callable, Awaitable
|
from typing import Callable, Awaitable
|
||||||
from fastapi import Request, HTTPException
|
from fastapi import Request, HTTPException, FastAPI
|
||||||
from fastapi.responses import JSONResponse
|
from fastapi.responses import JSONResponse
|
||||||
|
from fastapi.middleware.cors import CORSMiddleware
|
||||||
import time
|
import time
|
||||||
import re
|
import re
|
||||||
|
import os
|
||||||
|
|
||||||
from utils.structured_logger import get_server_logger, LogType
|
from utils.structured_logger import get_server_logger, LogType
|
||||||
from utils.exceptions import SecurityError
|
from utils.exceptions import SecurityError
|
||||||
@ -85,9 +87,35 @@ async def rate_limit_middleware(request: Request, call_next: Callable):
|
|||||||
return response
|
return response
|
||||||
|
|
||||||
|
|
||||||
def add_middleware(app):
|
def add_middleware(app: FastAPI):
|
||||||
"""Add all middleware to the FastAPI application."""
|
"""Add all middleware to the FastAPI application."""
|
||||||
# Add middleware in the appropriate order
|
# CORS (dev defaults; override via CORS_ALLOW_ORIGINS comma-separated list)
|
||||||
|
default_origins = [
|
||||||
|
"http://localhost:5173",
|
||||||
|
"http://127.0.0.1:5173",
|
||||||
|
]
|
||||||
|
env_origins = os.getenv("CORS_ALLOW_ORIGINS")
|
||||||
|
if env_origins:
|
||||||
|
origins = [o.strip() for o in env_origins.split(",") if o.strip()]
|
||||||
|
origin_regex = None
|
||||||
|
else:
|
||||||
|
origins = default_origins
|
||||||
|
# Helpful in dev: allow localhost/127.0.0.1 on any port
|
||||||
|
origin_regex = r"^https?://(localhost|127\.0\.0\.1)(:\d+)?$"
|
||||||
|
|
||||||
|
# Add CORS middleware first to handle preflight requests and allow origins.
|
||||||
|
app.add_middleware(
|
||||||
|
CORSMiddleware,
|
||||||
|
allow_origins=origins,
|
||||||
|
allow_origin_regex=origin_regex,
|
||||||
|
allow_credentials=True,
|
||||||
|
allow_methods=["*"],
|
||||||
|
allow_headers=["*"],
|
||||||
|
expose_headers=["X-Correlation-ID"],
|
||||||
|
max_age=600,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Add other middleware
|
||||||
app.middleware("http")(correlation_id_middleware)
|
app.middleware("http")(correlation_id_middleware)
|
||||||
app.middleware("http")(security_middleware)
|
app.middleware("http")(security_middleware)
|
||||||
# app.middleware("http")(rate_limit_middleware) # Enable if needed
|
# app.middleware("http")(rate_limit_middleware) # Enable if needed
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user