fix: 修复登录500错误和移除明文密码提示
- 修复 deps.py 中 get_vn_service 的引用错误 (vn_service.vn_service -> vn_service) - 移除登录页面上的明文密码提示 - 改进前端错误处理,避免数据加载失败导致登录显示错误
This commit is contained in:
@@ -0,0 +1,86 @@
|
||||
# Sanguo VeighNa 环境变量配置示例
|
||||
# 复制此文件为 .env 并根据需要修改
|
||||
|
||||
# ============================================
|
||||
# 数据目录配置(NAS 部署关键配置)
|
||||
# ============================================
|
||||
# Synology DSM 路径通常是 /volume1/...
|
||||
# 请根据实际情况修改此路径
|
||||
DATA_DIR=/volume1/docker/stock/sanguo_vnpy
|
||||
|
||||
# ============================================
|
||||
# 端口配置
|
||||
# ============================================
|
||||
HTTP_PORT=8000
|
||||
WS_PORT=8080
|
||||
NGINX_HTTP_PORT=80
|
||||
NGINX_HTTPS_PORT=443
|
||||
|
||||
# ============================================
|
||||
# 时区配置
|
||||
# ============================================
|
||||
TZ=Asia/Shanghai
|
||||
|
||||
# ============================================
|
||||
# VeighNa 日志配置
|
||||
# ============================================
|
||||
VNPY_LOG_LEVEL=INFO
|
||||
VNPY_LOG_FILE=/app/logs/vnpy.log
|
||||
|
||||
# ============================================
|
||||
# 数据库配置
|
||||
# ============================================
|
||||
DB_TYPE=sqlite
|
||||
DB_PATH=/app/data/vnpy.db
|
||||
|
||||
POSTGRES_DB=vnpy
|
||||
POSTGRES_USER=vnpy
|
||||
POSTGRES_PASSWORD=vnpy_password_change_me
|
||||
POSTGRES_HOST=postgres
|
||||
POSTGRES_PORT=5432
|
||||
|
||||
# ============================================
|
||||
# Web 服务配置
|
||||
# ============================================
|
||||
WEB_HOST=0.0.0.0
|
||||
WEB_PORT=8000
|
||||
WEB_WORKERS=2
|
||||
|
||||
# ============================================
|
||||
# 安全配置
|
||||
# ============================================
|
||||
JWT_SECRET_KEY=change-this-to-a-random-secret-key
|
||||
SESSION_TIMEOUT=3600
|
||||
|
||||
# ============================================
|
||||
# Redis 配置
|
||||
# ============================================
|
||||
REDIS_HOST=redis
|
||||
REDIS_PORT=6379
|
||||
REDIS_PASSWORD=
|
||||
|
||||
# ============================================
|
||||
# Nginx 配置
|
||||
# ============================================
|
||||
ENABLE_NGINX=false
|
||||
|
||||
# ============================================
|
||||
# 资源限制
|
||||
# ============================================
|
||||
VNPY_CPU_LIMIT=2.0
|
||||
VNPY_MEMORY_LIMIT=2G
|
||||
VNPY_CPU_RESERVATION=0.5
|
||||
VNPY_MEMORY_RESERVATION=512M
|
||||
|
||||
REDIS_CPU_LIMIT=0.5
|
||||
REDIS_MEMORY_LIMIT=256M
|
||||
|
||||
NGINX_CPU_LIMIT=0.5
|
||||
NGINX_MEMORY_LIMIT=256M
|
||||
|
||||
# ============================================
|
||||
# 开发选项
|
||||
# ============================================
|
||||
SKIP_REDIS_CHECK=0
|
||||
USE_POSTGRES=0
|
||||
DEBUG=0
|
||||
@@ -0,0 +1,71 @@
|
||||
# Sanguo VeighNa Docker 镜像 - NAS 适配版
|
||||
# 使用 Python 3.10(Synology NAS 已有镜像)
|
||||
# 单阶段构建,简化部署流程
|
||||
|
||||
FROM python:3.10-slim
|
||||
|
||||
# 设置环境变量
|
||||
ENV TZ=Asia/Shanghai \
|
||||
DEBIAN_FRONTEND=noninteractive \
|
||||
PYTHONUNBUFFERED=1 \
|
||||
PYTHONDONTWRITEBYTECODE=1 \
|
||||
VNPY_LOG_LEVEL=INFO \
|
||||
TA_LIBRARY_PATH=/usr/local/lib \
|
||||
TA_INCLUDE_PATH=/usr/local/include
|
||||
|
||||
# 安装运行时和编译依赖
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
curl \
|
||||
build-essential \
|
||||
wget \
|
||||
gcc \
|
||||
g++ \
|
||||
make \
|
||||
libc6-dev \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# 下载并编译 TA-Lib
|
||||
RUN wget -q http://prdownloads.sourceforge.net/ta-lib/ta-lib-0.4.0-src.tar.gz \
|
||||
&& tar -xzf ta-lib-0.4.0-src.tar.gz \
|
||||
&& cd ta-lib \
|
||||
&& ./configure --prefix=/usr \
|
||||
&& make \
|
||||
&& make install \
|
||||
&& cd .. \
|
||||
&& rm -rf ta-lib ta-lib-0.4.0-src.tar.gz
|
||||
|
||||
# 更新 ldconfig
|
||||
RUN ldconfig
|
||||
|
||||
# 设置工作目录
|
||||
WORKDIR /app
|
||||
|
||||
# 创建数据目录
|
||||
RUN mkdir -p /app/data /app/logs /app/config /app/temp
|
||||
|
||||
# 复制 requirements 并安装依赖
|
||||
COPY requirements-docker.txt .
|
||||
RUN pip install --no-cache-dir --upgrade pip && \
|
||||
pip install --no-cache-dir -r requirements-docker.txt
|
||||
|
||||
# 复制应用代码(包含所有 sanguo 模块)
|
||||
COPY sanguo_web/ /app/sanguo_web/
|
||||
COPY vnpy_v4.4.0/vnpy/ /app/vnpy/
|
||||
COPY sanguo_trader/ /app/sanguo_trader/
|
||||
COPY sanguo_research/ /app/sanguo_research/
|
||||
COPY sanguo_data/ /app/sanguo_data/
|
||||
COPY sanguo_common/ /app/sanguo_common/
|
||||
|
||||
# 复制启动脚本
|
||||
COPY docker/entrypoint.sh /app/
|
||||
RUN chmod +x /app/entrypoint.sh
|
||||
|
||||
# 暴露端口
|
||||
EXPOSE 8000 8080
|
||||
|
||||
# 健康检查
|
||||
HEALTHCHECK --interval=30s --timeout=10s --start-period=30s --retries=3 \
|
||||
CMD curl -f http://localhost:8000/health || exit 1
|
||||
|
||||
# 启动命令
|
||||
ENTRYPOINT ["/app/entrypoint.sh"]
|
||||
@@ -0,0 +1,59 @@
|
||||
# Sanguo VeighNa 基础镜像
|
||||
# 包含所有 Python 依赖,仅在依赖变更时重建
|
||||
FROM python:3.11-slim
|
||||
|
||||
LABEL maintainer="sanguo"
|
||||
LABEL description="Sanguo VeighNa Base Image with all dependencies"
|
||||
|
||||
# 设置工作目录
|
||||
WORKDIR /build
|
||||
|
||||
# 安装系统依赖(编译时需要)
|
||||
RUN apt-get update && apt-get install -y \
|
||||
gcc g++ make wget \
|
||||
build-essential \
|
||||
libssl-dev libffi-dev \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# 安装 TA-Lib
|
||||
ENV TA_LIBRARY_PATH=/usr/local/lib
|
||||
ENV TA_HEADER_PATH=/usr/include
|
||||
RUN wget -q http://prdownloads.sourceforge.net/ta-lib/ta-lib-0.4.0-src.tar.gz && \
|
||||
tar -xzf ta-lib-0.4.0-src.tar.gz && \
|
||||
cd ta-lib && \
|
||||
./configure --prefix=/usr && \
|
||||
make && make install && \
|
||||
cd .. && \
|
||||
rm -rf ta-lib ta-lib-0.4.0-src.tar.gz
|
||||
|
||||
# 使用清华镜像加速
|
||||
ARG PIP_INDEX_URL=https://pypi.tuna.tsinghua.edu.cn/simple
|
||||
ARG PIP_TRUSTED_HOST=pypi.tuna.tsinghua.edu.cn
|
||||
ENV PIP_INDEX_URL=${PIP_INDEX_URL}
|
||||
ENV PIP_TRUSTED_HOST=${PIP_TRUSTED_HOST}
|
||||
|
||||
# CPU 兼容性配置
|
||||
ENV POLARS_SKIP_CPU_CHECK=1
|
||||
|
||||
# 复制依赖文件
|
||||
COPY requirements-docker.txt .
|
||||
|
||||
# 安装所有 Python 依赖
|
||||
# --root-user-action=ignore 抑制 "running as root" 警告
|
||||
RUN pip install --no-cache-dir --root-user-action=ignore -r requirements-docker.txt
|
||||
|
||||
# 清理编译工具以减小镜像体积
|
||||
RUN apt-get purge -y gcc g++ make wget && \
|
||||
apt-get autoremove -y && \
|
||||
rm -rf /var/lib/apt/lists/* /tmp/*
|
||||
|
||||
# 设置最终工作目录
|
||||
WORKDIR /app
|
||||
|
||||
# 安装 Web 服务基础依赖
|
||||
RUN pip install --no-cache-dir --root-user-action=ignore uvicorn[standard] fastapi
|
||||
|
||||
# 元数据标签
|
||||
LABEL build_date="2025-07-02"
|
||||
LABEL python_version="3.11"
|
||||
LABEL description="Sanguo VeighNa Base - Ready for application layer"
|
||||
@@ -0,0 +1,43 @@
|
||||
# Sanguo VeighNa Docker 镜像 - 本地构建版
|
||||
# 使用国内 PyPI 镜像源,跳过 apt-get 和 TA-Lib 编译
|
||||
|
||||
FROM python:3.10-slim
|
||||
|
||||
# 设置环境变量
|
||||
ENV TZ=Asia/Shanghai \
|
||||
DEBIAN_FRONTEND=noninteractive \
|
||||
PYTHONUNBUFFERED=1 \
|
||||
PYTHONDONTWRITEBYTECODE=1 \
|
||||
VNPY_LOG_LEVEL=INFO \
|
||||
TA_LIBRARY_PATH=/usr/local/lib \
|
||||
TA_INCLUDE_PATH=/usr/local/include \
|
||||
PIP_DEFAULT_TIMEOUT=300 \
|
||||
# 使用清华 PyPI 镜像
|
||||
PIP_INDEX_URL=https://pypi.tuna.tsinghua.edu.cn/simple \
|
||||
PIP_TRUSTED_HOST=pypi.tuna.tsinghua.edu.cn
|
||||
|
||||
# 设置工作目录
|
||||
WORKDIR /app
|
||||
|
||||
# 创建数据目录
|
||||
RUN mkdir -p /app/data /app/logs /app/config /app/temp
|
||||
|
||||
# 复制 requirements 并安装依赖
|
||||
COPY requirements-docker.txt .
|
||||
RUN pip install --no-cache-dir --upgrade pip && \
|
||||
pip install --no-cache-dir -r requirements-docker.txt
|
||||
|
||||
# 复制应用代码
|
||||
COPY sanguo_web/ /app/sanguo_web/
|
||||
COPY vnpy_v4.4.0/vnpy/ /app/vnpy/
|
||||
COPY run_web.py /app/
|
||||
|
||||
# 复制启动脚本
|
||||
COPY docker/entrypoint.sh /app/
|
||||
RUN chmod +x /app/entrypoint.sh
|
||||
|
||||
# 暴露端口
|
||||
EXPOSE 8000 8080
|
||||
|
||||
# 启动命令
|
||||
ENTRYPOINT ["/app/entrypoint.sh"]
|
||||
@@ -0,0 +1,35 @@
|
||||
# Sanguo VeighNa 应用镜像
|
||||
# 基于基础镜像,只包含应用代码
|
||||
FROM sanguo_vnpy:base
|
||||
|
||||
LABEL maintainer="sanguo"
|
||||
LABEL description="Sanguo VeighNa Application Layer"
|
||||
|
||||
# 设置工作目录
|
||||
WORKDIR /app
|
||||
|
||||
# 复制应用代码
|
||||
COPY sanguo_trader/ ./sanguo_trader/
|
||||
COPY sanguo_research/ ./sanguo_research/
|
||||
COPY sanguo_data/ ./sanguo_data/
|
||||
COPY sanguo_common/ ./sanguo_common/
|
||||
COPY sanguo_web/ ./sanguo_web/
|
||||
COPY vnpy_v4.4.0/vnpy/ ./vnpy/
|
||||
COPY config/ ./config/
|
||||
|
||||
# 复制启动脚本
|
||||
COPY docker/entrypoint.sh .
|
||||
RUN chmod +x entrypoint.sh
|
||||
|
||||
# 创建数据目录
|
||||
RUN mkdir -p /app/data /app/logs
|
||||
|
||||
# 暴露端口
|
||||
EXPOSE 8000 8080
|
||||
|
||||
# 健康检查
|
||||
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
|
||||
CMD curl -f http://localhost:8000/health || exit 1
|
||||
|
||||
# 启动
|
||||
ENTRYPOINT ["./entrypoint.sh"]
|
||||
Executable
+160
@@ -0,0 +1,160 @@
|
||||
#!/bin/bash
|
||||
# Sanguo VeighNa 分层构建脚本
|
||||
# 用法: ./docker/build.sh [base|app|all|clean]
|
||||
|
||||
set -e
|
||||
|
||||
# 配置
|
||||
PROJECT_DIR="$(cd "$(dirname "$0")/.." && pwd)"
|
||||
REGISTRY="${REGISTRY:-sanguo_vnpy}"
|
||||
BASE_TAG="${REGISTRY}:base"
|
||||
APP_TAG="${REGISTRY}:latest"
|
||||
|
||||
# 颜色输出
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
RED='\033[0;31m'
|
||||
NC='\033[0m'
|
||||
|
||||
log_info() {
|
||||
echo -e "${GREEN}[INFO]${NC} $1"
|
||||
}
|
||||
|
||||
log_warn() {
|
||||
echo -e "${YELLOW}[WARN]${NC} $1"
|
||||
}
|
||||
|
||||
log_error() {
|
||||
echo -e "${RED}[ERROR]${NC} $1"
|
||||
}
|
||||
|
||||
# 检查 Docker
|
||||
check_docker() {
|
||||
if ! command -v docker &> /dev/null; then
|
||||
log_error "Docker 未安装"
|
||||
exit 1
|
||||
fi
|
||||
log_info "Docker 版本: $(docker --version)"
|
||||
}
|
||||
|
||||
# 构建基础镜像
|
||||
build_base() {
|
||||
log_info "开始构建基础镜像..."
|
||||
log_info "预计时间: 20-30 分钟"
|
||||
|
||||
cd "$PROJECT_DIR"
|
||||
|
||||
docker build \
|
||||
-f docker/Dockerfile.base \
|
||||
-t "$BASE_TAG" \
|
||||
--build-arg PIP_INDEX_URL=https://pypi.tuna.tsinghua.edu.cn/simple \
|
||||
--build-arg PIP_TRUSTED_HOST=pypi.tuna.tsinghua.edu.cn \
|
||||
.
|
||||
|
||||
log_info "基础镜像构建完成: $BASE_TAG"
|
||||
docker images "$BASE_TAG"
|
||||
}
|
||||
|
||||
# 构建应用镜像
|
||||
build_app() {
|
||||
log_info "开始构建应用镜像..."
|
||||
log_info "预计时间: 3-5 分钟"
|
||||
|
||||
cd "$PROJECT_DIR"
|
||||
|
||||
# 检查基础镜像
|
||||
if ! docker image inspect "$BASE_TAG" &> /dev/null; then
|
||||
log_warn "基础镜像不存在,先构建基础镜像"
|
||||
build_base
|
||||
fi
|
||||
|
||||
docker build \
|
||||
-f docker/Dockerfile.nas \
|
||||
-t "$APP_TAG" \
|
||||
.
|
||||
|
||||
log_info "应用镜像构建完成: $APP_TAG"
|
||||
docker images "$APP_TAG"
|
||||
}
|
||||
|
||||
# 全量构建
|
||||
build_all() {
|
||||
log_info "开始全量构建..."
|
||||
build_base
|
||||
build_app
|
||||
}
|
||||
|
||||
# 清理旧镜像
|
||||
clean_images() {
|
||||
log_info "清理旧镜像..."
|
||||
|
||||
# 删除悬空镜像
|
||||
docker image prune -f
|
||||
|
||||
# 询问是否删除旧版本
|
||||
read -p "是否删除旧版本镜像? (y/N) " -n 1 -r
|
||||
echo
|
||||
if [[ $REPLY =~ ^[Yy]$ ]]; then
|
||||
docker images "$REGISTRY" --format "{{.ID}} {{.Tag}}" | \
|
||||
grep -v "$BASE_TAG\|$APP_TAG" | \
|
||||
awk '{print $1}' | \
|
||||
xargs -r docker rmi
|
||||
log_info "旧镜像已清理"
|
||||
fi
|
||||
}
|
||||
|
||||
# 显示帮助
|
||||
show_help() {
|
||||
cat << EOF
|
||||
Sanguo VeighNa 分层构建脚本
|
||||
|
||||
用法: $0 [命令]
|
||||
|
||||
命令:
|
||||
base 构建基础镜像 (依赖层,约30分钟)
|
||||
app 构建应用镜像 (应用层,约5分钟)
|
||||
all 全量构建 (base + app)
|
||||
clean 清理旧镜像
|
||||
help 显示此帮助
|
||||
|
||||
示例:
|
||||
$0 base # 首次构建或依赖更新时
|
||||
$0 app # 代码更新后快速重建
|
||||
$0 all # 完整构建
|
||||
$0 clean # 清理旧镜像
|
||||
|
||||
环境变量:
|
||||
REGISTRY 镜像前缀 (默认: sanguo_vnpy)
|
||||
|
||||
EOF
|
||||
}
|
||||
|
||||
# 主逻辑
|
||||
main() {
|
||||
check_docker
|
||||
|
||||
case "${1:-help}" in
|
||||
base)
|
||||
build_base
|
||||
;;
|
||||
app)
|
||||
build_app
|
||||
;;
|
||||
all)
|
||||
build_all
|
||||
;;
|
||||
clean)
|
||||
clean_images
|
||||
;;
|
||||
help|--help|-h)
|
||||
show_help
|
||||
;;
|
||||
*)
|
||||
log_error "未知命令: $1"
|
||||
show_help
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
main "$@"
|
||||
Executable
+282
@@ -0,0 +1,282 @@
|
||||
#!/bin/bash
|
||||
# Sanguo VeighNa Synology NAS 快速部署脚本
|
||||
# 用于在 Synology NAS 上快速部署和配置
|
||||
|
||||
set -e
|
||||
|
||||
# ============================================
|
||||
# 颜色定义
|
||||
# ============================================
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# ============================================
|
||||
# 日志函数
|
||||
# ============================================
|
||||
log_info() {
|
||||
echo -e "${GREEN}[INFO]${NC} $1"
|
||||
}
|
||||
|
||||
log_warn() {
|
||||
echo -e "${YELLOW}[WARN]${NC} $1"
|
||||
}
|
||||
|
||||
log_error() {
|
||||
echo -e "${RED}[ERROR]${NC} $1"
|
||||
}
|
||||
|
||||
log_step() {
|
||||
echo -e "${BLUE}[STEP]${NC} $1"
|
||||
}
|
||||
|
||||
# ============================================
|
||||
# 打印 Banner
|
||||
# ============================================
|
||||
print_banner() {
|
||||
cat << "EOF"
|
||||
╔════════════════════════════════════════════════════════════╗
|
||||
║ ║
|
||||
║ _____ ____ ____ _ _____ _ ___ _ ║
|
||||
║ | __ \ / __ \ / __ \ | ||_ _|__ _| | |_ _|___| | ║
|
||||
║ | |__) | | | | | | |_| | |/ _` | |_____|| | _ \ | ║
|
||||
║ | ___/| | | | | | |_ | | (_| | |_____| | || ||| ║
|
||||
║ |_| | |__| | \___/\__| |\_____|_| |___||_||_| ║
|
||||
║ \____/ ║
|
||||
║ ║
|
||||
║ Synology NAS 部署脚本 v1.0.0 ║
|
||||
║ ║
|
||||
╚════════════════════════════════════════════════════════════╝
|
||||
EOF
|
||||
}
|
||||
|
||||
# ============================================
|
||||
# 检查权限
|
||||
# ============================================
|
||||
check_permission() {
|
||||
log_step "检查执行权限..."
|
||||
|
||||
if [ "$EUID" -ne 0 ]; then
|
||||
log_error "请使用 sudo 运行此脚本"
|
||||
echo "用法: sudo bash deploy-synology.sh"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log_info "权限检查通过"
|
||||
}
|
||||
|
||||
# ============================================
|
||||
# 加载配置
|
||||
# ============================================
|
||||
load_config() {
|
||||
log_step "加载配置..."
|
||||
|
||||
# 获取脚本所在目录
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
|
||||
# 设置默认值
|
||||
DATA_DIR=${DATA_DIR:-/volume1/docker/stock/sanguo_vnpy}
|
||||
HTTP_PORT=${HTTP_PORT:-8000}
|
||||
WS_PORT=${WS_PORT:-8080}
|
||||
TZ=${TZ:-Asia/Shanghai}
|
||||
|
||||
# 尝试加载 .env 文件
|
||||
if [ -f "$SCRIPT_DIR/.env" ]; then
|
||||
log_info "从 .env 文件加载配置..."
|
||||
source "$SCRIPT_DIR/.env"
|
||||
else
|
||||
log_warn ".env 文件不存在,使用默认配置"
|
||||
if [ -f "$SCRIPT_DIR/.env.example" ]; then
|
||||
log_info "从 .env.example 创建 .env 文件..."
|
||||
cp "$SCRIPT_DIR/.env.example" "$SCRIPT_DIR/.env"
|
||||
log_warn "请编辑 .env 文件配置您的环境"
|
||||
log_info "配置完成后重新运行此脚本"
|
||||
exit 0
|
||||
fi
|
||||
fi
|
||||
|
||||
log_info "配置加载完成"
|
||||
log_info "数据目录: $DATA_DIR"
|
||||
log_info "HTTP 端口: $HTTP_PORT"
|
||||
log_info "WebSocket 端口: $WS_PORT"
|
||||
log_info "时区: $TZ"
|
||||
}
|
||||
|
||||
# ============================================
|
||||
# 检查 Docker
|
||||
# ============================================
|
||||
check_docker() {
|
||||
log_step "检查 Docker 环境..."
|
||||
|
||||
if ! command -v docker &> /dev/null; then
|
||||
log_error "Docker 未安装"
|
||||
log_info "请在 DSM 中安装 Container Manager 套件"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! command -v docker-compose &> /dev/null; then
|
||||
log_error "Docker Compose 未安装"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log_info "Docker 环境检查通过"
|
||||
}
|
||||
|
||||
# ============================================
|
||||
# 创建数据目录
|
||||
# ============================================
|
||||
create_directories() {
|
||||
log_step "创建数据目录..."
|
||||
|
||||
# 主目录
|
||||
mkdir -p "$DATA_DIR"
|
||||
|
||||
# 子目录
|
||||
mkdir -p "$DATA_DIR/data"
|
||||
mkdir -p "$DATA_DIR/logs"
|
||||
mkdir -p "$DATA_DIR/config"
|
||||
mkdir -p "$DATA_DIR/temp"
|
||||
mkdir -p "$DATA_DIR/redis"
|
||||
mkdir -p "$DATA_DIR/nginx"
|
||||
mkdir -p "$DATA_DIR/nginx/ssl"
|
||||
|
||||
# 设置权限
|
||||
chmod -R 755 "$DATA_DIR"
|
||||
|
||||
log_info "数据目录创建完成: $DATA_DIR"
|
||||
}
|
||||
|
||||
# ============================================
|
||||
# 备份现有数据
|
||||
# ============================================
|
||||
backup_data() {
|
||||
log_step "检查是否需要备份..."
|
||||
|
||||
if [ -d "$DATA_DIR/data" ] && [ "$(ls -A $DATA_DIR/data 2>/dev/null)" ]; then
|
||||
BACKUP_DIR="/volume1/Backup"
|
||||
mkdir -p "$BACKUP_DIR"
|
||||
|
||||
BACKUP_FILE="$BACKUP_DIR/vnpy_backup_$(date +%Y%m%d_%H%M%S).tar.gz"
|
||||
|
||||
log_warn "检测到现有数据,创建备份..."
|
||||
tar czf "$BACKUP_FILE" -C "$DATA_DIR" .
|
||||
|
||||
log_info "备份已创建: $BACKUP_FILE"
|
||||
log_info "备份大小: $(du -h $BACKUP_FILE | cut -f1)"
|
||||
else
|
||||
log_info "未检测到现有数据,跳过备份"
|
||||
fi
|
||||
}
|
||||
|
||||
# ============================================
|
||||
# 构建 Docker 镜像
|
||||
# ============================================
|
||||
build_image() {
|
||||
log_step "构建 Docker 镜像..."
|
||||
|
||||
cd "$SCRIPT_DIR"
|
||||
|
||||
log_info "开始构建(这可能需要几分钟)..."
|
||||
docker-compose build
|
||||
|
||||
log_info "Docker 镜像构建完成"
|
||||
}
|
||||
|
||||
# ============================================
|
||||
# 启动服务
|
||||
# ============================================
|
||||
start_services() {
|
||||
log_step "启动服务..."
|
||||
|
||||
cd "$SCRIPT_DIR"
|
||||
|
||||
# 停止现有服务(如果有)
|
||||
if docker-compose ps | grep -q "Up"; then
|
||||
log_warn "检测到运行中的服务,先停止..."
|
||||
docker-compose down
|
||||
fi
|
||||
|
||||
# 启动服务
|
||||
log_info "启动服务..."
|
||||
docker-compose up -d
|
||||
|
||||
log_info "服务启动完成"
|
||||
}
|
||||
|
||||
# ============================================
|
||||
# 检查服务状态
|
||||
# ============================================
|
||||
check_status() {
|
||||
log_step "检查服务状态..."
|
||||
|
||||
cd "$SCRIPT_DIR"
|
||||
|
||||
# 等待服务启动
|
||||
sleep 5
|
||||
|
||||
# 检查容器状态
|
||||
docker-compose ps
|
||||
|
||||
echo ""
|
||||
log_info "检查服务日志(最后 20 行):"
|
||||
docker-compose logs --tail=20 vnpy
|
||||
}
|
||||
|
||||
# ============================================
|
||||
# 显示访问信息
|
||||
# ============================================
|
||||
show_access_info() {
|
||||
log_step "部署完成!"
|
||||
|
||||
echo ""
|
||||
echo "=========================================="
|
||||
log_info "访问地址:"
|
||||
echo ""
|
||||
echo " HTTP: http://$(hostname -I | awk '{print $1}'):$HTTP_PORT"
|
||||
echo " WebSocket: ws://$(hostname -I | awk '{print $1}'):$WS_PORT"
|
||||
echo ""
|
||||
log_info "默认登录凭据:"
|
||||
echo ""
|
||||
echo " 用户名: admin"
|
||||
echo " 密码: admin123"
|
||||
echo ""
|
||||
log_warn "重要提示:"
|
||||
echo " 1. 请立即修改默认密码"
|
||||
echo " 2. 数据目录: $DATA_DIR"
|
||||
echo " 3. 查看日志: docker-compose logs -f"
|
||||
echo " 4. 停止服务: docker-compose down"
|
||||
echo "=========================================="
|
||||
echo ""
|
||||
}
|
||||
|
||||
# ============================================
|
||||
# 主函数
|
||||
# ============================================
|
||||
main() {
|
||||
print_banner
|
||||
echo ""
|
||||
|
||||
check_permission
|
||||
load_config
|
||||
check_docker
|
||||
create_directories
|
||||
backup_data
|
||||
build_image
|
||||
start_services
|
||||
check_status
|
||||
show_access_info
|
||||
|
||||
log_info "部署脚本执行完成!"
|
||||
}
|
||||
|
||||
# ============================================
|
||||
# 信号处理
|
||||
# ============================================
|
||||
trap 'log_error "脚本被中断"; exit 1' SIGINT SIGTERM
|
||||
|
||||
# ============================================
|
||||
# 执行主函数
|
||||
# ============================================
|
||||
main "$@"
|
||||
@@ -0,0 +1,262 @@
|
||||
version: '3.8'
|
||||
|
||||
# Sanguo VeighNa Docker 编排配置
|
||||
# 用于 NAS 部署和开发环境
|
||||
# 支持环境变量配置和绑定挂载
|
||||
|
||||
# 加载环境变量
|
||||
# 使用前请复制 .env.example 为 .env 并修改配置
|
||||
|
||||
services:
|
||||
# ============================================
|
||||
# VeighNa 核心服务
|
||||
# ============================================
|
||||
vnpy:
|
||||
build:
|
||||
context: ..
|
||||
dockerfile: docker/Dockerfile
|
||||
container_name: sanguo-vnpy
|
||||
restart: unless-stopped
|
||||
|
||||
# 容器名称
|
||||
hostname: vnpy-server
|
||||
|
||||
# 环境变量
|
||||
environment:
|
||||
# Python 配置
|
||||
- PYTHONUNBUFFERED=1
|
||||
- PYTHONDONTWRITEBYTECODE=1
|
||||
- TZ=${TZ:-Asia/Shanghai}
|
||||
|
||||
# VeighNa 配置
|
||||
- VNPY_LOG_LEVEL=${VNPY_LOG_LEVEL:-INFO}
|
||||
- VNPY_LOG_FILE=${VNPY_LOG_FILE:-/app/logs/vnpy.log}
|
||||
|
||||
# 数据库配置
|
||||
- DB_TYPE=${DB_TYPE:-sqlite}
|
||||
- DB_PATH=${DB_PATH:-/app/data/vnpy.db}
|
||||
|
||||
# Web 服务配置
|
||||
- WEB_HOST=${WEB_HOST:-0.0.0.0}
|
||||
- WEB_PORT=${WEB_PORT:-8000}
|
||||
- WEB_WORKERS=${WEB_WORKERS:-2}
|
||||
|
||||
# 安全配置
|
||||
- JWT_SECRET_KEY=${JWT_SECRET_KEY:-change-this-to-a-random-secret-key}
|
||||
- SESSION_TIMEOUT=${SESSION_TIMEOUT:-3600}
|
||||
|
||||
# Redis 配置
|
||||
- REDIS_HOST=${REDIS_HOST:-redis}
|
||||
- REDIS_PORT=${REDIS_PORT:-6379}
|
||||
- REDIS_PASSWORD=${REDIS_PASSWORD:-}
|
||||
|
||||
# PostgreSQL 配置
|
||||
- POSTGRES_DB=${POSTGRES_DB:-vnpy}
|
||||
- POSTGRES_USER=${POSTGRES_USER:-vnpy}
|
||||
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD:-vnpy_password_change_me}
|
||||
- POSTGRES_HOST=${POSTGRES_HOST:-postgres}
|
||||
- POSTGRES_PORT=${POSTGRES_PORT:-5432}
|
||||
|
||||
# 开发选项
|
||||
- SKIP_REDIS_CHECK=${SKIP_REDIS_CHECK:-0}
|
||||
- USE_POSTGRES=${USE_POSTGRES:-0}
|
||||
- DEBUG=${DEBUG:-0}
|
||||
|
||||
# 端口映射
|
||||
ports:
|
||||
- "${HTTP_PORT:-8000}:8000" # HTTP API
|
||||
- "${WS_PORT:-8080}:8080" # WebSocket
|
||||
|
||||
# 数据卷 - 使用绑定挂载到 NAS stock 目录
|
||||
volumes:
|
||||
# 数据持久化目录 - 挂载到 NAS stock 目录
|
||||
- ${DATA_DIR}/data:/app/data
|
||||
- ${DATA_DIR}/logs:/app/logs
|
||||
- ${DATA_DIR}/config:/app/config
|
||||
- ${DATA_DIR}/temp:/app/temp
|
||||
|
||||
# 配置文件(只读)- 如果有外部配置
|
||||
- ../config:/app/config:ro
|
||||
|
||||
# 网络
|
||||
networks:
|
||||
- vnpy-network
|
||||
|
||||
# 依赖项
|
||||
depends_on:
|
||||
- redis
|
||||
|
||||
# 资源限制
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
cpus: '${VNPY_CPU_LIMIT:-2.0}'
|
||||
memory: ${VNPY_MEMORY_LIMIT:-2G}
|
||||
reservations:
|
||||
cpus: '${VNPY_CPU_RESERVATION:-0.5}'
|
||||
memory: ${VNPY_MEMORY_RESERVATION:-512M}
|
||||
|
||||
# ============================================
|
||||
# Redis 服务(缓存和消息队列)
|
||||
# ============================================
|
||||
redis:
|
||||
image: redis:7-alpine
|
||||
container_name: sanguo-redis
|
||||
restart: unless-stopped
|
||||
|
||||
# 命令参数
|
||||
command: >
|
||||
sh -c '
|
||||
if [ -n "${REDIS_PASSWORD}" ]; then
|
||||
redis-server --appendonly yes --requirepass ${REDIS_PASSWORD}
|
||||
else
|
||||
redis-server --appendonly yes
|
||||
fi
|
||||
'
|
||||
|
||||
# 环境变量
|
||||
environment:
|
||||
- TZ=${TZ:-Asia/Shanghai}
|
||||
|
||||
# 数据卷 - 挂载到 NAS stock 目录
|
||||
volumes:
|
||||
- ${DATA_DIR}/redis:/data
|
||||
|
||||
# 网络
|
||||
networks:
|
||||
- vnpy-network
|
||||
|
||||
# 资源限制
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
cpus: '${REDIS_CPU_LIMIT:-0.5}'
|
||||
memory: ${REDIS_MEMORY_LIMIT:-256M}
|
||||
|
||||
# ============================================
|
||||
# Nginx 反向代理(可选)
|
||||
# ============================================
|
||||
nginx:
|
||||
image: nginx:alpine
|
||||
container_name: sanguo-nginx
|
||||
restart: unless-stopped
|
||||
profiles:
|
||||
- nginx
|
||||
- ${ENABLE_NGINX:+nginx}
|
||||
|
||||
# 环境变量
|
||||
environment:
|
||||
- TZ=${TZ:-Asia/Shanghai}
|
||||
|
||||
# 端口映射
|
||||
ports:
|
||||
- "${NGINX_HTTP_PORT:-80}:80"
|
||||
- "${NGINX_HTTPS_PORT:-443}:443"
|
||||
|
||||
# 配置文件
|
||||
volumes:
|
||||
- ../docker/nginx/nginx.conf:/etc/nginx/nginx.conf:ro
|
||||
- ${DATA_DIR}/nginx/ssl:/etc/nginx/ssl:ro
|
||||
- ${DATA_DIR}/nginx/cache:/var/cache/nginx
|
||||
- ${DATA_DIR}/logs/nginx:/var/log/nginx
|
||||
|
||||
# 依赖项
|
||||
depends_on:
|
||||
- vnpy
|
||||
|
||||
# 网络
|
||||
networks:
|
||||
- vnpy-network
|
||||
|
||||
# 资源限制
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
cpus: '${NGINX_CPU_LIMIT:-0.5}'
|
||||
memory: ${NGINX_MEMORY_LIMIT:-256M}
|
||||
|
||||
# ============================================
|
||||
# PostgreSQL 数据库(可选,升级用)
|
||||
# ============================================
|
||||
postgres:
|
||||
image: postgres:15-alpine
|
||||
container_name: sanguo-postgres
|
||||
restart: unless-stopped
|
||||
profiles:
|
||||
- postgres
|
||||
- ${USE_POSTGRES:+postgres}
|
||||
|
||||
# 环境变量
|
||||
environment:
|
||||
- POSTGRES_DB=${POSTGRES_DB:-vnpy}
|
||||
- POSTGRES_USER=${POSTGRES_USER:-vnpy}
|
||||
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD:-vnpy_password_change_me}
|
||||
- TZ=${TZ:-Asia/Shanghai}
|
||||
- PGDATA=/var/lib/postgresql/data/pgdata
|
||||
|
||||
# 数据卷 - 挂载到 NAS stock 目录
|
||||
volumes:
|
||||
- ${DATA_DIR}/postgres:/var/lib/postgresql/data
|
||||
|
||||
# 网络
|
||||
networks:
|
||||
- vnpy-network
|
||||
|
||||
# 资源限制
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
cpus: '1.0'
|
||||
memory: 512M
|
||||
|
||||
# ============================================
|
||||
# 网络定义
|
||||
# ============================================
|
||||
networks:
|
||||
vnpy-network:
|
||||
name: sanguo_vnpy_network
|
||||
driver: bridge
|
||||
|
||||
# ============================================
|
||||
# 使用说明
|
||||
# ============================================
|
||||
#
|
||||
# 基本使用:
|
||||
#
|
||||
# 1. 首次使用前,配置环境变量:
|
||||
# cp .env.example .env
|
||||
# vi .env # 修改 DATA_DIR 等配置
|
||||
#
|
||||
# 2. 创建数据目录:
|
||||
# mkdir -p ${DATA_DIR}/{data,logs,config,temp,redis,nginx}
|
||||
#
|
||||
# 3. 启动核心服务:
|
||||
# docker-compose up -d
|
||||
#
|
||||
# 4. 启动所有服务(含 Nginx):
|
||||
# docker-compose --profile nginx up -d
|
||||
#
|
||||
# 5. 启动所有服务(含 PostgreSQL):
|
||||
# docker-compose --profile postgres up -d
|
||||
#
|
||||
# 管理命令:
|
||||
#
|
||||
# 查看日志:
|
||||
# docker-compose logs -f vnpy
|
||||
#
|
||||
# 停止服务:
|
||||
# docker-compose down
|
||||
#
|
||||
# 重启服务:
|
||||
# docker-compose restart vnpy
|
||||
#
|
||||
# 进入容器:
|
||||
# docker-compose exec vnpy bash
|
||||
#
|
||||
# 更新并重启:
|
||||
# docker-compose up -d --build
|
||||
#
|
||||
# 数据备份:
|
||||
# tar czf vnpy_backup_$(date +%Y%m%d).tar.gz ${DATA_DIR}
|
||||
#
|
||||
# ============================================
|
||||
@@ -0,0 +1,340 @@
|
||||
#!/bin/bash
|
||||
# Sanguo VeighNa Docker 容器启动脚本
|
||||
|
||||
set -e
|
||||
|
||||
# ============================================
|
||||
# 颜色定义
|
||||
# ============================================
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# ============================================
|
||||
# 日志函数
|
||||
# ============================================
|
||||
log_info() {
|
||||
echo -e "${GREEN}[INFO]${NC} $1"
|
||||
}
|
||||
|
||||
log_warn() {
|
||||
echo -e "${YELLOW}[WARN]${NC} $1"
|
||||
}
|
||||
|
||||
log_error() {
|
||||
echo -e "${RED}[ERROR]${NC} $1"
|
||||
}
|
||||
|
||||
log_step() {
|
||||
echo -e "${BLUE}[STEP]${NC} $1"
|
||||
}
|
||||
|
||||
# ============================================
|
||||
# 打印启动信息
|
||||
# ============================================
|
||||
print_banner() {
|
||||
cat << "EOF"
|
||||
╔════════════════════════════════════════════════════════════╗
|
||||
║ ║
|
||||
║ _____ ____ ____ _ _____ _ ___ _ ║
|
||||
║ | __ \ / __ \ / __ \ | ||_ _|__ _| | |_ _|___| | ║
|
||||
║ | |__) | | | | | | |_| | |/ _` | |_____|| | _ \ | ║
|
||||
║ | ___/| | | | | | |_ | | (_| | |_____| | || ||| ║
|
||||
║ |_| | |__| | \___/\__| |\_____|_| |___||_||_| ║
|
||||
║ \____/ ║
|
||||
║ ║
|
||||
║ Docker Web 版本 v1.0.0 ║
|
||||
║ 基于 VeighNa 4.4.0 构建 ║
|
||||
║ ║
|
||||
╚════════════════════════════════════════════════════════════╝
|
||||
EOF
|
||||
}
|
||||
|
||||
# ============================================
|
||||
# 检查环境变量
|
||||
# ============================================
|
||||
check_env() {
|
||||
log_step "检查环境变量..."
|
||||
|
||||
# 设置默认值
|
||||
export TZ="${TZ:-Asia/Shanghai}"
|
||||
export PYTHONUNBUFFERED="${PYTHONUNBUFFERED:-1}"
|
||||
export PYTHONDONTWRITEBYTECODE="${PYTHONDONTWRITEBYTECODE:-1}"
|
||||
|
||||
# VeighNa 配置
|
||||
export VNPY_LOG_LEVEL="${VNPY_LOG_LEVEL:-INFO}"
|
||||
export VNPY_LOG_FILE="${VNPY_LOG_FILE:-/app/logs/vnpy.log}"
|
||||
|
||||
# 数据库配置
|
||||
export DB_TYPE="${DB_TYPE:-sqlite}"
|
||||
export DB_PATH="${DB_PATH:-/app/data/vnpy.db}"
|
||||
|
||||
# Web 服务配置
|
||||
export WEB_HOST="${WEB_HOST:-0.0.0.0}"
|
||||
export WEB_PORT="${WEB_PORT:-8000}"
|
||||
export WEB_WORKERS="${WEB_WORKERS:-2}"
|
||||
|
||||
# 安全配置
|
||||
export JWT_SECRET_KEY="${JWT_SECRET_KEY:-change-this-to-a-random-secret-key}"
|
||||
export SESSION_TIMEOUT="${SESSION_TIMEOUT:-3600}"
|
||||
|
||||
# Redis 配置
|
||||
export REDIS_HOST="${REDIS_HOST:-redis}"
|
||||
export REDIS_PORT="${REDIS_PORT:-6379}"
|
||||
|
||||
# 开发选项
|
||||
export SKIP_REDIS_CHECK="${SKIP_REDIS_CHECK:-0}"
|
||||
export USE_POSTGRES="${USE_POSTGRES:-0}"
|
||||
export DEBUG="${DEBUG:-0}"
|
||||
|
||||
# 打印关键配置
|
||||
log_info "时区: $TZ"
|
||||
log_info "日志级别: $VNPY_LOG_LEVEL"
|
||||
log_info "数据库类型: $DB_TYPE"
|
||||
log_info "Web 端口: $WEB_PORT"
|
||||
log_info "Workers: $WEB_WORKERS"
|
||||
|
||||
log_info "环境变量检查完成"
|
||||
}
|
||||
|
||||
# ============================================
|
||||
# 创建数据目录
|
||||
# ============================================
|
||||
create_directories() {
|
||||
log_step "创建数据目录..."
|
||||
|
||||
# 创建主要目录
|
||||
mkdir -p /app/data
|
||||
mkdir -p /app/logs
|
||||
mkdir -p /app/temp
|
||||
mkdir -p /app/config
|
||||
|
||||
# 创建日志子目录
|
||||
mkdir -p /app/logs/web
|
||||
mkdir -p /app/logs/vnpy
|
||||
|
||||
# 创建数据子目录
|
||||
mkdir -p /app/data/database
|
||||
mkdir -p /app/data/storage
|
||||
|
||||
# 设置权限
|
||||
chmod -R 755 /app/data
|
||||
chmod -R 755 /app/logs
|
||||
chmod -R 755 /app/temp
|
||||
|
||||
log_info "数据目录创建完成"
|
||||
}
|
||||
|
||||
# ============================================
|
||||
# 初始化数据库
|
||||
# ============================================
|
||||
init_database() {
|
||||
log_step "初始化数据库..."
|
||||
|
||||
if [ "$DB_TYPE" = "sqlite" ]; then
|
||||
if [ ! -f /app/data/vnpy.db ]; then
|
||||
log_info "创建 SQLite 数据库目录..."
|
||||
mkdir -p /app/data/database
|
||||
|
||||
# 数据库初始化将在 Web 服务启动时完成
|
||||
log_info "SQLite 数据库准备完成"
|
||||
else
|
||||
log_info "SQLite 数据库已存在"
|
||||
fi
|
||||
elif [ "$DB_TYPE" = "postgres" ] && [ "$USE_POSTGRES" = "1" ]; then
|
||||
log_info "使用 PostgreSQL 数据库"
|
||||
# PostgreSQL 配置由环境变量提供
|
||||
else
|
||||
log_warn "未知的数据库类型: $DB_TYPE,使用默认 SQLite"
|
||||
export DB_TYPE=sqlite
|
||||
fi
|
||||
}
|
||||
|
||||
# ============================================
|
||||
# 生成 SSL 证书(自签名,用于测试)
|
||||
# ============================================
|
||||
generate_ssl_cert() {
|
||||
if [ ! -f /etc/nginx/ssl/cert.pem ]; then
|
||||
log_warn "SSL 证书不存在,生成自签名证书..."
|
||||
|
||||
mkdir -p /etc/nginx/ssl
|
||||
openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
|
||||
-keyout /etc/nginx/ssl/key.pem \
|
||||
-out /etc/nginx/ssl/cert.pem \
|
||||
-subj "/C=CN/ST=Beijing/L=Beijing/O=Sanguo/CN=localhost" \
|
||||
2>/dev/null
|
||||
|
||||
log_warn "自签名证书已生成,请勿用于生产环境"
|
||||
fi
|
||||
}
|
||||
|
||||
# ============================================
|
||||
# 等待依赖服务
|
||||
# ============================================
|
||||
wait_for_services() {
|
||||
log_step "等待依赖服务启动..."
|
||||
|
||||
# 等待 Redis
|
||||
if [ "$SKIP_REDIS_CHECK" != "1" ]; then
|
||||
log_info "等待 Redis 服务..."
|
||||
|
||||
REDIS_MAX_WAIT=30
|
||||
REDIS_WAIT_COUNT=0
|
||||
|
||||
while ! nc -z ${REDIS_HOST:-redis} ${REDIS_PORT:-6379} 2>/dev/null; do
|
||||
REDIS_WAIT_COUNT=$((REDIS_WAIT_COUNT + 1))
|
||||
if [ $REDIS_WAIT_COUNT -ge $REDIS_MAX_WAIT ]; then
|
||||
log_error "Redis 连接超时"
|
||||
log_info "设置 SKIP_REDIS_CHECK=1 可跳过此检查(开发环境)"
|
||||
exit 1
|
||||
fi
|
||||
sleep 1
|
||||
done
|
||||
|
||||
log_info "Redis 服务已就绪 (${REDIS_HOST}:${REDIS_PORT})"
|
||||
else
|
||||
log_warn "跳过 Redis 检查"
|
||||
fi
|
||||
|
||||
# 等待 PostgreSQL(如果启用)
|
||||
if [ "$USE_POSTGRES" = "1" ]; then
|
||||
log_info "等待 PostgreSQL 服务..."
|
||||
|
||||
POSTGRES_MAX_WAIT=30
|
||||
POSTGRES_WAIT_COUNT=0
|
||||
|
||||
while ! nc -z ${POSTGRES_HOST:-postgres} ${POSTGRES_PORT:-5432} 2>/dev/null; do
|
||||
POSTGRES_WAIT_COUNT=$((POSTGRES_WAIT_COUNT + 1))
|
||||
if [ $POSTGRES_WAIT_COUNT -ge $POSTGRES_MAX_WAIT ]; then
|
||||
log_error "PostgreSQL 连接超时"
|
||||
exit 1
|
||||
fi
|
||||
sleep 1
|
||||
done
|
||||
|
||||
log_info "PostgreSQL 服务已就绪"
|
||||
fi
|
||||
}
|
||||
|
||||
# ============================================
|
||||
# 健康检查
|
||||
# ============================================
|
||||
health_check() {
|
||||
log_step "执行健康检查..."
|
||||
|
||||
# 检查 Python 环境
|
||||
if ! python --version &>/dev/null; then
|
||||
log_error "Python 环境检查失败"
|
||||
exit 1
|
||||
fi
|
||||
log_info "Python 版本: $(python --version)"
|
||||
|
||||
# 检查关键模块
|
||||
python -c "import sys; print(f'Python 路径: {sys.executable}')" || {
|
||||
log_error "Python 系统检查失败"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# 检查数据目录
|
||||
if [ ! -d "/app/data" ]; then
|
||||
log_error "数据目录不存在: /app/data"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log_info "健康检查完成"
|
||||
}
|
||||
|
||||
# ============================================
|
||||
# 启动 Web 服务
|
||||
# ============================================
|
||||
start_web_service() {
|
||||
log_step "启动 Web 服务..."
|
||||
|
||||
# 切换到工作目录
|
||||
cd /app
|
||||
|
||||
# 确定 uvicorn 参数
|
||||
UVICORN_ARGS="--host ${WEB_HOST} --port ${WEB_PORT}"
|
||||
|
||||
# Worker 配置
|
||||
if [ "${WEB_WORKERS:-2}" -gt 1 ]; then
|
||||
log_info "使用多 Worker 模式: ${WEB_WORKERS} workers"
|
||||
UVICORN_ARGS="$UVICORN_ARGS --workers ${WEB_WORKERS}"
|
||||
else
|
||||
log_info "使用单 Worker 模式"
|
||||
fi
|
||||
|
||||
# 日志级别
|
||||
UVICORN_ARGS="$UVICORN_ARGS --log-level ${VNPY_LOG_LEVEL:-info}"
|
||||
|
||||
# 访问日志
|
||||
UVICORN_ARGS="$UVICORN_ARGS --access-log"
|
||||
|
||||
# 调试模式(仅开发环境)
|
||||
if [ "$DEBUG" = "1" ]; then
|
||||
log_warn "启用调试模式(不应在生产环境使用)"
|
||||
UVICORN_ARGS="$UVICORN_ARGS --reload"
|
||||
fi
|
||||
|
||||
# 启动服务
|
||||
log_info "启动命令: uvicorn sanguo_web.api:app $UVICORN_ARGS"
|
||||
exec uvicorn sanguo_web.api:app $UVICORN_ARGS
|
||||
}
|
||||
|
||||
# ============================================
|
||||
# 显示访问信息
|
||||
# ============================================
|
||||
show_access_info() {
|
||||
echo ""
|
||||
log_info "=========================================="
|
||||
log_info "服务启动中..."
|
||||
log_info "=========================================="
|
||||
echo ""
|
||||
log_info "访问地址:"
|
||||
echo ""
|
||||
echo " HTTP: http://localhost:${WEB_PORT:-8000}"
|
||||
echo " WebSocket: ws://localhost:${WS_PORT:-8080}"
|
||||
echo ""
|
||||
log_info "默认登录:"
|
||||
echo ""
|
||||
echo " 用户名: admin"
|
||||
echo " 密码: admin123"
|
||||
echo ""
|
||||
log_warn "请在首次登录后修改默认密码!"
|
||||
echo ""
|
||||
log_info "=========================================="
|
||||
echo ""
|
||||
}
|
||||
|
||||
# ============================================
|
||||
# 主函数
|
||||
# ============================================
|
||||
main() {
|
||||
print_banner
|
||||
echo ""
|
||||
|
||||
check_env
|
||||
create_directories
|
||||
init_database
|
||||
generate_ssl_cert
|
||||
wait_for_services
|
||||
health_check
|
||||
show_access_info
|
||||
|
||||
log_info "所有检查完成,启动服务..."
|
||||
echo ""
|
||||
|
||||
start_web_service
|
||||
}
|
||||
|
||||
# ============================================
|
||||
# 信号处理
|
||||
# ============================================
|
||||
trap 'log_info "收到退出信号,优雅关闭..."; exit 0' SIGTERM SIGINT
|
||||
|
||||
# ============================================
|
||||
# 执行主函数
|
||||
# ============================================
|
||||
main "$@"
|
||||
@@ -0,0 +1,196 @@
|
||||
# Sanguo VeighNa Nginx 配置
|
||||
# 用于反向代理、HTTPS 和静态文件服务
|
||||
|
||||
user nginx;
|
||||
worker_processes auto;
|
||||
error_log /var/log/nginx/error.log warn;
|
||||
pid /var/run/nginx.pid;
|
||||
|
||||
events {
|
||||
worker_connections 1024;
|
||||
use epoll;
|
||||
}
|
||||
|
||||
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"';
|
||||
|
||||
log_format json '$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 json;
|
||||
|
||||
# 性能优化
|
||||
sendfile on;
|
||||
tcp_nopush on;
|
||||
tcp_nodelay on;
|
||||
keepalive_timeout 65;
|
||||
types_hash_max_size 2048;
|
||||
client_max_body_size 100M;
|
||||
|
||||
# Gzip 压缩
|
||||
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;
|
||||
|
||||
# 缓存配置
|
||||
proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=vnpy_cache:10m max_size=1g inactive=60m use_temp_path=off;
|
||||
|
||||
# ============================================
|
||||
# HTTP 服务器 - 重定向到 HTTPS
|
||||
# ============================================
|
||||
server {
|
||||
listen 80;
|
||||
server_name _;
|
||||
|
||||
# 允许健康检查
|
||||
location /health {
|
||||
access_log off;
|
||||
return 200 "healthy\n";
|
||||
add_header Content-Type text/plain;
|
||||
}
|
||||
|
||||
# 其他请求重定向到 HTTPS
|
||||
location / {
|
||||
return 301 https://$host$request_uri;
|
||||
}
|
||||
}
|
||||
|
||||
# ============================================
|
||||
# HTTPS 服务器
|
||||
# ============================================
|
||||
server {
|
||||
listen 443 ssl http2;
|
||||
server_name _;
|
||||
|
||||
# SSL 证书配置
|
||||
ssl_certificate /etc/nginx/ssl/cert.pem;
|
||||
ssl_certificate_key /etc/nginx/ssl/key.pem;
|
||||
|
||||
# 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;
|
||||
|
||||
# 安全头
|
||||
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
|
||||
add_header X-Frame-Options "SAMEORIGIN" always;
|
||||
add_header X-Content-Type-Options "nosniff" always;
|
||||
add_header X-XSS-Protection "1; mode=block" always;
|
||||
|
||||
# ============================================
|
||||
# API 代理
|
||||
# ============================================
|
||||
location /api/ {
|
||||
proxy_pass http://vnpy:8000;
|
||||
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_connect_timeout 60s;
|
||||
proxy_send_timeout 60s;
|
||||
proxy_read_timeout 60s;
|
||||
}
|
||||
|
||||
# ============================================
|
||||
# WebSocket 代理
|
||||
# ============================================
|
||||
location /ws/ {
|
||||
proxy_pass http://vnpy:8080;
|
||||
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_connect_timeout 7d;
|
||||
proxy_send_timeout 7d;
|
||||
proxy_read_timeout 7d;
|
||||
}
|
||||
|
||||
# ============================================
|
||||
# 健康检查
|
||||
# ============================================
|
||||
location /health {
|
||||
proxy_pass http://vnpy:8000/health;
|
||||
access_log off;
|
||||
}
|
||||
|
||||
# ============================================
|
||||
# 静态文件
|
||||
# ============================================
|
||||
location /static/ {
|
||||
alias /app/static/;
|
||||
expires 7d;
|
||||
add_header Cache-Control "public, immutable";
|
||||
}
|
||||
|
||||
location /favicon.ico {
|
||||
access_log off;
|
||||
log_not_found off;
|
||||
}
|
||||
|
||||
# ============================================
|
||||
# 根路径 - 前端应用
|
||||
# ============================================
|
||||
location / {
|
||||
proxy_pass http://vnpy:8000;
|
||||
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;
|
||||
}
|
||||
|
||||
# ============================================
|
||||
# 安全:拒绝访问敏感文件
|
||||
# ============================================
|
||||
location ~ /\. {
|
||||
deny all;
|
||||
access_log off;
|
||||
log_not_found off;
|
||||
}
|
||||
|
||||
location ~ ~\.(git|svn|env)$ {
|
||||
deny all;
|
||||
access_log off;
|
||||
log_not_found off;
|
||||
}
|
||||
}
|
||||
|
||||
# ============================================
|
||||
# 流控制(可选)
|
||||
# ============================================
|
||||
limit_req_zone $binary_remote_addr zone=api_limit:10m rate=10r/s;
|
||||
limit_req_zone $binary_remote_addr zone=ws_limit:10m rate=5r/s;
|
||||
|
||||
limit_conn_zone $binary_remote_addr zone=conn_limit:10m;
|
||||
|
||||
# 应用限流
|
||||
location /api/ {
|
||||
limit_req zone=api_limit burst=20 nodelay;
|
||||
limit_conn conn_limit 10;
|
||||
}
|
||||
|
||||
location /ws/ {
|
||||
limit_req zone=ws_limit burst=10 nodelay;
|
||||
limit_conn conn_limit 5;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user