7809373f88
- docker/scripts/generate-config.sh
- Add --enable-sni443/--no-sni443 flags; default enable in full+domain.
- Generate Nginx stream{} with ssl_preread SNI routing: turn.<domain> -> coturn:5349; others -> web:8443.
- When SNI is enabled, serve HTTPS on 8443 (http layer); otherwise keep 443.
- deploy.sh:
- Add --with-sni443 and propagate to config generation and LE provisioning.
- No compose changes required; 8443 remains internal.
- Notes:
- Backward compatible. SNI is auto-enabled for full+domain, can be toggled with flags.
- Leverages existing LE automation and TURN cert reuse.
668 lines
22 KiB
Bash
668 lines
22 KiB
Bash
#!/bin/bash
|
||
|
||
set -e # 遇到错误立即退出
|
||
|
||
# 颜色定义
|
||
RED='\033[0;31m'
|
||
GREEN='\033[0;32m'
|
||
YELLOW='\033[1;33m'
|
||
BLUE='\033[0;34m'
|
||
NC='\033[0m'
|
||
|
||
# 脚本目录
|
||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||
DOCKER_SCRIPTS_DIR="$SCRIPT_DIR/docker/scripts"
|
||
|
||
# 日志函数
|
||
log_info() {
|
||
echo -e "${BLUE}ℹ️ $1${NC}"
|
||
}
|
||
|
||
log_success() {
|
||
echo -e "${GREEN}✅ $1${NC}"
|
||
}
|
||
|
||
log_warning() {
|
||
echo -e "${YELLOW}⚠️ $1${NC}"
|
||
}
|
||
|
||
log_error() {
|
||
echo -e "${RED}❌ $1${NC}"
|
||
}
|
||
|
||
# 显示帮助信息
|
||
show_help() {
|
||
cat << EOF
|
||
PrivyDrop Docker 一键部署脚本
|
||
|
||
用法: $0 [选项]
|
||
|
||
选项:
|
||
--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 显示帮助信息
|
||
|
||
示例:
|
||
$0 # 基础部署
|
||
$0 --mode public --with-turn # 公网部署 + TURN服务器
|
||
$0 --domain example.com --mode full # 完整HTTPS部署
|
||
$0 --clean # 清理部署
|
||
|
||
要求:
|
||
- Docker Engine 和 Docker Compose V2(命令为 `docker compose`)
|
||
|
||
EOF
|
||
}
|
||
|
||
# 解析命令行参数
|
||
parse_arguments() {
|
||
DOMAIN_NAME=""
|
||
DEPLOYMENT_MODE=""
|
||
WITH_NGINX=false
|
||
WITH_TURN=false
|
||
CLEAN_MODE=false
|
||
LE_EMAIL=""
|
||
WITH_SNI443=false
|
||
|
||
while [[ $# -gt 0 ]]; do
|
||
case $1 in
|
||
--domain)
|
||
DOMAIN_NAME="$2"
|
||
shift 2
|
||
;;
|
||
--mode)
|
||
DEPLOYMENT_MODE="$2"
|
||
shift 2
|
||
;;
|
||
--with-nginx)
|
||
WITH_NGINX=true
|
||
shift
|
||
;;
|
||
--with-turn)
|
||
WITH_TURN=true
|
||
shift
|
||
;;
|
||
--with-sni443)
|
||
WITH_SNI443=true
|
||
shift
|
||
;;
|
||
--le-email)
|
||
LE_EMAIL="$2"
|
||
shift 2
|
||
;;
|
||
--clean)
|
||
CLEAN_MODE=true
|
||
shift
|
||
;;
|
||
--help)
|
||
show_help
|
||
exit 0
|
||
;;
|
||
*)
|
||
log_error "未知参数: $1"
|
||
show_help
|
||
exit 1
|
||
;;
|
||
esac
|
||
done
|
||
|
||
# 导出变量供其他脚本使用
|
||
export DOMAIN_NAME
|
||
export DEPLOYMENT_MODE
|
||
export WITH_NGINX
|
||
export WITH_TURN
|
||
}
|
||
|
||
# 检查依赖
|
||
check_dependencies() {
|
||
log_info "检查依赖..."
|
||
|
||
local missing_deps=()
|
||
|
||
if ! command -v docker &> /dev/null; then
|
||
missing_deps+=("docker")
|
||
fi
|
||
|
||
if ! docker compose version &> /dev/null; then
|
||
missing_deps+=("docker compose (V2)")
|
||
fi
|
||
|
||
if ! command -v curl &> /dev/null; then
|
||
missing_deps+=("curl")
|
||
fi
|
||
|
||
if ! command -v openssl &> /dev/null; then
|
||
missing_deps+=("openssl")
|
||
fi
|
||
|
||
if [[ ${#missing_deps[@]} -gt 0 ]]; then
|
||
log_error "缺少依赖: ${missing_deps[*]}"
|
||
echo ""
|
||
echo "请安装缺少的依赖:"
|
||
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/"
|
||
;;
|
||
curl)
|
||
echo " curl: sudo apt-get install curl (Ubuntu/Debian)"
|
||
;;
|
||
openssl)
|
||
echo " openssl: sudo apt-get install openssl (Ubuntu/Debian)"
|
||
;;
|
||
esac
|
||
done
|
||
exit 1
|
||
fi
|
||
|
||
log_success "依赖检查通过"
|
||
}
|
||
|
||
# 安装并准备 Let's Encrypt(certbot)
|
||
ensure_certbot() {
|
||
if command -v certbot >/dev/null 2>&1; then
|
||
return 0
|
||
fi
|
||
log_info "安装 certbot (需要 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 或在支持的系统上运行"
|
||
exit 1
|
||
fi
|
||
}
|
||
|
||
# 写入 certbot 部署钩子:续期后复制证书并热重载服务
|
||
install_certbot_deploy_hook() {
|
||
local repo_dir="$SCRIPT_DIR"
|
||
local hook_dir="/etc/letsencrypt/renewal-hooks/deploy"
|
||
local hook_file="$hook_dir/privydrop-reload.sh"
|
||
local compose_file="$repo_dir/docker-compose.yml"
|
||
|
||
sudo mkdir -p "$hook_dir"
|
||
sudo bash -c "cat > '$hook_file'" << EOF
|
||
#!/bin/bash
|
||
set -e
|
||
REPO_DIR="$repo_dir"
|
||
COMPOSE_FILE="$compose_file"
|
||
|
||
# RENEWED_LINEAGE 由 certbot 传入,指向 live/<domain>
|
||
if [[ -z "\$RENEWED_LINEAGE" ]]; then
|
||
exit 0
|
||
fi
|
||
|
||
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,如失败则重启
|
||
docker compose -f "\$COMPOSE_FILE" exec -T nginx nginx -s reload 2>/dev/null || \
|
||
docker compose -f "\$COMPOSE_FILE" restart nginx || true
|
||
|
||
# 优先向 coturn 发送 HUP,不行则重启(若未启用则忽略)
|
||
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 定时器
|
||
if command -v systemctl >/dev/null 2>&1; then
|
||
sudo systemctl enable --now certbot.timer 2>/dev/null || true
|
||
fi
|
||
}
|
||
|
||
# 使用 webroot 首次签发并启用 443 配置
|
||
provision_letsencrypt_cert() {
|
||
# 仅在 full 模式且启用 nginx 且存在域名时执行
|
||
if [[ "$DEPLOYMENT_MODE" != "full" || "$WITH_NGINX" != "true" ]]; then
|
||
return 0
|
||
fi
|
||
if [[ -z "$DOMAIN_NAME" ]]; then
|
||
log_warning "full 模式未指定 --domain,跳过 Let’s Encrypt"
|
||
return 0
|
||
fi
|
||
if [[ -z "$LE_EMAIL" ]]; then
|
||
log_warning "未指定 --le-email,将使用 --register-unsafely-without-email"
|
||
fi
|
||
|
||
ensure_certbot
|
||
install_certbot_deploy_hook
|
||
|
||
mkdir -p docker/letsencrypt-www docker/ssl
|
||
|
||
# 如果证书已存在(含 -0001 谱系),跳过签发
|
||
if [[ -f "/etc/letsencrypt/live/$DOMAIN_NAME/fullchain.pem" ]] || ls -1d /etc/letsencrypt/live/${DOMAIN_NAME}* >/dev/null 2>&1; then
|
||
log_info "检测到已存在的证书/谱系,跳过首次签发"
|
||
else
|
||
log_info "通过 webroot 模式签发 Let's Encrypt 证书..."
|
||
local email_args="--email $LE_EMAIL"
|
||
if [[ -z "$LE_EMAIL" ]]; then
|
||
email_args="--register-unsafely-without-email"
|
||
fi
|
||
# 需要 80 端口可达且 nginx 已启动
|
||
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 输出"
|
||
return 1
|
||
}
|
||
fi
|
||
|
||
# 解析谱系目录(兼容 -0001/-0002 后缀)并复制到 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}*"
|
||
return 1
|
||
fi
|
||
|
||
sudo cp "$lineage_dir/fullchain.pem" docker/ssl/server-cert.pem
|
||
sudo cp "$lineage_dir/privkey.pem" docker/ssl/server-key.pem
|
||
sudo chmod 600 docker/ssl/server-key.pem || true
|
||
|
||
# 启用 443 配置(证书已就绪):不清理,仅追加;传递 SNI 开关(默认 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
|
||
docker compose exec -T nginx nginx -s reload || docker compose restart nginx
|
||
}
|
||
|
||
# 清理现有部署
|
||
clean_deployment() {
|
||
if [[ "$CLEAN_MODE" == "true" ]]; then
|
||
log_warning "清理现有部署..."
|
||
|
||
# 停止并删除容器
|
||
if [[ -f "docker-compose.yml" ]]; then
|
||
docker compose down -v --remove-orphans 2>/dev/null || true
|
||
fi
|
||
# 优雅停止后兜底强制清理命名容器
|
||
docker stop -t 10 privydrop-nginx privydrop-coturn 2>/dev/null || true
|
||
docker rm -f privydrop-nginx privydrop-coturn 2>/dev/null || true
|
||
# 兜底清理项目网络(若存在)
|
||
docker network rm privydrop_privydrop-network 2>/dev/null || true
|
||
|
||
# 删除镜像
|
||
docker images | grep privydrop | awk '{print $3}' | xargs -r docker rmi -f 2>/dev/null || true
|
||
|
||
# 清理配置文件
|
||
rm -rf docker/nginx/conf.d/*.conf docker/ssl/* logs/* .env 2>/dev/null || true
|
||
|
||
log_success "清理完成"
|
||
|
||
if [[ $# -eq 1 ]]; then # 如果只有--clean参数
|
||
exit 0
|
||
fi
|
||
fi
|
||
}
|
||
|
||
# 确保 TURN 服务按需启动(当指定 --with-turn 时)
|
||
ensure_turn_running() {
|
||
if [[ "$WITH_TURN" != "true" ]]; then
|
||
return 0
|
||
fi
|
||
# 未运行则显式启用 profile 启动 coturn
|
||
if ! docker compose ps | grep -q "privydrop-coturn"; then
|
||
log_info "启动 TURN 服务 (profile: turn)..."
|
||
docker compose --profile turn up -d coturn || true
|
||
fi
|
||
}
|
||
|
||
# 环境检测和配置生成
|
||
setup_environment() {
|
||
log_info "设置环境..."
|
||
|
||
# 确保脚本可执行
|
||
chmod +x "$DOCKER_SCRIPTS_DIR"/*.sh 2>/dev/null || true
|
||
|
||
# 运行环境检测
|
||
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 "环境检测失败"
|
||
exit 1
|
||
fi
|
||
|
||
# 生成配置文件
|
||
if ! bash "$DOCKER_SCRIPTS_DIR/generate-config.sh" $detect_args; then
|
||
log_error "配置生成失败"
|
||
exit 1
|
||
fi
|
||
|
||
log_success "环境设置完成"
|
||
}
|
||
|
||
# 构建和启动服务
|
||
deploy_services() {
|
||
log_info "构建和启动服务..."
|
||
|
||
# 确保日志目录存在并放宽权限,避免容器无法写日志(coturn/nginx 等)
|
||
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)"
|
||
|
||
# 停止现有服务
|
||
if docker compose ps | grep -q "Up"; then
|
||
log_info "停止现有服务..."
|
||
docker compose down
|
||
fi
|
||
|
||
# 确定启用的服务(Compose V2 需要将 --profile 放在子命令之前)
|
||
local profiles=""
|
||
if [[ "$WITH_NGINX" == "true" ]]; then
|
||
profiles="$profiles --profile nginx"
|
||
fi
|
||
if [[ "$WITH_TURN" == "true" ]]; then
|
||
profiles="$profiles --profile turn"
|
||
fi
|
||
|
||
# 构建镜像(先并行,失败则串行回退)
|
||
log_info "构建Docker镜像..."
|
||
set +e
|
||
docker compose build --parallel
|
||
local build_status=$?
|
||
set -e
|
||
if [[ $build_status -ne 0 ]]; then
|
||
log_warning "并行构建失败,回退为串行构建..."
|
||
docker compose build
|
||
fi
|
||
|
||
# 启动服务(--profile 需置于 up 之前)
|
||
log_info "启动服务..."
|
||
# shellcheck disable=SC2086
|
||
docker compose $profiles up -d
|
||
|
||
log_success "服务启动完成"
|
||
}
|
||
|
||
# 等待服务就绪
|
||
wait_for_services() {
|
||
log_info "等待服务就绪..."
|
||
|
||
local max_attempts=60
|
||
local attempt=0
|
||
local services_ready=false
|
||
|
||
while [[ $attempt -lt $max_attempts ]]; do
|
||
local backend_ready=false
|
||
local frontend_ready=false
|
||
|
||
# 检查后端健康状态
|
||
if curl -f http://localhost:3001/health &> /dev/null; then
|
||
backend_ready=true
|
||
fi
|
||
|
||
# 检查前端健康状态
|
||
if curl -f http://localhost:3002/api/health &> /dev/null; then
|
||
frontend_ready=true
|
||
fi
|
||
|
||
if [[ "$backend_ready" == "true" ]] && [[ "$frontend_ready" == "true" ]]; then
|
||
services_ready=true
|
||
break
|
||
fi
|
||
|
||
attempt=$((attempt + 1))
|
||
echo -n "."
|
||
sleep 2
|
||
done
|
||
|
||
echo ""
|
||
|
||
if [[ "$services_ready" == "true" ]]; then
|
||
log_success "所有服务已就绪"
|
||
return 0
|
||
else
|
||
log_error "服务启动超时"
|
||
log_info "查看服务状态: docker compose ps"
|
||
log_info "查看服务日志: docker compose logs -f"
|
||
return 1
|
||
fi
|
||
}
|
||
|
||
# 运行部署后检查
|
||
post_deployment_checks() {
|
||
log_info "运行部署后检查..."
|
||
|
||
# 检查容器状态
|
||
log_info "检查容器状态..."
|
||
docker compose ps
|
||
|
||
# full+nginx 场景追加 HTTPS 健康检查(若定义了域名)
|
||
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"
|
||
if curl -fsS "https://$dname/api/health" >/dev/null; then
|
||
log_success "HTTPS 健康检查通过"
|
||
else
|
||
log_warning "HTTPS 健康检查失败。若证书刚签发,请稍等或执行: bash docker/scripts/generate-config.sh --mode full --domain $dname --no-clean && docker compose exec -T nginx nginx -s reload"
|
||
fi
|
||
fi
|
||
fi
|
||
|
||
# 运行健康检查测试
|
||
if [[ -f "test-health-apis.sh" ]]; then
|
||
log_info "运行健康检查测试..."
|
||
if bash test-health-apis.sh; then
|
||
log_success "健康检查测试通过"
|
||
else
|
||
log_warning "健康检查测试失败,但服务可能仍然正常"
|
||
fi
|
||
fi
|
||
|
||
log_success "部署后检查完成"
|
||
}
|
||
|
||
# 显示部署结果
|
||
show_deployment_info() {
|
||
echo ""
|
||
echo -e "${GREEN}🎉 PrivyDrop 部署完成!${NC}"
|
||
echo ""
|
||
|
||
# 读取配置信息
|
||
local local_ip=""
|
||
local public_ip=""
|
||
local frontend_port=""
|
||
local backend_port=""
|
||
local deployment_mode=""
|
||
local network_mode=""
|
||
local domain_name=""
|
||
local turn_enabled_env=""
|
||
|
||
if [[ -f ".env" ]]; then
|
||
local_ip=$(grep "LOCAL_IP=" .env | cut -d'=' -f2)
|
||
public_ip=$(grep "PUBLIC_IP=" .env | cut -d'=' -f2)
|
||
frontend_port=$(grep "FRONTEND_PORT=" .env | cut -d'=' -f2)
|
||
backend_port=$(grep "BACKEND_PORT=" .env | cut -d'=' -f2)
|
||
deployment_mode=$(grep "DEPLOYMENT_MODE=" .env | cut -d'=' -f2)
|
||
network_mode=$(grep "NETWORK_MODE=" .env | cut -d'=' -f2)
|
||
domain_name=$(grep "DOMAIN_NAME=" .env | cut -d'=' -f2)
|
||
turn_enabled_env=$(grep "TURN_ENABLED=" .env | cut -d'=' -f2)
|
||
fi
|
||
|
||
echo -e "${BLUE}📋 访问信息:${NC}"
|
||
|
||
# 判定是否公网场景(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
|
||
if [[ -n "$domain_name" ]]; then
|
||
if [[ "$WITH_NGINX" == "true" || "$deployment_mode" == "full" ]]; then
|
||
echo " 公网访问: https://$domain_name"
|
||
echo " API 地址: https://$domain_name"
|
||
else
|
||
echo " 公网访问: 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}"
|
||
else
|
||
# 回退:无法获取公网IP时给出局域网与本机
|
||
echo " 前端应用: http://localhost:${frontend_port:-3002}"
|
||
echo " 后端API: http://localhost:${backend_port:-3001}"
|
||
fi
|
||
else
|
||
# 内网/基础模式:本机+局域网
|
||
echo " 前端应用: http://localhost:${frontend_port:-3002}"
|
||
echo " 后端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}"
|
||
fi
|
||
fi
|
||
|
||
if [[ "$WITH_NGINX" == "true" ]]; then
|
||
echo ""
|
||
echo -e "${BLUE}🔀 Nginx代理:${NC}"
|
||
if [[ -n "$domain_name" ]]; then
|
||
echo " HTTP: http://$domain_name"
|
||
[[ -f "docker/ssl/server-cert.pem" ]] && echo " HTTPS: https://$domain_name"
|
||
elif [[ -n "$public_ip" ]]; then
|
||
echo " HTTP: http://$public_ip"
|
||
[[ -f "docker/ssl/server-cert.pem" ]] && echo " HTTPS: https://$public_ip"
|
||
else
|
||
echo " HTTP: http://localhost"
|
||
[[ -f "docker/ssl/server-cert.pem" ]] && echo " HTTPS: https://localhost"
|
||
fi
|
||
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"
|
||
|
||
if [[ -f "docker/ssl/ca-cert.pem" ]]; then
|
||
echo ""
|
||
echo -e "${BLUE}🔒 SSL证书:${NC}"
|
||
echo " CA证书: docker/ssl/ca-cert.pem"
|
||
echo " 要信任HTTPS连接,请将CA证书导入浏览器"
|
||
fi
|
||
|
||
if [[ "$WITH_TURN" == "true" || "$turn_enabled_env" == "true" ]]; then
|
||
local turn_username=""
|
||
local turn_realm=""
|
||
if [[ -f ".env" ]]; then
|
||
turn_username=$(grep "TURN_USERNAME=" .env | cut -d'=' -f2)
|
||
turn_realm=$(grep "TURN_REALM=" .env | cut -d'=' -f2)
|
||
fi
|
||
|
||
echo ""
|
||
echo -e "${BLUE}🔄 TURN服务器:${NC}"
|
||
# 展示优先域名的 TURN 信息,否则展示公网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 分流)"
|
||
elif [[ -n "$public_ip" ]]; then
|
||
echo " STUN: stun:${public_ip}:3478"
|
||
echo " TURN: turn:${public_ip}:3478"
|
||
else
|
||
echo " STUN: stun:${local_ip}:3478"
|
||
echo " TURN: turn:${local_ip}:3478"
|
||
fi
|
||
echo " 用户名: ${turn_username:-privydrop}"
|
||
echo " 密码: (保存在.env文件中)"
|
||
fi
|
||
|
||
echo ""
|
||
echo -e "${YELLOW}💡 提示:${NC}"
|
||
echo " - 首次启动可能需要几分钟来下载和构建镜像"
|
||
echo " - 如遇问题,请查看日志: docker compose logs -f"
|
||
echo " - 更多帮助: $0 --help"
|
||
echo ""
|
||
|
||
# 公网场景追加:如何测试域名(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 ""
|
||
fi
|
||
}
|
||
|
||
# 主函数
|
||
main() {
|
||
echo -e "${BLUE}=== PrivyDrop Docker 一键部署 ===${NC}"
|
||
echo ""
|
||
|
||
# 解析命令行参数
|
||
parse_arguments "$@"
|
||
|
||
# 检查依赖
|
||
check_dependencies
|
||
echo ""
|
||
|
||
# 清理模式
|
||
clean_deployment
|
||
# 若仅执行清理(未指定其它参数),直接退出,避免进入环境检测
|
||
if [[ "$CLEAN_MODE" == "true" && -z "$DEPLOYMENT_MODE" && "$WITH_NGINX" == "false" && "$WITH_TURN" == "false" && -z "$DOMAIN_NAME" ]]; then
|
||
log_success "清理完成(仅清理模式),已退出。"
|
||
exit 0
|
||
fi
|
||
|
||
# 环境设置
|
||
setup_environment
|
||
echo ""
|
||
|
||
# 部署服务
|
||
deploy_services
|
||
echo ""
|
||
|
||
# 若为 full + nginx,自动签发证书并启用 443
|
||
provision_letsencrypt_cert || true
|
||
# 确保 TURN 启动(当请求了 --with-turn 时)
|
||
ensure_turn_running || true
|
||
|
||
# 等待服务就绪
|
||
if wait_for_services; then
|
||
echo ""
|
||
post_deployment_checks
|
||
show_deployment_info
|
||
else
|
||
log_error "部署失败,请检查日志: docker compose logs"
|
||
exit 1
|
||
fi
|
||
}
|
||
|
||
# 捕获中断信号
|
||
trap 'log_warning "部署被中断"; exit 1' INT TERM
|
||
|
||
# 运行主函数
|
||
main "$@"
|