chore(doc): Replace Chinese comments with English comments
This commit is contained in:
+5
-5
@@ -1,4 +1,4 @@
|
||||
# 构建阶段
|
||||
# Build stage
|
||||
FROM node:18-alpine AS builder
|
||||
|
||||
ARG HTTP_PROXY
|
||||
@@ -15,18 +15,18 @@ RUN npm ci
|
||||
COPY . .
|
||||
RUN npm run build
|
||||
|
||||
# 运行阶段
|
||||
# Run stage
|
||||
FROM node:18-alpine AS runtime
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# 复制预构建的文件
|
||||
# Copy prebuilt artifacts
|
||||
COPY --from=builder /app/dist ./dist
|
||||
COPY --from=builder /app/node_modules ./node_modules
|
||||
COPY --from=builder /app/package.json ./
|
||||
COPY health-check.js ./
|
||||
|
||||
# 创建用户并设置权限
|
||||
# Create user and set permissions
|
||||
RUN addgroup -g 1001 -S nodejs && \
|
||||
adduser -S backend -u 1001 -G nodejs && \
|
||||
chown -R backend:nodejs /app
|
||||
@@ -34,7 +34,7 @@ RUN addgroup -g 1001 -S nodejs && \
|
||||
USER backend
|
||||
EXPOSE 3001
|
||||
|
||||
# 使用Node.js脚本做健康检查(替代curl)
|
||||
# Use a Node.js script for health checks (instead of curl)
|
||||
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
|
||||
CMD node health-check.js
|
||||
|
||||
|
||||
@@ -9,10 +9,10 @@ const DEV_ORIGINS = [
|
||||
/^http:\/\/192\.168\.\d+\.\d+:3002$/, // LAN addresses with new port
|
||||
];
|
||||
|
||||
// 解析生产环境下的多来源配置(逗号分隔)
|
||||
// Parse multi-origin config in production (comma-separated)
|
||||
const parseProdOrigins = (): string | RegExp | (string | RegExp)[] => {
|
||||
const v = CONFIG.CORS_ORIGIN?.trim();
|
||||
if (!v) return DEV_ORIGINS; // 回退到开发白名单
|
||||
if (!v) return DEV_ORIGINS; // Fallback to the development whitelist
|
||||
if (v.includes(",")) {
|
||||
return v
|
||||
.split(",")
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e # 遇到错误立即退出
|
||||
set -e # Exit immediately on error
|
||||
|
||||
# 颜色定义
|
||||
# Color definitions
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m'
|
||||
|
||||
# 脚本目录
|
||||
# Script directory
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
DOCKER_SCRIPTS_DIR="$SCRIPT_DIR/docker/scripts"
|
||||
|
||||
# 日志函数
|
||||
# Logging helpers
|
||||
log_info() {
|
||||
echo -e "${BLUE}ℹ️ $1${NC}"
|
||||
}
|
||||
@@ -30,39 +30,39 @@ log_error() {
|
||||
echo -e "${RED}❌ $1${NC}"
|
||||
}
|
||||
|
||||
# 显示帮助信息
|
||||
# Show help
|
||||
show_help() {
|
||||
cat << EOF
|
||||
PrivyDrop Docker 一键部署脚本
|
||||
PrivyDrop Docker Deployment Script
|
||||
|
||||
用法: $0 [选项]
|
||||
Usage: $0 [options]
|
||||
|
||||
选项:
|
||||
--domain DOMAIN 指定域名 (用于HTTPS部署)
|
||||
--mode MODE 部署模式: basic|public|full|private
|
||||
basic/private: 内网HTTP部署 (默认,private 将跳过网络检测)
|
||||
public: 公网HTTP部署 + TURN服务器
|
||||
full: 完整HTTPS部署 + TURN服务器
|
||||
--with-nginx 启用Nginx反向代理
|
||||
--with-turn 启用TURN服务器
|
||||
--with-sni443 启用 443 SNI 分流 (full 模式默认启用)
|
||||
--le-email EMAIL 使用 Let's Encrypt 时的证书邮箱(full 模式推荐传入)
|
||||
--clean 清理现有容器和数据
|
||||
--help 显示帮助信息
|
||||
Options:
|
||||
--domain DOMAIN Specify domain (for HTTPS deployments)
|
||||
--mode MODE Deployment mode: basic|public|full|private
|
||||
basic/private: Intranet HTTP (default; private skips network detection)
|
||||
public: Public HTTP + TURN server
|
||||
full: Full HTTPS + TURN server
|
||||
--with-nginx Enable Nginx reverse proxy
|
||||
--with-turn Enable TURN server
|
||||
--with-sni443 Enable 443 SNI routing (enabled by default in full mode)
|
||||
--le-email EMAIL Email for Let's Encrypt (recommended in full mode)
|
||||
--clean Clean existing containers and data
|
||||
--help Show help
|
||||
|
||||
示例:
|
||||
$0 # 基础部署
|
||||
$0 --mode public --with-turn # 公网部署 + TURN服务器
|
||||
$0 --domain example.com --mode full # 完整HTTPS部署
|
||||
$0 --clean # 清理部署
|
||||
Examples:
|
||||
$0 # Basic deployment
|
||||
$0 --mode public --with-turn # Public deployment + TURN server
|
||||
$0 --domain example.com --mode full # Full HTTPS deployment
|
||||
$0 --clean # Clean deployment
|
||||
|
||||
要求:
|
||||
- Docker Engine 和 Docker Compose V2(命令为 `docker compose`)
|
||||
Requirements:
|
||||
- Docker Engine and Docker Compose V2 (command `docker compose`)
|
||||
|
||||
EOF
|
||||
}
|
||||
|
||||
# 解析命令行参数
|
||||
# Parse command-line arguments
|
||||
parse_arguments() {
|
||||
DOMAIN_NAME=""
|
||||
DEPLOYMENT_MODE=""
|
||||
@@ -107,23 +107,23 @@ parse_arguments() {
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
log_error "未知参数: $1"
|
||||
log_error "Unknown argument: $1"
|
||||
show_help
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# 导出变量供其他脚本使用
|
||||
# Export variables for other scripts
|
||||
export DOMAIN_NAME
|
||||
export DEPLOYMENT_MODE
|
||||
export WITH_NGINX
|
||||
export WITH_TURN
|
||||
}
|
||||
|
||||
# 检查依赖
|
||||
# Check dependencies
|
||||
check_dependencies() {
|
||||
log_info "检查依赖..."
|
||||
log_info "Checking dependencies..."
|
||||
|
||||
local missing_deps=()
|
||||
|
||||
@@ -144,16 +144,16 @@ check_dependencies() {
|
||||
fi
|
||||
|
||||
if [[ ${#missing_deps[@]} -gt 0 ]]; then
|
||||
log_error "缺少依赖: ${missing_deps[*]}"
|
||||
log_error "Missing dependencies: ${missing_deps[*]}"
|
||||
echo ""
|
||||
echo "请安装缺少的依赖:"
|
||||
echo "Please install the missing dependencies:"
|
||||
for dep in "${missing_deps[@]}"; do
|
||||
case $dep in
|
||||
docker)
|
||||
echo " Docker: https://docs.docker.com/get-docker/"
|
||||
;;
|
||||
"docker compose (V2)")
|
||||
echo " Docker Compose V2 插件: https://docs.docker.com/compose/install/"
|
||||
echo " Docker Compose V2 plugin: https://docs.docker.com/compose/install/"
|
||||
;;
|
||||
curl)
|
||||
echo " curl: sudo apt-get install curl (Ubuntu/Debian)"
|
||||
@@ -166,24 +166,24 @@ check_dependencies() {
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log_success "依赖检查通过"
|
||||
log_success "Dependency checks passed"
|
||||
}
|
||||
|
||||
# 安装并准备 Let's Encrypt(certbot)
|
||||
# Install and prepare Let's Encrypt (certbot)
|
||||
ensure_certbot() {
|
||||
if command -v certbot >/dev/null 2>&1; then
|
||||
return 0
|
||||
fi
|
||||
log_info "安装 certbot (需要 sudo 权限)..."
|
||||
log_info "Installing certbot (requires sudo)..."
|
||||
if command -v apt-get >/dev/null 2>&1; then
|
||||
sudo apt-get update -y && sudo apt-get install -y certbot
|
||||
else
|
||||
log_error "未检测到 apt-get,请手动安装 certbot 或在支持的系统上运行"
|
||||
log_error "apt-get not found. Please install certbot manually or run on a supported system"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# 写入 certbot 部署钩子:续期后复制证书并热重载服务
|
||||
# Write certbot deploy hook: copy certs and hot-reload services after renewal
|
||||
install_certbot_deploy_hook() {
|
||||
local repo_dir="$SCRIPT_DIR"
|
||||
local hook_dir="/etc/letsencrypt/renewal-hooks/deploy"
|
||||
@@ -197,7 +197,7 @@ set -e
|
||||
REPO_DIR="$repo_dir"
|
||||
COMPOSE_FILE="$compose_file"
|
||||
|
||||
# RENEWED_LINEAGE 由 certbot 传入,指向 live/<domain>
|
||||
# RENEWED_LINEAGE is provided by certbot and points to live/<domain>
|
||||
if [[ -z "\$RENEWED_LINEAGE" ]]; then
|
||||
exit 0
|
||||
fi
|
||||
@@ -206,34 +206,34 @@ cp "\$RENEWED_LINEAGE/fullchain.pem" "\$REPO_DIR/docker/ssl/server-cert.pem"
|
||||
cp "\$RENEWED_LINEAGE/privkey.pem" "\$REPO_DIR/docker/ssl/server-key.pem"
|
||||
chmod 600 "\$REPO_DIR/docker/ssl/server-key.pem" || true
|
||||
|
||||
# 热重载 nginx,如失败则重启
|
||||
# Hot-reload nginx; restart if it fails
|
||||
docker compose -f "\$COMPOSE_FILE" exec -T nginx nginx -s reload 2>/dev/null || \
|
||||
docker compose -f "\$COMPOSE_FILE" restart nginx || true
|
||||
|
||||
# 优先向 coturn 发送 HUP,不行则重启(若未启用则忽略)
|
||||
# Prefer sending HUP to coturn; restart if needed (ignore if disabled)
|
||||
docker compose -f "\$COMPOSE_FILE" exec -T coturn sh -c 'kill -HUP 1' 2>/dev/null || \
|
||||
docker compose -f "\$COMPOSE_FILE" restart coturn || true
|
||||
EOF
|
||||
sudo chmod +x "$hook_file"
|
||||
|
||||
# 尝试启用 systemd 定时器
|
||||
# Attempt to enable systemd timer
|
||||
if command -v systemctl >/dev/null 2>&1; then
|
||||
sudo systemctl enable --now certbot.timer 2>/dev/null || true
|
||||
fi
|
||||
}
|
||||
|
||||
# 使用 webroot 首次签发并启用 443 配置
|
||||
# Issue via webroot and enable 443 config
|
||||
provision_letsencrypt_cert() {
|
||||
# 仅在 full 模式且启用 nginx 且存在域名时执行
|
||||
# Only in full mode with nginx enabled and domain set
|
||||
if [[ "$DEPLOYMENT_MODE" != "full" || "$WITH_NGINX" != "true" ]]; then
|
||||
return 0
|
||||
fi
|
||||
if [[ -z "$DOMAIN_NAME" ]]; then
|
||||
log_warning "full 模式未指定 --domain,跳过 Let’s Encrypt"
|
||||
log_warning "Full mode without --domain; skipping Let's Encrypt"
|
||||
return 0
|
||||
fi
|
||||
if [[ -z "$LE_EMAIL" ]]; then
|
||||
log_warning "未指定 --le-email,将使用 --register-unsafely-without-email"
|
||||
log_warning "No --le-email specified; using --register-unsafely-without-email"
|
||||
fi
|
||||
|
||||
ensure_certbot
|
||||
@@ -241,32 +241,32 @@ provision_letsencrypt_cert() {
|
||||
|
||||
mkdir -p docker/letsencrypt-www docker/ssl
|
||||
|
||||
# 如果证书已存在(含 -0001 谱系),跳过签发
|
||||
# If certificates already exist (including -0001 lineage), skip issuance
|
||||
if [[ -f "/etc/letsencrypt/live/$DOMAIN_NAME/fullchain.pem" ]] || ls -1d /etc/letsencrypt/live/${DOMAIN_NAME}* >/dev/null 2>&1; then
|
||||
log_info "检测到已存在的证书/谱系,跳过首次签发"
|
||||
log_info "Detected existing certificates/lineage; skipping initial issuance"
|
||||
else
|
||||
log_info "通过 webroot 模式签发 Let's Encrypt 证书..."
|
||||
log_info "Issuing Let's Encrypt certificate via webroot..."
|
||||
local email_args="--email $LE_EMAIL"
|
||||
if [[ -z "$LE_EMAIL" ]]; then
|
||||
email_args="--register-unsafely-without-email"
|
||||
fi
|
||||
# 需要 80 端口可达且 nginx 已启动
|
||||
# Requires port 80 reachable and nginx running
|
||||
sudo certbot certonly --webroot -w "$(pwd)/docker/letsencrypt-www" \
|
||||
-d "$DOMAIN_NAME" -d "turn.$DOMAIN_NAME" \
|
||||
$email_args --agree-tos --non-interactive || {
|
||||
log_error "证书签发失败,请查看 certbot 输出"
|
||||
log_error "Certificate issuance failed; please check certbot output"
|
||||
return 1
|
||||
}
|
||||
fi
|
||||
|
||||
# 解析谱系目录(兼容 -0001/-0002 后缀)并复制到 docker/ssl
|
||||
# Resolve lineage directory (supports -0001/-0002 suffixes) and copy to docker/ssl
|
||||
local lineage_dir
|
||||
lineage_dir=$(readlink -f "/etc/letsencrypt/live/$DOMAIN_NAME" 2>/dev/null || true)
|
||||
if [[ -z "$lineage_dir" || ! -d "$lineage_dir" ]]; then
|
||||
lineage_dir=$(ls -1d /etc/letsencrypt/live/${DOMAIN_NAME}* 2>/dev/null | sort | tail -1)
|
||||
fi
|
||||
if [[ -z "$lineage_dir" || ! -f "$lineage_dir/fullchain.pem" ]]; then
|
||||
log_error "未找到有效证书谱系目录,请检查 /etc/letsencrypt/live/${DOMAIN_NAME}*"
|
||||
log_error "No valid certificate lineage directory found. Check /etc/letsencrypt/live/${DOMAIN_NAME}*"
|
||||
return 1
|
||||
fi
|
||||
|
||||
@@ -274,99 +274,99 @@ provision_letsencrypt_cert() {
|
||||
sudo cp "$lineage_dir/privkey.pem" docker/ssl/server-key.pem
|
||||
sudo chmod 600 docker/ssl/server-key.pem || true
|
||||
|
||||
# 启用 443 配置(证书已就绪):不清理,仅追加;传递 SNI 开关(默认 full 启用)
|
||||
# Enable 443 config (certs ready): append only; pass SNI flag (enabled by default in full)
|
||||
local gen_args=(--mode full --domain "$DOMAIN_NAME" --no-clean --ssl-mode letsencrypt)
|
||||
[[ "$WITH_SNI443" == "true" ]] && gen_args+=(--enable-sni443)
|
||||
bash "$DOCKER_SCRIPTS_DIR/generate-config.sh" "${gen_args[@]}" || true
|
||||
|
||||
# 热重载 nginx 以启用 443
|
||||
# Hot-reload nginx to enable 443
|
||||
docker compose exec -T nginx nginx -s reload || docker compose restart nginx
|
||||
}
|
||||
|
||||
# 清理现有部署
|
||||
# Clean existing deployment
|
||||
clean_deployment() {
|
||||
if [[ "$CLEAN_MODE" == "true" ]]; then
|
||||
log_warning "清理现有部署..."
|
||||
log_warning "Cleaning existing deployment..."
|
||||
|
||||
# 停止并删除容器
|
||||
# Stop and remove containers
|
||||
if [[ -f "docker-compose.yml" ]]; then
|
||||
docker compose down -v --remove-orphans 2>/dev/null || true
|
||||
fi
|
||||
# 优雅停止后兜底强制清理命名容器
|
||||
# After graceful stop, force-clean named containers as fallback
|
||||
docker stop -t 10 privydrop-nginx privydrop-coturn 2>/dev/null || true
|
||||
docker rm -f privydrop-nginx privydrop-coturn 2>/dev/null || true
|
||||
# 兜底清理项目网络(若存在)
|
||||
# Fallback: remove project network (if present)
|
||||
docker network rm privydrop_privydrop-network 2>/dev/null || true
|
||||
|
||||
# 删除镜像
|
||||
# Remove images
|
||||
docker images | grep privydrop | awk '{print $3}' | xargs -r docker rmi -f 2>/dev/null || true
|
||||
|
||||
# 清理配置文件
|
||||
# Clean configuration files
|
||||
rm -rf docker/nginx/conf.d/*.conf docker/ssl/* logs/* .env 2>/dev/null || true
|
||||
|
||||
log_success "清理完成"
|
||||
log_success "Cleanup complete"
|
||||
|
||||
if [[ $# -eq 1 ]]; then # 如果只有--clean参数
|
||||
if [[ $# -eq 1 ]]; then # If only --clean parameter
|
||||
exit 0
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
# 确保 TURN 服务按需启动(当指定 --with-turn 时)
|
||||
# Ensure TURN service starts when requested (--with-turn)
|
||||
ensure_turn_running() {
|
||||
if [[ "$WITH_TURN" != "true" ]]; then
|
||||
return 0
|
||||
fi
|
||||
# 未运行则显式启用 profile 启动 coturn
|
||||
# If not running, start coturn via profile
|
||||
if ! docker compose ps | grep -q "privydrop-coturn"; then
|
||||
log_info "启动 TURN 服务 (profile: turn)..."
|
||||
log_info "Starting TURN service (profile: turn)..."
|
||||
docker compose --profile turn up -d coturn || true
|
||||
fi
|
||||
}
|
||||
|
||||
# 环境检测和配置生成
|
||||
# Environment detection and configuration generation
|
||||
setup_environment() {
|
||||
log_info "设置环境..."
|
||||
log_info "Setting up environment..."
|
||||
|
||||
# 确保脚本可执行
|
||||
# Ensure scripts are executable
|
||||
chmod +x "$DOCKER_SCRIPTS_DIR"/*.sh 2>/dev/null || true
|
||||
|
||||
# 运行环境检测
|
||||
# Run environment detection
|
||||
local detect_args=""
|
||||
[[ -n "$DOMAIN_NAME" ]] && detect_args="--domain $DOMAIN_NAME"
|
||||
[[ -n "$DEPLOYMENT_MODE" ]] && detect_args="$detect_args --mode $DEPLOYMENT_MODE"
|
||||
[[ "$WITH_SNI443" == "true" ]] && detect_args="$detect_args --enable-sni443"
|
||||
|
||||
if ! bash "$DOCKER_SCRIPTS_DIR/detect-environment.sh" $detect_args; then
|
||||
log_error "环境检测失败"
|
||||
log_error "Environment detection failed"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 生成配置文件
|
||||
# Generate configuration files
|
||||
if ! bash "$DOCKER_SCRIPTS_DIR/generate-config.sh" $detect_args; then
|
||||
log_error "配置生成失败"
|
||||
log_error "Configuration generation failed"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log_success "环境设置完成"
|
||||
log_success "Environment setup complete"
|
||||
}
|
||||
|
||||
# 构建和启动服务
|
||||
# Build and start services
|
||||
deploy_services() {
|
||||
log_info "构建和启动服务..."
|
||||
log_info "Building and starting services..."
|
||||
|
||||
# 确保日志目录存在并放宽权限,避免容器无法写日志(coturn/nginx 等)
|
||||
# Ensure log directories exist and relax permissions so containers (coturn/nginx etc.) can write logs
|
||||
mkdir -p logs logs/nginx logs/backend logs/frontend logs/coturn 2>/dev/null || true
|
||||
chmod 777 -R logs 2>/dev/null || true
|
||||
log_info "日志目录已准备并授权: ./logs (权限 777)"
|
||||
log_info "Log directories prepared and permissions set: ./logs (mode 777)"
|
||||
|
||||
# 停止现有服务
|
||||
# Stop existing services
|
||||
if docker compose ps | grep -q "Up"; then
|
||||
log_info "停止现有服务..."
|
||||
log_info "Stopping existing services..."
|
||||
docker compose down
|
||||
fi
|
||||
|
||||
# 确定启用的服务(Compose V2 需要将 --profile 放在子命令之前)
|
||||
# Determine enabled services (Compose V2 requires --profile before the subcommand)
|
||||
local profiles=""
|
||||
if [[ "$WITH_NGINX" == "true" ]]; then
|
||||
profiles="$profiles --profile nginx"
|
||||
@@ -375,28 +375,28 @@ deploy_services() {
|
||||
profiles="$profiles --profile turn"
|
||||
fi
|
||||
|
||||
# 构建镜像(先并行,失败则串行回退)
|
||||
log_info "构建Docker镜像..."
|
||||
# Build images (parallel first, fall back to serial on failure)
|
||||
log_info "Building Docker images..."
|
||||
set +e
|
||||
docker compose build --parallel
|
||||
local build_status=$?
|
||||
set -e
|
||||
if [[ $build_status -ne 0 ]]; then
|
||||
log_warning "并行构建失败,回退为串行构建..."
|
||||
log_warning "Parallel build failed; falling back to serial build..."
|
||||
docker compose build
|
||||
fi
|
||||
|
||||
# 启动服务(--profile 需置于 up 之前)
|
||||
log_info "启动服务..."
|
||||
# Start services (--profile must precede up)
|
||||
log_info "Starting services..."
|
||||
# shellcheck disable=SC2086
|
||||
docker compose $profiles up -d
|
||||
|
||||
log_success "服务启动完成"
|
||||
log_success "Services started"
|
||||
}
|
||||
|
||||
# 等待服务就绪
|
||||
# Wait for services to be ready
|
||||
wait_for_services() {
|
||||
log_info "等待服务就绪..."
|
||||
log_info "Waiting for services to be ready..."
|
||||
|
||||
local max_attempts=60
|
||||
local attempt=0
|
||||
@@ -406,12 +406,12 @@ wait_for_services() {
|
||||
local backend_ready=false
|
||||
local frontend_ready=false
|
||||
|
||||
# 检查后端健康状态
|
||||
# Check backend health
|
||||
if curl -f http://localhost:3001/health &> /dev/null; then
|
||||
backend_ready=true
|
||||
fi
|
||||
|
||||
# 检查前端健康状态
|
||||
# Check frontend health
|
||||
if curl -f http://localhost:3002/api/health &> /dev/null; then
|
||||
frontend_ready=true
|
||||
fi
|
||||
@@ -429,58 +429,58 @@ wait_for_services() {
|
||||
echo ""
|
||||
|
||||
if [[ "$services_ready" == "true" ]]; then
|
||||
log_success "所有服务已就绪"
|
||||
log_success "All services are ready"
|
||||
return 0
|
||||
else
|
||||
log_error "服务启动超时"
|
||||
log_info "查看服务状态: docker compose ps"
|
||||
log_info "查看服务日志: docker compose logs -f"
|
||||
log_error "Service startup timed out"
|
||||
log_info "View service status: docker compose ps"
|
||||
log_info "View service logs: docker compose logs -f"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# 运行部署后检查
|
||||
# Run post-deployment checks
|
||||
post_deployment_checks() {
|
||||
log_info "运行部署后检查..."
|
||||
log_info "Running post-deployment checks..."
|
||||
|
||||
# 检查容器状态
|
||||
log_info "检查容器状态..."
|
||||
# Check container status
|
||||
log_info "Checking container status..."
|
||||
docker compose ps
|
||||
|
||||
# full+nginx 场景追加 HTTPS 健康检查(若定义了域名)
|
||||
# In full+nginx, add HTTPS health check (if domain defined)
|
||||
if [[ -f ".env" ]]; then
|
||||
local dep_mode="$(grep "DEPLOYMENT_MODE=" .env | cut -d'=' -f2)"
|
||||
local dname="$(grep "DOMAIN_NAME=" .env | cut -d'=' -f2)"
|
||||
if [[ "$dep_mode" == "full" && -n "$dname" ]]; then
|
||||
log_info "测试: HTTPS 健康检查 https://$dname/api/health"
|
||||
log_info "Test: HTTPS health check https://$dname/api/health"
|
||||
if curl -fsS "https://$dname/api/health" >/dev/null; then
|
||||
log_success "HTTPS 健康检查通过"
|
||||
log_success "HTTPS health check passed"
|
||||
else
|
||||
log_warning "HTTPS 健康检查失败。若证书刚签发,请稍等或执行: bash docker/scripts/generate-config.sh --mode full --domain $dname --no-clean && docker compose exec -T nginx nginx -s reload"
|
||||
log_warning "HTTPS health check failed. If the certificate was just issued, wait a bit or run: bash docker/scripts/generate-config.sh --mode full --domain $dname --no-clean && docker compose exec -T nginx nginx -s reload"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
# 运行健康检查测试
|
||||
# Run health-check tests
|
||||
if [[ -f "test-health-apis.sh" ]]; then
|
||||
log_info "运行健康检查测试..."
|
||||
log_info "Running health-check tests..."
|
||||
if bash test-health-apis.sh; then
|
||||
log_success "健康检查测试通过"
|
||||
log_success "Health-check tests passed"
|
||||
else
|
||||
log_warning "健康检查测试失败,但服务可能仍然正常"
|
||||
log_warning "Health-check tests failed, but services may still be working"
|
||||
fi
|
||||
fi
|
||||
|
||||
log_success "部署后检查完成"
|
||||
log_success "Post-deployment checks complete"
|
||||
}
|
||||
|
||||
# 显示部署结果
|
||||
# Show deployment results
|
||||
show_deployment_info() {
|
||||
echo ""
|
||||
echo -e "${GREEN}🎉 PrivyDrop 部署完成!${NC}"
|
||||
echo -e "${GREEN}🎉 PrivyDrop deployment complete!${NC}"
|
||||
echo ""
|
||||
|
||||
# 读取配置信息
|
||||
# Read configuration
|
||||
local local_ip=""
|
||||
local public_ip=""
|
||||
local frontend_port=""
|
||||
@@ -501,47 +501,47 @@ show_deployment_info() {
|
||||
turn_enabled_env=$(grep "TURN_ENABLED=" .env | cut -d'=' -f2)
|
||||
fi
|
||||
|
||||
echo -e "${BLUE}📋 访问信息:${NC}"
|
||||
echo -e "${BLUE}📋 Access Info:${NC}"
|
||||
|
||||
# 判定是否公网场景(public/full)
|
||||
# Determine if public scenario (public/full)
|
||||
local is_public="false"
|
||||
if [[ "$deployment_mode" == "public" || "$deployment_mode" == "full" || "$network_mode" == "public" ]]; then
|
||||
is_public="true"
|
||||
fi
|
||||
|
||||
if [[ "$is_public" == "true" ]]; then
|
||||
# 公网展示优先域名,其次公网IP
|
||||
# For public scenarios, prefer domain, then public IP
|
||||
if [[ -n "$domain_name" ]]; then
|
||||
if [[ "$WITH_NGINX" == "true" || "$deployment_mode" == "full" ]]; then
|
||||
echo " 公网访问: https://$domain_name"
|
||||
echo " API 地址: https://$domain_name"
|
||||
echo " Public access: https://$domain_name"
|
||||
echo " API: https://$domain_name"
|
||||
else
|
||||
echo " 公网访问: http://$domain_name:${frontend_port:-3002}"
|
||||
echo " API 地址: http://$domain_name:${backend_port:-3001}"
|
||||
echo " Public access: http://$domain_name:${frontend_port:-3002}"
|
||||
echo " API: http://$domain_name:${backend_port:-3001}"
|
||||
fi
|
||||
elif [[ -n "$public_ip" ]]; then
|
||||
echo " 公网访问: http://$public_ip:${frontend_port:-3002}"
|
||||
echo " API 地址: http://$public_ip:${backend_port:-3001}"
|
||||
echo " Public access: http://$public_ip:${frontend_port:-3002}"
|
||||
echo " API: http://$public_ip:${backend_port:-3001}"
|
||||
else
|
||||
# 回退:无法获取公网IP时给出局域网与本机
|
||||
echo " 前端应用: http://localhost:${frontend_port:-3002}"
|
||||
echo " 后端API: http://localhost:${backend_port:-3001}"
|
||||
# Fallback: show LAN and localhost if public IP is unavailable
|
||||
echo " Frontend: http://localhost:${frontend_port:-3002}"
|
||||
echo " Backend API: http://localhost:${backend_port:-3001}"
|
||||
fi
|
||||
else
|
||||
# 内网/基础模式:本机+局域网
|
||||
echo " 前端应用: http://localhost:${frontend_port:-3002}"
|
||||
echo " 后端API: http://localhost:${backend_port:-3001}"
|
||||
# Private/basic: localhost + LAN
|
||||
echo " Frontend: http://localhost:${frontend_port:-3002}"
|
||||
echo " Backend API: http://localhost:${backend_port:-3001}"
|
||||
if [[ -n "$local_ip" ]] && [[ "$local_ip" != "127.0.0.1" ]]; then
|
||||
echo ""
|
||||
echo -e "${BLUE}🌐 局域网访问:${NC}"
|
||||
echo " 前端应用: http://$local_ip:${frontend_port:-3002}"
|
||||
echo " 后端API: http://$local_ip:${backend_port:-3001}"
|
||||
echo -e "${BLUE}🌐 LAN Access:${NC}"
|
||||
echo " Frontend: http://$local_ip:${frontend_port:-3002}"
|
||||
echo " Backend API: http://$local_ip:${backend_port:-3001}"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ "$WITH_NGINX" == "true" ]]; then
|
||||
echo ""
|
||||
echo -e "${BLUE}🔀 Nginx代理:${NC}"
|
||||
echo -e "${BLUE}🔀 Nginx Proxy:${NC}"
|
||||
if [[ -n "$domain_name" ]]; then
|
||||
echo " HTTP: http://$domain_name"
|
||||
[[ -f "docker/ssl/server-cert.pem" ]] && echo " HTTPS: https://$domain_name"
|
||||
@@ -555,18 +555,18 @@ show_deployment_info() {
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo -e "${BLUE}🔧 管理命令:${NC}"
|
||||
echo " 查看状态: docker compose ps"
|
||||
echo " 查看日志: docker compose logs -f [服务名]"
|
||||
echo " 重启服务: docker compose restart [服务名]"
|
||||
echo " 停止服务: docker compose down"
|
||||
echo " 完全清理: $0 --clean"
|
||||
echo -e "${BLUE}🔧 Management Commands:${NC}"
|
||||
echo " Status: docker compose ps"
|
||||
echo " Logs: docker compose logs -f [service]"
|
||||
echo " Restart: docker compose restart [service]"
|
||||
echo " Stop: docker compose down"
|
||||
echo " Full cleanup: $0 --clean"
|
||||
|
||||
if [[ -f "docker/ssl/ca-cert.pem" ]]; then
|
||||
echo ""
|
||||
echo -e "${BLUE}🔒 SSL证书:${NC}"
|
||||
echo " CA证书: docker/ssl/ca-cert.pem"
|
||||
echo " 要信任HTTPS连接,请将CA证书导入浏览器"
|
||||
echo -e "${BLUE}🔒 SSL Certificates:${NC}"
|
||||
echo " CA certificate: docker/ssl/ca-cert.pem"
|
||||
echo " To trust HTTPS, import the CA certificate into your browser"
|
||||
fi
|
||||
|
||||
if [[ "$WITH_TURN" == "true" || "$turn_enabled_env" == "true" ]]; then
|
||||
@@ -578,12 +578,12 @@ show_deployment_info() {
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo -e "${BLUE}🔄 TURN服务器:${NC}"
|
||||
# 展示优先域名的 TURN 信息,否则展示公网IP
|
||||
echo -e "${BLUE}🔄 TURN Server:${NC}"
|
||||
# Prefer domain for TURN info; otherwise show public IP
|
||||
if [[ -n "$domain_name" ]]; then
|
||||
echo " STUN: stun:${domain_name}:3478"
|
||||
echo " TURN (UDP): turn:${domain_name}:3478"
|
||||
echo " TURN (TLS): turns:turn.${domain_name}:443 (如已配置 443 SNI 分流)"
|
||||
echo " TURN (TLS): turns:turn.${domain_name}:443 (if 443 SNI split is configured)"
|
||||
elif [[ -n "$public_ip" ]]; then
|
||||
echo " STUN: stun:${public_ip}:3478"
|
||||
echo " TURN: turn:${public_ip}:3478"
|
||||
@@ -591,77 +591,77 @@ show_deployment_info() {
|
||||
echo " STUN: stun:${local_ip}:3478"
|
||||
echo " TURN: turn:${local_ip}:3478"
|
||||
fi
|
||||
echo " 用户名: ${turn_username:-privydrop}"
|
||||
echo " 密码: (保存在.env文件中)"
|
||||
echo " Username: ${turn_username:-privydrop}"
|
||||
echo " Password: (stored in .env)"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo -e "${YELLOW}💡 提示:${NC}"
|
||||
echo " - 首次启动可能需要几分钟来下载和构建镜像"
|
||||
echo " - 如遇问题,请查看日志: docker compose logs -f"
|
||||
echo " - 更多帮助: $0 --help"
|
||||
echo -e "${YELLOW}💡 Tips:${NC}"
|
||||
echo " - First run may take several minutes to download and build images"
|
||||
echo " - If issues occur, check logs: docker compose logs -f"
|
||||
echo " - More help: $0 --help"
|
||||
echo ""
|
||||
|
||||
# 公网场景追加:如何测试域名(HTTPS+Nginx)
|
||||
# Public scenario: how to test a domain (HTTPS+Nginx)
|
||||
if [[ "$is_public" == "true" && -z "$domain_name" ]]; then
|
||||
echo -e "${BLUE}🌍 公网域名部署(HTTPS + Nginx)快速测试:${NC}"
|
||||
echo " 1) 将你的域名 A 记录解析到 ${public_ip:-<server-ip>}"
|
||||
echo " 可选:将 turn.<your-domain> 也解析到同一IP,用于 TURN 主机名"
|
||||
echo " 2) 运行: ./deploy.sh --mode full --domain <your-domain> --with-nginx --with-turn"
|
||||
echo " 3) 放行端口: 80, 443, 3478/udp, 5349/tcp, 5349/udp"
|
||||
echo " 4) 验证: https://<your-domain> 正常打开,/api/health 返回 200"
|
||||
echo " WebRTC: 打开 webrtc-internals,观察是否出现 relay 候选 (TURN)"
|
||||
echo " 注: 目前 Docker 版本未启用 443 SNI 转发至 coturn,如需 turns:443 请后续启用 stream 分流。"
|
||||
echo -e "${BLUE}🌍 Public domain deployment (HTTPS + Nginx) quick test:${NC}"
|
||||
echo " 1) Point your domain A record to ${public_ip:-<server-ip>}"
|
||||
echo " Optional: also point turn.<your-domain> to the same IP for TURN hostname"
|
||||
echo " 2) Run: ./deploy.sh --mode full --domain <your-domain> --with-nginx --with-turn"
|
||||
echo " 3) Open ports: 80, 443, 3478/udp, 5349/tcp, 5349/udp"
|
||||
echo " 4) Verify: https://<your-domain> opens, /api/health returns 200"
|
||||
echo " WebRTC: open chrome://webrtc-internals and check for relay candidates (TURN)"
|
||||
echo " Note: The Docker setup does not enable 443 SNI to coturn by default; enable stream SNI if you need turns:443."
|
||||
echo ""
|
||||
fi
|
||||
}
|
||||
|
||||
# 主函数
|
||||
# Main function
|
||||
main() {
|
||||
echo -e "${BLUE}=== PrivyDrop Docker 一键部署 ===${NC}"
|
||||
echo -e "${BLUE}=== PrivyDrop Docker One-Click Deployment ===${NC}"
|
||||
echo ""
|
||||
|
||||
# 解析命令行参数
|
||||
# Parse command-line arguments
|
||||
parse_arguments "$@"
|
||||
|
||||
# 检查依赖
|
||||
# Check dependencies
|
||||
check_dependencies
|
||||
echo ""
|
||||
|
||||
# 清理模式
|
||||
# Clean mode
|
||||
clean_deployment
|
||||
# 若仅执行清理(未指定其它参数),直接退出,避免进入环境检测
|
||||
# If only cleaning (no other args), exit early to skip env detection
|
||||
if [[ "$CLEAN_MODE" == "true" && -z "$DEPLOYMENT_MODE" && "$WITH_NGINX" == "false" && "$WITH_TURN" == "false" && -z "$DOMAIN_NAME" ]]; then
|
||||
log_success "清理完成(仅清理模式),已退出。"
|
||||
log_success "Cleanup complete (clean-only mode). Exiting."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# 环境设置
|
||||
# Environment setup
|
||||
setup_environment
|
||||
echo ""
|
||||
|
||||
# 部署服务
|
||||
# Deploy services
|
||||
deploy_services
|
||||
echo ""
|
||||
|
||||
# 若为 full + nginx,自动签发证书并启用 443
|
||||
# If full + nginx, automatically issue certs and enable 443
|
||||
provision_letsencrypt_cert || true
|
||||
# 确保 TURN 启动(当请求了 --with-turn 时)
|
||||
# Ensure TURN is running (when requested with --with-turn)
|
||||
ensure_turn_running || true
|
||||
|
||||
# 等待服务就绪
|
||||
# Wait for services to be ready
|
||||
if wait_for_services; then
|
||||
echo ""
|
||||
post_deployment_checks
|
||||
show_deployment_info
|
||||
else
|
||||
log_error "部署失败,请检查日志: docker compose logs"
|
||||
log_error "Deployment failed. Please check logs: docker compose logs"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# 捕获中断信号
|
||||
trap 'log_warning "部署被中断"; exit 1' INT TERM
|
||||
# Trap interrupt signals
|
||||
trap 'log_warning "Deployment interrupted"; exit 1' INT TERM
|
||||
|
||||
# 运行主函数
|
||||
# Run main function
|
||||
main "$@"
|
||||
|
||||
+5
-5
@@ -1,5 +1,5 @@
|
||||
services:
|
||||
# Redis缓存服务
|
||||
# Redis cache service
|
||||
redis:
|
||||
image: redis:7-alpine
|
||||
container_name: privydrop-redis
|
||||
@@ -16,7 +16,7 @@ services:
|
||||
retries: 3
|
||||
start_period: 5s
|
||||
|
||||
# 后端信令服务
|
||||
# Backend signaling service
|
||||
backend:
|
||||
build:
|
||||
context: ./backend
|
||||
@@ -49,7 +49,7 @@ services:
|
||||
retries: 3
|
||||
start_period: 40s
|
||||
|
||||
# 前端应用
|
||||
# Frontend app
|
||||
frontend:
|
||||
build:
|
||||
context: ./frontend
|
||||
@@ -83,7 +83,7 @@ services:
|
||||
retries: 3
|
||||
start_period: 120s
|
||||
|
||||
# Nginx反向代理
|
||||
# Nginx reverse proxy
|
||||
nginx:
|
||||
image: nginx:alpine
|
||||
container_name: privydrop-nginx
|
||||
@@ -105,7 +105,7 @@ services:
|
||||
profiles:
|
||||
- nginx
|
||||
|
||||
# TURN/STUN服务器 (可选,用于NAT穿透)
|
||||
# TURN/STUN server (optional, for NAT traversal)
|
||||
coturn:
|
||||
image: coturn/coturn:4.6.2
|
||||
container_name: privydrop-coturn
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
#!/bin/bash
|
||||
|
||||
# 颜色定义
|
||||
# Color definitions
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# 全局变量
|
||||
# Global variables
|
||||
NETWORK_MODE=""
|
||||
LOCAL_IP=""
|
||||
PUBLIC_IP=""
|
||||
@@ -181,7 +181,7 @@ resolve_local_ip() {
|
||||
LOCAL_IP="${IP_CANDIDATES[0]}"
|
||||
}
|
||||
|
||||
# 日志函数
|
||||
# Logging helpers
|
||||
log_info() {
|
||||
echo -e "${BLUE}ℹ️ $1${NC}"
|
||||
}
|
||||
@@ -198,22 +198,22 @@ log_error() {
|
||||
echo -e "${RED}❌ $1${NC}"
|
||||
}
|
||||
|
||||
# 检测网络环境
|
||||
# Detect network environment
|
||||
detect_network_environment() {
|
||||
log_info "检测网络环境..."
|
||||
log_info "Detecting network environment..."
|
||||
|
||||
resolve_local_ip
|
||||
|
||||
if [[ -z "$LOCAL_IP" ]]; then
|
||||
LOCAL_IP="127.0.0.1"
|
||||
log_warning "无法自动检测本机IP,使用默认值: $LOCAL_IP"
|
||||
log_warning "Unable to detect host IP; using default: $LOCAL_IP"
|
||||
fi
|
||||
|
||||
if [[ "$FORCED_MODE" == "private" ]]; then
|
||||
NETWORK_MODE="private"
|
||||
PUBLIC_IP=""
|
||||
log_info "已通过参数指定网络模式: $NETWORK_MODE"
|
||||
echo " 本机IP: $LOCAL_IP"
|
||||
log_info "Network mode set via parameters: $NETWORK_MODE"
|
||||
echo " Local IP: $LOCAL_IP"
|
||||
return 0
|
||||
fi
|
||||
|
||||
@@ -227,26 +227,26 @@ detect_network_environment() {
|
||||
if is_routable_public_ip "$PUBLIC_IP"; then
|
||||
mode_guess="public"
|
||||
else
|
||||
log_warning "检测到测试或保留网段公网IP,按内网环境处理"
|
||||
log_warning "Public IP is test/reserved range; treating as private"
|
||||
fi
|
||||
else
|
||||
log_warning "公网连接不稳定,按内网环境处理"
|
||||
log_warning "Public connectivity unstable; treating as private"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ -z "$FORCED_MODE" ]]; then
|
||||
if [[ "$mode_guess" == "public" ]]; then
|
||||
echo " 本机IP: $LOCAL_IP"
|
||||
echo " 公网IP: $PUBLIC_IP"
|
||||
echo " Local IP: $LOCAL_IP"
|
||||
echo " Public IP: $PUBLIC_IP"
|
||||
printed_prompt_info="true"
|
||||
read -r -p "是否按公网模式继续?(Y/n): " confirm </dev/tty 2>/dev/null || confirm="Y"
|
||||
read -r -p "Continue in public mode? (Y/n): " confirm </dev/tty 2>/dev/null || confirm="Y"
|
||||
confirm=${confirm:-Y}
|
||||
if [[ "$confirm" =~ ^[Yy]$ ]]; then
|
||||
NETWORK_MODE="public"
|
||||
else
|
||||
NETWORK_MODE="private"
|
||||
PUBLIC_IP=""
|
||||
log_warning "按用户选择,已切换为内网模式"
|
||||
log_warning "Switched to private mode per user choice"
|
||||
fi
|
||||
else
|
||||
NETWORK_MODE="private"
|
||||
@@ -254,7 +254,7 @@ detect_network_environment() {
|
||||
else
|
||||
NETWORK_MODE="$FORCED_MODE"
|
||||
if [[ "$FORCED_MODE" == "public" && -z "$PUBLIC_IP" ]]; then
|
||||
log_warning "未能检测到公网IP,仍按公网模式继续,请确认网络配置"
|
||||
log_warning "Could not detect public IP; continuing as public mode. Please verify network config"
|
||||
fi
|
||||
fi
|
||||
|
||||
@@ -263,121 +263,121 @@ detect_network_environment() {
|
||||
fi
|
||||
|
||||
if [[ "$FORCED_MODE" == "public" ]]; then
|
||||
log_info "已通过参数指定网络模式: $NETWORK_MODE"
|
||||
log_info "Network mode set via parameters: $NETWORK_MODE"
|
||||
elif [[ "$NETWORK_MODE" == "public" ]]; then
|
||||
log_success "检测到公网环境"
|
||||
log_success "Public network detected"
|
||||
else
|
||||
log_success "检测到内网环境"
|
||||
log_success "Private network detected"
|
||||
fi
|
||||
|
||||
if [[ "$printed_prompt_info" == "false" ]]; then
|
||||
echo " 本机IP: $LOCAL_IP"
|
||||
echo " Local IP: $LOCAL_IP"
|
||||
if [[ "$NETWORK_MODE" == "public" && -n "$PUBLIC_IP" ]]; then
|
||||
echo " 公网IP: $PUBLIC_IP"
|
||||
echo " Public IP: $PUBLIC_IP"
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
# 检查系统资源
|
||||
# Check system resources
|
||||
check_system_resources() {
|
||||
log_info "检查系统资源..."
|
||||
log_info "Checking system resources..."
|
||||
|
||||
local warnings=0
|
||||
|
||||
# 检查内存
|
||||
# Check memory
|
||||
if command -v free >/dev/null 2>&1; then
|
||||
TOTAL_MEM=$(free -m | awk 'NR==2{print $2}')
|
||||
if [[ $TOTAL_MEM -lt 512 ]]; then
|
||||
log_error "内存不足: ${TOTAL_MEM}MB (建议至少512MB)"
|
||||
log_error "Insufficient memory: ${TOTAL_MEM}MB (512MB+ recommended)"
|
||||
return 1
|
||||
elif [[ $TOTAL_MEM -lt 1024 ]]; then
|
||||
log_warning "内存较少: ${TOTAL_MEM}MB (建议至少1GB)"
|
||||
log_warning "Low memory: ${TOTAL_MEM}MB (1GB+ recommended)"
|
||||
warnings=$((warnings + 1))
|
||||
else
|
||||
log_success "内存充足: ${TOTAL_MEM}MB"
|
||||
log_success "Memory OK: ${TOTAL_MEM}MB"
|
||||
fi
|
||||
else
|
||||
log_warning "无法检测内存使用情况"
|
||||
log_warning "Unable to read memory usage"
|
||||
warnings=$((warnings + 1))
|
||||
fi
|
||||
|
||||
# 检查磁盘空间
|
||||
# Check disk usage
|
||||
DISK_USAGE=$(df -h / | awk 'NR==2{print $5}' | sed 's/%//')
|
||||
if [[ $DISK_USAGE -gt 95 ]]; then
|
||||
log_error "磁盘空间不足: ${DISK_USAGE}%已使用"
|
||||
log_error "Insufficient disk space: ${DISK_USAGE}% used"
|
||||
return 1
|
||||
elif [[ $DISK_USAGE -gt 80 ]]; then
|
||||
log_warning "磁盘空间紧张: ${DISK_USAGE}%已使用"
|
||||
log_warning "Disk space tight: ${DISK_USAGE}% used"
|
||||
warnings=$((warnings + 1))
|
||||
else
|
||||
log_success "磁盘空间充足: ${DISK_USAGE}%已使用"
|
||||
log_success "Disk space OK: ${DISK_USAGE}% used"
|
||||
fi
|
||||
|
||||
# 检查可用磁盘空间
|
||||
# Check available disk space
|
||||
AVAILABLE_SPACE=$(df -BG / | awk 'NR==2{print $4}' | sed 's/G//')
|
||||
if [[ $AVAILABLE_SPACE -lt 2 ]]; then
|
||||
log_error "可用磁盘空间不足: ${AVAILABLE_SPACE}GB (建议至少2GB)"
|
||||
log_error "Not enough free disk space: ${AVAILABLE_SPACE}GB (2GB+ recommended)"
|
||||
return 1
|
||||
fi
|
||||
|
||||
if [[ $warnings -gt 0 ]]; then
|
||||
log_warning "系统资源检查通过,但有 $warnings 个警告"
|
||||
log_warning "System resource check passed with $warnings warning(s)"
|
||||
else
|
||||
log_success "系统资源检查通过"
|
||||
log_success "System resource check passed"
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
# 验证Docker环境
|
||||
# Validate Docker environment
|
||||
verify_docker_installation() {
|
||||
log_info "检查Docker环境..."
|
||||
log_info "Checking Docker environment..."
|
||||
|
||||
if ! command -v docker &> /dev/null; then
|
||||
log_error "Docker未安装"
|
||||
echo "请安装Docker: https://docs.docker.com/get-docker/"
|
||||
log_error "Docker is not installed"
|
||||
echo "Please install Docker: https://docs.docker.com/get-docker/"
|
||||
return 1
|
||||
fi
|
||||
|
||||
if ! command -v docker-compose &> /dev/null && ! docker compose version &> /dev/null; then
|
||||
log_error "Docker Compose未安装"
|
||||
echo "请安装Docker Compose: https://docs.docker.com/compose/install/"
|
||||
log_error "Docker Compose is not installed"
|
||||
echo "Please install Docker Compose: https://docs.docker.com/compose/install/"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# 检查Docker服务状态
|
||||
# Check Docker service status
|
||||
if ! docker info &> /dev/null; then
|
||||
log_error "Docker服务未运行"
|
||||
echo "请启动Docker服务"
|
||||
log_error "Docker service is not running"
|
||||
echo "Please start the Docker service"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# 检查Docker版本
|
||||
# Check Docker version
|
||||
DOCKER_VERSION=$(docker --version | grep -oE '[0-9]+\.[0-9]+' | head -1)
|
||||
log_success "Docker版本: $DOCKER_VERSION"
|
||||
log_success "Docker version: $DOCKER_VERSION"
|
||||
|
||||
# 检查Docker Compose版本
|
||||
# Check Docker Compose version
|
||||
if command -v docker-compose &> /dev/null; then
|
||||
COMPOSE_VERSION=$(docker-compose --version | grep -oE '[0-9]+\.[0-9]+' | head -1)
|
||||
log_success "Docker Compose版本: $COMPOSE_VERSION"
|
||||
log_success "Docker Compose version: $COMPOSE_VERSION"
|
||||
else
|
||||
COMPOSE_VERSION=$(docker compose version --short 2>/dev/null || echo "内置")
|
||||
log_success "Docker Compose版本: $COMPOSE_VERSION"
|
||||
COMPOSE_VERSION=$(docker compose version --short 2>/dev/null || echo "built-in")
|
||||
log_success "Docker Compose version: $COMPOSE_VERSION"
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
# 检查端口占用
|
||||
# Check port usage
|
||||
check_port_availability() {
|
||||
local ports="$1"
|
||||
log_info "检查端口占用..."
|
||||
log_info "Checking port usage..."
|
||||
|
||||
local occupied_ports=()
|
||||
|
||||
IFS=',' read -ra PORT_ARRAY <<< "$ports"
|
||||
for port in "${PORT_ARRAY[@]}"; do
|
||||
port=$(echo "$port" | xargs) # 去除空格
|
||||
port=$(echo "$port" | xargs) # Trim spaces
|
||||
if command -v ss >/dev/null 2>&1; then
|
||||
if ss -tuln | grep -q ":$port "; then
|
||||
occupied_ports+=("$port")
|
||||
@@ -387,40 +387,40 @@ check_port_availability() {
|
||||
occupied_ports+=("$port")
|
||||
fi
|
||||
else
|
||||
log_warning "无法检查端口占用情况 (缺少ss和netstat命令)"
|
||||
log_warning "Unable to check port usage (missing ss and netstat)"
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
|
||||
if [[ ${#occupied_ports[@]} -gt 0 ]]; then
|
||||
log_warning "以下端口已被占用: ${occupied_ports[*]}"
|
||||
log_info "可以通过修改 .env 中的端口,或先执行 './deploy.sh --clean' / 'docker-compose down' 清理旧容器"
|
||||
log_warning "Ports in use: ${occupied_ports[*]}"
|
||||
log_info "Change ports in .env, or run './deploy.sh --clean' / 'docker-compose down' to clean old containers"
|
||||
else
|
||||
log_success "所有端口都可用"
|
||||
log_success "All ports available"
|
||||
fi
|
||||
}
|
||||
|
||||
# 检测部署模式
|
||||
# Detect deployment mode
|
||||
detect_deployment_mode() {
|
||||
log_info "确定部署模式..."
|
||||
log_info "Determining deployment mode..."
|
||||
|
||||
if [[ "$NETWORK_MODE" == "public" ]] && [[ -n "$DOMAIN_NAME" ]]; then
|
||||
DEPLOYMENT_MODE="full"
|
||||
log_success "部署模式: 完整模式 (HTTPS + TURN服务器)"
|
||||
log_success "Deployment mode: full (HTTPS + TURN server)"
|
||||
elif [[ "$NETWORK_MODE" == "public" ]]; then
|
||||
DEPLOYMENT_MODE="public"
|
||||
log_success "部署模式: 公网模式 (HTTP + 自签证书)"
|
||||
log_success "Deployment mode: public (HTTP + self-signed)"
|
||||
else
|
||||
DEPLOYMENT_MODE="basic"
|
||||
log_success "部署模式: 基础模式 (内网HTTP)"
|
||||
log_success "Deployment mode: basic (intranet HTTP)"
|
||||
fi
|
||||
}
|
||||
|
||||
# 主函数
|
||||
# Main function
|
||||
main() {
|
||||
echo -e "${BLUE}=== PrivyDrop Docker 环境检测 ===${NC}\n"
|
||||
echo -e "${BLUE}=== PrivyDrop Docker Environment Check ===${NC}\n"
|
||||
|
||||
# 读取命令行参数
|
||||
# Parse command-line arguments
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case $1 in
|
||||
--domain)
|
||||
@@ -452,18 +452,18 @@ main() {
|
||||
esac
|
||||
done
|
||||
|
||||
# 执行检测
|
||||
# Run checks
|
||||
detect_network_environment
|
||||
echo ""
|
||||
|
||||
if ! check_system_resources; then
|
||||
log_error "系统资源检查失败,请解决资源问题后重试"
|
||||
log_error "System resource check failed; resolve resource issues and retry"
|
||||
exit 1
|
||||
fi
|
||||
echo ""
|
||||
|
||||
if ! verify_docker_installation; then
|
||||
log_error "Docker环境检查失败,请安装并启动Docker"
|
||||
log_error "Docker environment check failed; please install and start Docker"
|
||||
exit 1
|
||||
fi
|
||||
echo ""
|
||||
@@ -474,14 +474,14 @@ main() {
|
||||
detect_deployment_mode
|
||||
echo ""
|
||||
|
||||
log_success "环境检测完成!"
|
||||
echo -e "${BLUE}检测结果:${NC}"
|
||||
echo " 网络模式: $NETWORK_MODE"
|
||||
echo " 本机IP: $LOCAL_IP"
|
||||
[[ -n "$PUBLIC_IP" ]] && echo " 公网IP: $PUBLIC_IP"
|
||||
echo " 部署模式: $DEPLOYMENT_MODE"
|
||||
log_success "Environment check complete!"
|
||||
echo -e "${BLUE}Results:${NC}"
|
||||
echo " Network mode: $NETWORK_MODE"
|
||||
echo " Local IP: $LOCAL_IP"
|
||||
[[ -n "$PUBLIC_IP" ]] && echo " Public IP: $PUBLIC_IP"
|
||||
echo " Deployment mode: $DEPLOYMENT_MODE"
|
||||
|
||||
# 导出环境变量供其他脚本使用
|
||||
# Export env vars for other scripts
|
||||
export NETWORK_MODE
|
||||
export LOCAL_IP
|
||||
export PUBLIC_IP
|
||||
@@ -492,7 +492,7 @@ main() {
|
||||
return 0
|
||||
}
|
||||
|
||||
# 如果脚本被直接执行
|
||||
# If the script is executed directly
|
||||
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
|
||||
main "$@"
|
||||
fi
|
||||
|
||||
+191
-191
@@ -1,17 +1,17 @@
|
||||
#!/bin/bash
|
||||
|
||||
# 导入环境检测脚本
|
||||
# Import environment detection script
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
source "$SCRIPT_DIR/detect-environment.sh"
|
||||
|
||||
# 颜色定义
|
||||
# Color definitions
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# 日志函数
|
||||
# Logging helpers
|
||||
log_info() {
|
||||
echo -e "${BLUE}ℹ️ $1${NC}"
|
||||
}
|
||||
@@ -28,7 +28,7 @@ log_error() {
|
||||
echo -e "${RED}❌ $1${NC}"
|
||||
}
|
||||
|
||||
# 默认与全局参数
|
||||
# Defaults and global parameters
|
||||
WITH_TURN="${WITH_TURN:-false}"
|
||||
TURN_EXTERNAL_IP_OVERRIDE=""
|
||||
TURN_MIN_PORT_DEFAULT=49152
|
||||
@@ -43,13 +43,13 @@ parse_turn_port_range() {
|
||||
return 0
|
||||
fi
|
||||
if [[ ! "$range" =~ ^([0-9]{2,5})-([0-9]{2,5})$ ]]; then
|
||||
log_error "--turn-port-range 格式应为 MIN-MAX,例如 49152-49252"
|
||||
log_error "--turn-port-range must be MIN-MAX, e.g., 49152-49252"
|
||||
exit 1
|
||||
fi
|
||||
local min="${BASH_REMATCH[1]}"
|
||||
local max="${BASH_REMATCH[2]}"
|
||||
if (( min < 1 || max > 65535 || min >= max )); then
|
||||
log_error "无效端口段:$min-$max,应在 1-65535 且 MIN<MAX"
|
||||
log_error "Invalid port range: $min-$max; must be within 1-65535 and MIN<MAX"
|
||||
exit 1
|
||||
fi
|
||||
TURN_MIN_PORT="$min"
|
||||
@@ -61,101 +61,101 @@ RESET_SSL=false
|
||||
|
||||
cleanup_previous_artifacts() {
|
||||
if [[ "$NO_CLEAN" == "true" ]]; then
|
||||
log_info "跳过清理历史生成物 (--no-clean)"
|
||||
log_info "Skipping cleanup of previous artifacts (--no-clean)"
|
||||
return 0
|
||||
fi
|
||||
log_warning "清理上一次生成的配置产物 (保留 SSL 证书)..."
|
||||
log_warning "Cleaning previous generated artifacts (keeping SSL certificates)..."
|
||||
rm -f .env 2>/dev/null || true
|
||||
rm -f docker/nginx/nginx.conf 2>/dev/null || true
|
||||
rm -f docker/nginx/conf.d/*.conf 2>/dev/null || true
|
||||
rm -f docker/coturn/turnserver.conf 2>/dev/null || true
|
||||
# 默认不清理 docker/ssl,除非显式 --reset-ssl
|
||||
# Do not clean docker/ssl by default unless --reset-ssl is set
|
||||
if [[ "$RESET_SSL" == "true" ]]; then
|
||||
log_warning "按请求重置 SSL 证书目录: docker/ssl/*"
|
||||
log_warning "Resetting SSL directory as requested: docker/ssl/*"
|
||||
rm -f docker/ssl/* 2>/dev/null || true
|
||||
fi
|
||||
}
|
||||
|
||||
# 显示帮助信息
|
||||
# Show help
|
||||
show_help() {
|
||||
cat << 'EOF'
|
||||
PrivyDrop 配置生成脚本(Docker 版)
|
||||
PrivyDrop Config Generator (Docker)
|
||||
|
||||
用法: bash docker/scripts/generate-config.sh [选项]
|
||||
Usage: bash docker/scripts/generate-config.sh [options]
|
||||
|
||||
选项:
|
||||
--mode MODE 生成模式: private|basic|public|full
|
||||
private/basic: 内网HTTP;默认不启用TURN,前端直连后端
|
||||
public: 公网HTTP + 启用TURN(无域名也可,TURN host=公网IP优先)
|
||||
full: 完整HTTPS + 启用TURN(建议配合域名,前端走域名HTTPS)
|
||||
--with-turn 在任意模式下启用TURN(含private/basic)。默认 external-ip=LOCAL_IP
|
||||
--turn-external-ip IP 显式指定TURN external-ip;不指定则使用 PUBLIC_IP,否则回退 LOCAL_IP
|
||||
--turn-port-range R 指定TURN端口段(UDP),格式 MIN-MAX;默认 49152-49252
|
||||
--domain DOMAIN 指定域名(用于 Nginx/证书/TURN realm,如 turn.DOMAIN)
|
||||
--local-ip IP 指定本机局域网IP(不传则自动探测)
|
||||
--enable-sni443 启用 443 SNI 分流(turn.DOMAIN → coturn:5349,其余 → web:8443)
|
||||
--no-sni443 关闭 443 SNI 分流(HTTPS 直接监听 443)
|
||||
--help 显示本帮助
|
||||
--no-clean 跳过清理历史生成物(推荐用于二次生成避免清理 SSL)
|
||||
--reset-ssl 强制清理 docker/ssl/*(默认不清理)
|
||||
--ssl-mode MODE 证书模式:letsencrypt|self-signed|provided
|
||||
- full 模式默认 letsencrypt;private/public 默认 self-signed
|
||||
Options:
|
||||
--mode MODE Generation mode: private|basic|public|full
|
||||
private/basic: Intranet HTTP; TURN disabled by default; frontend talks directly to backend
|
||||
public: Public HTTP + TURN enabled (works without domain; TURN host prefers public IP)
|
||||
full: Full HTTPS + TURN enabled (domain recommended; frontend via domain HTTPS)
|
||||
--with-turn Enable TURN in any mode (including private/basic). Default external-ip=LOCAL_IP
|
||||
--turn-external-ip IP Explicit TURN external-ip; if not set, use PUBLIC_IP, otherwise fallback to LOCAL_IP
|
||||
--turn-port-range R TURN UDP port range, format MIN-MAX; default 49152-49252
|
||||
--domain DOMAIN Domain (for Nginx/certs/TURN realm, e.g., turn.DOMAIN)
|
||||
--local-ip IP Local intranet IP (auto-detected if omitted)
|
||||
--enable-sni443 Enable 443 SNI split (turn.DOMAIN → coturn:5349, others → web:8443)
|
||||
--no-sni443 Disable 443 SNI split (HTTPS listens directly on 443)
|
||||
--help Show this help
|
||||
--no-clean Skip cleaning previous outputs (useful for regeneration without wiping SSL)
|
||||
--reset-ssl Force clean docker/ssl/* (not cleaned by default)
|
||||
--ssl-mode MODE Cert mode: letsencrypt|self-signed|provided
|
||||
- full defaults to letsencrypt; private/public default to self-signed
|
||||
|
||||
环境变量(可选):
|
||||
PUBLIC_IP 显式指定公网IP;仅在 public/full 模式有效。
|
||||
TURN external-ip 写入优先使用 PUBLIC_IP,
|
||||
留空则回退为 LOCAL_IP(仅同局域网可用,穿透受限)。
|
||||
Environment variables (optional):
|
||||
PUBLIC_IP Explicit public IP; only used in public/full.
|
||||
TURN external-ip prefers PUBLIC_IP,
|
||||
fallback to LOCAL_IP (LAN-only; NAT traversal limited).
|
||||
|
||||
生成内容(自动写入关键变量):
|
||||
- .env 核心环境变量(含 NEXT_PUBLIC_API_URL/CORS 等)
|
||||
- docker/nginx/* Nginx 反向代理配置(private/basic 也会生成 HTTP 配置)
|
||||
- docker/ssl/* 自签证书(private/basic/public 生成;full 可替换为正式证书)
|
||||
- docker/coturn/turnserver.conf 在 public/full 或使用 --with-turn 时生成/覆盖
|
||||
Outputs (with key variables set automatically):
|
||||
- .env Core env vars (including NEXT_PUBLIC_API_URL/CORS)
|
||||
- docker/nginx/* Nginx reverse proxy configs (HTTP also generated for private/basic)
|
||||
- docker/ssl/* Self-signed certs (generated for private/basic/public; replace with real certs for full)
|
||||
- docker/coturn/turnserver.conf Generated/overwritten in public/full or when --with-turn is set
|
||||
|
||||
重要说明:
|
||||
- TURN external-ip 赋值逻辑为 external-ip=${PUBLIC_IP:-${LOCAL_IP}}
|
||||
即优先使用 PUBLIC_IP,否则回退 LOCAL_IP。
|
||||
- private/basic 模式不会覆盖 docker/coturn/turnserver.conf,
|
||||
若此前生成过 TURN 配置,该文件可能保留历史 external-ip。
|
||||
Notes:
|
||||
- TURN external-ip is set as external-ip=${PUBLIC_IP:-${LOCAL_IP}}
|
||||
i.e., prefer PUBLIC_IP, otherwise fallback to LOCAL_IP.
|
||||
- private/basic does not overwrite docker/coturn/turnserver.conf;
|
||||
if TURN was generated before, that file may retain a previous external-ip.
|
||||
|
||||
示例:
|
||||
# 1) 纯内网(推荐开发/内网快速跑通)
|
||||
Examples:
|
||||
# 1) Pure intranet (recommended for dev/quick LAN testing)
|
||||
bash docker/scripts/generate-config.sh --mode private [--local-ip 192.168.0.113]
|
||||
|
||||
# 2) 内网 + 启用TURN(默认 external-ip=LOCAL_IP,端口段=49152-49252)
|
||||
# 2) Intranet + TURN (default external-ip=LOCAL_IP, ports=49152-49252)
|
||||
bash docker/scripts/generate-config.sh --mode private --with-turn [--local-ip 192.168.0.113]
|
||||
|
||||
# 3) 内网 + 启用TURN(自定义端口段/显式external-ip)
|
||||
# 3) Intranet + TURN (custom port range / explicit external-ip)
|
||||
bash docker/scripts/generate-config.sh --mode private --with-turn \
|
||||
--turn-port-range 56000-56100 --turn-external-ip 192.168.0.113 \
|
||||
[--local-ip 192.168.0.113]
|
||||
|
||||
# 4) 公网HTTP + TURN(自动探测公网IP,不带域名也可;自动注入 NEXT_PUBLIC_API_URL)
|
||||
# 4) Public HTTP + TURN (auto-detect public IP; inject NEXT_PUBLIC_API_URL)
|
||||
bash docker/scripts/generate-config.sh --mode public --local-ip 192.168.0.113
|
||||
|
||||
# 5) 公网HTTP + TURN(指定公网IP,避免外网探测)
|
||||
# 5) Public HTTP + TURN (explicit public IP; avoid external detection)
|
||||
PUBLIC_IP=1.2.3.4 bash docker/scripts/generate-config.sh --mode public --local-ip 192.168.0.113
|
||||
|
||||
# 6) HTTPS + TURN(有域名)
|
||||
# 6) HTTPS + TURN (with domain)
|
||||
bash docker/scripts/generate-config.sh --mode full --domain example.com --local-ip 192.168.0.113
|
||||
|
||||
内网带TURN测试提示(不改脚本的最小步骤):
|
||||
A) 一步生成(推荐):
|
||||
Intranet with TURN quick tip (minimal changes):
|
||||
A) One-step (recommended):
|
||||
bash docker/scripts/generate-config.sh --mode private --with-turn --local-ip 192.168.0.113
|
||||
然后 bash ./deploy.sh --mode private --with-turn
|
||||
B) 分步生成:
|
||||
先按 private 生成部署前后端,再 docker compose up -d coturn
|
||||
then bash ./deploy.sh --mode private --with-turn
|
||||
B) Step-by-step:
|
||||
Generate private for web/backend first, then docker compose up -d coturn
|
||||
|
||||
EOF
|
||||
}
|
||||
|
||||
# 生成环境变量文件
|
||||
# Generate environment variables file
|
||||
generate_env_file() {
|
||||
log_info "生成环境变量配置..."
|
||||
log_info "Generating environment variable config..."
|
||||
|
||||
local env_file=".env"
|
||||
|
||||
# 读取已有配置以保留用户自定义字段(如代理、TURN)
|
||||
# Read existing config to keep user-defined fields (e.g., proxy, TURN)
|
||||
declare -A existing_env=()
|
||||
if [[ -f "$env_file" ]]; then
|
||||
while IFS= read -r line; do
|
||||
@@ -168,14 +168,14 @@ generate_env_file() {
|
||||
done < "$env_file"
|
||||
fi
|
||||
|
||||
# 生成随机密码(同时保存到全局变量,供后续生成 TURN 配置使用)
|
||||
# Generate a random password (also saved globally for TURN configuration later)
|
||||
local turn_password="${existing_env[TURN_PASSWORD]}"
|
||||
if [[ -z "$turn_password" ]]; then
|
||||
turn_password=$(openssl rand -base64 32 2>/dev/null || echo "privydrop$(date +%s)")
|
||||
fi
|
||||
|
||||
# 计算不同部署模式下的访问入口
|
||||
# 同时支持 localhost 与 本机IP,两者都可用于浏览器访问,便于Docker直连或本机调试
|
||||
# Compute access endpoints for different deployment modes
|
||||
# Support both localhost and host IP for browser access; helpful for Docker direct access or local debugging
|
||||
local cors_origin="http://${LOCAL_IP}:3002,http://localhost:3002"
|
||||
local api_url="http://${LOCAL_IP}:3001"
|
||||
local ssl_mode="self-signed"
|
||||
@@ -188,20 +188,20 @@ generate_env_file() {
|
||||
local next_public_turn_password=""
|
||||
|
||||
if [[ "$DEPLOYMENT_MODE" == "public" ]]; then
|
||||
# 公网无域名:前端直连后端,自动写入基于 PUBLIC_IP(无则回退 LOCAL_IP)
|
||||
# Public without domain: frontend connects directly to backend; use PUBLIC_IP (fallback LOCAL_IP)
|
||||
local effective_public_host="${PUBLIC_IP:-$LOCAL_IP}"
|
||||
cors_origin="http://${effective_public_host}:3002,http://localhost:3002"
|
||||
api_url="http://${effective_public_host}:3001"
|
||||
turn_enabled="true"
|
||||
elif [[ "$DEPLOYMENT_MODE" == "full" ]]; then
|
||||
# 有域名HTTPS:前端与后端都走域名,由 Nginx /api 转发
|
||||
# With domain + HTTPS: both frontend and backend via domain; Nginx proxies /api
|
||||
cors_origin="https://${DOMAIN_NAME:-$LOCAL_IP}"
|
||||
api_url="https://${DOMAIN_NAME:-$LOCAL_IP}"
|
||||
ssl_mode="letsencrypt"
|
||||
turn_enabled="true"
|
||||
fi
|
||||
|
||||
# 若显式启用 TURN,则覆盖模式默认
|
||||
# If TURN explicitly enabled, override mode defaults
|
||||
if [[ "$WITH_TURN" == "true" ]]; then
|
||||
turn_enabled="true"
|
||||
fi
|
||||
@@ -211,7 +211,7 @@ generate_env_file() {
|
||||
turn_host_value="turn.${DOMAIN_NAME}"
|
||||
turn_realm_value="turn.${DOMAIN_NAME}"
|
||||
else
|
||||
# 无域名时:主机优先使用 PUBLIC_IP,其次回退 LOCAL_IP
|
||||
# Without domain: prefer PUBLIC_IP; fallback to LOCAL_IP
|
||||
turn_host_value="${PUBLIC_IP:-$LOCAL_IP}"
|
||||
turn_realm_value="turn.local"
|
||||
fi
|
||||
@@ -221,7 +221,7 @@ generate_env_file() {
|
||||
next_public_turn_password="$turn_password"
|
||||
fi
|
||||
|
||||
# 端口段(默认 49152-49252,可被 --turn-port-range 覆盖)
|
||||
# Port range (default 49152-49252; overridable via --turn-port-range)
|
||||
local turn_min_port_value="${TURN_MIN_PORT:-$TURN_MIN_PORT_DEFAULT}"
|
||||
local turn_max_port_value="${TURN_MAX_PORT:-$TURN_MAX_PORT_DEFAULT}"
|
||||
|
||||
@@ -230,7 +230,7 @@ generate_env_file() {
|
||||
local https_proxy_value="${HTTPS_PROXY:-${existing_env[HTTPS_PROXY]}}"
|
||||
local no_proxy_value="${NO_PROXY:-${existing_env[NO_PROXY]:-$default_no_proxy}}"
|
||||
|
||||
# 将关键 TURN 参数暴露给后续步骤
|
||||
# Expose key TURN parameters to later steps
|
||||
TURN_ENABLED="$turn_enabled"
|
||||
TURN_USERNAME="$turn_username_value"
|
||||
TURN_PASSWORD="$turn_password"
|
||||
@@ -240,13 +240,13 @@ generate_env_file() {
|
||||
TURN_MAX_PORT="$turn_max_port_value"
|
||||
|
||||
cat > "$env_file" << EOF
|
||||
# PrivyDrop Docker 配置文件
|
||||
# 自动生成时间: $(date)
|
||||
# 网络模式: $NETWORK_MODE
|
||||
# 部署模式: $DEPLOYMENT_MODE
|
||||
# PrivyDrop Docker configuration
|
||||
# Generated at: $(date)
|
||||
# Network mode: $NETWORK_MODE
|
||||
# Deployment mode: $DEPLOYMENT_MODE
|
||||
|
||||
# =============================================================================
|
||||
# 网络配置
|
||||
# Network config
|
||||
# =============================================================================
|
||||
CORS_ORIGIN=${cors_origin}
|
||||
NEXT_PUBLIC_API_URL=${api_url}
|
||||
@@ -255,7 +255,7 @@ NEXT_PUBLIC_TURN_USERNAME=${next_public_turn_username}
|
||||
NEXT_PUBLIC_TURN_PASSWORD=${next_public_turn_password}
|
||||
|
||||
# =============================================================================
|
||||
# 端口配置
|
||||
# Port config
|
||||
# =============================================================================
|
||||
FRONTEND_PORT=3002
|
||||
BACKEND_PORT=3001
|
||||
@@ -263,13 +263,13 @@ HTTP_PORT=80
|
||||
HTTPS_PORT=443
|
||||
|
||||
# =============================================================================
|
||||
# Redis配置
|
||||
# Redis config
|
||||
# =============================================================================
|
||||
REDIS_HOST=redis
|
||||
REDIS_PORT=6379
|
||||
|
||||
# =============================================================================
|
||||
# 部署配置
|
||||
# Deployment config
|
||||
# =============================================================================
|
||||
DEPLOYMENT_MODE=${DEPLOYMENT_MODE}
|
||||
NETWORK_MODE=${NETWORK_MODE}
|
||||
@@ -277,13 +277,13 @@ LOCAL_IP=${LOCAL_IP}
|
||||
PUBLIC_IP=${PUBLIC_IP:-}
|
||||
|
||||
# =============================================================================
|
||||
# SSL配置
|
||||
# SSL config
|
||||
# =============================================================================
|
||||
SSL_MODE=${ssl_mode}
|
||||
DOMAIN_NAME=${DOMAIN_NAME:-}
|
||||
|
||||
# =============================================================================
|
||||
# TURN服务器配置 (可选)
|
||||
# TURN server config (optional)
|
||||
# =============================================================================
|
||||
TURN_ENABLED=${turn_enabled}
|
||||
TURN_USERNAME=${turn_username_value}
|
||||
@@ -293,29 +293,29 @@ TURN_MIN_PORT=${turn_min_port_value}
|
||||
TURN_MAX_PORT=${turn_max_port_value}
|
||||
|
||||
# =============================================================================
|
||||
# Nginx配置
|
||||
# Nginx config
|
||||
# =============================================================================
|
||||
NGINX_SERVER_NAME=${DOMAIN_NAME:-${LOCAL_IP}}
|
||||
|
||||
# =============================================================================
|
||||
# 日志配置
|
||||
# Logging config
|
||||
# =============================================================================
|
||||
LOG_LEVEL=info
|
||||
|
||||
# =============================================================================
|
||||
# 代理配置 (可选)
|
||||
# Proxy config (optional)
|
||||
# =============================================================================
|
||||
HTTP_PROXY=${http_proxy_value}
|
||||
HTTPS_PROXY=${https_proxy_value}
|
||||
NO_PROXY=${no_proxy_value}
|
||||
EOF
|
||||
|
||||
log_success "环境变量配置已生成: $env_file"
|
||||
log_success "Environment variable config generated: $env_file"
|
||||
}
|
||||
|
||||
# 生成Nginx配置
|
||||
# Generate Nginx config
|
||||
generate_nginx_config() {
|
||||
log_info "生成Nginx配置..."
|
||||
log_info "Generating Nginx config..."
|
||||
|
||||
mkdir -p docker/nginx/conf.d
|
||||
|
||||
@@ -323,7 +323,7 @@ generate_nginx_config() {
|
||||
local upstream_backend="backend:3001"
|
||||
local upstream_frontend="frontend:3002"
|
||||
|
||||
# 生成主Nginx配置
|
||||
# Generate main Nginx config
|
||||
cat > docker/nginx/nginx.conf << 'EOF'
|
||||
user nginx;
|
||||
worker_processes auto;
|
||||
@@ -340,14 +340,14 @@ http {
|
||||
include /etc/nginx/mime.types;
|
||||
default_type application/octet-stream;
|
||||
|
||||
# 日志格式
|
||||
# Log format
|
||||
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;
|
||||
|
||||
# 基础配置
|
||||
# Basic settings
|
||||
sendfile on;
|
||||
tcp_nopush on;
|
||||
tcp_nodelay on;
|
||||
@@ -355,12 +355,12 @@ http {
|
||||
types_hash_max_size 2048;
|
||||
server_tokens off;
|
||||
|
||||
# 客户端配置
|
||||
# Client settings
|
||||
client_max_body_size 100M;
|
||||
client_header_timeout 60s;
|
||||
client_body_timeout 60s;
|
||||
|
||||
# Gzip配置
|
||||
# Gzip settings
|
||||
gzip on;
|
||||
gzip_vary on;
|
||||
gzip_min_length 1000;
|
||||
@@ -374,15 +374,15 @@ http {
|
||||
application/xml+rss
|
||||
application/json;
|
||||
|
||||
# 包含站点配置
|
||||
# Include site configs
|
||||
include /etc/nginx/conf.d/*.conf;
|
||||
}
|
||||
EOF
|
||||
|
||||
# 生成站点配置
|
||||
# Generate site config
|
||||
mkdir -p docker/letsencrypt-www
|
||||
cat > docker/nginx/conf.d/default.conf << EOF
|
||||
# 上游服务定义
|
||||
# Upstream definitions
|
||||
upstream backend {
|
||||
server ${upstream_backend};
|
||||
keepalive 32;
|
||||
@@ -393,29 +393,29 @@ upstream frontend {
|
||||
keepalive 32;
|
||||
}
|
||||
|
||||
# HTTP服务器配置
|
||||
# HTTP server config
|
||||
server {
|
||||
listen 80;
|
||||
server_name ${server_name};
|
||||
|
||||
# 安全头
|
||||
# Security headers
|
||||
add_header X-Frame-Options DENY;
|
||||
add_header X-Content-Type-Options nosniff;
|
||||
add_header X-XSS-Protection "1; mode=block";
|
||||
|
||||
# ACME 回源,用于 Let's Encrypt 签发/续期
|
||||
# ACME upstream for Let's Encrypt issuance/renewal
|
||||
location /.well-known/acme-challenge/ {
|
||||
root /var/www/certbot;
|
||||
}
|
||||
|
||||
# 健康检查端点
|
||||
# Health check endpoint
|
||||
location /nginx-health {
|
||||
access_log off;
|
||||
return 200 "healthy\n";
|
||||
add_header Content-Type text/plain;
|
||||
}
|
||||
|
||||
# 后端API代理
|
||||
# Backend API proxy
|
||||
location /api/ {
|
||||
proxy_pass http://backend/api/;
|
||||
proxy_http_version 1.1;
|
||||
@@ -427,13 +427,13 @@ server {
|
||||
proxy_set_header X-Forwarded-Proto \$scheme;
|
||||
proxy_cache_bypass \$http_upgrade;
|
||||
|
||||
# 超时配置
|
||||
# Timeout settings
|
||||
proxy_connect_timeout 60s;
|
||||
proxy_send_timeout 60s;
|
||||
proxy_read_timeout 60s;
|
||||
}
|
||||
|
||||
# 后端健康检查代理
|
||||
# Backend health-check proxy
|
||||
location /health {
|
||||
proxy_pass http://backend/health;
|
||||
proxy_http_version 1.1;
|
||||
@@ -443,7 +443,7 @@ server {
|
||||
proxy_set_header X-Forwarded-Proto \$scheme;
|
||||
}
|
||||
|
||||
# Socket.IO代理
|
||||
# Socket.IO proxy
|
||||
location /socket.io/ {
|
||||
proxy_pass http://backend;
|
||||
proxy_http_version 1.1;
|
||||
@@ -454,12 +454,12 @@ server {
|
||||
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto \$scheme;
|
||||
|
||||
# WebSocket特殊配置
|
||||
# WebSocket-specific settings
|
||||
proxy_buffering off;
|
||||
proxy_cache off;
|
||||
}
|
||||
|
||||
# 前端应用代理
|
||||
# Frontend app proxy
|
||||
location / {
|
||||
proxy_pass http://frontend;
|
||||
proxy_http_version 1.1;
|
||||
@@ -471,41 +471,41 @@ server {
|
||||
proxy_set_header X-Forwarded-Proto \$scheme;
|
||||
proxy_cache_bypass \$http_upgrade;
|
||||
|
||||
# Next.js特殊配置
|
||||
# Next.js-specific settings
|
||||
proxy_buffering off;
|
||||
}
|
||||
}
|
||||
EOF
|
||||
|
||||
log_success "Nginx配置已生成"
|
||||
echo " 主配置: docker/nginx/nginx.conf"
|
||||
echo " 站点配置: docker/nginx/conf.d/default.conf"
|
||||
log_success "Nginx config generated"
|
||||
echo " Main config: docker/nginx/nginx.conf"
|
||||
echo " Site config: docker/nginx/conf.d/default.conf"
|
||||
}
|
||||
|
||||
# 生成SSL证书
|
||||
# Generate SSL certificates
|
||||
generate_ssl_certificates() {
|
||||
if [[ "$SSL_MODE" == "self-signed" ]] || [[ "$NETWORK_MODE" == "private" ]]; then
|
||||
log_info "生成自签名SSL证书..."
|
||||
log_info "Generating self-signed SSL certificates..."
|
||||
|
||||
mkdir -p docker/ssl
|
||||
|
||||
# 生成CA私钥
|
||||
# Generate CA private key
|
||||
openssl genrsa -out docker/ssl/ca-key.pem 4096 2>/dev/null
|
||||
|
||||
# 生成CA证书
|
||||
# Generate CA certificate
|
||||
openssl req -new -x509 -days 365 -key docker/ssl/ca-key.pem \
|
||||
-out docker/ssl/ca-cert.pem \
|
||||
-subj "/C=CN/ST=Local/L=Local/O=PrivyDrop/CN=PrivyDrop-CA" 2>/dev/null
|
||||
|
||||
# 生成服务器私钥
|
||||
# Generate server private key
|
||||
openssl genrsa -out docker/ssl/server-key.pem 4096 2>/dev/null
|
||||
|
||||
# 生成服务器证书请求
|
||||
# Generate server CSR
|
||||
openssl req -new -key docker/ssl/server-key.pem \
|
||||
-out docker/ssl/server.csr \
|
||||
-subj "/C=CN/ST=Local/L=Local/O=PrivyDrop/CN=${LOCAL_IP}" 2>/dev/null
|
||||
|
||||
# 创建扩展配置
|
||||
# Create extensions config
|
||||
cat > docker/ssl/server.ext << EOF
|
||||
authorityKeyIdentifier=keyid,issuer
|
||||
basicConstraints=CA:FALSE
|
||||
@@ -520,32 +520,32 @@ IP.1 = ${LOCAL_IP}
|
||||
IP.2 = 127.0.0.1
|
||||
EOF
|
||||
|
||||
# 签名服务器证书
|
||||
# Sign server certificate
|
||||
openssl x509 -req -days 365 -in docker/ssl/server.csr \
|
||||
-CA docker/ssl/ca-cert.pem -CAkey docker/ssl/ca-key.pem \
|
||||
-out docker/ssl/server-cert.pem -CAcreateserial \
|
||||
-extensions v3_req -extfile docker/ssl/server.ext 2>/dev/null
|
||||
|
||||
# 清理临时文件
|
||||
# Clean temporary files
|
||||
rm -f docker/ssl/server.csr docker/ssl/server.ext docker/ssl/ca-cert.srl
|
||||
|
||||
# 设置权限
|
||||
# Set permissions
|
||||
chmod 600 docker/ssl/*-key.pem
|
||||
chmod 644 docker/ssl/*-cert.pem
|
||||
|
||||
log_success "SSL证书已生成: docker/ssl/"
|
||||
log_info "要信任证书,请导入CA证书: docker/ssl/ca-cert.pem"
|
||||
log_success "SSL certificates generated: docker/ssl/"
|
||||
log_info "To trust the cert, import the CA cert: docker/ssl/ca-cert.pem"
|
||||
|
||||
# 自签场景直接生成 443 配置
|
||||
# For self-signed, generate 443 config immediately
|
||||
if [[ "$DEPLOYMENT_MODE" != "basic" ]]; then
|
||||
generate_https_nginx_config
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
# 生成HTTPS Nginx配置
|
||||
# Generate HTTPS Nginx config
|
||||
generate_https_nginx_config() {
|
||||
log_info "生成HTTPS Nginx配置..."
|
||||
log_info "Generating HTTPS Nginx config..."
|
||||
local https_port="443"
|
||||
if [[ "$ENABLE_SNI443" == "true" ]]; then
|
||||
https_port="8443"
|
||||
@@ -553,12 +553,12 @@ generate_https_nginx_config() {
|
||||
|
||||
cat >> docker/nginx/conf.d/default.conf << EOF
|
||||
|
||||
# HTTPS服务器配置
|
||||
# HTTPS server config
|
||||
server {
|
||||
listen ${https_port} ssl http2;
|
||||
server_name ${DOMAIN_NAME:-${LOCAL_IP}};
|
||||
|
||||
# SSL配置
|
||||
# SSL settings
|
||||
ssl_certificate /etc/nginx/ssl/server-cert.pem;
|
||||
ssl_certificate_key /etc/nginx/ssl/server-key.pem;
|
||||
ssl_protocols TLSv1.2 TLSv1.3;
|
||||
@@ -567,20 +567,20 @@ server {
|
||||
ssl_session_cache shared:SSL:10m;
|
||||
ssl_session_timeout 10m;
|
||||
|
||||
# 安全头
|
||||
# Security headers
|
||||
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
|
||||
add_header X-Frame-Options DENY;
|
||||
add_header X-Content-Type-Options nosniff;
|
||||
add_header X-XSS-Protection "1; mode=block";
|
||||
|
||||
# 健康检查端点
|
||||
# Health check endpoint
|
||||
location /nginx-health {
|
||||
access_log off;
|
||||
return 200 "healthy\n";
|
||||
add_header Content-Type text/plain;
|
||||
}
|
||||
|
||||
# 后端API代理
|
||||
# Backend API proxy
|
||||
location /api/ {
|
||||
proxy_pass http://backend/api/;
|
||||
proxy_http_version 1.1;
|
||||
@@ -593,7 +593,7 @@ server {
|
||||
proxy_cache_bypass \$http_upgrade;
|
||||
}
|
||||
|
||||
# 后端健康检查代理
|
||||
# Backend health-check proxy
|
||||
location /health {
|
||||
proxy_pass http://backend/health;
|
||||
proxy_http_version 1.1;
|
||||
@@ -603,7 +603,7 @@ server {
|
||||
proxy_set_header X-Forwarded-Proto https;
|
||||
}
|
||||
|
||||
# Socket.IO代理
|
||||
# Socket.IO proxy
|
||||
location /socket.io/ {
|
||||
proxy_pass http://backend;
|
||||
proxy_http_version 1.1;
|
||||
@@ -617,7 +617,7 @@ server {
|
||||
proxy_cache off;
|
||||
}
|
||||
|
||||
# 前端应用代理
|
||||
# Frontend app proxy
|
||||
location / {
|
||||
proxy_pass http://frontend;
|
||||
proxy_http_version 1.1;
|
||||
@@ -633,24 +633,24 @@ server {
|
||||
}
|
||||
EOF
|
||||
|
||||
log_success "HTTPS配置已添加"
|
||||
log_success "HTTPS config added"
|
||||
}
|
||||
|
||||
# 生成 Nginx stream SNI 分流(443)
|
||||
# Generate Nginx stream SNI split (443)
|
||||
generate_stream_sni443() {
|
||||
if [[ "$ENABLE_SNI443" != "true" ]]; then
|
||||
return 0
|
||||
fi
|
||||
if [[ -z "$DOMAIN_NAME" ]]; then
|
||||
log_warning "SNI 443 需要域名,未指定域名,跳过 stream 配置"
|
||||
log_warning "SNI 443 requires a domain; none specified, skipping stream config"
|
||||
return 0
|
||||
fi
|
||||
# 避免重复追加
|
||||
# Avoid duplicate appends
|
||||
if grep -q "## SNI 443 stream" docker/nginx/nginx.conf 2>/dev/null; then
|
||||
log_info "已存在 SNI 443 stream 配置,跳过追加"
|
||||
log_info "SNI 443 stream config already exists; skipping"
|
||||
return 0
|
||||
fi
|
||||
log_info "追加 SNI 443 stream 配置到 nginx.conf"
|
||||
log_info "Append SNI 443 stream config to nginx.conf"
|
||||
cat >> docker/nginx/nginx.conf << EOF
|
||||
|
||||
## SNI 443 stream
|
||||
@@ -672,34 +672,34 @@ stream {
|
||||
EOF
|
||||
}
|
||||
|
||||
# 当证书存在时再启用 443 配置(适用于 letsencrypt/provided)
|
||||
# Enable 443 only when certs exist (for letsencrypt/provided)
|
||||
enable_https_if_cert_present() {
|
||||
if [[ -f "docker/ssl/server-cert.pem" && -f "docker/ssl/server-key.pem" ]]; then
|
||||
# SNI 开启时,先追加 stream 分流,再生成 8443/443 的 HTTPS
|
||||
# With SNI enabled, append stream split first, then generate HTTPS on 8443/443
|
||||
if [[ "$ENABLE_SNI443" == "true" && -n "$DOMAIN_NAME" ]]; then
|
||||
generate_stream_sni443
|
||||
fi
|
||||
# 若 default.conf 中尚未存在 HTTPS server,则追加(端口根据 SNI 开关决定)
|
||||
# If HTTPS server is not present in default.conf, append it (port depends on SNI flag)
|
||||
local expected="listen 443 ssl"
|
||||
[[ "$ENABLE_SNI443" == "true" ]] && expected="listen 8443 ssl"
|
||||
if ! grep -q "$expected" docker/nginx/conf.d/default.conf 2>/dev/null; then
|
||||
generate_https_nginx_config
|
||||
else
|
||||
log_info "检测到已存在 HTTPS(${ENABLE_SNI443:+SNI=on}) 配置,跳过追加"
|
||||
log_info "Existing HTTPS (${ENABLE_SNI443:+SNI=on}) config detected; skipping"
|
||||
fi
|
||||
else
|
||||
log_warning "未检测到证书 (docker/ssl/server-*.pem),暂不启用 443 配置"
|
||||
log_warning "No certificates detected (docker/ssl/server-*.pem); 443 config not enabled yet"
|
||||
fi
|
||||
}
|
||||
|
||||
# 生成Coturn配置
|
||||
# Generate Coturn config
|
||||
generate_coturn_config() {
|
||||
if [[ "$TURN_ENABLED" == "true" ]]; then
|
||||
log_info "生成Coturn TURN服务器配置..."
|
||||
log_info "Generating Coturn TURN server config..."
|
||||
|
||||
mkdir -p docker/coturn
|
||||
|
||||
# 计算 external-ip:优先 --turn-external-ip,再次 PUBLIC_IP,最后 LOCAL_IP
|
||||
# Compute external-ip: prefer --turn-external-ip, then PUBLIC_IP, then LOCAL_IP
|
||||
local external_ip_value
|
||||
if [[ -n "$TURN_EXTERNAL_IP_OVERRIDE" ]]; then
|
||||
external_ip_value="$TURN_EXTERNAL_IP_OVERRIDE"
|
||||
@@ -713,68 +713,68 @@ generate_coturn_config() {
|
||||
local max_port_value="${TURN_MAX_PORT:-$TURN_MAX_PORT_DEFAULT}"
|
||||
|
||||
cat > docker/coturn/turnserver.conf << EOF
|
||||
# PrivyDrop TURN服务器配置
|
||||
# 自动生成时间: $(date)
|
||||
# PrivyDrop TURN server configuration
|
||||
# Generated at: $(date)
|
||||
|
||||
# 监听端口
|
||||
# Listen ports
|
||||
listening-port=3478
|
||||
tls-listening-port=5349
|
||||
|
||||
# 监听IP
|
||||
# Listen IPs
|
||||
listening-ip=0.0.0.0
|
||||
relay-ip=0.0.0.0
|
||||
|
||||
# 外部IP (用于NAT环境)
|
||||
# External IP (for NAT)
|
||||
external-ip=${external_ip_value}
|
||||
|
||||
# 服务器域名
|
||||
# Server domain
|
||||
realm=${TURN_REALM}
|
||||
server-name=${TURN_REALM}
|
||||
|
||||
# 认证方式
|
||||
# Authentication method
|
||||
lt-cred-mech
|
||||
|
||||
# 用户认证
|
||||
# User authentication
|
||||
user=${TURN_USERNAME}:${TURN_PASSWORD}
|
||||
|
||||
# SSL证书 (如果启用TLS)
|
||||
# SSL certificates (if TLS enabled)
|
||||
cert=/etc/ssl/certs/server-cert.pem
|
||||
pkey=/etc/ssl/certs/server-key.pem
|
||||
|
||||
# 日志配置
|
||||
# Logging configuration
|
||||
no-stdout-log
|
||||
log-file=/var/log/turnserver.log
|
||||
verbose
|
||||
|
||||
# 安全配置
|
||||
# Security settings
|
||||
no-cli
|
||||
no-loopback-peers
|
||||
no-multicast-peers
|
||||
|
||||
# 性能配置
|
||||
# Performance settings
|
||||
min-port=${min_port_value}
|
||||
max-port=${max_port_value}
|
||||
|
||||
# 数据库 (可选)
|
||||
# Database (optional)
|
||||
# userdb=/var/lib/turn/turndb
|
||||
|
||||
# 其他配置
|
||||
# Miscellaneous
|
||||
mobility
|
||||
no-tlsv1
|
||||
no-tlsv1_1
|
||||
EOF
|
||||
|
||||
log_success "Coturn配置已生成: docker/coturn/turnserver.conf"
|
||||
log_info "TURN服务器用户名: ${TURN_USERNAME}"
|
||||
log_warning "TURN服务器密码已保存在.env文件中"
|
||||
log_success "Coturn config generated: docker/coturn/turnserver.conf"
|
||||
log_info "TURN server username: ${TURN_USERNAME}"
|
||||
log_warning "TURN server password saved in .env"
|
||||
fi
|
||||
}
|
||||
|
||||
# 生成Docker忽略文件
|
||||
# Generate Docker ignore files
|
||||
generate_dockerignore() {
|
||||
log_info "生成Docker忽略文件..."
|
||||
log_info "Generating Docker ignore files..."
|
||||
|
||||
# 后端.dockerignore
|
||||
# Backend .dockerignore
|
||||
cat > backend/.dockerignore << EOF
|
||||
node_modules
|
||||
npm-debug.log*
|
||||
@@ -791,7 +791,7 @@ logs
|
||||
*.log
|
||||
EOF
|
||||
|
||||
# 前端.dockerignore
|
||||
# Frontend .dockerignore
|
||||
cat > frontend/.dockerignore << EOF
|
||||
node_modules
|
||||
.next
|
||||
@@ -810,28 +810,28 @@ public/sw.js
|
||||
public/workbox-*.js
|
||||
EOF
|
||||
|
||||
log_success "Docker忽略文件已生成"
|
||||
log_success "Docker ignore files generated"
|
||||
}
|
||||
|
||||
# 创建日志目录
|
||||
# Create log directories
|
||||
create_log_directories() {
|
||||
log_info "创建日志目录..."
|
||||
log_info "Creating log directories..."
|
||||
|
||||
mkdir -p logs/{nginx,backend,frontend,coturn}
|
||||
|
||||
# 设置权限
|
||||
# Set permissions
|
||||
chmod 755 logs
|
||||
chmod 755 logs/*
|
||||
|
||||
log_success "日志目录已创建: logs/"
|
||||
log_success "Log directories created: logs/"
|
||||
}
|
||||
|
||||
# 主函数
|
||||
# Main function
|
||||
main() {
|
||||
echo -e "${BLUE}=== PrivyDrop 配置生成 ===${NC}"
|
||||
echo -e "${BLUE}=== PrivyDrop Config Generation ===${NC}"
|
||||
echo ""
|
||||
|
||||
# 解析参数(与环境检测脚本保持一致)
|
||||
# Parse arguments (consistent with the environment detection script)
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case $1 in
|
||||
--domain)
|
||||
@@ -893,32 +893,32 @@ main() {
|
||||
esac
|
||||
done
|
||||
|
||||
# 先清理上一次生成物(避免历史残留误导)
|
||||
# Clean previous outputs first (avoid stale leftovers)
|
||||
cleanup_previous_artifacts
|
||||
|
||||
# 首先运行环境检测
|
||||
# Run environment detection first
|
||||
if ! detect_network_environment; then
|
||||
log_error "环境检测失败"
|
||||
log_error "Environment detection failed"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! check_system_resources; then
|
||||
log_error "系统资源检查失败"
|
||||
log_error "System resource check failed"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
detect_deployment_mode
|
||||
echo ""
|
||||
|
||||
# 生成所有配置文件
|
||||
# Generate all configuration files
|
||||
generate_env_file
|
||||
echo ""
|
||||
|
||||
generate_nginx_config
|
||||
echo ""
|
||||
|
||||
# 证书生成策略:
|
||||
# - private/public 默认自签;full 默认 letsencrypt(由部署脚本触发签发与复制)
|
||||
# Certificate generation policy:
|
||||
# - private/public use self-signed; full uses letsencrypt (issued/copied by deploy script)
|
||||
if [[ -z "$SSL_MODE" ]]; then
|
||||
if [[ "$DEPLOYMENT_MODE" == "full" ]]; then
|
||||
SSL_MODE="letsencrypt"
|
||||
@@ -927,7 +927,7 @@ main() {
|
||||
fi
|
||||
fi
|
||||
|
||||
# SNI 443 默认启用:full 模式且有域名,除非显式 --no-sni443
|
||||
# SNI on 443 enabled by default: full mode with domain, unless --no-sni443
|
||||
if [[ -z "$ENABLE_SNI443" ]]; then
|
||||
if [[ "$DEPLOYMENT_MODE" == "full" && -n "$DOMAIN_NAME" ]]; then
|
||||
ENABLE_SNI443=true
|
||||
@@ -939,7 +939,7 @@ main() {
|
||||
generate_ssl_certificates
|
||||
echo ""
|
||||
|
||||
# full/provided/letsencrypt:仅在证书就绪时启用 443
|
||||
# full/provided/letsencrypt: enable 443 only when certs are ready
|
||||
if [[ "$DEPLOYMENT_MODE" == "full" ]]; then
|
||||
enable_https_if_cert_present
|
||||
echo ""
|
||||
@@ -954,20 +954,20 @@ main() {
|
||||
create_log_directories
|
||||
echo ""
|
||||
|
||||
log_success "🎉 所有配置文件生成完成!"
|
||||
log_success "🎉 All configuration files generated!"
|
||||
echo ""
|
||||
echo -e "${BLUE}生成的文件:${NC}"
|
||||
echo " .env - 环境变量配置"
|
||||
echo " docker/nginx/ - Nginx配置"
|
||||
echo " docker/ssl/ - SSL证书"
|
||||
[[ "$TURN_ENABLED" == "true" ]] && echo " docker/coturn/ - TURN服务器配置"
|
||||
echo " logs/ - 日志目录"
|
||||
echo -e "${BLUE}Generated files:${NC}"
|
||||
echo " .env - Environment variables"
|
||||
echo " docker/nginx/ - Nginx config"
|
||||
echo " docker/ssl/ - SSL certificates"
|
||||
[[ "$TURN_ENABLED" == "true" ]] && echo " docker/coturn/ - TURN server config"
|
||||
echo " logs/ - Log directories"
|
||||
echo ""
|
||||
echo -e "${BLUE}下一步:${NC}"
|
||||
echo " 运行 './deploy.sh' 开始部署"
|
||||
echo -e "${BLUE}Next steps:${NC}"
|
||||
echo " Run './deploy.sh' to start deployment"
|
||||
}
|
||||
|
||||
# 如果脚本被直接执行
|
||||
# If the script is executed directly
|
||||
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
|
||||
main "$@"
|
||||
fi
|
||||
|
||||
+129
-129
@@ -1,21 +1,21 @@
|
||||
#!/bin/bash
|
||||
|
||||
# PrivyDrop Docker 部署测试脚本
|
||||
# 用于验证部署的完整性和功能
|
||||
# PrivyDrop Docker deployment test script
|
||||
# Validate deployment integrity and functionality
|
||||
|
||||
# 颜色定义
|
||||
# Color definitions
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m'
|
||||
|
||||
# 测试结果统计
|
||||
# Test result counters
|
||||
TESTS_PASSED=0
|
||||
TESTS_FAILED=0
|
||||
TOTAL_TESTS=0
|
||||
|
||||
# 日志函数
|
||||
# Logging helpers
|
||||
log_info() {
|
||||
echo -e "${BLUE}ℹ️ $1${NC}"
|
||||
}
|
||||
@@ -34,13 +34,13 @@ log_warning() {
|
||||
echo -e "${YELLOW}⚠️ $1${NC}"
|
||||
}
|
||||
|
||||
# 测试函数
|
||||
# Test functions
|
||||
run_test() {
|
||||
local test_name="$1"
|
||||
local test_command="$2"
|
||||
|
||||
TOTAL_TESTS=$((TOTAL_TESTS + 1))
|
||||
log_info "测试: $test_name"
|
||||
log_info "Test: $test_name"
|
||||
|
||||
if eval "$test_command" >/dev/null 2>&1; then
|
||||
log_success "$test_name"
|
||||
@@ -51,35 +51,35 @@ run_test() {
|
||||
fi
|
||||
}
|
||||
|
||||
# Docker环境测试
|
||||
# Docker environment tests
|
||||
test_docker_environment() {
|
||||
echo -e "${BLUE}=== Docker环境测试 ===${NC}"
|
||||
echo -e "${BLUE}=== Docker Environment Tests ===${NC}"
|
||||
|
||||
run_test "Docker已安装" "command -v docker"
|
||||
run_test "Docker服务运行中" "docker info"
|
||||
run_test "Docker Compose可用" "docker-compose --version || docker compose version"
|
||||
run_test "Docker installed" "command -v docker"
|
||||
run_test "Docker daemon running" "docker info"
|
||||
run_test "Docker Compose available" "docker-compose --version || docker compose version"
|
||||
|
||||
echo ""
|
||||
}
|
||||
|
||||
# 容器状态测试
|
||||
# Container status tests
|
||||
test_container_status() {
|
||||
echo -e "${BLUE}=== 容器状态测试 ===${NC}"
|
||||
echo -e "${BLUE}=== Container Status Tests ===${NC}"
|
||||
|
||||
# 检查容器是否存在和运行
|
||||
# Check if containers exist and are running
|
||||
local containers=("privydrop-redis" "privydrop-backend" "privydrop-frontend")
|
||||
|
||||
for container in "${containers[@]}"; do
|
||||
run_test "容器 $container 运行中" "docker ps | grep -q $container"
|
||||
run_test "Container $container is running" "docker ps | grep -q $container"
|
||||
done
|
||||
|
||||
# 检查容器健康状态
|
||||
# Check container health
|
||||
for container in "${containers[@]}"; do
|
||||
if docker ps --format "table {{.Names}}\t{{.Status}}" | grep -q "$container.*healthy"; then
|
||||
log_success "容器 $container 健康状态正常"
|
||||
log_success "Container $container health OK"
|
||||
TESTS_PASSED=$((TESTS_PASSED + 1))
|
||||
else
|
||||
log_warning "容器 $container 健康状态未知或不健康"
|
||||
log_warning "Container $container health unknown or unhealthy"
|
||||
fi
|
||||
TOTAL_TESTS=$((TOTAL_TESTS + 1))
|
||||
done
|
||||
@@ -87,78 +87,78 @@ test_container_status() {
|
||||
echo ""
|
||||
}
|
||||
|
||||
# 网络连接测试
|
||||
# Network connectivity tests
|
||||
test_network_connectivity() {
|
||||
echo -e "${BLUE}=== 网络连接测试 ===${NC}"
|
||||
echo -e "${BLUE}=== Network Connectivity Tests ===${NC}"
|
||||
|
||||
# 测试端口连通性
|
||||
local ports=("3002:前端" "3001:后端" "6379:Redis")
|
||||
# Test port connectivity
|
||||
local ports=("3002:Frontend" "3001:Backend" "6379:Redis")
|
||||
|
||||
for port_info in "${ports[@]}"; do
|
||||
local port=$(echo "$port_info" | cut -d':' -f1)
|
||||
local service=$(echo "$port_info" | cut -d':' -f2)
|
||||
|
||||
run_test "$service 端口 $port 可访问" "nc -z localhost $port"
|
||||
run_test "$service port $port reachable" "nc -z localhost $port"
|
||||
done
|
||||
|
||||
# 测试容器间网络
|
||||
run_test "后端可连接Redis" "docker-compose exec -T backend sh -c 'nc -z redis 6379'"
|
||||
run_test "前端可连接后端" "curl -f http://localhost:3001/health"
|
||||
# Test inter-container networking
|
||||
run_test "Backend can connect to Redis" "docker-compose exec -T backend sh -c 'nc -z redis 6379'"
|
||||
run_test "Frontend can reach backend" "curl -f http://localhost:3001/health"
|
||||
|
||||
echo ""
|
||||
}
|
||||
|
||||
# API功能测试
|
||||
# API functionality tests
|
||||
test_api_functionality() {
|
||||
echo -e "${BLUE}=== API功能测试 ===${NC}"
|
||||
echo -e "${BLUE}=== API Functionality Tests ===${NC}"
|
||||
|
||||
# 健康检查API
|
||||
run_test "后端健康检查API" "curl -f http://localhost:3001/health"
|
||||
run_test "前端健康检查API" "curl -f http://localhost:3002/api/health"
|
||||
# Health check APIs
|
||||
run_test "Backend health check API" "curl -f http://localhost:3001/health"
|
||||
run_test "Frontend health check API" "curl -f http://localhost:3002/api/health"
|
||||
|
||||
# 后端详细健康检查
|
||||
# Backend detailed health check
|
||||
if curl -f http://localhost:3001/health/detailed >/dev/null 2>&1; then
|
||||
local redis_status=$(curl -s http://localhost:3001/health/detailed | jq -r '.dependencies.redis.status' 2>/dev/null)
|
||||
if [[ "$redis_status" == "connected" ]]; then
|
||||
log_success "Redis连接状态正常"
|
||||
log_success "Redis connection OK"
|
||||
TESTS_PASSED=$((TESTS_PASSED + 1))
|
||||
else
|
||||
log_error "Redis连接状态异常"
|
||||
log_error "Redis connection issue"
|
||||
TESTS_FAILED=$((TESTS_FAILED + 1))
|
||||
fi
|
||||
else
|
||||
log_error "详细健康检查API不可用"
|
||||
log_error "Detailed health check API unavailable"
|
||||
TESTS_FAILED=$((TESTS_FAILED + 1))
|
||||
fi
|
||||
TOTAL_TESTS=$((TOTAL_TESTS + 1))
|
||||
|
||||
# 业务API测试
|
||||
run_test "获取房间API" "curl -f http://localhost:3001/api/get_room"
|
||||
run_test "创建房间API" "curl -f -X POST -H 'Content-Type: application/json' -d '{\"roomId\":\"test123\"}' http://localhost:3001/api/create_room"
|
||||
# Application API tests
|
||||
run_test "Get room API" "curl -f http://localhost:3001/api/get_room"
|
||||
run_test "Create room API" "curl -f -X POST -H 'Content-Type: application/json' -d '{\"roomId\":\"test123\"}' http://localhost:3001/api/create_room"
|
||||
|
||||
echo ""
|
||||
}
|
||||
|
||||
# WebRTC功能测试
|
||||
# WebRTC functionality tests
|
||||
test_webrtc_functionality() {
|
||||
echo -e "${BLUE}=== WebRTC功能测试 ===${NC}"
|
||||
echo -e "${BLUE}=== WebRTC Functionality Tests ===${NC}"
|
||||
|
||||
# 测试前端页面加载
|
||||
# Test frontend page load
|
||||
if curl -f http://localhost:3002 >/dev/null 2>&1; then
|
||||
log_success "前端页面可访问"
|
||||
log_success "Frontend page reachable"
|
||||
TESTS_PASSED=$((TESTS_PASSED + 1))
|
||||
else
|
||||
log_error "前端页面不可访问"
|
||||
log_error "Frontend page not reachable"
|
||||
TESTS_FAILED=$((TESTS_FAILED + 1))
|
||||
fi
|
||||
TOTAL_TESTS=$((TOTAL_TESTS + 1))
|
||||
|
||||
# 测试Socket.IO连接 (简单测试)
|
||||
# Test Socket.IO connection (basic)
|
||||
if curl -f http://localhost:3001/socket.io/socket.io.js >/dev/null 2>&1; then
|
||||
log_success "Socket.IO客户端脚本可访问"
|
||||
log_success "Socket.IO client script reachable"
|
||||
TESTS_PASSED=$((TESTS_PASSED + 1))
|
||||
else
|
||||
log_error "Socket.IO客户端脚本不可访问"
|
||||
log_error "Socket.IO client script not reachable"
|
||||
TESTS_FAILED=$((TESTS_FAILED + 1))
|
||||
fi
|
||||
TOTAL_TESTS=$((TOTAL_TESTS + 1))
|
||||
@@ -166,64 +166,64 @@ test_webrtc_functionality() {
|
||||
echo ""
|
||||
}
|
||||
|
||||
# 性能测试
|
||||
# Performance tests
|
||||
test_performance() {
|
||||
echo -e "${BLUE}=== 性能测试 ===${NC}"
|
||||
echo -e "${BLUE}=== Performance Tests ===${NC}"
|
||||
|
||||
# 内存使用测试
|
||||
# Memory usage test
|
||||
local backend_memory=$(docker stats --no-stream --format "table {{.Container}}\t{{.MemUsage}}" | grep privydrop-backend | awk '{print $2}' | cut -d'/' -f1)
|
||||
local frontend_memory=$(docker stats --no-stream --format "table {{.Container}}\t{{.MemUsage}}" | grep privydrop-frontend | awk '{print $2}' | cut -d'/' -f1)
|
||||
|
||||
if [[ -n "$backend_memory" ]]; then
|
||||
log_info "后端内存使用: $backend_memory"
|
||||
log_info "Backend memory usage: $backend_memory"
|
||||
fi
|
||||
|
||||
if [[ -n "$frontend_memory" ]]; then
|
||||
log_info "前端内存使用: $frontend_memory"
|
||||
log_info "Frontend memory usage: $frontend_memory"
|
||||
fi
|
||||
|
||||
# 响应时间测试
|
||||
# Response time test
|
||||
local response_time=$(curl -o /dev/null -s -w '%{time_total}' http://localhost:3001/health)
|
||||
if (( $(echo "$response_time < 1.0" | bc -l) )); then
|
||||
log_success "API响应时间正常: ${response_time}s"
|
||||
log_success "API response time OK: ${response_time}s"
|
||||
TESTS_PASSED=$((TESTS_PASSED + 1))
|
||||
else
|
||||
log_warning "API响应时间较慢: ${response_time}s"
|
||||
log_warning "API response time slow: ${response_time}s"
|
||||
fi
|
||||
TOTAL_TESTS=$((TOTAL_TESTS + 1))
|
||||
|
||||
echo ""
|
||||
}
|
||||
|
||||
# 安全测试
|
||||
# Security tests
|
||||
test_security() {
|
||||
echo -e "${BLUE}=== 安全测试 ===${NC}"
|
||||
echo -e "${BLUE}=== Security Tests ===${NC}"
|
||||
|
||||
# 检查容器用户
|
||||
# Check container users
|
||||
local backend_user=$(docker-compose exec -T backend whoami 2>/dev/null || echo "unknown")
|
||||
local frontend_user=$(docker-compose exec -T frontend whoami 2>/dev/null || echo "unknown")
|
||||
|
||||
if [[ "$backend_user" != "root" ]]; then
|
||||
log_success "后端容器使用非root用户: $backend_user"
|
||||
log_success "Backend container uses non-root user: $backend_user"
|
||||
TESTS_PASSED=$((TESTS_PASSED + 1))
|
||||
else
|
||||
log_warning "后端容器使用root用户"
|
||||
log_warning "Backend container runs as root"
|
||||
fi
|
||||
TOTAL_TESTS=$((TOTAL_TESTS + 1))
|
||||
|
||||
if [[ "$frontend_user" != "root" ]]; then
|
||||
log_success "前端容器使用非root用户: $frontend_user"
|
||||
log_success "Frontend container uses non-root user: $frontend_user"
|
||||
TESTS_PASSED=$((TESTS_PASSED + 1))
|
||||
else
|
||||
log_warning "前端容器使用root用户"
|
||||
log_warning "Frontend container runs as root"
|
||||
fi
|
||||
TOTAL_TESTS=$((TOTAL_TESTS + 1))
|
||||
|
||||
# 检查敏感信息泄露
|
||||
# Check for sensitive info leakage
|
||||
if curl -s http://localhost:3001/health/detailed | grep -q "password\|secret\|key" >/dev/null 2>&1; then
|
||||
log_warning "健康检查API可能泄露敏感信息"
|
||||
log_warning "Health check API may leak sensitive info"
|
||||
else
|
||||
log_success "健康检查API未泄露敏感信息"
|
||||
log_success "Health check API does not leak sensitive info"
|
||||
TESTS_PASSED=$((TESTS_PASSED + 1))
|
||||
fi
|
||||
TOTAL_TESTS=$((TOTAL_TESTS + 1))
|
||||
@@ -231,27 +231,27 @@ test_security() {
|
||||
echo ""
|
||||
}
|
||||
|
||||
# 日志测试
|
||||
# Logging tests
|
||||
test_logging() {
|
||||
echo -e "${BLUE}=== 日志测试 ===${NC}"
|
||||
echo -e "${BLUE}=== Logging Tests ===${NC}"
|
||||
|
||||
# 检查日志目录
|
||||
# Check log directories
|
||||
if [[ -d "logs" ]]; then
|
||||
log_success "日志目录存在"
|
||||
log_success "Log directory exists"
|
||||
TESTS_PASSED=$((TESTS_PASSED + 1))
|
||||
else
|
||||
log_warning "日志目录不存在"
|
||||
log_warning "Log directory does not exist"
|
||||
fi
|
||||
TOTAL_TESTS=$((TOTAL_TESTS + 1))
|
||||
|
||||
# 检查日志文件
|
||||
# Check log files
|
||||
local log_files=("logs/backend" "logs/frontend")
|
||||
for log_dir in "${log_files[@]}"; do
|
||||
if [[ -d "$log_dir" ]]; then
|
||||
log_success "日志目录 $log_dir 存在"
|
||||
log_success "Log directory $log_dir exists"
|
||||
TESTS_PASSED=$((TESTS_PASSED + 1))
|
||||
else
|
||||
log_info "日志目录 $log_dir 不存在 (可能正常)"
|
||||
log_info "Log directory $log_dir not found (may be normal)"
|
||||
fi
|
||||
TOTAL_TESTS=$((TOTAL_TESTS + 1))
|
||||
done
|
||||
@@ -259,39 +259,39 @@ test_logging() {
|
||||
echo ""
|
||||
}
|
||||
|
||||
# 配置文件测试
|
||||
# Configuration file tests
|
||||
test_configuration() {
|
||||
echo -e "${BLUE}=== 配置文件测试 ===${NC}"
|
||||
echo -e "${BLUE}=== Configuration File Tests ===${NC}"
|
||||
|
||||
# 检查环境变量文件
|
||||
# Check env file
|
||||
if [[ -f ".env" ]]; then
|
||||
log_success ".env 文件存在"
|
||||
log_success ".env file exists"
|
||||
TESTS_PASSED=$((TESTS_PASSED + 1))
|
||||
|
||||
# 检查关键配置项
|
||||
# Check key configuration entries
|
||||
local required_vars=("LOCAL_IP" "CORS_ORIGIN" "NEXT_PUBLIC_API_URL")
|
||||
for var in "${required_vars[@]}"; do
|
||||
if grep -q "^$var=" .env; then
|
||||
log_success "配置项 $var 已设置"
|
||||
log_success "Config $var is set"
|
||||
TESTS_PASSED=$((TESTS_PASSED + 1))
|
||||
else
|
||||
log_error "配置项 $var 未设置"
|
||||
log_error "Config $var is not set"
|
||||
TESTS_FAILED=$((TESTS_FAILED + 1))
|
||||
fi
|
||||
TOTAL_TESTS=$((TOTAL_TESTS + 1))
|
||||
done
|
||||
else
|
||||
log_error ".env 文件不存在"
|
||||
log_error ".env file not found"
|
||||
TESTS_FAILED=$((TESTS_FAILED + 1))
|
||||
fi
|
||||
TOTAL_TESTS=$((TOTAL_TESTS + 1))
|
||||
|
||||
# 检查Docker Compose文件
|
||||
# Check Docker Compose file
|
||||
if [[ -f "docker-compose.yml" ]]; then
|
||||
log_success "docker-compose.yml 文件存在"
|
||||
log_success "docker-compose.yml exists"
|
||||
TESTS_PASSED=$((TESTS_PASSED + 1))
|
||||
else
|
||||
log_error "docker-compose.yml 文件不存在"
|
||||
log_error "docker-compose.yml not found"
|
||||
TESTS_FAILED=$((TESTS_FAILED + 1))
|
||||
fi
|
||||
TOTAL_TESTS=$((TOTAL_TESTS + 1))
|
||||
@@ -299,25 +299,25 @@ test_configuration() {
|
||||
echo ""
|
||||
}
|
||||
|
||||
# 清理测试
|
||||
# Cleanup tests
|
||||
test_cleanup() {
|
||||
echo -e "${BLUE}=== 清理功能测试 ===${NC}"
|
||||
echo -e "${BLUE}=== Cleanup Tests ===${NC}"
|
||||
|
||||
# 测试清理命令是否可用
|
||||
# Verify cleanup commands work
|
||||
if [[ -f "deploy.sh" ]]; then
|
||||
log_success "部署脚本存在"
|
||||
log_success "Deployment script exists"
|
||||
TESTS_PASSED=$((TESTS_PASSED + 1))
|
||||
|
||||
# 测试帮助命令
|
||||
# Test help command
|
||||
if bash deploy.sh --help >/dev/null 2>&1; then
|
||||
log_success "部署脚本帮助功能正常"
|
||||
log_success "Deployment script help works"
|
||||
TESTS_PASSED=$((TESTS_PASSED + 1))
|
||||
else
|
||||
log_error "部署脚本帮助功能异常"
|
||||
log_error "Deployment script help fails"
|
||||
TESTS_FAILED=$((TESTS_FAILED + 1))
|
||||
fi
|
||||
else
|
||||
log_error "部署脚本不存在"
|
||||
log_error "Deployment script not found"
|
||||
TESTS_FAILED=$((TESTS_FAILED + 1))
|
||||
fi
|
||||
TOTAL_TESTS=$((TOTAL_TESTS + 2))
|
||||
@@ -325,65 +325,65 @@ test_cleanup() {
|
||||
echo ""
|
||||
}
|
||||
|
||||
# 生成测试报告
|
||||
# Generate test report
|
||||
generate_report() {
|
||||
echo -e "${BLUE}=== 测试报告 ===${NC}"
|
||||
echo -e "${BLUE}=== Test Report ===${NC}"
|
||||
echo ""
|
||||
|
||||
echo "📊 测试统计:"
|
||||
echo " 总测试数: $TOTAL_TESTS"
|
||||
echo -e " 通过: ${GREEN}$TESTS_PASSED${NC}"
|
||||
echo -e " 失败: ${RED}$TESTS_FAILED${NC}"
|
||||
echo "📊 Test stats:"
|
||||
echo " Total tests: $TOTAL_TESTS"
|
||||
echo -e " Passed: ${GREEN}$TESTS_PASSED${NC}"
|
||||
echo -e " Failed: ${RED}$TESTS_FAILED${NC}"
|
||||
|
||||
local success_rate=$((TESTS_PASSED * 100 / TOTAL_TESTS))
|
||||
echo " 成功率: $success_rate%"
|
||||
echo " Success rate: $success_rate%"
|
||||
|
||||
echo ""
|
||||
echo "📋 系统信息:"
|
||||
echo " Docker版本: $(docker --version)"
|
||||
echo " Docker Compose版本: $(docker-compose --version 2>/dev/null || docker compose version 2>/dev/null || echo '未知')"
|
||||
echo " 操作系统: $(uname -s) $(uname -r)"
|
||||
echo " 测试时间: $(date)"
|
||||
echo "📋 System info:"
|
||||
echo " Docker version: $(docker --version)"
|
||||
echo " Docker Compose version: $(docker-compose --version 2>/dev/null || docker compose version 2>/dev/null || echo 'unknown')"
|
||||
echo " OS: $(uname -s) $(uname -r)"
|
||||
echo " Test time: $(date)"
|
||||
|
||||
echo ""
|
||||
if [[ $TESTS_FAILED -eq 0 ]]; then
|
||||
echo -e "${GREEN}🎉 所有测试通过!PrivyDrop 部署成功!${NC}"
|
||||
echo -e "${GREEN}🎉 All tests passed! PrivyDrop deployment successful!${NC}"
|
||||
echo ""
|
||||
echo "🔗 访问链接:"
|
||||
echo " 前端应用: http://localhost:3002"
|
||||
echo " 后端API: http://localhost:3001"
|
||||
echo "🔗 Access links:"
|
||||
echo " Frontend: http://localhost:3002"
|
||||
echo " Backend API: http://localhost:3001"
|
||||
|
||||
# 显示局域网访问地址
|
||||
# Show LAN access addresses
|
||||
if [[ -f ".env" ]]; then
|
||||
local local_ip=$(grep "LOCAL_IP=" .env | cut -d'=' -f2)
|
||||
if [[ -n "$local_ip" && "$local_ip" != "127.0.0.1" ]]; then
|
||||
echo ""
|
||||
echo "🌐 局域网访问:"
|
||||
echo " 前端应用: http://$local_ip:3002"
|
||||
echo " 后端API: http://$local_ip:3001"
|
||||
echo "🌐 LAN access:"
|
||||
echo " Frontend: http://$local_ip:3002"
|
||||
echo " Backend API: http://$local_ip:3001"
|
||||
fi
|
||||
fi
|
||||
|
||||
return 0
|
||||
else
|
||||
echo -e "${RED}❌ 有 $TESTS_FAILED 个测试失败${NC}"
|
||||
echo -e "${RED}❌ $TESTS_FAILED test(s) failed${NC}"
|
||||
echo ""
|
||||
echo "🔧 故障排除建议:"
|
||||
echo " 1. 查看容器状态: docker-compose ps"
|
||||
echo " 2. 查看容器日志: docker-compose logs -f"
|
||||
echo " 3. 重新部署: bash deploy.sh"
|
||||
echo " 4. 完全清理后重新部署: bash deploy.sh --clean"
|
||||
echo "🔧 Troubleshooting tips:"
|
||||
echo " 1. View container status: docker-compose ps"
|
||||
echo " 2. View container logs: docker-compose logs -f"
|
||||
echo " 3. Redeploy: bash deploy.sh"
|
||||
echo " 4. Clean and redeploy: bash deploy.sh --clean"
|
||||
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# 主函数
|
||||
# Main function
|
||||
main() {
|
||||
echo -e "${BLUE}=== PrivyDrop Docker 部署测试开始 ===${NC}"
|
||||
echo -e "${BLUE}=== PrivyDrop Docker Deployment Tests ===${NC}"
|
||||
echo ""
|
||||
|
||||
# 检查必要工具
|
||||
# Check required tools
|
||||
local missing_tools=()
|
||||
for tool in curl jq bc nc; do
|
||||
if ! command -v "$tool" >/dev/null 2>&1; then
|
||||
@@ -392,12 +392,12 @@ main() {
|
||||
done
|
||||
|
||||
if [[ ${#missing_tools[@]} -gt 0 ]]; then
|
||||
log_warning "缺少测试工具: ${missing_tools[*]}"
|
||||
log_info "建议安装: sudo apt-get install curl jq bc netcat"
|
||||
log_warning "Missing test tools: ${missing_tools[*]}"
|
||||
log_info "Suggested install: sudo apt-get install curl jq bc netcat"
|
||||
echo ""
|
||||
fi
|
||||
|
||||
# 运行所有测试
|
||||
# Run all tests
|
||||
test_docker_environment
|
||||
test_container_status
|
||||
test_network_connectivity
|
||||
@@ -409,12 +409,12 @@ main() {
|
||||
test_configuration
|
||||
test_cleanup
|
||||
|
||||
# 生成报告
|
||||
# Generate report
|
||||
generate_report
|
||||
}
|
||||
|
||||
# 捕获中断信号
|
||||
trap 'echo -e "\n${YELLOW}测试被中断${NC}"; exit 1' INT TERM
|
||||
# Trap interrupt signals
|
||||
trap 'echo -e "\n${YELLOW}Tests interrupted${NC}"; exit 1' INT TERM
|
||||
|
||||
# 运行主函数
|
||||
main "$@"
|
||||
# Run main function
|
||||
main "$@"
|
||||
|
||||
+18
-18
@@ -1,4 +1,4 @@
|
||||
# 多阶段构建 - 构建阶段
|
||||
# Multi-stage build — build stage
|
||||
FROM node:18-alpine AS builder
|
||||
|
||||
ARG HTTP_PROXY
|
||||
@@ -10,54 +10,54 @@ ENV http_proxy ${HTTP_PROXY} \
|
||||
no_proxy ${NO_PROXY}
|
||||
|
||||
WORKDIR /app
|
||||
# 复制package文件
|
||||
# Copy package files
|
||||
COPY package*.json ./
|
||||
COPY pnpm-lock.yaml ./
|
||||
|
||||
# 安装pnpm(使用npm,避免网络问题)
|
||||
# Install pnpm
|
||||
RUN npm install -g pnpm --no-audit --no-fund
|
||||
|
||||
# 安装依赖
|
||||
# Install dependencies
|
||||
RUN pnpm install --frozen-lockfile
|
||||
|
||||
# 复制源代码
|
||||
# Copy source code
|
||||
COPY . .
|
||||
|
||||
# 在依赖安装之后再声明与使用构建期公开变量,避免仅 API/TURN 改动导致依赖层缓存失效
|
||||
# Declare and use build-time public vars after deps installation to avoid cache invalidation when only API/TURN change
|
||||
ARG NEXT_PUBLIC_API_URL
|
||||
ARG NEXT_PUBLIC_TURN_HOST
|
||||
ARG NEXT_PUBLIC_TURN_USERNAME
|
||||
ARG NEXT_PUBLIC_TURN_PASSWORD
|
||||
|
||||
# 前端构建期注入可公开环境变量(用于客户端直连后端与 TURN)
|
||||
# Inject public env vars during frontend build (for client direct access to backend and TURN)
|
||||
ENV NEXT_PUBLIC_API_URL=${NEXT_PUBLIC_API_URL}
|
||||
ENV NEXT_PUBLIC_TURN_HOST=${NEXT_PUBLIC_TURN_HOST}
|
||||
ENV NEXT_PUBLIC_TURN_USERNAME=${NEXT_PUBLIC_TURN_USERNAME}
|
||||
ENV NEXT_PUBLIC_TURN_PASSWORD=${NEXT_PUBLIC_TURN_PASSWORD}
|
||||
|
||||
# 设置环境变量
|
||||
# Set environment variables
|
||||
ENV NEXT_TELEMETRY_DISABLED 1
|
||||
ENV NODE_ENV production
|
||||
|
||||
# 构建应用
|
||||
# Build the app
|
||||
RUN pnpm build
|
||||
|
||||
# 生产阶段
|
||||
# Production stage
|
||||
FROM node:18-alpine AS runner
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# 创建非root用户
|
||||
# Create a non-root user
|
||||
RUN addgroup -g 1001 -S nodejs && \
|
||||
adduser -S nextjs -u 1001 -G nodejs
|
||||
|
||||
# 复制构建产物
|
||||
# Copy build artifacts
|
||||
COPY --from=builder /app/public ./public
|
||||
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
|
||||
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
|
||||
COPY health-check.js ./
|
||||
|
||||
# 设置环境变量
|
||||
# Set environment variables
|
||||
ENV NODE_ENV production
|
||||
ENV NEXT_TELEMETRY_DISABLED 1
|
||||
ENV PORT 3002
|
||||
@@ -65,18 +65,18 @@ ENV HOSTNAME "0.0.0.0"
|
||||
|
||||
USER nextjs
|
||||
|
||||
# 暴露端口
|
||||
# Expose ports
|
||||
EXPOSE 3002
|
||||
|
||||
# 使用Node.js脚本做健康检查(替代curl)
|
||||
# Use a Node.js script for health checks (instead of curl)
|
||||
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
|
||||
CMD node health-check.js
|
||||
|
||||
# 启动应用
|
||||
# Start the app
|
||||
CMD ["node", "server.js"]
|
||||
|
||||
# 运行期保留公开变量(非必需,但便于服务端渲染读取)
|
||||
# 需在当前阶段重新声明 ARG,以便在此阶段展开到 ENV
|
||||
# Keep public env vars at runtime (optional; helps SSR read them)
|
||||
# Re-declare ARGs in this stage so they can expand into ENV
|
||||
ARG NEXT_PUBLIC_API_URL
|
||||
ARG NEXT_PUBLIC_TURN_HOST
|
||||
ARG NEXT_PUBLIC_TURN_USERNAME
|
||||
|
||||
@@ -49,7 +49,7 @@ const ClipboardApp = () => {
|
||||
handleDownloadFile,
|
||||
} = useFileTransferHandler({ messages, putMessageInMs });
|
||||
|
||||
// 简化的 WebRTC 连接初始化
|
||||
// Simplified WebRTC connection initialization
|
||||
const {
|
||||
requestFile,
|
||||
requestFolder,
|
||||
@@ -60,7 +60,7 @@ const ClipboardApp = () => {
|
||||
putMessageInMs,
|
||||
});
|
||||
|
||||
// 大大简化的房间管理 - 不再需要传递任何 WebRTC 依赖
|
||||
// Greatly simplified room management - No longer need to pass any WebRTC dependencies
|
||||
const {
|
||||
processRoomIdInput,
|
||||
joinRoom,
|
||||
|
||||
@@ -1,41 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# PrivyDrop Docker 部署快速测试脚本
|
||||
# 这是对 docker/scripts/test-deployment.sh 的简化版本
|
||||
|
||||
echo "🧪 运行 PrivyDrop Docker 部署测试..."
|
||||
echo ""
|
||||
|
||||
# 检查是否存在详细测试脚本
|
||||
if [[ -f "docker/scripts/test-deployment.sh" ]]; then
|
||||
echo "📋 运行详细测试..."
|
||||
bash docker/scripts/test-deployment.sh
|
||||
else
|
||||
echo "⚠️ 详细测试脚本不存在,运行基础测试..."
|
||||
|
||||
# 基础测试
|
||||
echo "🔍 检查容器状态..."
|
||||
docker-compose ps
|
||||
|
||||
echo ""
|
||||
echo "🏥 检查健康状态..."
|
||||
|
||||
# 检查后端健康
|
||||
if curl -f http://localhost:3001/health >/dev/null 2>&1; then
|
||||
echo "✅ 后端服务正常"
|
||||
else
|
||||
echo "❌ 后端服务异常"
|
||||
fi
|
||||
|
||||
# 检查前端健康
|
||||
if curl -f http://localhost:3002/api/health >/dev/null 2>&1; then
|
||||
echo "✅ 前端服务正常"
|
||||
else
|
||||
echo "❌ 前端服务异常"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "🔗 访问链接:"
|
||||
echo " 前端应用: http://localhost:3002"
|
||||
echo " 后端API: http://localhost:3001"
|
||||
fi
|
||||
+60
-60
@@ -1,18 +1,18 @@
|
||||
#!/bin/bash
|
||||
|
||||
# 颜色定义
|
||||
# Color definitions
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# 测试结果统计
|
||||
# Test result counters
|
||||
TESTS_PASSED=0
|
||||
TESTS_FAILED=0
|
||||
TOTAL_TESTS=0
|
||||
|
||||
# 日志函数
|
||||
# Logging helpers
|
||||
log_info() {
|
||||
echo -e "${BLUE}ℹ️ $1${NC}"
|
||||
}
|
||||
@@ -31,7 +31,7 @@ log_warning() {
|
||||
echo -e "${YELLOW}⚠️ $1${NC}"
|
||||
}
|
||||
|
||||
# 测试函数
|
||||
# Test functions
|
||||
test_api() {
|
||||
local url="$1"
|
||||
local description="$2"
|
||||
@@ -39,84 +39,84 @@ test_api() {
|
||||
|
||||
TOTAL_TESTS=$((TOTAL_TESTS + 1))
|
||||
echo ""
|
||||
log_info "测试: $description"
|
||||
log_info "Test: $description"
|
||||
log_info "URL: $url"
|
||||
|
||||
# 发送请求并获取响应
|
||||
# Send request and capture response
|
||||
response=$(curl -s -w "\n%{http_code}" "$url" 2>/dev/null)
|
||||
|
||||
if [ $? -ne 0 ]; then
|
||||
log_error "请求失败 - 无法连接到服务"
|
||||
log_error "Request failed - unable to connect to service"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# 分离响应体和状态码
|
||||
# Split response body and status code
|
||||
http_code=$(echo "$response" | tail -n1)
|
||||
response_body=$(echo "$response" | head -n -1)
|
||||
|
||||
# 检查HTTP状态码
|
||||
# Check HTTP status code
|
||||
if [ "$http_code" -eq "$expected_status" ]; then
|
||||
log_success "HTTP状态码正确: $http_code"
|
||||
log_success "HTTP status code OK: $http_code"
|
||||
else
|
||||
log_error "HTTP状态码错误: 期望 $expected_status, 实际 $http_code"
|
||||
log_error "HTTP status code mismatch: expected $expected_status, got $http_code"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# 检查JSON格式
|
||||
# Validate JSON format
|
||||
if echo "$response_body" | jq . >/dev/null 2>&1; then
|
||||
log_success "响应格式为有效JSON"
|
||||
log_success "Response is valid JSON"
|
||||
|
||||
# 显示格式化的JSON响应
|
||||
echo -e "${BLUE}响应内容:${NC}"
|
||||
# Pretty-print JSON response
|
||||
echo -e "${BLUE}Response body:${NC}"
|
||||
echo "$response_body" | jq .
|
||||
|
||||
# 检查必要字段
|
||||
# Verify required fields
|
||||
status=$(echo "$response_body" | jq -r '.status // empty')
|
||||
service=$(echo "$response_body" | jq -r '.service // empty')
|
||||
timestamp=$(echo "$response_body" | jq -r '.timestamp // empty')
|
||||
|
||||
if [ -n "$status" ] && [ -n "$service" ] && [ -n "$timestamp" ]; then
|
||||
log_success "包含必要字段: status, service, timestamp"
|
||||
log_success "Contains required fields: status, service, timestamp"
|
||||
else
|
||||
log_error "缺少必要字段"
|
||||
log_error "Missing required fields"
|
||||
return 1
|
||||
fi
|
||||
|
||||
else
|
||||
log_error "响应不是有效的JSON格式"
|
||||
echo "响应内容: $response_body"
|
||||
log_error "Response is not valid JSON"
|
||||
echo "Response body: $response_body"
|
||||
return 1
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
# 检查服务是否运行
|
||||
# Check if service is running
|
||||
check_service() {
|
||||
local port="$1"
|
||||
local service_name="$2"
|
||||
|
||||
if nc -z localhost "$port" 2>/dev/null; then
|
||||
log_success "$service_name 服务运行中 (端口 $port)"
|
||||
log_success "$service_name is running (port $port)"
|
||||
return 0
|
||||
else
|
||||
log_error "$service_name 服务未运行 (端口 $port)"
|
||||
log_error "$service_name is not running (port $port)"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# 等待服务启动
|
||||
# Wait for service to start
|
||||
wait_for_service() {
|
||||
local port="$1"
|
||||
local service_name="$2"
|
||||
local max_attempts=30
|
||||
local attempt=0
|
||||
|
||||
log_info "等待 $service_name 服务启动..."
|
||||
log_info "Waiting for $service_name to start..."
|
||||
|
||||
while [ $attempt -lt $max_attempts ]; do
|
||||
if nc -z localhost "$port" 2>/dev/null; then
|
||||
log_success "$service_name 服务已启动"
|
||||
log_success "$service_name started"
|
||||
return 0
|
||||
fi
|
||||
|
||||
@@ -125,94 +125,94 @@ wait_for_service() {
|
||||
sleep 2
|
||||
done
|
||||
|
||||
log_error "$service_name 服务启动超时"
|
||||
log_error "$service_name startup timed out"
|
||||
return 1
|
||||
}
|
||||
|
||||
# 主测试函数
|
||||
# Main test function
|
||||
main() {
|
||||
echo -e "${BLUE}=== PrivyDrop 健康检查API测试 ===${NC}"
|
||||
echo -e "${BLUE}=== PrivyDrop Health Check API Tests ===${NC}"
|
||||
echo ""
|
||||
|
||||
# 检查必要工具
|
||||
# Check required tools
|
||||
if ! command -v curl &> /dev/null; then
|
||||
log_error "curl 未安装,请先安装 curl"
|
||||
log_error "curl is not installed; please install curl"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! command -v jq &> /dev/null; then
|
||||
log_error "jq 未安装,请先安装 jq 用于JSON解析"
|
||||
log_error "jq is not installed; please install jq for JSON parsing"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! command -v nc &> /dev/null; then
|
||||
log_error "netcat 未安装,请先安装 nc 用于端口检查"
|
||||
log_error "netcat is not installed; please install nc for port checks"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 检查服务状态
|
||||
echo -e "${BLUE}=== 检查服务状态 ===${NC}"
|
||||
# Check service status
|
||||
echo -e "${BLUE}=== Check Service Status ===${NC}"
|
||||
backend_running=false
|
||||
frontend_running=false
|
||||
|
||||
if check_service 3001 "后端"; then
|
||||
if check_service 3001 "Backend"; then
|
||||
backend_running=true
|
||||
fi
|
||||
|
||||
if check_service 3002 "前端"; then
|
||||
if check_service 3002 "Frontend"; then
|
||||
frontend_running=true
|
||||
fi
|
||||
|
||||
# 如果服务未运行,提供启动提示
|
||||
# Show startup hints if services are not running
|
||||
if [ "$backend_running" = false ]; then
|
||||
echo ""
|
||||
log_warning "后端服务未运行,请先启动后端服务:"
|
||||
log_warning "Backend is not running; please start it:"
|
||||
echo " cd backend && npm run dev"
|
||||
echo ""
|
||||
fi
|
||||
|
||||
if [ "$frontend_running" = false ]; then
|
||||
echo ""
|
||||
log_warning "前端服务未运行,请先启动前端服务:"
|
||||
log_warning "Frontend is not running; please start it:"
|
||||
echo " cd frontend && pnpm dev"
|
||||
echo ""
|
||||
fi
|
||||
|
||||
# 测试后端健康检查API
|
||||
# Test backend health check APIs
|
||||
if [ "$backend_running" = true ]; then
|
||||
echo -e "${BLUE}=== 测试后端健康检查API ===${NC}"
|
||||
echo -e "${BLUE}=== Test Backend Health Check APIs ===${NC}"
|
||||
|
||||
test_api "http://localhost:3001/health" "后端基础健康检查"
|
||||
test_api "http://localhost:3001/api/health" "后端API路径健康检查"
|
||||
test_api "http://localhost:3001/health/detailed" "后端详细健康检查"
|
||||
test_api "http://localhost:3001/health" "Backend basic health check"
|
||||
test_api "http://localhost:3001/api/health" "Backend API path health check"
|
||||
test_api "http://localhost:3001/health/detailed" "Backend detailed health check"
|
||||
fi
|
||||
|
||||
# 测试前端健康检查API
|
||||
# Test frontend health check APIs
|
||||
if [ "$frontend_running" = true ]; then
|
||||
echo -e "${BLUE}=== 测试前端健康检查API ===${NC}"
|
||||
echo -e "${BLUE}=== Test Frontend Health Check APIs ===${NC}"
|
||||
|
||||
test_api "http://localhost:3002/api/health" "前端基础健康检查"
|
||||
test_api "http://localhost:3002/api/health/detailed" "前端详细健康检查"
|
||||
test_api "http://localhost:3002/api/health" "Frontend basic health check"
|
||||
test_api "http://localhost:3002/api/health/detailed" "Frontend detailed health check"
|
||||
fi
|
||||
|
||||
# 测试结果汇总
|
||||
# Test results summary
|
||||
echo ""
|
||||
echo -e "${BLUE}=== 测试结果汇总 ===${NC}"
|
||||
echo "总测试数: $TOTAL_TESTS"
|
||||
echo -e "通过: ${GREEN}$TESTS_PASSED${NC}"
|
||||
echo -e "失败: ${RED}$TESTS_FAILED${NC}"
|
||||
echo -e "${BLUE}=== Test Results Summary ===${NC}"
|
||||
echo "Total tests: $TOTAL_TESTS"
|
||||
echo -e "Passed: ${GREEN}$TESTS_PASSED${NC}"
|
||||
echo -e "Failed: ${RED}$TESTS_FAILED${NC}"
|
||||
|
||||
if [ $TESTS_FAILED -eq 0 ]; then
|
||||
echo -e "${GREEN}🎉 所有测试通过!${NC}"
|
||||
echo -e "${GREEN}🎉 All tests passed!${NC}"
|
||||
exit 0
|
||||
else
|
||||
echo -e "${RED}❌ 有 $TESTS_FAILED 个测试失败${NC}"
|
||||
echo -e "${RED}❌ $TESTS_FAILED test(s) failed${NC}"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# 捕获中断信号
|
||||
trap 'echo -e "\n${YELLOW}测试被中断${NC}"; exit 1' INT TERM
|
||||
# Trap interrupt signals
|
||||
trap 'echo -e "\n${YELLOW}Tests interrupted${NC}"; exit 1' INT TERM
|
||||
|
||||
# 运行主函数
|
||||
main "$@"
|
||||
# Run main function
|
||||
main "$@"
|
||||
|
||||
Reference in New Issue
Block a user