feat: 添加前后端一体化 Docker 部署支持
- 创建根目录 Dockerfile,支持前后端一起部署到一个容器 - 添加 Nginx 配置,自动代理后端 API 和 WebSocket - 创建启动脚本,同时启动后端和 Nginx - 更新 docker-compose.yml 支持一体化部署 - 创建一体化部署脚本 deploy.sh - 修复前端环境变量支持,默认使用相对路径 - 优化部署脚本:.env 文件不存在时创建后终止部署 - 更新部署文档,添加一体化部署说明
This commit is contained in:
@@ -0,0 +1,44 @@
|
||||
# Git
|
||||
.git/
|
||||
.gitignore
|
||||
|
||||
# IDE
|
||||
.idea/
|
||||
*.iml
|
||||
*.iws
|
||||
*.ipr
|
||||
.vscode/
|
||||
*.swp
|
||||
*.swo
|
||||
*~
|
||||
.DS_Store
|
||||
|
||||
# 构建产物
|
||||
backend/build/
|
||||
backend/.gradle/
|
||||
backend/out/
|
||||
backend/bin/
|
||||
frontend/dist/
|
||||
frontend/node_modules/
|
||||
frontend/.vite/
|
||||
frontend/.cache/
|
||||
|
||||
# 日志
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
|
||||
# 环境配置
|
||||
.env
|
||||
.env.local
|
||||
.env.*.local
|
||||
|
||||
# 其他
|
||||
*.bak
|
||||
*.backup
|
||||
*.old
|
||||
clob-client/
|
||||
polymarket-demo/
|
||||
|
||||
+79
@@ -0,0 +1,79 @@
|
||||
# 多阶段构建:前后端一体化部署
|
||||
# 阶段1:构建前端
|
||||
FROM node:18-alpine AS frontend-build
|
||||
|
||||
WORKDIR /app/frontend
|
||||
|
||||
# 复制前端文件
|
||||
COPY frontend/package*.json ./
|
||||
RUN npm ci
|
||||
|
||||
COPY frontend/ ./
|
||||
|
||||
# 构建前端(使用相对路径,通过 Nginx 代理)
|
||||
RUN npm run build
|
||||
|
||||
# 阶段2:构建后端
|
||||
FROM gradle:8.5-jdk17 AS backend-build
|
||||
|
||||
WORKDIR /app/backend
|
||||
|
||||
# 复制 Gradle 配置文件
|
||||
COPY backend/build.gradle.kts backend/settings.gradle.kts ./
|
||||
COPY backend/gradle ./gradle
|
||||
|
||||
# 下载依赖(利用 Docker 缓存)
|
||||
RUN gradle dependencies --no-daemon || true
|
||||
|
||||
# 复制源代码
|
||||
COPY backend/src ./src
|
||||
|
||||
# 构建应用
|
||||
RUN gradle bootJar --no-daemon
|
||||
|
||||
# 阶段3:运行环境
|
||||
FROM openjdk:17-jre-slim
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# 安装 Nginx 和必要的工具
|
||||
RUN apt-get update && \
|
||||
apt-get install -y nginx curl && \
|
||||
rm -rf /var/lib/apt/lists/* && \
|
||||
rm -rf /etc/nginx/sites-enabled/default
|
||||
|
||||
# 从构建阶段复制文件
|
||||
COPY --from=frontend-build /app/frontend/dist /usr/share/nginx/html
|
||||
COPY --from=backend-build /app/backend/build/libs/*.jar app.jar
|
||||
|
||||
# 复制 Nginx 配置
|
||||
COPY docker/nginx.conf /etc/nginx/nginx.conf
|
||||
|
||||
# 创建启动脚本
|
||||
COPY docker/start.sh /app/start.sh
|
||||
RUN chmod +x /app/start.sh
|
||||
|
||||
# 创建 Nginx 运行目录
|
||||
RUN mkdir -p /var/log/nginx /var/lib/nginx /var/cache/nginx && \
|
||||
chown -R appuser:appuser /var/log/nginx /var/lib/nginx /var/cache/nginx
|
||||
|
||||
# 创建非 root 用户
|
||||
RUN useradd -m -u 1000 appuser && \
|
||||
chown -R appuser:appuser /app && \
|
||||
chown -R appuser:appuser /usr/share/nginx/html && \
|
||||
chown -R appuser:appuser /var/log/nginx && \
|
||||
chown -R appuser:appuser /var/lib/nginx && \
|
||||
chown -R appuser:appuser /etc/nginx
|
||||
|
||||
USER appuser
|
||||
|
||||
# 暴露端口
|
||||
EXPOSE 80
|
||||
|
||||
# 健康检查
|
||||
HEALTHCHECK --interval=30s --timeout=3s --start-period=40s --retries=3 \
|
||||
CMD curl -f http://localhost/api/health || exit 1
|
||||
|
||||
# 启动服务(同时启动 Nginx 和后端)
|
||||
ENTRYPOINT ["/app/start.sh"]
|
||||
|
||||
@@ -124,25 +124,33 @@ npm run dev
|
||||
|
||||
#### 快速部署
|
||||
|
||||
**后端(Java 方式)**:
|
||||
**一体化部署(推荐)** - 前后端一起部署到一个容器:
|
||||
```bash
|
||||
# 在项目根目录
|
||||
./deploy.sh
|
||||
```
|
||||
|
||||
**分别部署**:
|
||||
|
||||
后端(Java 方式):
|
||||
```bash
|
||||
cd backend
|
||||
./deploy.sh java
|
||||
```
|
||||
|
||||
**后端(Docker 方式)**:
|
||||
后端(Docker 方式):
|
||||
```bash
|
||||
cd backend
|
||||
./deploy.sh docker
|
||||
```
|
||||
|
||||
**前端**:
|
||||
前端:
|
||||
```bash
|
||||
cd frontend
|
||||
# 使用默认后端地址
|
||||
# 使用默认后端地址(相对路径)
|
||||
./build.sh
|
||||
|
||||
# 或指定自定义后端地址
|
||||
# 或指定自定义后端地址(跨域场景)
|
||||
./build.sh --api-url http://your-backend-server.com:8000
|
||||
```
|
||||
|
||||
|
||||
+6
-1
@@ -150,7 +150,12 @@ JWT_SECRET=your-jwt-secret-key-change-in-production
|
||||
# 管理员密码重置密钥(生产环境必须修改)
|
||||
ADMIN_RESET_PASSWORD_KEY=your-admin-reset-key-change-in-production
|
||||
EOF
|
||||
warn "请编辑 .env 文件配置相关参数"
|
||||
error ".env 文件已创建,请先编辑配置相关参数后再运行部署脚本"
|
||||
error "特别是以下参数必须修改:"
|
||||
error " - DB_PASSWORD: 数据库密码"
|
||||
error " - JWT_SECRET: JWT 密钥(生产环境)"
|
||||
error " - ADMIN_RESET_PASSWORD_KEY: 管理员密码重置密钥(生产环境)"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 构建并启动
|
||||
|
||||
@@ -0,0 +1,114 @@
|
||||
#!/bin/bash
|
||||
|
||||
# PolyHermes 一体化部署脚本
|
||||
# 将前后端一起部署到一个 Docker 容器中
|
||||
|
||||
set -e
|
||||
|
||||
# 颜色输出
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# 打印信息
|
||||
info() {
|
||||
echo -e "${GREEN}[INFO]${NC} $1"
|
||||
}
|
||||
|
||||
warn() {
|
||||
echo -e "${YELLOW}[WARN]${NC} $1"
|
||||
}
|
||||
|
||||
error() {
|
||||
echo -e "${RED}[ERROR]${NC} $1"
|
||||
}
|
||||
|
||||
# 检查 Docker 环境
|
||||
check_docker() {
|
||||
if ! command -v docker &> /dev/null; then
|
||||
error "Docker 未安装,请先安装 Docker"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! command -v docker-compose &> /dev/null; then
|
||||
error "Docker Compose 未安装,请先安装 Docker Compose"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
info "Docker 环境检查通过"
|
||||
}
|
||||
|
||||
# 创建 .env 文件(如果不存在)
|
||||
create_env_file() {
|
||||
if [ ! -f ".env" ]; then
|
||||
warn ".env 文件不存在,创建示例文件..."
|
||||
cat > .env <<EOF
|
||||
# 数据库配置
|
||||
DB_URL=jdbc:mysql://mysql:3306/polymarket_bot?useSSL=false&serverTimezone=UTC&characterEncoding=utf8mb4
|
||||
DB_USERNAME=root
|
||||
DB_PASSWORD=your_password_here
|
||||
|
||||
# Spring Profile
|
||||
SPRING_PROFILES_ACTIVE=prod
|
||||
|
||||
# 服务器端口(对外暴露的端口)
|
||||
SERVER_PORT=80
|
||||
|
||||
# MySQL 端口(可选,用于外部连接)
|
||||
MYSQL_PORT=3306
|
||||
|
||||
# Polygon RPC
|
||||
POLYGON_RPC_URL=https://polygon-rpc.com
|
||||
|
||||
# JWT 密钥(生产环境必须修改)
|
||||
JWT_SECRET=your-jwt-secret-key-change-in-production
|
||||
|
||||
# 管理员密码重置密钥(生产环境必须修改)
|
||||
ADMIN_RESET_PASSWORD_KEY=your-admin-reset-key-change-in-production
|
||||
EOF
|
||||
error ".env 文件已创建,请先编辑配置相关参数后再运行部署脚本"
|
||||
error "特别是以下参数必须修改:"
|
||||
error " - DB_PASSWORD: 数据库密码"
|
||||
error " - JWT_SECRET: JWT 密钥(生产环境)"
|
||||
error " - ADMIN_RESET_PASSWORD_KEY: 管理员密码重置密钥(生产环境)"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# 构建并启动
|
||||
deploy() {
|
||||
info "构建 Docker 镜像..."
|
||||
docker-compose build
|
||||
|
||||
info "启动服务..."
|
||||
docker-compose up -d
|
||||
|
||||
info "等待服务启动..."
|
||||
sleep 5
|
||||
|
||||
info "检查服务状态..."
|
||||
docker-compose ps
|
||||
|
||||
info "查看日志: docker-compose logs -f"
|
||||
info "停止服务: docker-compose down"
|
||||
}
|
||||
|
||||
# 主函数
|
||||
main() {
|
||||
echo "=========================================="
|
||||
echo " PolyHermes 一体化部署脚本"
|
||||
echo "=========================================="
|
||||
echo ""
|
||||
|
||||
check_docker
|
||||
create_env_file
|
||||
deploy
|
||||
|
||||
echo ""
|
||||
info "部署完成!"
|
||||
info "访问地址: http://localhost:${SERVER_PORT:-80}"
|
||||
}
|
||||
|
||||
main "$@"
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
version: '3.8'
|
||||
|
||||
services:
|
||||
app:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile
|
||||
container_name: polymarket-bot
|
||||
ports:
|
||||
- "${SERVER_PORT:-80}:80"
|
||||
environment:
|
||||
- SPRING_PROFILES_ACTIVE=${SPRING_PROFILES_ACTIVE:-prod}
|
||||
- DB_URL=${DB_URL:-jdbc:mysql://mysql:3306/polymarket_bot?useSSL=false&serverTimezone=UTC&characterEncoding=utf8mb4}
|
||||
- DB_USERNAME=${DB_USERNAME:-root}
|
||||
- DB_PASSWORD=${DB_PASSWORD:-}
|
||||
- SERVER_PORT=8000
|
||||
- POLYGON_RPC_URL=${POLYGON_RPC_URL:-https://polygon-rpc.com}
|
||||
- JWT_SECRET=${JWT_SECRET:-change-me-in-production}
|
||||
- ADMIN_RESET_PASSWORD_KEY=${ADMIN_RESET_PASSWORD_KEY:-change-me-in-production}
|
||||
depends_on:
|
||||
mysql:
|
||||
condition: service_healthy
|
||||
restart: unless-stopped
|
||||
networks:
|
||||
- polymarket-network
|
||||
|
||||
mysql:
|
||||
image: mysql:8.2
|
||||
container_name: polymarket-bot-mysql
|
||||
ports:
|
||||
- "${MYSQL_PORT:-3306}:3306"
|
||||
environment:
|
||||
- MYSQL_ROOT_PASSWORD=${DB_PASSWORD:-rootpassword}
|
||||
- MYSQL_DATABASE=polymarket_bot
|
||||
- MYSQL_CHARACTER_SET_SERVER=utf8mb4
|
||||
- MYSQL_COLLATION_SERVER=utf8mb4_unicode_ci
|
||||
volumes:
|
||||
- mysql-data:/var/lib/mysql
|
||||
healthcheck:
|
||||
test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "root", "-p${DB_PASSWORD:-rootpassword}"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
restart: unless-stopped
|
||||
networks:
|
||||
- polymarket-network
|
||||
|
||||
volumes:
|
||||
mysql-data:
|
||||
|
||||
networks:
|
||||
polymarket-network:
|
||||
driver: bridge
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
user appuser;
|
||||
worker_processes auto;
|
||||
error_log /var/log/nginx/error.log warn;
|
||||
pid /tmp/nginx.pid;
|
||||
|
||||
events {
|
||||
worker_connections 1024;
|
||||
}
|
||||
|
||||
http {
|
||||
include /etc/nginx/mime.types;
|
||||
default_type application/octet-stream;
|
||||
|
||||
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
|
||||
'$status $body_bytes_sent "$http_referer" '
|
||||
'"$http_user_agent" "$http_x_forwarded_for"';
|
||||
|
||||
access_log /var/log/nginx/access.log main;
|
||||
|
||||
sendfile on;
|
||||
tcp_nopush on;
|
||||
tcp_nodelay on;
|
||||
keepalive_timeout 65;
|
||||
types_hash_max_size 2048;
|
||||
|
||||
gzip on;
|
||||
gzip_vary on;
|
||||
gzip_proxied any;
|
||||
gzip_comp_level 6;
|
||||
gzip_types text/plain text/css text/xml text/javascript application/json application/javascript application/xml+rss application/rss+xml font/truetype font/opentype application/vnd.ms-fontobject image/svg+xml;
|
||||
|
||||
# 上游后端服务
|
||||
upstream backend {
|
||||
server localhost:8000;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 80;
|
||||
server_name _;
|
||||
root /usr/share/nginx/html;
|
||||
index index.html;
|
||||
|
||||
# API 代理
|
||||
location /api {
|
||||
proxy_pass http://backend;
|
||||
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;
|
||||
|
||||
# WebSocket 支持
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
}
|
||||
|
||||
# WebSocket 代理
|
||||
location /ws {
|
||||
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;
|
||||
|
||||
# WebSocket 超时设置
|
||||
proxy_read_timeout 86400;
|
||||
proxy_send_timeout 86400;
|
||||
}
|
||||
|
||||
# 前端静态文件
|
||||
location / {
|
||||
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";
|
||||
}
|
||||
}
|
||||
|
||||
# 健康检查
|
||||
location /health {
|
||||
access_log off;
|
||||
return 200 "healthy\n";
|
||||
add_header Content-Type text/plain;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Executable
+42
@@ -0,0 +1,42 @@
|
||||
#!/bin/bash
|
||||
|
||||
# 启动脚本:同时启动 Nginx 和后端服务
|
||||
|
||||
set -e
|
||||
|
||||
# 函数:清理进程
|
||||
cleanup() {
|
||||
echo "收到退出信号,清理进程..."
|
||||
if [ -n "$BACKEND_PID" ]; then
|
||||
kill $BACKEND_PID 2>/dev/null || true
|
||||
fi
|
||||
nginx -s quit 2>/dev/null || true
|
||||
exit 0
|
||||
}
|
||||
|
||||
# 注册信号处理
|
||||
trap cleanup SIGTERM SIGINT
|
||||
|
||||
# 启动后端服务(后台运行)
|
||||
echo "启动后端服务..."
|
||||
java -jar /app/app.jar --spring.profiles.active=${SPRING_PROFILES_ACTIVE:-prod} &
|
||||
BACKEND_PID=$!
|
||||
|
||||
# 等待后端服务启动
|
||||
echo "等待后端服务启动..."
|
||||
for i in {1..60}; do
|
||||
if curl -f http://localhost:8000/api/health > /dev/null 2>&1; then
|
||||
echo "后端服务已启动"
|
||||
break
|
||||
fi
|
||||
if [ $i -eq 60 ]; then
|
||||
echo "后端服务启动超时"
|
||||
exit 1
|
||||
fi
|
||||
sleep 1
|
||||
done
|
||||
|
||||
# 启动 Nginx(前台运行,作为主进程)
|
||||
echo "启动 Nginx..."
|
||||
exec nginx -g "daemon off;"
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
## 目录
|
||||
|
||||
- [一体化部署(推荐)](#一体化部署推荐)
|
||||
- [后端部署](#后端部署)
|
||||
- [Java 直接部署](#java-直接部署)
|
||||
- [Docker 部署](#docker-部署)
|
||||
@@ -11,6 +12,82 @@
|
||||
- [环境配置](#环境配置)
|
||||
- [常见问题](#常见问题)
|
||||
|
||||
## 一体化部署(推荐)
|
||||
|
||||
将前后端一起部署到一个 Docker 容器中,使用 Nginx 提供前端静态文件并代理后端 API。
|
||||
|
||||
### 前置要求
|
||||
|
||||
- Docker 20.10+
|
||||
- Docker Compose 2.0+
|
||||
|
||||
### 部署步骤
|
||||
|
||||
1. **使用部署脚本(推荐)**
|
||||
|
||||
```bash
|
||||
# 在项目根目录
|
||||
./deploy.sh
|
||||
```
|
||||
|
||||
脚本会自动:
|
||||
- 检查 Docker 环境
|
||||
- 创建 `.env` 配置文件(如果不存在)
|
||||
- 构建 Docker 镜像(包含前后端)
|
||||
- 启动服务(应用 + MySQL)
|
||||
|
||||
2. **手动部署**
|
||||
|
||||
```bash
|
||||
# 创建 .env 文件
|
||||
cat > .env <<EOF
|
||||
DB_URL=jdbc:mysql://mysql:3306/polymarket_bot?useSSL=false&serverTimezone=UTC&characterEncoding=utf8mb4
|
||||
DB_USERNAME=root
|
||||
DB_PASSWORD=your_password_here
|
||||
SPRING_PROFILES_ACTIVE=prod
|
||||
SERVER_PORT=80
|
||||
POLYGON_RPC_URL=https://polygon-rpc.com
|
||||
JWT_SECRET=your-jwt-secret-key-change-in-production
|
||||
ADMIN_RESET_PASSWORD_KEY=your-admin-reset-key-change-in-production
|
||||
EOF
|
||||
|
||||
# 构建并启动
|
||||
docker-compose build
|
||||
docker-compose up -d
|
||||
|
||||
# 查看日志
|
||||
docker-compose logs -f
|
||||
|
||||
# 停止服务
|
||||
docker-compose down
|
||||
```
|
||||
|
||||
3. **访问应用**
|
||||
|
||||
- 前端和后端统一访问:`http://localhost:80`
|
||||
- Nginx 自动处理:
|
||||
- `/api/*` → 后端 API(`localhost:8000`)
|
||||
- `/ws` → 后端 WebSocket(`localhost:8000`)
|
||||
- 其他路径 → 前端静态文件
|
||||
|
||||
### 架构说明
|
||||
|
||||
```
|
||||
用户请求
|
||||
↓
|
||||
Nginx (端口 80)
|
||||
├─ /api/* → 后端服务 (localhost:8000)
|
||||
├─ /ws → 后端 WebSocket (localhost:8000)
|
||||
└─ /* → 前端静态文件 (/usr/share/nginx/html)
|
||||
```
|
||||
|
||||
### 优势
|
||||
|
||||
- ✅ 单一容器,简化部署
|
||||
- ✅ 统一端口,无需配置 CORS
|
||||
- ✅ 自动处理前后端路由
|
||||
- ✅ 生产环境就绪
|
||||
|
||||
## 后端部署
|
||||
|
||||
### Java 直接部署
|
||||
|
||||
+83
-43
@@ -1,7 +1,8 @@
|
||||
#!/bin/bash
|
||||
|
||||
# PolyHermes 前端构建脚本
|
||||
# 支持自定义后端 API 地址
|
||||
# 支持通过环境变量配置后端 URL(用于跨域场景)
|
||||
# 默认使用相对路径,通过反向代理转发到后端
|
||||
|
||||
set -e
|
||||
|
||||
@@ -11,10 +12,6 @@ GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# 默认配置
|
||||
DEFAULT_API_URL="http://127.0.0.1:8000"
|
||||
API_URL="${VITE_API_URL:-$DEFAULT_API_URL}"
|
||||
|
||||
# 打印信息
|
||||
info() {
|
||||
echo -e "${GREEN}[INFO]${NC} $1"
|
||||
@@ -44,40 +41,70 @@ check_node() {
|
||||
info "Node.js 环境检查通过: $(node -v)"
|
||||
}
|
||||
|
||||
# 创建环境配置文件
|
||||
# 创建环境配置文件(可选)
|
||||
create_env_file() {
|
||||
info "创建环境配置文件..."
|
||||
|
||||
# 解析 API URL,提取协议、主机和端口
|
||||
if [[ $API_URL == http* ]]; then
|
||||
# 提取协议和主机
|
||||
PROTOCOL=$(echo $API_URL | sed -E 's|^([^:]+)://.*|\1|')
|
||||
HOST_PORT=$(echo $API_URL | sed -E 's|^[^:]+://([^/]+).*|\1|')
|
||||
HOST=$(echo $HOST_PORT | cut -d':' -f1)
|
||||
PORT=$(echo $HOST_PORT | cut -d':' -f2)
|
||||
# 如果设置了环境变量,创建 .env.production 文件
|
||||
if [ -n "$VITE_API_URL" ] || [ -n "$VITE_WS_URL" ]; then
|
||||
info "检测到环境变量,创建 .env.production 文件..."
|
||||
|
||||
# 构建 WebSocket URL
|
||||
if [ "$PROTOCOL" = "https" ]; then
|
||||
WS_PROTOCOL="wss"
|
||||
# 解析 API URL,提取协议、主机和端口
|
||||
if [ -n "$VITE_API_URL" ]; then
|
||||
API_URL="$VITE_API_URL"
|
||||
if [[ $API_URL == http* ]]; then
|
||||
PROTOCOL=$(echo $API_URL | sed -E 's|^([^:]+)://.*|\1|')
|
||||
if [ "$PROTOCOL" = "https" ]; then
|
||||
WS_PROTOCOL="wss"
|
||||
else
|
||||
WS_PROTOCOL="ws"
|
||||
fi
|
||||
HOST_PORT=$(echo $API_URL | sed -E 's|^[^:]+://([^/]+).*|\1|')
|
||||
WS_URL="${WS_PROTOCOL}://${HOST_PORT}"
|
||||
else
|
||||
error "VITE_API_URL 格式错误,应为 http://host:port 或 https://host:port"
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
WS_PROTOCOL="ws"
|
||||
# 如果只设置了 WS_URL,从 WS_URL 推导 API_URL
|
||||
if [ -n "$VITE_WS_URL" ]; then
|
||||
WS_URL="$VITE_WS_URL"
|
||||
if [[ $WS_URL == ws* ]]; then
|
||||
WS_PROTOCOL=$(echo $WS_URL | sed -E 's|^([^:]+)://.*|\1|')
|
||||
if [ "$WS_PROTOCOL" = "wss" ]; then
|
||||
API_PROTOCOL="https"
|
||||
else
|
||||
API_PROTOCOL="http"
|
||||
fi
|
||||
HOST_PORT=$(echo $WS_URL | sed -E 's|^[^:]+://([^/]+).*|\1|')
|
||||
API_URL="${API_PROTOCOL}://${HOST_PORT}"
|
||||
else
|
||||
error "VITE_WS_URL 格式错误,应为 ws://host:port 或 wss://host:port"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
WS_URL="${WS_PROTOCOL}://${HOST}:${PORT}"
|
||||
else
|
||||
error "API URL 格式错误,应为 http://host:port 或 https://host:port"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 创建 .env.production 文件
|
||||
cat > .env.production <<EOF
|
||||
# 后端 API 地址
|
||||
VITE_API_URL=$API_URL
|
||||
VITE_WS_URL=$WS_URL
|
||||
|
||||
# 如果同时设置了两个环境变量,使用设置的值
|
||||
if [ -n "$VITE_WS_URL" ]; then
|
||||
WS_URL="$VITE_WS_URL"
|
||||
fi
|
||||
|
||||
cat > .env.production <<EOF
|
||||
# 后端 API 地址(可选,用于跨域场景)
|
||||
# 如果未设置,前端将使用相对路径 /api(通过反向代理转发)
|
||||
VITE_API_URL=${VITE_API_URL:-}
|
||||
VITE_WS_URL=${VITE_WS_URL:-${WS_URL:-}}
|
||||
EOF
|
||||
|
||||
info "环境配置已创建: .env.production"
|
||||
info " API URL: $API_URL"
|
||||
info " WebSocket URL: $WS_URL"
|
||||
|
||||
info "环境配置已创建: .env.production"
|
||||
if [ -n "$VITE_API_URL" ]; then
|
||||
info " API URL: $VITE_API_URL"
|
||||
fi
|
||||
if [ -n "$VITE_WS_URL" ] || [ -n "$WS_URL" ]; then
|
||||
info " WebSocket URL: ${VITE_WS_URL:-$WS_URL}"
|
||||
fi
|
||||
else
|
||||
info "未设置环境变量,前端将使用相对路径(通过反向代理转发)"
|
||||
fi
|
||||
}
|
||||
|
||||
# 构建应用
|
||||
@@ -114,23 +141,36 @@ main() {
|
||||
|
||||
# 解析参数
|
||||
if [ "$1" = "--api-url" ] && [ -n "$2" ]; then
|
||||
API_URL="$2"
|
||||
info "使用自定义 API 地址: $API_URL"
|
||||
elif [ -n "$VITE_API_URL" ]; then
|
||||
info "使用环境变量 API 地址: $API_URL"
|
||||
else
|
||||
info "使用默认 API 地址: $API_URL"
|
||||
warn "提示: 可通过环境变量 VITE_API_URL 或参数 --api-url 自定义后端地址"
|
||||
export VITE_API_URL="$2"
|
||||
info "使用自定义 API 地址: $VITE_API_URL"
|
||||
elif [ "$1" = "--ws-url" ] && [ -n "$2" ]; then
|
||||
export VITE_WS_URL="$2"
|
||||
info "使用自定义 WebSocket 地址: $VITE_WS_URL"
|
||||
fi
|
||||
|
||||
create_env_file
|
||||
|
||||
if [ -z "$VITE_API_URL" ] && [ -z "$VITE_WS_URL" ]; then
|
||||
info "前端将使用相对路径 /api 和 /ws"
|
||||
info "请确保在生产环境配置反向代理(Nginx/Apache)"
|
||||
fi
|
||||
|
||||
build_app
|
||||
|
||||
echo ""
|
||||
info "构建完成!"
|
||||
info "部署方式:"
|
||||
info " 1. 静态文件服务器:将 dist/ 目录部署到 Nginx、Apache 等"
|
||||
info " 2. 使用 serve 预览:npx serve -s dist -l 3000"
|
||||
if [ -n "$VITE_API_URL" ] || [ -n "$VITE_WS_URL" ]; then
|
||||
info " 已配置后端地址,前端将直接请求后端服务器"
|
||||
else
|
||||
info " 1. 静态文件服务器:将 dist/ 目录部署到 Nginx、Apache 等"
|
||||
info " 2. 配置反向代理:/api -> 后端地址,/ws -> 后端 WebSocket 地址"
|
||||
fi
|
||||
info " 3. 使用 serve 预览:npx serve -s dist -l 3000"
|
||||
echo ""
|
||||
info "提示:可通过环境变量配置后端地址:"
|
||||
info " VITE_API_URL=http://your-backend.com:8000 ./build.sh"
|
||||
info " 或使用参数:./build.sh --api-url http://your-backend.com:8000"
|
||||
}
|
||||
|
||||
main "$@"
|
||||
|
||||
@@ -5,17 +5,16 @@ import { wsManager } from './websocket'
|
||||
|
||||
/**
|
||||
* API 基础配置
|
||||
* 支持通过环境变量 VITE_API_URL 配置后端地址
|
||||
* 默认使用相对路径 /api(适用于同域部署)
|
||||
* 如果设置了 VITE_API_URL,则使用完整 URL
|
||||
* 默认使用相对路径 /api(通过反向代理转发)
|
||||
* 如果设置了 VITE_API_URL 环境变量,则使用完整 URL(用于跨域场景)
|
||||
*/
|
||||
const getBaseURL = (): string => {
|
||||
const envApiUrl = import.meta.env.VITE_API_URL
|
||||
if (envApiUrl) {
|
||||
// 如果设置了环境变量,使用完整 URL
|
||||
// 如果设置了环境变量,使用完整 URL(支持跨域)
|
||||
return `${envApiUrl}/api`
|
||||
}
|
||||
// 否则使用相对路径(适用于开发环境代理或同域部署)
|
||||
// 否则使用相对路径(通过反向代理转发)
|
||||
return '/api'
|
||||
}
|
||||
|
||||
|
||||
@@ -325,17 +325,18 @@ class WebSocketManager {
|
||||
|
||||
/**
|
||||
* 获取 WebSocket URL(带token认证)
|
||||
* 支持通过环境变量 VITE_WS_URL 配置 WebSocket 地址
|
||||
* 默认使用相对路径 /ws(通过反向代理转发)
|
||||
* 如果设置了 VITE_WS_URL 环境变量,则使用完整 URL(用于跨域场景)
|
||||
*/
|
||||
private getWebSocketUrl(): string {
|
||||
const envWsUrl = import.meta.env.VITE_WS_URL
|
||||
let wsBaseUrl: string
|
||||
|
||||
if (envWsUrl) {
|
||||
// 如果设置了环境变量,使用完整 URL
|
||||
// 如果设置了环境变量,使用完整 URL(支持跨域)
|
||||
wsBaseUrl = envWsUrl
|
||||
} else {
|
||||
// 否则使用相对路径(适用于开发环境代理或同域部署)
|
||||
// 否则使用相对路径(通过反向代理转发)
|
||||
const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:'
|
||||
const host = window.location.host
|
||||
wsBaseUrl = `${protocol}//${host}`
|
||||
|
||||
@@ -6,7 +6,8 @@ export default defineConfig(({ mode }) => {
|
||||
// 加载环境变量
|
||||
const env = loadEnv(mode, process.cwd(), '')
|
||||
|
||||
// 从环境变量获取 API 地址,默认使用 localhost:8000
|
||||
// 从环境变量获取后端地址,用于开发环境代理
|
||||
// 如果未设置,使用默认值 localhost:8000
|
||||
const API_URL = env.VITE_API_URL || 'http://localhost:8000'
|
||||
const WS_URL = env.VITE_WS_URL || 'ws://localhost:8000'
|
||||
|
||||
@@ -17,8 +18,7 @@ export default defineConfig(({ mode }) => {
|
||||
proxy: {
|
||||
'/api': {
|
||||
target: API_URL,
|
||||
changeOrigin: true,
|
||||
rewrite: (path) => path.replace(/^\/api/, '/api')
|
||||
changeOrigin: true
|
||||
},
|
||||
'/ws': {
|
||||
target: WS_URL,
|
||||
@@ -26,11 +26,6 @@ export default defineConfig(({ mode }) => {
|
||||
changeOrigin: true
|
||||
}
|
||||
}
|
||||
},
|
||||
// 定义环境变量,在构建时注入
|
||||
define: {
|
||||
'import.meta.env.VITE_API_URL': JSON.stringify(API_URL),
|
||||
'import.meta.env.VITE_WS_URL': JSON.stringify(WS_URL)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user