gcx/frontend/portal/deploy.sh

250 lines
7.7 KiB
Bash

#!/bin/bash
# =============================================================================
# Genex Portal - 部署管理脚本
# =============================================================================
#
# 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 配置
#
# 默认域名: gogenex.com
# 默认邮箱: admin@gogenex.com
#
# =============================================================================
set -e
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'
PROJECT_NAME="genex-portal"
IMAGE_NAME="genex-portal"
CONTAINER_NAME="genex-portal"
DEFAULT_PORT=3001
DEFAULT_DOMAIN="gogenex.com"
DEFAULT_EMAIL="admin@gogenex.com"
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"; }
check_docker() {
if ! command -v docker &> /dev/null; then
log_error "Docker 未安装"
exit 1
fi
if ! docker info &> /dev/null; then
log_error "Docker 服务未运行"
exit 1
fi
}
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
}
build() {
log_info "构建 Docker 镜像..."
$COMPOSE_CMD build --no-cache
log_success "镜像构建完成"
}
start() {
log_info "部署 Genex Portal..."
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 "服务部署成功! http://localhost:$PORT"
else
log_error "服务启动失败,查看日志: ./deploy.sh logs"
exit 1
fi
}
stop() {
log_info "停止服务..."
$COMPOSE_CMD down
log_success "服务已停止"
}
restart() { 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}}"
}
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 + SSL..."
if [ ! -f "$conf_file" ]; then
log_error "配置文件不存在: $conf_file"
exit 1
fi
if [ "$EUID" -ne 0 ]; then
log_error "需要 root 权限: sudo ./deploy.sh nginx install $domain $email"
exit 1
fi
# 安装依赖
log_info "[1/5] 检查依赖..."
command -v nginx &> /dev/null || { apt update && apt install -y nginx; systemctl enable nginx; systemctl start nginx; }
command -v certbot &> /dev/null || apt install -y certbot python3-certbot-nginx
# HTTP 临时配置
log_info "[2/5] 部署 HTTP 临时配置..."
mkdir -p /var/www/certbot
cat > /etc/nginx/sites-available/$domain << HTTPEOF
server {
listen 80;
listen [::]:80;
server_name $domain www.$domain;
location /.well-known/acme-challenge/ { root /var/www/certbot; }
location / {
proxy_pass http://127.0.0.1:${DEFAULT_PORT};
proxy_set_header Host \$host;
proxy_set_header X-Real-IP \$remote_addr;
}
}
HTTPEOF
ln -sf /etc/nginx/sites-available/$domain /etc/nginx/sites-enabled/
nginx -t && systemctl reload nginx
# SSL 证书
log_info "[3/5] 申请 SSL 证书..."
if [ -d "/etc/letsencrypt/live/$domain" ]; then
log_warn "证书已存在,跳过"
else
certbot certonly --webroot --webroot-path=/var/www/certbot \
--email $email --agree-tos --no-eff-email \
-d $domain -d www.$domain
log_success "SSL 证书申请成功!"
fi
# HTTPS 完整配置
log_info "[4/5] 部署 HTTPS 配置..."
cp "$conf_file" /etc/nginx/sites-available/$domain
nginx -t && systemctl reload nginx
# 自动续期
log_info "[5/5] 配置自动续期..."
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
systemctl reload nginx
HOOKEOF
chmod +x /etc/letsencrypt/renewal-hooks/deploy/reload-nginx.sh
fi
log_success "完成! https://$domain"
}
cmd_nginx_ssl() {
local domain="${1:-$DEFAULT_DOMAIN}"
local email="${2:-$DEFAULT_EMAIL}"
[ "$EUID" -ne 0 ] && { log_error "需要 root 权限"; exit 1; }
mkdir -p /var/www/certbot
if [ -d "/etc/letsencrypt/live/$domain" ]; then
certbot renew --cert-name $domain
else
certbot certonly --webroot --webroot-path=/var/www/certbot \
--email $email --agree-tos --no-eff-email \
-d $domain -d www.$domain
fi
log_success "SSL 证书完成"
}
cmd_nginx_status() {
systemctl is-active nginx &>/dev/null && echo -e "Nginx: ${GREEN}运行中${NC}" || echo -e "Nginx: ${RED}未运行${NC}"
command -v certbot &>/dev/null && certbot certificates 2>/dev/null | grep -E "(Certificate Name|Expiry Date|Domains)" | sed 's/^/ /'
}
cmd_nginx_reload() {
[ "$EUID" -ne 0 ] && { log_error "需要 root 权限"; exit 1; }
nginx -t && systemctl reload nginx
log_success "Nginx 已重载"
}
show_help() {
echo ""
echo "Genex Portal 部署脚本"
echo ""
echo "Docker: build | start | stop | restart | logs | status | clean"
echo "Nginx: nginx install [domain] [email] | nginx ssl | nginx status | nginx reload"
echo ""
echo "默认域名: $DEFAULT_DOMAIN 端口: $DEFAULT_PORT"
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 "未知命令: $2"; exit 1 ;;
esac
;;
help|--help|-h) show_help ;;
*) log_error "未知命令: $1"; show_help; exit 1 ;;
esac
}
main "$@"