chore:Initial addition of Docker related content

This commit is contained in:
david_bai
2025-09-11 06:44:43 +08:00
parent 6f8f4f65bb
commit 158433bb0b
19 changed files with 3653 additions and 3 deletions
+269
View File
@@ -0,0 +1,269 @@
#!/bin/bash
# 颜色定义
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# 全局变量
NETWORK_MODE=""
LOCAL_IP=""
PUBLIC_IP=""
DEPLOYMENT_MODE="basic"
# 日志函数
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}"
}
# 检测网络环境
detect_network_environment() {
log_info "检测网络环境..."
# 获取本机IP
LOCAL_IP=$(ip route get 1.1.1.1 2>/dev/null | awk '{print $7}' | head -1)
if [[ -z "$LOCAL_IP" ]]; then
LOCAL_IP=$(hostname -I | awk '{print $1}')
fi
if [[ -z "$LOCAL_IP" ]]; then
LOCAL_IP="127.0.0.1"
log_warning "无法自动检测本机IP,使用默认值: $LOCAL_IP"
fi
# 检测公网连接
if curl -s --connect-timeout 5 --max-time 10 ifconfig.me > /dev/null 2>&1; then
PUBLIC_IP=$(curl -s --connect-timeout 5 --max-time 10 ifconfig.me 2>/dev/null || echo "")
if [[ -n "$PUBLIC_IP" ]]; then
NETWORK_MODE="public"
log_success "检测到公网环境"
echo " 本机IP: $LOCAL_IP"
echo " 公网IP: $PUBLIC_IP"
else
NETWORK_MODE="private"
log_warning "公网连接不稳定,按内网环境处理"
echo " 本机IP: $LOCAL_IP"
fi
else
NETWORK_MODE="private"
log_success "检测到内网环境"
echo " 本机IP: $LOCAL_IP"
fi
}
# 检查系统资源
check_system_resources() {
log_info "检查系统资源..."
local warnings=0
# 检查内存
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)"
return 1
elif [[ $TOTAL_MEM -lt 1024 ]]; then
log_warning "内存较少: ${TOTAL_MEM}MB (建议至少1GB)"
warnings=$((warnings + 1))
else
log_success "内存充足: ${TOTAL_MEM}MB"
fi
else
log_warning "无法检测内存使用情况"
warnings=$((warnings + 1))
fi
# 检查磁盘空间
DISK_USAGE=$(df -h / | awk 'NR==2{print $5}' | sed 's/%//')
if [[ $DISK_USAGE -gt 95 ]]; then
log_error "磁盘空间不足: ${DISK_USAGE}%已使用"
return 1
elif [[ $DISK_USAGE -gt 80 ]]; then
log_warning "磁盘空间紧张: ${DISK_USAGE}%已使用"
warnings=$((warnings + 1))
else
log_success "磁盘空间充足: ${DISK_USAGE}%已使用"
fi
# 检查可用磁盘空间
AVAILABLE_SPACE=$(df -BG / | awk 'NR==2{print $4}' | sed 's/G//')
if [[ $AVAILABLE_SPACE -lt 2 ]]; then
log_error "可用磁盘空间不足: ${AVAILABLE_SPACE}GB (建议至少2GB)"
return 1
fi
if [[ $warnings -gt 0 ]]; then
log_warning "系统资源检查通过,但有 $warnings 个警告"
else
log_success "系统资源检查通过"
fi
return 0
}
# 验证Docker环境
verify_docker_installation() {
log_info "检查Docker环境..."
if ! command -v docker &> /dev/null; then
log_error "Docker未安装"
echo "请安装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/"
return 1
fi
# 检查Docker服务状态
if ! docker info &> /dev/null; then
log_error "Docker服务未运行"
echo "请启动Docker服务"
return 1
fi
# 检查Docker版本
DOCKER_VERSION=$(docker --version | grep -oE '[0-9]+\.[0-9]+' | head -1)
log_success "Docker版本: $DOCKER_VERSION"
# 检查Docker Compose版本
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"
else
COMPOSE_VERSION=$(docker compose version --short 2>/dev/null || echo "内置")
log_success "Docker Compose版本: $COMPOSE_VERSION"
fi
return 0
}
# 检查端口占用
check_port_availability() {
local ports="$1"
log_info "检查端口占用..."
local occupied_ports=()
IFS=',' read -ra PORT_ARRAY <<< "$ports"
for port in "${PORT_ARRAY[@]}"; do
port=$(echo "$port" | xargs) # 去除空格
if command -v ss >/dev/null 2>&1; then
if ss -tuln | grep -q ":$port "; then
occupied_ports+=("$port")
fi
elif command -v netstat >/dev/null 2>&1; then
if netstat -tuln 2>/dev/null | grep -q ":$port "; then
occupied_ports+=("$port")
fi
else
log_warning "无法检查端口占用情况 (缺少ss和netstat命令)"
return 0
fi
done
if [[ ${#occupied_ports[@]} -gt 0 ]]; then
log_warning "以下端口已被占用: ${occupied_ports[*]}"
log_info "可以通过修改.env文件中的端口配置来解决冲突"
else
log_success "所有端口都可用"
fi
}
# 检测部署模式
detect_deployment_mode() {
log_info "确定部署模式..."
if [[ "$NETWORK_MODE" == "public" ]] && [[ -n "$DOMAIN_NAME" ]]; then
DEPLOYMENT_MODE="full"
log_success "部署模式: 完整模式 (HTTPS + TURN服务器)"
elif [[ "$NETWORK_MODE" == "public" ]]; then
DEPLOYMENT_MODE="public"
log_success "部署模式: 公网模式 (HTTP + 自签证书)"
else
DEPLOYMENT_MODE="basic"
log_success "部署模式: 基础模式 (内网HTTP)"
fi
}
# 主函数
main() {
echo -e "${BLUE}=== PrivyDrop Docker 环境检测 ===${NC}\n"
# 读取命令行参数
while [[ $# -gt 0 ]]; do
case $1 in
--domain)
DOMAIN_NAME="$2"
shift 2
;;
--mode)
DEPLOYMENT_MODE="$2"
shift 2
;;
*)
shift
;;
esac
done
# 执行检测
detect_network_environment
echo ""
if ! check_system_resources; then
log_error "系统资源检查失败,请解决资源问题后重试"
exit 1
fi
echo ""
if ! verify_docker_installation; then
log_error "Docker环境检查失败,请安装并启动Docker"
exit 1
fi
echo ""
check_port_availability "80,443,3000,3001,3478,5349,6379"
echo ""
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"
# 导出环境变量供其他脚本使用
export NETWORK_MODE
export LOCAL_IP
export PUBLIC_IP
export DEPLOYMENT_MODE
export DOMAIN_NAME
return 0
}
# 如果脚本被直接执行
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
main "$@"
fi
+600
View File
@@ -0,0 +1,600 @@
#!/bin/bash
# 导入环境检测脚本
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "$SCRIPT_DIR/detect-environment.sh"
# 颜色定义
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# 日志函数
log_info() {
echo -e "${BLUE}$1${NC}"
}
log_success() {
echo -e "${GREEN}$1${NC}"
}
log_warning() {
echo -e "${YELLOW}⚠️ $1${NC}"
}
log_error() {
echo -e "${RED}$1${NC}"
}
# 生成环境变量文件
generate_env_file() {
log_info "生成环境变量配置..."
local env_file=".env"
# 生成随机密码
local turn_password=$(openssl rand -base64 32 2>/dev/null || echo "privydrop$(date +%s)")
cat > "$env_file" << EOF
# PrivyDrop Docker 配置文件
# 自动生成时间: $(date)
# 网络模式: $NETWORK_MODE
# 部署模式: $DEPLOYMENT_MODE
# =============================================================================
# 网络配置
# =============================================================================
CORS_ORIGIN=http://${LOCAL_IP}
NEXT_PUBLIC_API_URL=http://${LOCAL_IP}:3001
# =============================================================================
# 端口配置
# =============================================================================
FRONTEND_PORT=3000
BACKEND_PORT=3001
HTTP_PORT=80
HTTPS_PORT=443
# =============================================================================
# Redis配置
# =============================================================================
REDIS_HOST=redis
REDIS_PORT=6379
# =============================================================================
# 部署配置
# =============================================================================
DEPLOYMENT_MODE=${DEPLOYMENT_MODE}
NETWORK_MODE=${NETWORK_MODE}
LOCAL_IP=${LOCAL_IP}
PUBLIC_IP=${PUBLIC_IP:-}
# =============================================================================
# SSL配置
# =============================================================================
SSL_MODE=self-signed
DOMAIN_NAME=${DOMAIN_NAME:-}
# =============================================================================
# TURN服务器配置 (可选)
# =============================================================================
TURN_ENABLED=${TURN_ENABLED:-false}
TURN_USERNAME=privydrop
TURN_PASSWORD=${turn_password}
TURN_REALM=${DOMAIN_NAME:-turn.local}
# =============================================================================
# Nginx配置
# =============================================================================
NGINX_SERVER_NAME=${DOMAIN_NAME:-${LOCAL_IP}}
# =============================================================================
# 日志配置
# =============================================================================
LOG_LEVEL=info
EOF
# 根据部署模式调整配置
if [[ "$DEPLOYMENT_MODE" == "full" ]]; then
sed -i "s|CORS_ORIGIN=http://|CORS_ORIGIN=https://|g" "$env_file"
sed -i "s|NEXT_PUBLIC_API_URL=http://|NEXT_PUBLIC_API_URL=https://|g" "$env_file"
sed -i "s|SSL_MODE=self-signed|SSL_MODE=letsencrypt|g" "$env_file"
sed -i "s|TURN_ENABLED=false|TURN_ENABLED=true|g" "$env_file"
elif [[ "$DEPLOYMENT_MODE" == "public" ]]; then
sed -i "s|TURN_ENABLED=false|TURN_ENABLED=true|g" "$env_file"
fi
log_success "环境变量配置已生成: $env_file"
}
# 生成Nginx配置
generate_nginx_config() {
log_info "生成Nginx配置..."
mkdir -p docker/nginx/conf.d
local server_name="${DOMAIN_NAME:-${LOCAL_IP} localhost}"
local upstream_backend="backend:3001"
local upstream_frontend="frontend:3000"
# 生成主Nginx配置
cat > docker/nginx/nginx.conf << 'EOF'
user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
use epoll;
multi_accept on;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
# 日志格式
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
# 基础配置
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
types_hash_max_size 2048;
server_tokens off;
# 客户端配置
client_max_body_size 100M;
client_header_timeout 60s;
client_body_timeout 60s;
# Gzip配置
gzip on;
gzip_vary on;
gzip_min_length 1000;
gzip_proxied expired no-cache no-store private auth;
gzip_types
text/plain
text/css
text/xml
text/javascript
application/javascript
application/xml+rss
application/json;
# 包含站点配置
include /etc/nginx/conf.d/*.conf;
}
EOF
# 生成站点配置
cat > docker/nginx/conf.d/default.conf << EOF
# 上游服务定义
upstream backend {
server ${upstream_backend};
keepalive 32;
}
upstream frontend {
server ${upstream_frontend};
keepalive 32;
}
# HTTP服务器配置
server {
listen 80;
server_name ${server_name};
# 安全头
add_header X-Frame-Options DENY;
add_header X-Content-Type-Options nosniff;
add_header X-XSS-Protection "1; mode=block";
# 健康检查端点
location /nginx-health {
access_log off;
return 200 "healthy\n";
add_header Content-Type text/plain;
}
# 后端API代理
location /api/ {
proxy_pass http://backend/api/;
proxy_http_version 1.1;
proxy_set_header Upgrade \$http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host \$host;
proxy_set_header X-Real-IP \$remote_addr;
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto \$scheme;
proxy_cache_bypass \$http_upgrade;
# 超时配置
proxy_connect_timeout 60s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
}
# 后端健康检查代理
location /health {
proxy_pass http://backend/health;
proxy_http_version 1.1;
proxy_set_header Host \$host;
proxy_set_header X-Real-IP \$remote_addr;
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto \$scheme;
}
# Socket.IO代理
location /socket.io/ {
proxy_pass http://backend;
proxy_http_version 1.1;
proxy_set_header Upgrade \$http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host \$host;
proxy_set_header X-Real-IP \$remote_addr;
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto \$scheme;
# WebSocket特殊配置
proxy_buffering off;
proxy_cache off;
}
# 前端应用代理
location / {
proxy_pass http://frontend;
proxy_http_version 1.1;
proxy_set_header Upgrade \$http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host \$host;
proxy_set_header X-Real-IP \$remote_addr;
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto \$scheme;
proxy_cache_bypass \$http_upgrade;
# Next.js特殊配置
proxy_buffering off;
}
}
EOF
log_success "Nginx配置已生成"
echo " 主配置: docker/nginx/nginx.conf"
echo " 站点配置: docker/nginx/conf.d/default.conf"
}
# 生成SSL证书
generate_ssl_certificates() {
if [[ "$SSL_MODE" == "self-signed" ]] || [[ "$NETWORK_MODE" == "private" ]]; then
log_info "生成自签名SSL证书..."
mkdir -p docker/ssl
# 生成CA私钥
openssl genrsa -out docker/ssl/ca-key.pem 4096 2>/dev/null
# 生成CA证书
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
# 生成服务器私钥
openssl genrsa -out docker/ssl/server-key.pem 4096 2>/dev/null
# 生成服务器证书请求
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
# 创建扩展配置
cat > docker/ssl/server.ext << EOF
authorityKeyIdentifier=keyid,issuer
basicConstraints=CA:FALSE
keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment
subjectAltName = @alt_names
[alt_names]
DNS.1 = localhost
DNS.2 = *.local
DNS.3 = ${DOMAIN_NAME:-privydrop.local}
IP.1 = ${LOCAL_IP}
IP.2 = 127.0.0.1
EOF
# 签名服务器证书
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
# 清理临时文件
rm -f docker/ssl/server.csr docker/ssl/server.ext docker/ssl/ca-cert.srl
# 设置权限
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"
# 生成HTTPS Nginx配置
if [[ "$DEPLOYMENT_MODE" != "basic" ]]; then
generate_https_nginx_config
fi
fi
}
# 生成HTTPS Nginx配置
generate_https_nginx_config() {
log_info "生成HTTPS Nginx配置..."
cat >> docker/nginx/conf.d/default.conf << EOF
# HTTPS服务器配置
server {
listen 443 ssl http2;
server_name ${DOMAIN_NAME:-${LOCAL_IP}};
# SSL配置
ssl_certificate /etc/nginx/ssl/server-cert.pem;
ssl_certificate_key /etc/nginx/ssl/server-key.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384;
ssl_prefer_server_ciphers off;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
# 安全头
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
add_header X-Frame-Options DENY;
add_header X-Content-Type-Options nosniff;
add_header X-XSS-Protection "1; mode=block";
# 健康检查端点
location /nginx-health {
access_log off;
return 200 "healthy\n";
add_header Content-Type text/plain;
}
# 后端API代理
location /api/ {
proxy_pass http://backend/api/;
proxy_http_version 1.1;
proxy_set_header Upgrade \$http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host \$host;
proxy_set_header X-Real-IP \$remote_addr;
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto https;
proxy_cache_bypass \$http_upgrade;
}
# 后端健康检查代理
location /health {
proxy_pass http://backend/health;
proxy_http_version 1.1;
proxy_set_header Host \$host;
proxy_set_header X-Real-IP \$remote_addr;
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto https;
}
# Socket.IO代理
location /socket.io/ {
proxy_pass http://backend;
proxy_http_version 1.1;
proxy_set_header Upgrade \$http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host \$host;
proxy_set_header X-Real-IP \$remote_addr;
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto https;
proxy_buffering off;
proxy_cache off;
}
# 前端应用代理
location / {
proxy_pass http://frontend;
proxy_http_version 1.1;
proxy_set_header Upgrade \$http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host \$host;
proxy_set_header X-Real-IP \$remote_addr;
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto https;
proxy_cache_bypass \$http_upgrade;
proxy_buffering off;
}
}
EOF
log_success "HTTPS配置已添加"
}
# 生成Coturn配置
generate_coturn_config() {
if [[ "$TURN_ENABLED" == "true" ]]; then
log_info "生成Coturn TURN服务器配置..."
mkdir -p docker/coturn
cat > docker/coturn/turnserver.conf << EOF
# PrivyDrop TURN服务器配置
# 自动生成时间: $(date)
# 监听端口
listening-port=3478
tls-listening-port=5349
# 监听IP
listening-ip=0.0.0.0
relay-ip=0.0.0.0
# 外部IP (用于NAT环境)
external-ip=${PUBLIC_IP:-${LOCAL_IP}}
# 服务器域名
realm=${TURN_REALM}
server-name=${TURN_REALM}
# 认证方式
lt-cred-mech
# 用户认证
user=${TURN_USERNAME}:${TURN_PASSWORD}
# SSL证书 (如果启用TLS)
cert=/etc/ssl/certs/server-cert.pem
pkey=/etc/ssl/certs/server-key.pem
# 日志配置
no-stdout-log
log-file=/var/log/turnserver.log
verbose
# 安全配置
no-cli
no-loopback-peers
no-multicast-peers
# 性能配置
min-port=49152
max-port=65535
# 数据库 (可选)
# userdb=/var/lib/turn/turndb
# 其他配置
mobility
no-tlsv1
no-tlsv1_1
EOF
log_success "Coturn配置已生成: docker/coturn/turnserver.conf"
log_info "TURN服务器用户名: ${TURN_USERNAME}"
log_warning "TURN服务器密码已保存在.env文件中"
fi
}
# 生成Docker忽略文件
generate_dockerignore() {
log_info "生成Docker忽略文件..."
# 后端.dockerignore
cat > backend/.dockerignore << EOF
node_modules
npm-debug.log*
.npm
.env*
.git
.gitignore
README.md
Dockerfile
.dockerignore
coverage
.nyc_output
logs
*.log
EOF
# 前端.dockerignore
cat > frontend/.dockerignore << EOF
node_modules
.next
.git
.gitignore
README.md
Dockerfile
.dockerignore
.env*
npm-debug.log*
.npm
coverage
.nyc_output
*.log
public/sw.js
public/workbox-*.js
EOF
log_success "Docker忽略文件已生成"
}
# 创建日志目录
create_log_directories() {
log_info "创建日志目录..."
mkdir -p logs/{nginx,backend,frontend,coturn}
# 设置权限
chmod 755 logs
chmod 755 logs/*
log_success "日志目录已创建: logs/"
}
# 主函数
main() {
echo -e "${BLUE}=== PrivyDrop 配置生成 ===${NC}"
echo ""
# 首先运行环境检测
if ! detect_network_environment; then
log_error "环境检测失败"
exit 1
fi
if ! check_system_resources; then
log_error "系统资源检查失败"
exit 1
fi
detect_deployment_mode
echo ""
# 生成所有配置文件
generate_env_file
echo ""
generate_nginx_config
echo ""
generate_ssl_certificates
echo ""
generate_coturn_config
echo ""
generate_dockerignore
echo ""
create_log_directories
echo ""
log_success "🎉 所有配置文件生成完成!"
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 ""
echo -e "${BLUE}下一步:${NC}"
echo " 运行 './deploy.sh' 开始部署"
}
# 如果脚本被直接执行
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
main "$@"
fi
+420
View File
@@ -0,0 +1,420 @@
#!/bin/bash
# PrivyDrop Docker 部署测试脚本
# 用于验证部署的完整性和功能
# 颜色定义
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'
# 测试结果统计
TESTS_PASSED=0
TESTS_FAILED=0
TOTAL_TESTS=0
# 日志函数
log_info() {
echo -e "${BLUE}$1${NC}"
}
log_success() {
echo -e "${GREEN}$1${NC}"
TESTS_PASSED=$((TESTS_PASSED + 1))
}
log_error() {
echo -e "${RED}$1${NC}"
TESTS_FAILED=$((TESTS_FAILED + 1))
}
log_warning() {
echo -e "${YELLOW}⚠️ $1${NC}"
}
# 测试函数
run_test() {
local test_name="$1"
local test_command="$2"
TOTAL_TESTS=$((TOTAL_TESTS + 1))
log_info "测试: $test_name"
if eval "$test_command" >/dev/null 2>&1; then
log_success "$test_name"
return 0
else
log_error "$test_name"
return 1
fi
}
# Docker环境测试
test_docker_environment() {
echo -e "${BLUE}=== Docker环境测试 ===${NC}"
run_test "Docker已安装" "command -v docker"
run_test "Docker服务运行中" "docker info"
run_test "Docker Compose可用" "docker-compose --version || docker compose version"
echo ""
}
# 容器状态测试
test_container_status() {
echo -e "${BLUE}=== 容器状态测试 ===${NC}"
# 检查容器是否存在和运行
local containers=("privydrop-redis" "privydrop-backend" "privydrop-frontend")
for container in "${containers[@]}"; do
run_test "容器 $container 运行中" "docker ps | grep -q $container"
done
# 检查容器健康状态
for container in "${containers[@]}"; do
if docker ps --format "table {{.Names}}\t{{.Status}}" | grep -q "$container.*healthy"; then
log_success "容器 $container 健康状态正常"
TESTS_PASSED=$((TESTS_PASSED + 1))
else
log_warning "容器 $container 健康状态未知或不健康"
fi
TOTAL_TESTS=$((TOTAL_TESTS + 1))
done
echo ""
}
# 网络连接测试
test_network_connectivity() {
echo -e "${BLUE}=== 网络连接测试 ===${NC}"
# 测试端口连通性
local ports=("3000:前端" "3001:后端" "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"
done
# 测试容器间网络
run_test "后端可连接Redis" "docker-compose exec -T backend sh -c 'nc -z redis 6379'"
run_test "前端可连接后端" "curl -f http://localhost:3001/health"
echo ""
}
# API功能测试
test_api_functionality() {
echo -e "${BLUE}=== API功能测试 ===${NC}"
# 健康检查API
run_test "后端健康检查API" "curl -f http://localhost:3001/health"
run_test "前端健康检查API" "curl -f http://localhost:3000/api/health"
# 后端详细健康检查
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连接状态正常"
TESTS_PASSED=$((TESTS_PASSED + 1))
else
log_error "Redis连接状态异常"
TESTS_FAILED=$((TESTS_FAILED + 1))
fi
else
log_error "详细健康检查API不可用"
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"
echo ""
}
# WebRTC功能测试
test_webrtc_functionality() {
echo -e "${BLUE}=== WebRTC功能测试 ===${NC}"
# 测试前端页面加载
if curl -f http://localhost:3000 >/dev/null 2>&1; then
log_success "前端页面可访问"
TESTS_PASSED=$((TESTS_PASSED + 1))
else
log_error "前端页面不可访问"
TESTS_FAILED=$((TESTS_FAILED + 1))
fi
TOTAL_TESTS=$((TOTAL_TESTS + 1))
# 测试Socket.IO连接 (简单测试)
if curl -f http://localhost:3001/socket.io/socket.io.js >/dev/null 2>&1; then
log_success "Socket.IO客户端脚本可访问"
TESTS_PASSED=$((TESTS_PASSED + 1))
else
log_error "Socket.IO客户端脚本不可访问"
TESTS_FAILED=$((TESTS_FAILED + 1))
fi
TOTAL_TESTS=$((TOTAL_TESTS + 1))
echo ""
}
# 性能测试
test_performance() {
echo -e "${BLUE}=== 性能测试 ===${NC}"
# 内存使用测试
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"
fi
if [[ -n "$frontend_memory" ]]; then
log_info "前端内存使用: $frontend_memory"
fi
# 响应时间测试
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"
TESTS_PASSED=$((TESTS_PASSED + 1))
else
log_warning "API响应时间较慢: ${response_time}s"
fi
TOTAL_TESTS=$((TOTAL_TESTS + 1))
echo ""
}
# 安全测试
test_security() {
echo -e "${BLUE}=== 安全测试 ===${NC}"
# 检查容器用户
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"
TESTS_PASSED=$((TESTS_PASSED + 1))
else
log_warning "后端容器使用root用户"
fi
TOTAL_TESTS=$((TOTAL_TESTS + 1))
if [[ "$frontend_user" != "root" ]]; then
log_success "前端容器使用非root用户: $frontend_user"
TESTS_PASSED=$((TESTS_PASSED + 1))
else
log_warning "前端容器使用root用户"
fi
TOTAL_TESTS=$((TOTAL_TESTS + 1))
# 检查敏感信息泄露
if curl -s http://localhost:3001/health/detailed | grep -q "password\|secret\|key" >/dev/null 2>&1; then
log_warning "健康检查API可能泄露敏感信息"
else
log_success "健康检查API未泄露敏感信息"
TESTS_PASSED=$((TESTS_PASSED + 1))
fi
TOTAL_TESTS=$((TOTAL_TESTS + 1))
echo ""
}
# 日志测试
test_logging() {
echo -e "${BLUE}=== 日志测试 ===${NC}"
# 检查日志目录
if [[ -d "logs" ]]; then
log_success "日志目录存在"
TESTS_PASSED=$((TESTS_PASSED + 1))
else
log_warning "日志目录不存在"
fi
TOTAL_TESTS=$((TOTAL_TESTS + 1))
# 检查日志文件
local log_files=("logs/backend" "logs/frontend")
for log_dir in "${log_files[@]}"; do
if [[ -d "$log_dir" ]]; then
log_success "日志目录 $log_dir 存在"
TESTS_PASSED=$((TESTS_PASSED + 1))
else
log_info "日志目录 $log_dir 不存在 (可能正常)"
fi
TOTAL_TESTS=$((TOTAL_TESTS + 1))
done
echo ""
}
# 配置文件测试
test_configuration() {
echo -e "${BLUE}=== 配置文件测试 ===${NC}"
# 检查环境变量文件
if [[ -f ".env" ]]; then
log_success ".env 文件存在"
TESTS_PASSED=$((TESTS_PASSED + 1))
# 检查关键配置项
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 已设置"
TESTS_PASSED=$((TESTS_PASSED + 1))
else
log_error "配置项 $var 未设置"
TESTS_FAILED=$((TESTS_FAILED + 1))
fi
TOTAL_TESTS=$((TOTAL_TESTS + 1))
done
else
log_error ".env 文件不存在"
TESTS_FAILED=$((TESTS_FAILED + 1))
fi
TOTAL_TESTS=$((TOTAL_TESTS + 1))
# 检查Docker Compose文件
if [[ -f "docker-compose.yml" ]]; then
log_success "docker-compose.yml 文件存在"
TESTS_PASSED=$((TESTS_PASSED + 1))
else
log_error "docker-compose.yml 文件不存在"
TESTS_FAILED=$((TESTS_FAILED + 1))
fi
TOTAL_TESTS=$((TOTAL_TESTS + 1))
echo ""
}
# 清理测试
test_cleanup() {
echo -e "${BLUE}=== 清理功能测试 ===${NC}"
# 测试清理命令是否可用
if [[ -f "deploy.sh" ]]; then
log_success "部署脚本存在"
TESTS_PASSED=$((TESTS_PASSED + 1))
# 测试帮助命令
if bash deploy.sh --help >/dev/null 2>&1; then
log_success "部署脚本帮助功能正常"
TESTS_PASSED=$((TESTS_PASSED + 1))
else
log_error "部署脚本帮助功能异常"
TESTS_FAILED=$((TESTS_FAILED + 1))
fi
else
log_error "部署脚本不存在"
TESTS_FAILED=$((TESTS_FAILED + 1))
fi
TOTAL_TESTS=$((TOTAL_TESTS + 2))
echo ""
}
# 生成测试报告
generate_report() {
echo -e "${BLUE}=== 测试报告 ===${NC}"
echo ""
echo "📊 测试统计:"
echo " 总测试数: $TOTAL_TESTS"
echo -e " 通过: ${GREEN}$TESTS_PASSED${NC}"
echo -e " 失败: ${RED}$TESTS_FAILED${NC}"
local success_rate=$((TESTS_PASSED * 100 / TOTAL_TESTS))
echo " 成功率: $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 ""
if [[ $TESTS_FAILED -eq 0 ]]; then
echo -e "${GREEN}🎉 所有测试通过!PrivyDrop 部署成功!${NC}"
echo ""
echo "🔗 访问链接:"
echo " 前端应用: http://localhost:3000"
echo " 后端API: http://localhost:3001"
# 显示局域网访问地址
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:3000"
echo " 后端API: http://$local_ip:3001"
fi
fi
return 0
else
echo -e "${RED}❌ 有 $TESTS_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"
return 1
fi
}
# 主函数
main() {
echo -e "${BLUE}=== PrivyDrop Docker 部署测试开始 ===${NC}"
echo ""
# 检查必要工具
local missing_tools=()
for tool in curl jq bc nc; do
if ! command -v "$tool" >/dev/null 2>&1; then
missing_tools+=("$tool")
fi
done
if [[ ${#missing_tools[@]} -gt 0 ]]; then
log_warning "缺少测试工具: ${missing_tools[*]}"
log_info "建议安装: sudo apt-get install curl jq bc netcat"
echo ""
fi
# 运行所有测试
test_docker_environment
test_container_status
test_network_connectivity
test_api_functionality
test_webrtc_functionality
test_performance
test_security
test_logging
test_configuration
test_cleanup
# 生成报告
generate_report
}
# 捕获中断信号
trap 'echo -e "\n${YELLOW}测试被中断${NC}"; exit 1' INT TERM
# 运行主函数
main "$@"