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/
|
||||
temp/
|
||||
logs
|
||||
.aider*
|
||||
|
||||
/frontend
|
||||
README*
|
||||
|
||||
@ -5,3 +5,6 @@ FRONTEND_PORT=5173
|
||||
|
||||
# Frontend points to the backend service name and exposed port
|
||||
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` 文件。
|
||||
|
||||
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/
|
||||
temp/
|
||||
logs
|
||||
.aider*
|
||||
|
||||
README*
|
||||
compose.yml
|
||||
|
||||
2
frontend/.gitignore
vendored
2
frontend/.gitignore
vendored
@ -7,7 +7,7 @@ yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
|
||||
node_modules/
|
||||
node_modules
|
||||
dist
|
||||
dist-ssr
|
||||
*.local
|
||||
|
||||
@ -27,7 +27,7 @@ RUN npm run build
|
||||
# ---- Prod runtime: serve static dist with nginx ----
|
||||
FROM nginx:alpine AS prod
|
||||
# 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
|
||||
EXPOSE 80
|
||||
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
|
||||
}
|
||||
|
||||
const baseUrl = import.meta.env.VITE_API_BASE_URL || 'http://localhost:8000'
|
||||
const wsProtocol = baseUrl.startsWith('https') ? 'wss:' : 'ws:'
|
||||
const urlObj = new URL(baseUrl)
|
||||
const wsUrl = `${wsProtocol}//${urlObj.host}/ws`
|
||||
const apiBase = import.meta.env.VITE_API_BASE_URL || ''
|
||||
// Defaults: same-origin (works with Vite dev proxy)
|
||||
const defaultScheme = window.location.protocol === 'https:' ? 'wss:' : '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)
|
||||
ws = socket
|
||||
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
import yaml from 'js-yaml'
|
||||
|
||||
const API_BASE_URL = import.meta.env.VITE_API_BASE_URL || 'http://localhost:8000'
|
||||
const apiUrl = (path) => `${API_BASE_URL}${path}`
|
||||
const apiUrl = (path) => path
|
||||
|
||||
const addYamlSuffix = (filename) => {
|
||||
const trimmed = (filename || '').trim()
|
||||
|
||||
@ -4,15 +4,21 @@ import vue from '@vitejs/plugin-vue'
|
||||
// https://vite.dev/config/
|
||||
export default defineConfig(({ mode }) => {
|
||||
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 {
|
||||
plugins: [vue()],
|
||||
server: {
|
||||
host: true,
|
||||
proxy: {
|
||||
'/api': {
|
||||
target: target,
|
||||
changeOrigin: true,
|
||||
},
|
||||
'/ws': {
|
||||
target: target,
|
||||
ws: true,
|
||||
changeOrigin: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,10 +2,12 @@
|
||||
|
||||
import uuid
|
||||
from typing import Callable, Awaitable
|
||||
from fastapi import Request, HTTPException
|
||||
from fastapi import Request, HTTPException, FastAPI
|
||||
from fastapi.responses import JSONResponse
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
import time
|
||||
import re
|
||||
import os
|
||||
|
||||
from utils.structured_logger import get_server_logger, LogType
|
||||
from utils.exceptions import SecurityError
|
||||
@ -85,9 +87,35 @@ async def rate_limit_middleware(request: Request, call_next: Callable):
|
||||
return response
|
||||
|
||||
|
||||
def add_middleware(app):
|
||||
def add_middleware(app: FastAPI):
|
||||
"""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")(security_middleware)
|
||||
# app.middleware("http")(rate_limit_middleware) # Enable if needed
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user