576 lines
17 KiB
Bash
Executable File
576 lines
17 KiB
Bash
Executable File
#!/bin/bash
|
|
|
|
# =============================================================================
|
|
# Genex Admin Web - 部署管理脚本
|
|
# =============================================================================
|
|
#
|
|
# Docker 命令:
|
|
# ./deploy.sh build 仅构建 Docker 镜像
|
|
# ./deploy.sh start 构建并启动服务 (默认)
|
|
# ./deploy.sh stop 停止服务
|
|
# ./deploy.sh restart 重启服务
|
|
# ./deploy.sh logs 查看服务日志
|
|
# ./deploy.sh status 查看服务状态
|
|
# ./deploy.sh clean 清理容器和镜像
|
|
#
|
|
# Nginx + SSL 命令 (需要 root 权限):
|
|
# sudo ./deploy.sh nginx install [domain] [email] 安装 Nginx + SSL 证书
|
|
# sudo ./deploy.sh nginx ssl [domain] [email] 仅申请/续期 SSL 证书
|
|
# ./deploy.sh nginx status 查看 Nginx 状态
|
|
# ./deploy.sh nginx reload 重载 Nginx 配置
|
|
#
|
|
# 默认域名: admin.gogenex.cn
|
|
# 默认邮箱: admin@gogenex.cn
|
|
#
|
|
# =============================================================================
|
|
|
|
set -e
|
|
|
|
# 颜色定义
|
|
RED='\033[0;31m'
|
|
GREEN='\033[0;32m'
|
|
YELLOW='\033[1;33m'
|
|
BLUE='\033[0;34m'
|
|
NC='\033[0m' # No Color
|
|
|
|
# 项目信息
|
|
PROJECT_NAME="genex-admin-web"
|
|
IMAGE_NAME="genex-admin-web"
|
|
CONTAINER_NAME="genex-admin-web"
|
|
DEFAULT_PORT=3000
|
|
DEFAULT_DOMAIN="admin.gogenex.cn"
|
|
DEFAULT_EMAIL="admin@gogenex.cn"
|
|
|
|
# 脚本目录
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
|
|
# 日志函数
|
|
log_info() {
|
|
echo -e "${BLUE}[INFO]${NC} $1"
|
|
}
|
|
|
|
log_success() {
|
|
echo -e "${GREEN}[SUCCESS]${NC} $1"
|
|
}
|
|
|
|
log_warn() {
|
|
echo -e "${YELLOW}[WARN]${NC} $1"
|
|
}
|
|
|
|
log_error() {
|
|
echo -e "${RED}[ERROR]${NC} $1"
|
|
}
|
|
|
|
# =============================================================================
|
|
# Docker 环境检查
|
|
# =============================================================================
|
|
|
|
# 检查 Docker 是否安装
|
|
check_docker() {
|
|
if ! command -v docker &> /dev/null; then
|
|
log_error "Docker 未安装,请先安装 Docker"
|
|
exit 1
|
|
fi
|
|
|
|
if ! docker info &> /dev/null; then
|
|
log_error "Docker 服务未运行,请启动 Docker"
|
|
exit 1
|
|
fi
|
|
|
|
log_success "Docker 检查通过"
|
|
}
|
|
|
|
# 检查 Docker Compose 是否安装
|
|
check_docker_compose() {
|
|
if docker compose version &> /dev/null; then
|
|
COMPOSE_CMD="docker compose"
|
|
elif command -v docker-compose &> /dev/null; then
|
|
COMPOSE_CMD="docker-compose"
|
|
else
|
|
log_error "Docker Compose 未安装"
|
|
exit 1
|
|
fi
|
|
|
|
log_success "Docker Compose 检查通过 ($COMPOSE_CMD)"
|
|
}
|
|
|
|
# =============================================================================
|
|
# Docker 服务管理
|
|
# =============================================================================
|
|
|
|
# 构建镜像
|
|
build() {
|
|
log_info "开始构建 Docker 镜像..."
|
|
$COMPOSE_CMD build --no-cache
|
|
log_success "镜像构建完成"
|
|
}
|
|
|
|
# 启动服务
|
|
start() {
|
|
log_info "开始部署 Genex Admin Web..."
|
|
|
|
# 检查端口是否被占用
|
|
PORT=${PORT:-$DEFAULT_PORT}
|
|
if lsof -Pi :$PORT -sTCP:LISTEN -t >/dev/null 2>&1; then
|
|
log_warn "端口 $PORT 已被占用,尝试停止旧服务..."
|
|
stop
|
|
fi
|
|
|
|
# 构建并启动
|
|
$COMPOSE_CMD up -d --build
|
|
|
|
# 等待服务启动
|
|
log_info "等待服务启动..."
|
|
sleep 5
|
|
|
|
# 检查服务状态
|
|
if docker ps | grep -q $CONTAINER_NAME; then
|
|
log_success "服务部署成功!"
|
|
log_info "访问地址: http://localhost:$PORT"
|
|
log_info "健康检查: http://localhost:$PORT/api/health"
|
|
else
|
|
log_error "服务启动失败,请查看日志: ./deploy.sh logs"
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
# 停止服务
|
|
stop() {
|
|
log_info "停止服务..."
|
|
$COMPOSE_CMD down
|
|
log_success "服务已停止"
|
|
}
|
|
|
|
# 重启服务
|
|
restart() {
|
|
log_info "重启服务..."
|
|
stop
|
|
start
|
|
}
|
|
|
|
# 查看日志
|
|
logs() {
|
|
$COMPOSE_CMD logs -f
|
|
}
|
|
|
|
# 清理
|
|
clean() {
|
|
log_info "清理容器和镜像..."
|
|
|
|
# 停止并删除容器
|
|
$COMPOSE_CMD down --rmi local --volumes --remove-orphans
|
|
|
|
# 删除悬空镜像
|
|
docker image prune -f
|
|
|
|
log_success "清理完成"
|
|
}
|
|
|
|
# 显示状态
|
|
status() {
|
|
log_info "服务状态:"
|
|
docker ps -a --filter "name=$CONTAINER_NAME" --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"
|
|
}
|
|
|
|
# =============================================================================
|
|
# Nginx + SSL 证书管理
|
|
# =============================================================================
|
|
|
|
# 安装 Nginx + SSL 证书 (完整流程)
|
|
# 流程:
|
|
# 1. 安装 nginx 和 certbot (如未安装)
|
|
# 2. 部署 HTTP 临时配置 (用于 Let's Encrypt 验证)
|
|
# 3. 使用 certbot webroot 方式申请 SSL 证书
|
|
# 4. 部署完整 HTTPS 配置 (从 nginx/ 目录读取)
|
|
# 5. 设置 certbot 自动续期定时任务
|
|
cmd_nginx_install() {
|
|
local domain="${1:-$DEFAULT_DOMAIN}"
|
|
local email="${2:-$DEFAULT_EMAIL}"
|
|
local conf_file="$SCRIPT_DIR/nginx/${domain}.conf"
|
|
|
|
log_info "为域名 $domain 安装 Nginx + Let's Encrypt SSL..."
|
|
|
|
# 检查 HTTPS 配置模板是否存在
|
|
if [ ! -f "$conf_file" ]; then
|
|
log_error "Nginx 配置文件不存在: $conf_file"
|
|
log_error "请先在 nginx/ 目录下创建 ${domain}.conf"
|
|
log_info ""
|
|
log_info "可以复制模板并修改:"
|
|
log_info " cp nginx/admin.gogenex.cn.conf nginx/${domain}.conf"
|
|
log_info " 然后替换其中的域名和端口"
|
|
exit 1
|
|
fi
|
|
|
|
# 检查 root 权限
|
|
if [ "$EUID" -ne 0 ]; then
|
|
log_error "需要 root 权限运行"
|
|
log_info "用法: sudo ./deploy.sh nginx install $domain $email"
|
|
exit 1
|
|
fi
|
|
|
|
echo ""
|
|
echo "============================================"
|
|
echo " Nginx + SSL 安装"
|
|
echo "============================================"
|
|
echo " 域名: $domain"
|
|
echo " 邮箱: $email"
|
|
echo " 端口: $DEFAULT_PORT"
|
|
echo " 配置: $conf_file"
|
|
echo "============================================"
|
|
echo ""
|
|
|
|
# ---- 步骤 1: 安装依赖 ----
|
|
log_info "[1/5] 检查并安装依赖..."
|
|
|
|
if ! command -v nginx &> /dev/null; then
|
|
log_info "正在安装 Nginx..."
|
|
apt update && apt install -y nginx
|
|
systemctl enable nginx
|
|
systemctl start nginx
|
|
fi
|
|
log_success "Nginx 已就绪 ($(nginx -v 2>&1))"
|
|
|
|
if ! command -v certbot &> /dev/null; then
|
|
log_info "正在安装 Certbot..."
|
|
apt install -y certbot python3-certbot-nginx
|
|
fi
|
|
log_success "Certbot 已就绪 ($(certbot --version 2>&1))"
|
|
|
|
# ---- 步骤 2: 部署 HTTP 临时配置 ----
|
|
log_info "[2/5] 部署 HTTP 临时配置 (用于证书验证)..."
|
|
|
|
# 创建 certbot webroot 目录
|
|
mkdir -p /var/www/certbot
|
|
|
|
# 写入 HTTP-only 临时配置
|
|
cat > /etc/nginx/sites-available/$domain << HTTPEOF
|
|
# 临时 HTTP 配置 (用于 Let's Encrypt 证书申请)
|
|
# 申请成功后将被 HTTPS 配置替换
|
|
server {
|
|
listen 80;
|
|
listen [::]:80;
|
|
server_name $domain;
|
|
|
|
# Let's Encrypt ACME 验证
|
|
location /.well-known/acme-challenge/ {
|
|
root /var/www/certbot;
|
|
}
|
|
|
|
# 临时反向代理 (证书申请前也可访问)
|
|
location / {
|
|
proxy_pass http://127.0.0.1:${DEFAULT_PORT};
|
|
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;
|
|
}
|
|
}
|
|
HTTPEOF
|
|
|
|
# 启用站点配置
|
|
ln -sf /etc/nginx/sites-available/$domain /etc/nginx/sites-enabled/
|
|
|
|
# 移除默认站点 (避免冲突)
|
|
rm -f /etc/nginx/sites-enabled/default
|
|
|
|
# 测试并重载 Nginx
|
|
nginx -t && systemctl reload nginx
|
|
log_success "HTTP 配置完成 (http://$domain)"
|
|
|
|
# ---- 步骤 3: 申请 SSL 证书 ----
|
|
log_info "[3/5] 申请 Let's Encrypt SSL 证书..."
|
|
|
|
if [ -d "/etc/letsencrypt/live/$domain" ]; then
|
|
log_warn "证书已存在,跳过申请"
|
|
log_info "如需重新申请: sudo certbot certonly --force-renewal -d $domain"
|
|
else
|
|
echo ""
|
|
log_warn "请确保以下条件已满足:"
|
|
log_warn " 1. DNS A 记录 $domain 已指向本服务器 IP"
|
|
log_warn " 2. 服务器 80 端口可从外网访问 (防火墙已放行)"
|
|
echo ""
|
|
read -p "确认以上条件已满足,继续申请证书? (y/n): " confirm
|
|
if [ "$confirm" != "y" ] && [ "$confirm" != "Y" ]; then
|
|
log_info "已跳过证书申请,当前为 HTTP 模式"
|
|
log_info "稍后申请证书: sudo ./deploy.sh nginx ssl $domain"
|
|
return 0
|
|
fi
|
|
|
|
# 使用 webroot 方式申请证书
|
|
certbot certonly --webroot --webroot-path=/var/www/certbot \
|
|
--email $email --agree-tos --no-eff-email \
|
|
-d $domain
|
|
|
|
log_success "SSL 证书申请成功!"
|
|
fi
|
|
|
|
# ---- 步骤 4: 部署 HTTPS 完整配置 ----
|
|
log_info "[4/5] 部署 HTTPS 完整配置..."
|
|
|
|
# 从项目 nginx/ 目录复制完整 HTTPS 配置
|
|
cp "$conf_file" /etc/nginx/sites-available/$domain
|
|
|
|
# 测试并重载
|
|
nginx -t && systemctl reload nginx
|
|
log_success "HTTPS 配置已部署"
|
|
|
|
# ---- 步骤 5: 配置自动续期 ----
|
|
log_info "[5/5] 配置证书自动续期..."
|
|
|
|
# certbot 安装时通常已自动配置 systemd timer 或 cron
|
|
# 这里确保 renew hook 能正确重载 nginx
|
|
if [ ! -f /etc/letsencrypt/renewal-hooks/deploy/reload-nginx.sh ]; then
|
|
mkdir -p /etc/letsencrypt/renewal-hooks/deploy
|
|
cat > /etc/letsencrypt/renewal-hooks/deploy/reload-nginx.sh << 'HOOKEOF'
|
|
#!/bin/bash
|
|
# Let's Encrypt 证书续期后自动重载 Nginx
|
|
systemctl reload nginx
|
|
HOOKEOF
|
|
chmod +x /etc/letsencrypt/renewal-hooks/deploy/reload-nginx.sh
|
|
log_success "自动续期钩子已配置"
|
|
else
|
|
log_success "自动续期钩子已存在"
|
|
fi
|
|
|
|
# 测试续期 (dry-run)
|
|
certbot renew --dry-run 2>/dev/null && log_success "自动续期测试通过" || log_warn "自动续期测试未通过,请检查 certbot 配置"
|
|
|
|
# ---- 完成 ----
|
|
echo ""
|
|
echo "============================================"
|
|
echo -e " ${GREEN}Nginx + SSL 配置完成!${NC}"
|
|
echo "============================================"
|
|
echo ""
|
|
echo -e " HTTPS 访问: ${BLUE}https://$domain${NC}"
|
|
echo -e " HTTP 自动跳转到 HTTPS"
|
|
echo ""
|
|
echo " Nginx 日志:"
|
|
echo " access: /var/log/nginx/${domain}.access.log"
|
|
echo " error: /var/log/nginx/${domain}.error.log"
|
|
echo ""
|
|
echo " 证书路径:"
|
|
echo " cert: /etc/letsencrypt/live/$domain/fullchain.pem"
|
|
echo " key: /etc/letsencrypt/live/$domain/privkey.pem"
|
|
echo ""
|
|
echo " 证书续期: certbot 已配置自动续期 (每天检查)"
|
|
echo " 手动续期: sudo certbot renew"
|
|
echo ""
|
|
}
|
|
|
|
# 仅申请/续期 SSL 证书
|
|
cmd_nginx_ssl() {
|
|
local domain="${1:-$DEFAULT_DOMAIN}"
|
|
local email="${2:-$DEFAULT_EMAIL}"
|
|
local conf_file="$SCRIPT_DIR/nginx/${domain}.conf"
|
|
|
|
# 检查 root 权限
|
|
if [ "$EUID" -ne 0 ]; then
|
|
log_error "需要 root 权限运行"
|
|
log_info "用法: sudo ./deploy.sh nginx ssl $domain $email"
|
|
exit 1
|
|
fi
|
|
|
|
if [ -d "/etc/letsencrypt/live/$domain" ]; then
|
|
log_info "证书已存在,尝试续期..."
|
|
certbot renew --cert-name $domain
|
|
log_success "证书续期完成"
|
|
else
|
|
log_info "为 $domain 申请 SSL 证书..."
|
|
|
|
# 确保 webroot 目录存在
|
|
mkdir -p /var/www/certbot
|
|
|
|
certbot certonly --webroot --webroot-path=/var/www/certbot \
|
|
--email $email --agree-tos --no-eff-email \
|
|
-d $domain
|
|
|
|
log_success "SSL 证书申请成功!"
|
|
fi
|
|
|
|
# 如果 HTTPS 配置模板存在,部署它
|
|
if [ -f "$conf_file" ]; then
|
|
log_info "部署 HTTPS 配置..."
|
|
cp "$conf_file" /etc/nginx/sites-available/$domain
|
|
nginx -t && systemctl reload nginx
|
|
log_success "HTTPS 配置已部署: https://$domain"
|
|
else
|
|
log_warn "HTTPS 配置模板不存在: $conf_file"
|
|
log_warn "请手动配置 Nginx"
|
|
fi
|
|
}
|
|
|
|
# 查看 Nginx 状态
|
|
cmd_nginx_status() {
|
|
echo ""
|
|
echo "============================================"
|
|
echo " Nginx 状态"
|
|
echo "============================================"
|
|
echo ""
|
|
|
|
# Nginx 服务状态
|
|
if systemctl is-active nginx &>/dev/null; then
|
|
echo -e " Nginx 服务: ${GREEN}运行中${NC}"
|
|
else
|
|
echo -e " Nginx 服务: ${RED}未运行${NC}"
|
|
fi
|
|
|
|
# 域名配置
|
|
echo ""
|
|
echo " 已配置站点:"
|
|
for conf in /etc/nginx/sites-enabled/*; do
|
|
if [ -f "$conf" ]; then
|
|
local name=$(basename "$conf")
|
|
echo " - $name"
|
|
fi
|
|
done
|
|
|
|
# SSL 证书状态
|
|
echo ""
|
|
echo " SSL 证书:"
|
|
if command -v certbot &>/dev/null; then
|
|
certbot certificates 2>/dev/null | grep -E "(Certificate Name|Expiry Date|Domains)" | sed 's/^/ /'
|
|
else
|
|
echo " certbot 未安装"
|
|
fi
|
|
|
|
echo ""
|
|
}
|
|
|
|
# 重载 Nginx 配置
|
|
cmd_nginx_reload() {
|
|
if [ "$EUID" -ne 0 ]; then
|
|
log_error "需要 root 权限: sudo ./deploy.sh nginx reload"
|
|
exit 1
|
|
fi
|
|
|
|
nginx -t && systemctl reload nginx
|
|
log_success "Nginx 配置已重载"
|
|
}
|
|
|
|
# =============================================================================
|
|
# 帮助信息
|
|
# =============================================================================
|
|
|
|
show_help() {
|
|
echo ""
|
|
echo "Genex Admin Web 部署脚本"
|
|
echo ""
|
|
echo "使用方法: ./deploy.sh [命令]"
|
|
echo ""
|
|
echo "Docker 命令:"
|
|
echo " build 仅构建 Docker 镜像"
|
|
echo " start 构建并启动服务 (默认)"
|
|
echo " stop 停止服务"
|
|
echo " restart 重启服务"
|
|
echo " logs 查看服务日志"
|
|
echo " status 查看服务状态"
|
|
echo " clean 清理容器和镜像"
|
|
echo ""
|
|
echo "Nginx + SSL 命令:"
|
|
echo " nginx install [domain] [email] 安装 Nginx + Let's Encrypt SSL"
|
|
echo " nginx ssl [domain] [email] 申请/续期 SSL 证书"
|
|
echo " nginx status 查看 Nginx 和证书状态"
|
|
echo " nginx reload 重载 Nginx 配置"
|
|
echo ""
|
|
echo " help 显示此帮助信息"
|
|
echo ""
|
|
echo "环境变量:"
|
|
echo " PORT 服务端口 (默认: $DEFAULT_PORT)"
|
|
echo ""
|
|
echo "默认配置:"
|
|
echo " 域名: $DEFAULT_DOMAIN"
|
|
echo " 邮箱: $DEFAULT_EMAIL"
|
|
echo ""
|
|
echo "示例:"
|
|
echo " ./deploy.sh start # 默认端口 3000 启动"
|
|
echo " PORT=8080 ./deploy.sh start # 指定端口 8080 启动"
|
|
echo " sudo ./deploy.sh nginx install # 安装 Nginx + SSL (默认域名)"
|
|
echo " sudo ./deploy.sh nginx install myapp.com a@b.c # 自定义域名和邮箱"
|
|
echo " sudo ./deploy.sh nginx ssl # 续期证书"
|
|
echo ""
|
|
echo "首次部署完整流程:"
|
|
echo " 1. ./deploy.sh start # 启动 Docker 容器"
|
|
echo " 2. sudo ./deploy.sh nginx install # 安装 Nginx + SSL"
|
|
echo " 3. 访问 https://$DEFAULT_DOMAIN"
|
|
echo ""
|
|
}
|
|
|
|
# =============================================================================
|
|
# 主入口
|
|
# =============================================================================
|
|
|
|
main() {
|
|
# 切换到脚本所在目录
|
|
cd "$SCRIPT_DIR"
|
|
|
|
# 执行命令
|
|
case "${1:-start}" in
|
|
build)
|
|
check_docker
|
|
check_docker_compose
|
|
build
|
|
;;
|
|
start)
|
|
check_docker
|
|
check_docker_compose
|
|
start
|
|
;;
|
|
stop)
|
|
check_docker
|
|
check_docker_compose
|
|
stop
|
|
;;
|
|
restart)
|
|
check_docker
|
|
check_docker_compose
|
|
restart
|
|
;;
|
|
logs)
|
|
check_docker
|
|
check_docker_compose
|
|
logs
|
|
;;
|
|
status)
|
|
check_docker
|
|
status
|
|
;;
|
|
clean)
|
|
check_docker
|
|
check_docker_compose
|
|
clean
|
|
;;
|
|
nginx)
|
|
case "${2:-install}" in
|
|
install)
|
|
cmd_nginx_install "$3" "$4"
|
|
;;
|
|
ssl)
|
|
cmd_nginx_ssl "$3" "$4"
|
|
;;
|
|
status)
|
|
cmd_nginx_status
|
|
;;
|
|
reload)
|
|
cmd_nginx_reload
|
|
;;
|
|
*)
|
|
log_error "未知 nginx 命令: $2"
|
|
echo "用法: ./deploy.sh nginx [install|ssl|status|reload] [domain] [email]"
|
|
exit 1
|
|
;;
|
|
esac
|
|
;;
|
|
help|--help|-h)
|
|
show_help
|
|
;;
|
|
*)
|
|
log_error "未知命令: $1"
|
|
show_help
|
|
exit 1
|
|
;;
|
|
esac
|
|
}
|
|
|
|
main "$@"
|