Files
vue-driven-cloud-storage/install.sh
WanWanYun 20d852027f 修复v2: 改进stdin重定向方式
- 移除全局 exec < /dev/tty,避免管道执行中断
- 在每个read命令中单独指定 < /dev/tty
- 兼容 curl|bash 和 bash <(...) 两种执行方式
- 版本号 v1.0.2
2025-11-10 23:05:01 +08:00

1107 lines
30 KiB
Bash
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/bin/bash
################################################################################
# 玩玩云 (WanWanYun) - 一键部署脚本
# 项目地址: https://gitee.com/yu-yon/vue-driven-cloud-storage
# 版本: v1.0.1
################################################################################
set -e
# 颜色定义
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
PURPLE='\033[0;35m'
CYAN='\033[0;36m'
WHITE='\033[1;37m'
NC='\033[0m' # No Color
# 全局变量
PROJECT_NAME="wanwanyun"
PROJECT_DIR="/var/www/${PROJECT_NAME}"
REPO_URL="https://gitee.com/yu-yon/vue-driven-cloud-storage.git"
NODE_VERSION="18"
ADMIN_USERNAME=""
ADMIN_PASSWORD=""
DOMAIN=""
USE_DOMAIN=false
SSL_METHOD=""
################################################################################
# 工具函数
################################################################################
print_banner() {
clear
echo -e "${CYAN}"
echo "╔═══════════════════════════════════════════════════════════════╗"
echo "║ ║"
echo "║ 🌩️ 玩玩云 一键部署脚本 ║"
echo "║ ║"
echo "║ Cloud Storage Platform ║"
echo "║ ║"
echo "╚═══════════════════════════════════════════════════════════════╝"
echo -e "${NC}"
}
print_step() {
echo -e "\n${BLUE}$1${NC}"
}
print_success() {
echo -e "${GREEN}$1${NC}"
}
print_error() {
echo -e "${RED}$1${NC}"
}
print_warning() {
echo -e "${YELLOW}$1${NC}"
}
print_info() {
echo -e "${CYAN} $1${NC}"
}
# 检测操作系统
detect_os() {
if [[ -f /etc/os-release ]]; then
. /etc/os-release
OS=$ID
OS_VERSION=$VERSION_ID
else
print_error "无法检测操作系统"
exit 1
fi
}
# 检测系统架构
detect_arch() {
ARCH=$(uname -m)
case $ARCH in
x86_64)
ARCH="amd64"
;;
aarch64)
ARCH="arm64"
;;
*)
print_error "不支持的系统架构: $ARCH"
exit 1
;;
esac
}
# 检测root权限
check_root() {
if [[ $EUID -ne 0 ]]; then
print_error "此脚本需要root权限运行"
print_info "请使用: sudo bash install.sh"
exit 1
fi
}
################################################################################
# 环境检测
################################################################################
system_check() {
print_step "正在检测系统环境..."
# 检测操作系统
detect_os
print_success "操作系统: $OS $OS_VERSION"
# 检测架构
detect_arch
print_success "系统架构: $ARCH"
# 检测内存
TOTAL_MEM=$(free -m | awk '/^Mem:/{print $2}')
if [[ $TOTAL_MEM -lt 512 ]]; then
print_warning "内存不足512MB可能影响性能"
else
print_success "可用内存: ${TOTAL_MEM}MB"
fi
# 检测磁盘空间
DISK_AVAIL=$(df -m / | awk 'NR==2 {print $4}')
if [[ $DISK_AVAIL -lt 2048 ]]; then
print_warning "磁盘空间不足2GB可能影响运行"
else
print_success "可用磁盘: ${DISK_AVAIL}MB"
fi
# 检测网络
if ping -c 1 gitee.com &> /dev/null; then
print_success "网络连接正常"
else
print_error "无法连接到网络"
exit 1
fi
# 检测公网IP
PUBLIC_IP=$(curl -s ifconfig.me || curl -s icanhazip.com || echo "未知")
print_info "公网IP: $PUBLIC_IP"
echo ""
}
################################################################################
# 软件源配置
################################################################################
choose_mirror() {
print_step "选择软件包安装源"
echo ""
echo "请选择软件源:"
echo -e "${GREEN}[1]${NC} 官方源 (国外服务器推荐)"
echo -e "${GREEN}[2]${NC} 阿里云镜像源 (国内服务器推荐,速度更快)"
echo ""
while true; do
read -p "请输入选项 [1-2]: " mirror_choice < /dev/tty
case $mirror_choice in
1)
print_info "使用官方源"
USE_ALIYUN_MIRROR=false
break
;;
2)
print_info "使用阿里云镜像源"
USE_ALIYUN_MIRROR=true
configure_aliyun_mirror
break
;;
*)
print_error "无效选项,请重新选择"
;;
esac
done
echo ""
}
configure_aliyun_mirror() {
print_step "配置阿里云镜像源..."
case $OS in
ubuntu|debian)
# 备份原有源
if [[ ! -f /etc/apt/sources.list.bak ]]; then
cp /etc/apt/sources.list /etc/apt/sources.list.bak
fi
# 配置阿里云源
cat > /etc/apt/sources.list << EOF
deb http://mirrors.aliyun.com/$OS/ $(lsb_release -cs) main restricted universe multiverse
deb http://mirrors.aliyun.com/$OS/ $(lsb_release -cs)-updates main restricted universe multiverse
deb http://mirrors.aliyun.com/$OS/ $(lsb_release -cs)-backports main restricted universe multiverse
deb http://mirrors.aliyun.com/$OS/ $(lsb_release -cs)-security main restricted universe multiverse
EOF
print_success "阿里云源配置完成"
;;
centos|rhel)
# CentOS配置
if [[ ! -f /etc/yum.repos.d/CentOS-Base.repo.bak ]]; then
cp /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.bak
fi
curl -o /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo
yum clean all
yum makecache
print_success "阿里云源配置完成"
;;
esac
}
################################################################################
# 安装依赖环境
################################################################################
install_dependencies() {
print_step "正在安装依赖环境..."
case $OS in
ubuntu|debian)
apt-get update
apt-get install -y curl wget git unzip
install_nodejs_apt
install_nginx_apt
;;
centos|rhel)
yum install -y curl wget git unzip
install_nodejs_yum
install_nginx_yum
;;
esac
install_pm2
print_success "依赖环境安装完成"
echo ""
}
install_nodejs_apt() {
if command -v node &> /dev/null; then
NODE_VER=$(node -v | cut -d'v' -f2 | cut -d'.' -f1)
if [[ $NODE_VER -ge 18 ]]; then
print_success "Node.js 已安装: $(node -v)"
return
fi
fi
print_info "正在安装 Node.js ${NODE_VERSION}.x..."
curl -fsSL https://deb.nodesource.com/setup_${NODE_VERSION}.x | bash -
apt-get install -y nodejs
print_success "Node.js 安装完成: $(node -v)"
}
install_nodejs_yum() {
if command -v node &> /dev/null; then
NODE_VER=$(node -v | cut -d'v' -f2 | cut -d'.' -f1)
if [[ $NODE_VER -ge 18 ]]; then
print_success "Node.js 已安装: $(node -v)"
return
fi
fi
print_info "正在安装 Node.js ${NODE_VERSION}.x..."
curl -fsSL https://rpm.nodesource.com/setup_${NODE_VERSION}.x | bash -
yum install -y nodejs
print_success "Node.js 安装完成: $(node -v)"
}
install_nginx_apt() {
if command -v nginx &> /dev/null; then
print_success "Nginx 已安装: $(nginx -v 2>&1 | cut -d'/' -f2)"
return
fi
print_info "正在安装 Nginx..."
apt-get install -y nginx
systemctl enable nginx
print_success "Nginx 安装完成"
}
install_nginx_yum() {
if command -v nginx &> /dev/null; then
print_success "Nginx 已安装: $(nginx -v 2>&1 | cut -d'/' -f2)"
return
fi
print_info "正在安装 Nginx..."
yum install -y nginx
systemctl enable nginx
print_success "Nginx 安装完成"
}
install_pm2() {
if command -v pm2 &> /dev/null; then
print_success "PM2 已安装: $(pm2 -v)"
return
fi
print_info "正在安装 PM2..."
npm install -g pm2
pm2 startup
print_success "PM2 安装完成"
}
################################################################################
# 访问模式选择
################################################################################
choose_access_mode() {
print_step "选择访问模式"
echo ""
echo "请选择访问模式:"
echo -e "${GREEN}[1]${NC} 域名模式 (推荐支持HTTPS)"
echo -e "${GREEN}[2]${NC} IP模式 (仅HTTP适合测试)"
echo ""
while true; do
read -p "请输入选项 [1-2]: " mode_choice < /dev/tty
case $mode_choice in
1)
USE_DOMAIN=true
configure_domain
break
;;
2)
USE_DOMAIN=false
PUBLIC_IP=$(curl -s ifconfig.me || curl -s icanhazip.com || echo "未知")
print_info "将使用 IP 模式访问: http://${PUBLIC_IP}"
echo ""
break
;;
*)
print_error "无效选项,请重新选择"
;;
esac
done
}
configure_domain() {
echo ""
while true; do
read -p "请输入您的域名 (例如: wwy.example.com): " DOMAIN < /dev/tty
if [[ -z "$DOMAIN" ]]; then
print_error "域名不能为空"
continue
fi
# 验证域名格式
if [[ ! "$DOMAIN" =~ ^[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(\.[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$ ]]; then
print_error "域名格式不正确"
continue
fi
# 验证域名解析
print_info "正在验证域名解析..."
DOMAIN_IP=$(dig +short "$DOMAIN" | tail -n1)
PUBLIC_IP=$(curl -s ifconfig.me || curl -s icanhazip.com)
if [[ "$DOMAIN_IP" == "$PUBLIC_IP" ]]; then
print_success "域名已正确解析到当前服务器IP"
break
else
print_warning "域名未解析到当前服务器IP"
print_info "域名解析IP: $DOMAIN_IP"
print_info "当前服务器IP: $PUBLIC_IP"
read -p "是否继续? (y/n): " continue_choice < /dev/tty
if [[ "$continue_choice" == "y" || "$continue_choice" == "Y" ]]; then
break
fi
fi
done
choose_ssl_method
}
################################################################################
# SSL证书配置
################################################################################
choose_ssl_method() {
echo ""
print_step "选择SSL证书部署方式"
echo ""
echo -e "${YELLOW}【推荐方案】${NC}"
echo -e "${GREEN}[1]${NC} Certbot (Let's Encrypt官方工具)"
echo " - 最稳定可靠,支持自动续期"
echo ""
echo -e "${YELLOW}【备选方案】${NC}"
echo -e "${GREEN}[2]${NC} acme.sh + Let's Encrypt"
echo " - 纯Shell脚本更轻量级"
echo -e "${GREEN}[3]${NC} acme.sh + ZeroSSL"
echo " - Let's Encrypt的免费替代品"
echo -e "${GREEN}[4]${NC} acme.sh + Buypass"
echo " - 挪威免费CA有效期180天"
echo ""
echo -e "${YELLOW}【云服务商证书】${NC}"
echo -e "${GREEN}[5]${NC} 阿里云免费证书 (需提供AccessKey)"
echo -e "${GREEN}[6]${NC} 腾讯云免费证书 (需提供SecretKey)"
echo ""
echo -e "${YELLOW}【其他选项】${NC}"
echo -e "${GREEN}[7]${NC} 使用已有证书 (手动上传)"
echo -e "${GREEN}[8]${NC} 暂不配置HTTPS (可后续配置)"
echo ""
while true; do
read -p "请输入选项 [1-8]: " ssl_choice < /dev/tty
case $ssl_choice in
1|2|3|4|5|6|7|8)
SSL_METHOD=$ssl_choice
break
;;
*)
print_error "无效选项,请重新选择"
;;
esac
done
echo ""
}
deploy_ssl() {
if [[ "$USE_DOMAIN" != "true" ]]; then
return 0
fi
case $SSL_METHOD in
1)
deploy_certbot || ssl_fallback
;;
2)
deploy_acme_letsencrypt || ssl_fallback
;;
3)
deploy_acme_zerossl || ssl_fallback
;;
4)
deploy_acme_buypass || ssl_fallback
;;
5)
deploy_aliyun_ssl || ssl_fallback
;;
6)
deploy_tencent_ssl || ssl_fallback
;;
7)
deploy_manual_ssl
;;
8)
print_info "跳过HTTPS配置"
return 0
;;
esac
}
ssl_fallback() {
print_error "SSL证书部署失败"
echo ""
print_warning "建议尝试备选方案:"
echo -e "${GREEN}[2]${NC} acme.sh + Let's Encrypt (推荐)"
echo -e "${GREEN}[3]${NC} acme.sh + ZeroSSL"
echo -e "${GREEN}[4]${NC} acme.sh + Buypass"
echo -e "${GREEN}[8]${NC} 暂不配置HTTPS"
echo ""
while true; do
read -p "请选择备选方案 [2-4/8]: " retry_choice < /dev/tty
case $retry_choice in
2)
deploy_acme_letsencrypt && return 0
;;
3)
deploy_acme_zerossl && return 0
;;
4)
deploy_acme_buypass && return 0
;;
8)
print_info "跳过HTTPS配置"
SSL_METHOD=8
return 0
;;
*)
print_error "无效选项"
;;
esac
done
}
deploy_certbot() {
print_step "使用 Certbot 部署SSL证书..."
# 安装certbot
case $OS in
ubuntu|debian)
apt-get install -y certbot python3-certbot-nginx
;;
centos|rhel)
yum install -y certbot python3-certbot-nginx
;;
esac
# 申请证书
certbot --nginx -d "$DOMAIN" --non-interactive --agree-tos --email "admin@${DOMAIN}" --redirect
# 配置自动续期
systemctl enable certbot.timer
print_success "Certbot SSL证书部署成功"
return 0
}
deploy_acme_letsencrypt() {
print_step "使用 acme.sh + Let's Encrypt 部署SSL证书..."
# 安装acme.sh
if [[ ! -d ~/.acme.sh ]]; then
curl https://get.acme.sh | sh
fi
# 申请证书
~/.acme.sh/acme.sh --issue -d "$DOMAIN" --nginx
# 安装证书
mkdir -p /etc/nginx/ssl
~/.acme.sh/acme.sh --install-cert -d "$DOMAIN" \
--key-file /etc/nginx/ssl/${DOMAIN}.key \
--fullchain-file /etc/nginx/ssl/${DOMAIN}.crt \
--reloadcmd "systemctl reload nginx"
print_success "acme.sh SSL证书部署成功"
return 0
}
deploy_acme_zerossl() {
print_step "使用 acme.sh + ZeroSSL 部署SSL证书..."
# 安装acme.sh
if [[ ! -d ~/.acme.sh ]]; then
curl https://get.acme.sh | sh
fi
# 申请证书
~/.acme.sh/acme.sh --server zerossl --issue -d "$DOMAIN" --nginx
# 安装证书
mkdir -p /etc/nginx/ssl
~/.acme.sh/acme.sh --install-cert -d "$DOMAIN" \
--key-file /etc/nginx/ssl/${DOMAIN}.key \
--fullchain-file /etc/nginx/ssl/${DOMAIN}.crt \
--reloadcmd "systemctl reload nginx"
print_success "ZeroSSL证书部署成功"
return 0
}
deploy_acme_buypass() {
print_step "使用 acme.sh + Buypass 部署SSL证书..."
# 安装acme.sh
if [[ ! -d ~/.acme.sh ]]; then
curl https://get.acme.sh | sh
fi
# 申请证书
~/.acme.sh/acme.sh --server buypass --issue -d "$DOMAIN" --nginx
# 安装证书
mkdir -p /etc/nginx/ssl
~/.acme.sh/acme.sh --install-cert -d "$DOMAIN" \
--key-file /etc/nginx/ssl/${DOMAIN}.key \
--fullchain-file /etc/nginx/ssl/${DOMAIN}.crt \
--reloadcmd "systemctl reload nginx"
print_success "Buypass SSL证书部署成功"
return 0
}
deploy_aliyun_ssl() {
print_step "使用阿里云免费证书..."
print_warning "此功能需要您提供阿里云AccessKey"
echo ""
read -p "阿里云AccessKey ID: " ALIYUN_ACCESS_KEY_ID < /dev/tty
read -p "阿里云AccessKey Secret: " ALIYUN_ACCESS_KEY_SECRET < /dev/tty
# 这里需要调用阿里云API申请证书
# 暂时返回失败,提示用户使用其他方案
print_error "阿里云证书申请功能开发中,请选择其他方案"
return 1
}
deploy_tencent_ssl() {
print_step "使用腾讯云免费证书..."
print_warning "此功能需要您提供腾讯云SecretKey"
echo ""
read -p "腾讯云SecretId: " TENCENT_SECRET_ID < /dev/tty
read -p "腾讯云SecretKey: " TENCENT_SECRET_KEY < /dev/tty
# 这里需要调用腾讯云API申请证书
# 暂时返回失败,提示用户使用其他方案
print_error "腾讯云证书申请功能开发中,请选择其他方案"
return 1
}
deploy_manual_ssl() {
print_step "使用已有证书..."
echo ""
print_info "请将以下文件上传到服务器:"
print_info "- 证书文件: /tmp/ssl_cert.crt"
print_info "- 私钥文件: /tmp/ssl_key.key"
echo ""
read -p "上传完成后按回车继续..." < /dev/tty
if [[ -f /tmp/ssl_cert.crt ]] && [[ -f /tmp/ssl_key.key ]]; then
mkdir -p /etc/nginx/ssl
cp /tmp/ssl_cert.crt /etc/nginx/ssl/${DOMAIN}.crt
cp /tmp/ssl_key.key /etc/nginx/ssl/${DOMAIN}.key
chmod 600 /etc/nginx/ssl/${DOMAIN}.key
print_success "证书文件已复制"
return 0
else
print_error "证书文件未找到"
return 1
fi
}
################################################################################
# 项目部署
################################################################################
create_project_directory() {
print_step "创建项目目录..."
if [[ -d "$PROJECT_DIR" ]]; then
print_warning "项目目录已存在"
read -p "是否删除并重新创建? (y/n): " recreate < /dev/tty
if [[ "$recreate" == "y" || "$recreate" == "Y" ]]; then
rm -rf "$PROJECT_DIR"
else
print_error "部署已取消"
exit 1
fi
fi
mkdir -p "$PROJECT_DIR"
print_success "项目目录已创建: $PROJECT_DIR"
echo ""
}
download_project() {
print_step "正在从Gitee下载项目..."
cd /tmp
if [[ -d "${PROJECT_NAME}" ]]; then
rm -rf "${PROJECT_NAME}"
fi
git clone "$REPO_URL" "${PROJECT_NAME}"
# 复制文件到项目目录
cp -r "/tmp/${PROJECT_NAME}"/* "$PROJECT_DIR/"
# 清理临时文件
rm -rf "/tmp/${PROJECT_NAME}"
print_success "项目下载完成"
echo ""
}
configure_admin_account() {
print_step "配置管理员账号"
echo ""
while true; do
read -p "管理员用户名 [默认: admin]: " ADMIN_USERNAME < /dev/tty
ADMIN_USERNAME=${ADMIN_USERNAME:-admin}
if [[ ${#ADMIN_USERNAME} -lt 3 ]]; then
print_error "用户名至少3个字符"
continue
fi
break
done
while true; do
read -s -p "管理员密码至少6位: " ADMIN_PASSWORD < /dev/tty
echo ""
if [[ ${#ADMIN_PASSWORD} -lt 6 ]]; then
print_error "密码至少6个字符"
continue
fi
read -s -p "确认密码: " ADMIN_PASSWORD_CONFIRM < /dev/tty
echo ""
if [[ "$ADMIN_PASSWORD" != "$ADMIN_PASSWORD_CONFIRM" ]]; then
print_error "两次密码不一致"
continue
fi
break
done
print_success "管理员账号配置完成"
echo ""
}
install_backend_dependencies() {
print_step "安装后端依赖..."
cd "${PROJECT_DIR}/backend"
# 使用国内镜像加速
if [[ "$USE_ALIYUN_MIRROR" == "true" ]]; then
npm config set registry https://registry.npmmirror.com
fi
npm install --production
print_success "后端依赖安装完成"
echo ""
}
create_env_file() {
print_step "创建配置文件..."
# 生成随机JWT密钥
JWT_SECRET=$(openssl rand -base64 32)
cat > "${PROJECT_DIR}/backend/.env" << EOF
# 管理员账号
ADMIN_USERNAME=${ADMIN_USERNAME}
ADMIN_PASSWORD=${ADMIN_PASSWORD}
# JWT密钥
JWT_SECRET=${JWT_SECRET}
# 数据库路径
DATABASE_PATH=./data/database.db
# 存储目录
STORAGE_ROOT=./storage
# 服务端口
PORT=40001
# 环境
NODE_ENV=production
EOF
print_success "配置文件创建完成"
echo ""
}
create_data_directories() {
print_step "创建数据目录..."
mkdir -p "${PROJECT_DIR}/backend/data"
mkdir -p "${PROJECT_DIR}/backend/storage"
print_success "数据目录创建完成"
echo ""
}
configure_nginx() {
print_step "配置Nginx..."
if [[ "$USE_DOMAIN" == "true" ]]; then
if [[ "$SSL_METHOD" == "8" ]]; then
# HTTP配置
configure_nginx_http
else
# HTTPS配置
configure_nginx_https
fi
else
# IP模式HTTP配置
configure_nginx_http
fi
# 测试nginx配置
nginx -t
# 重启nginx
systemctl restart nginx
print_success "Nginx配置完成"
echo ""
}
configure_nginx_http() {
local server_name="${DOMAIN:-_}"
cat > /etc/nginx/sites-available/${PROJECT_NAME}.conf << EOF
server {
listen 80;
server_name ${server_name};
# 前端静态文件
location / {
root ${PROJECT_DIR}/frontend;
index app.html;
try_files \$uri \$uri/ /app.html;
}
# 后端API
location /api {
proxy_pass http://localhost:40001;
proxy_http_version 1.1;
proxy_set_header Upgrade \$http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host \$host;
proxy_cache_bypass \$http_upgrade;
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;
}
# 分享页面
location /s {
alias ${PROJECT_DIR}/frontend;
try_files /share.html =404;
}
# 静态资源
location /libs {
alias ${PROJECT_DIR}/frontend/libs;
expires 30d;
}
# 上传工具下载
location /download-tool {
alias ${PROJECT_DIR}/upload-tool/dist;
}
}
EOF
# 创建软链接
ln -sf /etc/nginx/sites-available/${PROJECT_NAME}.conf /etc/nginx/sites-enabled/${PROJECT_NAME}.conf
# 删除默认站点
rm -f /etc/nginx/sites-enabled/default
}
configure_nginx_https() {
cat > /etc/nginx/sites-available/${PROJECT_NAME}.conf << EOF
server {
listen 80;
server_name ${DOMAIN};
return 301 https://\$server_name\$request_uri;
}
server {
listen 443 ssl http2;
server_name ${DOMAIN};
ssl_certificate /etc/nginx/ssl/${DOMAIN}.crt;
ssl_certificate_key /etc/nginx/ssl/${DOMAIN}.key;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on;
# 前端静态文件
location / {
root ${PROJECT_DIR}/frontend;
index app.html;
try_files \$uri \$uri/ /app.html;
}
# 后端API
location /api {
proxy_pass http://localhost:40001;
proxy_http_version 1.1;
proxy_set_header Upgrade \$http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host \$host;
proxy_cache_bypass \$http_upgrade;
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;
}
# 分享页面
location /s {
alias ${PROJECT_DIR}/frontend;
try_files /share.html =404;
}
# 静态资源
location /libs {
alias ${PROJECT_DIR}/frontend/libs;
expires 30d;
}
# 上传工具下载
location /download-tool {
alias ${PROJECT_DIR}/upload-tool/dist;
}
}
EOF
# 创建软链接
ln -sf /etc/nginx/sites-available/${PROJECT_NAME}.conf /etc/nginx/sites-enabled/${PROJECT_NAME}.conf
# 删除默认站点
rm -f /etc/nginx/sites-enabled/default
}
start_backend_service() {
print_step "启动后端服务..."
cd "${PROJECT_DIR}/backend"
# 使用PM2启动
pm2 start server.js --name ${PROJECT_NAME}-backend
pm2 save
print_success "后端服务已启动"
echo ""
}
################################################################################
# 健康检查
################################################################################
health_check() {
print_step "正在进行健康检查..."
sleep 3
# 检查后端服务
if pm2 status | grep -q "${PROJECT_NAME}-backend.*online"; then
print_success "后端服务运行正常"
else
print_error "后端服务启动失败"
print_info "查看日志: pm2 logs ${PROJECT_NAME}-backend"
return 1
fi
# 检查端口
if netstat -tunlp | grep -q ":40001"; then
print_success "后端端口监听正常 (40001)"
else
print_error "后端端口监听异常"
return 1
fi
# 检查Nginx
if systemctl is-active --quiet nginx; then
print_success "Nginx服务运行正常"
else
print_error "Nginx服务异常"
return 1
fi
# 检查数据库
if [[ -f "${PROJECT_DIR}/backend/data/database.db" ]]; then
print_success "数据库初始化成功"
else
print_warning "数据库文件不存在"
fi
# 检查存储目录
if [[ -d "${PROJECT_DIR}/backend/storage" ]]; then
print_success "文件存储目录就绪"
else
print_warning "存储目录不存在"
fi
echo ""
return 0
}
################################################################################
# 完成提示
################################################################################
print_completion() {
clear
echo -e "${GREEN}"
echo "╔═══════════════════════════════════════════════════════════════╗"
echo "║ ║"
echo "║ 🎉 部署成功! ║"
echo "║ ║"
echo "╚═══════════════════════════════════════════════════════════════╝"
echo -e "${NC}"
echo ""
# 访问地址
if [[ "$USE_DOMAIN" == "true" ]]; then
if [[ "$SSL_METHOD" == "8" ]]; then
echo -e "${CYAN}访问地址:${NC} http://${DOMAIN}"
else
echo -e "${CYAN}访问地址:${NC} https://${DOMAIN}"
fi
else
PUBLIC_IP=$(curl -s ifconfig.me || curl -s icanhazip.com || echo "服务器IP")
echo -e "${CYAN}访问地址:${NC} http://${PUBLIC_IP}"
fi
echo -e "${CYAN}管理员账号:${NC} ${ADMIN_USERNAME}"
echo -e "${CYAN}管理员密码:${NC} ********"
echo ""
# 常用命令
echo -e "${YELLOW}常用命令:${NC}"
echo " 查看服务状态: pm2 status"
echo " 查看日志: pm2 logs ${PROJECT_NAME}-backend"
echo " 重启服务: pm2 restart ${PROJECT_NAME}-backend"
echo " 停止服务: pm2 stop ${PROJECT_NAME}-backend"
echo ""
# 配置文件位置
echo -e "${YELLOW}配置文件位置:${NC}"
echo " 后端配置: ${PROJECT_DIR}/backend/.env"
echo " Nginx配置: /etc/nginx/sites-enabled/${PROJECT_NAME}.conf"
echo " 数据库: ${PROJECT_DIR}/backend/data/database.db"
echo " 文件存储: ${PROJECT_DIR}/backend/storage"
echo ""
# SSL续期提示
if [[ "$USE_DOMAIN" == "true" ]] && [[ "$SSL_METHOD" != "8" ]]; then
echo -e "${YELLOW}SSL证书:${NC}"
case $SSL_METHOD in
1)
echo " 自动续期: 已配置Certbot自动续期"
;;
2|3|4)
echo " 自动续期: 已配置acme.sh自动续期"
;;
esac
echo ""
fi
echo -e "${GREEN}祝您使用愉快!${NC}"
echo ""
}
################################################################################
# 主流程
################################################################################
main() {
print_banner
# 检查root权限
check_root
# 系统检测
system_check
# 选择软件源
choose_mirror
# 安装依赖
install_dependencies
# 选择访问模式
choose_access_mode
# 配置管理员账号
configure_admin_account
# 创建项目目录
create_project_directory
# 下载项目
download_project
# 安装后端依赖
install_backend_dependencies
# 创建配置文件
create_env_file
# 创建数据目录
create_data_directories
# 部署SSL证书
deploy_ssl
# 配置Nginx
configure_nginx
# 启动后端服务
start_backend_service
# 健康检查
if ! health_check; then
print_error "健康检查未通过,请检查日志"
exit 1
fi
# 完成提示
print_completion
}
# 执行主流程
main