From 84ad3031c27f699f2819601c0c2513326c8aadd3 Mon Sep 17 00:00:00 2001 From: jingrow Date: Sun, 10 Aug 2025 14:25:18 +0800 Subject: [PATCH] =?UTF-8?q?=E9=87=8D=E6=9E=84install=5Fjsite.sh=E5=A2=9E?= =?UTF-8?q?=E5=8A=A0mode=E5=8F=82=E6=95=B0=E6=94=AF=E6=8C=81=E4=B8=8D?= =?UTF-8?q?=E5=90=8C=E4=BB=BB=E5=8A=A1=E7=B1=BB=E5=9E=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 189 +++++++++- install_jsite.sh | 674 ++++++++++++++++++++++++++++++++---- jsite.sh | 878 ----------------------------------------------- 3 files changed, 795 insertions(+), 946 deletions(-) delete mode 100644 jsite.sh diff --git a/README.md b/README.md index e9ce4e3..5345b22 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,189 @@ -# jshells +# jsite 自动化部署和管理脚本 + +这是一个集成了完整部署和日常管理功能的自动化脚本,支持多种任务模式。 + +## 功能特性 + +- 🚀 **完整部署**:一键部署完整的jsite环境 +- 🏗️ **网站管理**:创建、删除、构建、启动、停止、重启网站 +- 📊 **状态监控**:查看网站运行状态和日志 +- 🔧 **环境管理**:Docker、Node.js、PM2、Traefik等环境组件 +- ⚡ **智能端口分配**:自动分配和管理端口资源 + +## 使用方法 + +### 基本语法 + +```bash +./install_jsite.sh --mode <任务模式> [其他参数] +``` + +### 支持的任务模式 + +| 模式 | 功能 | 示例 | +|------|------|------| +| `deploy` | 完整部署模式(默认) | `--mode deploy --site-name myproject` | +| `create` | 创建新网站 | `--mode create --site-name myproject --git-repo <仓库地址>` | +| `delete` | 删除网站 | `--mode delete --site-name myproject` | +| `build` | 构建网站 | `--mode build --site-name myproject` | +| `start` | 启动网站 | `--mode start --site-name myproject` | +| `stop` | 停止网站 | `--mode stop --site-name myproject` | +| `restart` | 重启网站 | `--mode restart --site-name myproject` | +| `status` | 查看网站状态 | `--mode status --site-name myproject` | +| `list` | 列出所有网站 | `--mode list` | +| `logs` | 查看网站日志 | `--mode logs --site-name myproject` | + +### 常用参数 + +| 参数 | 说明 | 默认值 | +|------|------|--------| +| `--mode` | 任务模式 | `deploy` | +| `--site-name` | 网站名称 | `jingrow` | +| `--git-repo` | Git仓库地址 | `http://git.jingrow.com:3000/jsite/jingrow` | +| `--node-version` | Node.js版本 | `22` | +| `--start-port` | 起始端口 | `3001` | +| `--site-url` | 网站域名 | `starrbud.com` | + +### 使用示例 + +#### 1. 完整部署新项目 + +```bash +./install_jsite.sh \ + --mode deploy \ + --site-name myproject \ + --git-repo http://git.example.com/myproject \ + --site-url myproject.com \ + --public-ip 8.217.167.199 +``` + +#### 2. 创建新网站 + +```bash +./install_jsite.sh \ + --mode create \ + --site-name myproject \ + --git-repo http://git.example.com/myproject \ + --site-url myproject.com +``` + +#### 3. 构建和启动网站 + +```bash +# 构建项目 +./install_jsite.sh --mode build --site-name myproject + +# 启动项目 +./install_jsite.sh --mode start --site-name myproject +``` + +#### 4. 管理网站状态 + +```bash +# 查看状态 +./install_jsite.sh --mode status --site-name myproject + +# 停止网站 +./install_jsite.sh --mode stop --site-name myproject + +# 重启网站 +./install_jsite.sh --mode restart --site-name myproject +``` + +#### 5. 查看所有网站 + +```bash +./install_jsite.sh --mode list +``` + +#### 6. 查看日志 + +```bash +./install_jsite.sh --mode logs --site-name myproject +``` + +## 工作流程 + +### 完整部署流程 (deploy) +1. 创建jingrow用户 +2. 安装Docker(可选跳过) +3. 安装Node.js和NVM +4. 克隆项目代码 +5. 创建环境配置文件 +6. 安装项目依赖 +7. 安装PM2并启动项目 +8. 安装Traefik并配置反向代理 + +### 网站管理流程 +- **create**: 克隆代码 → 分配端口 → 创建配置 → 提示下一步 +- **build**: 安装依赖 → 构建项目 +- **start**: 检查状态 → 分配端口 → 启动PM2进程 +- **stop**: 停止PM2进程 +- **restart**: 重启PM2进程或启动(如果未运行) +- **delete**: 停止进程 → 清理配置 → 删除文件 + +## 目录结构 + +``` +/home/jingrow/ +├── jsite/ # 网站项目目录 +│ ├── myproject1/ # 项目1 +│ ├── myproject2/ # 项目2 +│ └── site_port.json # 端口分配记录 +├── traefik-docker/ # Traefik配置目录 +│ └── conf.d/website/ # 网站配置文件 +└── .nvm/ # Node.js版本管理 +``` + +## 注意事项 + +1. **权限要求**:脚本需要root权限运行 +2. **端口管理**:自动分配端口,避免冲突 +3. **环境依赖**:确保系统支持Docker和Node.js +4. **域名配置**:需要正确配置DNS解析 +5. **SSL证书**:Traefik自动申请Let's Encrypt证书 + +## 故障排除 + +### 常见问题 + +1. **端口被占用** + - 检查端口分配文件:`/home/jingrow/jsite/site_port.json` + - 手动修改端口或停止占用进程 + +2. **PM2启动失败** + - 检查项目依赖是否正确安装 + - 查看PM2日志:`pm2 logs <项目名>` + - 确认package.json中有start脚本 + +3. **Traefik配置问题** + - 检查配置文件:`/home/jingrow/traefik-docker/conf.d/website/` + - 重启Traefik服务 + +4. **域名访问问题** + - 确认DNS解析正确 + - 检查防火墙设置 + - 验证SSL证书状态 + +### 日志查看 + +```bash +# PM2日志 +pm2 logs <项目名> + +# Traefik日志 +docker logs + +# 系统日志 +journalctl -u docker +``` + +## 更新日志 + +- **v2.0**: 整合jsite.sh功能,支持多种任务模式 +- **v1.0**: 基础部署功能 + +## 许可证 + +MIT License diff --git a/install_jsite.sh b/install_jsite.sh index cb1c0cb..2d6b54f 100755 --- a/install_jsite.sh +++ b/install_jsite.sh @@ -22,6 +22,13 @@ NODE_VERSION="22" START_PORT=3001 PORT_INCREMENT=1 +# jsite管理相关配置 +JSITE_BASE_DIR="/home/jingrow/jsite" +TRAEFIK_CONFIG_DIR="/home/jingrow/traefik-docker/conf.d/website" + +# 任务模式 +MODE="deploy" # 默认模式:deploy, create, delete, build, start, stop, restart, status, list, logs + # 跳过选项 SKIP_DOCKER=false SKIP_TRAEFIK=false @@ -139,6 +146,12 @@ while [[ $# -gt 0 ]]; do shift ;; + # 任务模式 + --mode) + MODE="$2" + shift 2 + ;; + # 网络配置 --public-ip) PUBLIC_IP="$2" @@ -171,6 +184,18 @@ while [[ $# -gt 0 ]]; do -h|--help) echo "用法: $0 [选项]" echo "" + echo "任务模式 (--mode):" + echo " deploy 完整部署模式 (默认)" + echo " create 创建新网站" + echo " delete 删除网站" + echo " build 构建网站" + echo " start 启动网站" + echo " stop 停止网站" + echo " restart 重启网站" + echo " status 查看网站状态" + echo " list 列出所有网站" + echo " logs 查看网站日志" + echo "" echo "基础配置:" echo " --site-name NAME 项目名称 (默认: jingrow)" echo " --git-repo URL Git仓库地址" @@ -198,9 +223,15 @@ while [[ $# -gt 0 ]]; do echo " -h, --help 显示此帮助信息" echo "" echo "示例:" - echo " $0 --site-name myproject --public-ip 8.217.167.199" - echo " $0 --site-url example.com --api-key mykey123" - echo " $0 --force-update --public-ip 203.0.113.1" + echo " # 完整部署" + echo " $0 --mode deploy --site-name myproject --public-ip 8.217.167.199" + echo " # 创建网站" + echo " $0 --mode create --site-name myproject --git-repo http://git.example.com/project" + echo " # 管理网站" + echo " $0 --mode start --site-name myproject" + echo " $0 --mode stop --site-name myproject" + echo " $0 --mode status --site-name myproject" + echo " $0 --mode list" exit 0 ;; *) @@ -263,6 +294,44 @@ check_root() { fi } +# 检查jsite基础目录 +check_jsite_base_dir() { + if [ ! -d "$JSITE_BASE_DIR" ]; then + log_info "创建jsite基础目录: $JSITE_BASE_DIR" + mkdir -p "$JSITE_BASE_DIR" + chown "jingrow:jingrow" "$JSITE_BASE_DIR" + fi +} + +# 检查网站目录是否存在 +check_site_exists() { + local site_name="$1" + [ -d "$JSITE_BASE_DIR/$site_name" ] +} + +# 获取网站状态 +get_site_status() { + local site_name="$1" + + if ! check_site_exists "$site_name"; then + echo "not_exists" + return + fi + + # 检查PM2进程状态 + local pm2_status=$(su - "jingrow" -c " + export NVM_DIR=\"\$HOME/.nvm\" + [ -s \"\$NVM_DIR/nvm.sh\" ] && \. \"\$NVM_DIR/nvm.sh\" + pm2 list 2>/dev/null | grep -w '$site_name' | grep -c 'online' || echo '0' + ") + + if [ "$pm2_status" != "0" ]; then + echo "running" + else + echo "stopped" + fi +} + # 1. 创建jingrow用户 create_jingrow_user() { log_info "开始创建jingrow用户..." @@ -1319,89 +1388,560 @@ show_deployment_info() { log_success "部署脚本执行完成!" } -# 主函数 -main() { - log_info "开始jsite前端自动化部署..." - echo "" - log_info "部署参数:" - echo " - 项目名称: $SITE_NAME" - echo " - Git仓库: $GIT_REPO" - echo " - Node.js版本: $NODE_VERSION" - echo " - 跳过Docker: $SKIP_DOCKER" - echo " - 跳过Traefik: $SKIP_TRAEFIK" - echo " - 跳过依赖安装: $SKIP_DEPENDENCIES" - echo " - 跳过PM2: $SKIP_PM2" - echo " - 强制更新: $FORCE_UPDATE" - echo "" - log_info "端口配置:" - echo " - 起始端口: $START_PORT" - echo " - 端口增量: $PORT_INCREMENT" - echo "" - log_info ".env文件参数:" - echo " - 网站URL: $SITE_URL" - echo " - 站点名称: $SITE_NAME" - echo " - 服务器URL: $SERVER_URL" - echo " - 重新验证令牌: $REVALIDATE_TOKEN" - echo " - API密钥: $API_KEY" - echo " - API密钥: $API_SECRET" - echo "" +# ======================================== +# jsite管理功能函数 +# ======================================== + +# 生成Host规则 +generate_host_rule() { + local domain="$1" - check_root - create_jingrow_user + # 移除协议前缀 + domain=$(echo "$domain" | sed -E 's|^https?://||') + # 移除端口号 + domain=$(echo "$domain" | sed -E 's|:[0-9]+$||') - if [ "$SKIP_DOCKER" = false ]; then - install_docker + # 检查是否为一级域名 + if [[ "$domain" =~ ^[^.]+\.[^.]+$ ]]; then + echo "Host(\`$domain\`) || Host(\`www.$domain\`)" else - log_warning "跳过Docker安装" + echo "Host(\`$domain\`)" + fi +} + +# 创建traefik网站配置文件 +create_traefik_website_config_simple() { + local site_name="$1" + local domain="$2" + local port="$3" + + # 检查traefik配置目录是否存在 + if [ ! -d "$TRAEFIK_CONFIG_DIR" ]; then + log_warning "Traefik配置目录不存在: $TRAEFIK_CONFIG_DIR" + log_info "请先确保Traefik已正确安装" + return 1 fi - install_nodejs + local config_file="$TRAEFIK_CONFIG_DIR/$site_name.yml" + local host_rule=$(generate_host_rule "$domain") - if ! clone_jsite_project; then - log_error "项目克隆失败,部署终止" - exit 1 + # 获取本机IP地址 + local host_ip=$(get_optimal_host_ip) + + # 创建配置文件 + cat > "$config_file" << EOF +http: + routers: + main-https: + rule: &host_rule $host_rule + entryPoints: + - websecure + service: main-service + tls: + certResolver: myresolver + + main-http-redirect: + rule: *host_rule + entryPoints: + - web + middlewares: + - redirect-to-https + service: noop + + services: + main-service: + loadBalancer: + servers: + - url: "http://$host_ip:$port" + noop: + loadBalancer: + servers: + - url: "http://127.0.0.1:65535" # 占位用,无实际后端,仅用于HTTP跳转 + + middlewares: + redirect-to-https: + redirectScheme: + scheme: https + permanent: true +EOF + + chown "jingrow:jingrow" "$config_file" 2>/dev/null || true + chmod 644 "$config_file" + + log_success "Traefik配置文件已创建: $config_file" + log_info "域名: $domain -> $host_ip:$port" +} + +# 删除traefik网站配置文件 +remove_traefik_website_config() { + local site_name="$1" + local config_file="$TRAEFIK_CONFIG_DIR/$site_name.yml" + + if [ -f "$config_file" ]; then + rm -f "$config_file" + log_success "Traefik配置文件已删除: $config_file" + fi +} + +# 重启traefik服务以加载新配置 +restart_traefik() { + local traefik_dir="/home/jingrow/traefik-docker" + + if [ -d "$traefik_dir" ] && [ -f "$traefik_dir/docker-compose.yml" ]; then + log_info "重启Traefik服务以加载新配置..." + su - "jingrow" -c "cd '$traefik_dir' && docker compose restart" 2>/dev/null || { + log_warning "无法重启Traefik服务,请手动重启" + } + fi +} + +# 创建新网站 +create_site() { + log_info "开始创建网站: $SITE_NAME" + log_info "Git仓库: $GIT_REPO" + log_info "域名: $SITE_URL" + + # 检查网站是否已存在 + if check_site_exists "$SITE_NAME"; then + log_error "网站 $SITE_NAME 已存在" + return 1 fi + # 获取并分配端口 + local port=$(get_or_assign_port "$SITE_NAME") + + # 克隆项目 + log_info "克隆项目: $GIT_REPO" + if ! su - "jingrow" -c "cd '$JSITE_BASE_DIR' && git clone '$GIT_REPO' '$SITE_NAME'"; then + log_error "项目克隆失败" + return 1 + fi + + # 检查package.json是否存在 + if [ ! -f "$JSITE_BASE_DIR/$SITE_NAME/package.json" ]; then + log_error "package.json文件不存在,这可能不是一个有效的Node.js项目" + rm -rf "$JSITE_BASE_DIR/$SITE_NAME" + return 1 + fi + + # 保存端口分配 + save_port_assignment "$SITE_NAME" "$port" + + # 创建.env文件 create_env_file - if [ "$SKIP_DEPENDENCIES" = false ]; then - if ! install_project_dependencies; then - log_error "项目依赖安装失败,部署终止" - exit 1 + # 创建traefik配置文件 + if create_traefik_website_config_simple "$SITE_NAME" "$SITE_URL" "$port"; then + restart_traefik + fi + + log_success "网站 $SITE_NAME 创建成功" + log_info "项目路径: $JSITE_BASE_DIR/$SITE_NAME" + log_info "分配端口: $port" + log_info "绑定域名: $SITE_URL" + + # 提示下一步操作 + log_info "下一步操作:" + echo " 1. 构建项目: $0 --mode build --site-name $SITE_NAME" + echo " 2. 启动项目: $0 --mode start --site-name $SITE_NAME" +} + +# 删除网站 +delete_site() { + log_info "即将删除网站: $SITE_NAME" + read -p "确认删除?(y/N): " confirm + + if [ "$confirm" != "y" ] && [ "$confirm" != "Y" ]; then + log_info "取消删除操作" + return 0 + fi + + # 停止PM2进程 + local status=$(get_site_status "$SITE_NAME") + if [ "$status" = "running" ]; then + log_info "停止运行中的进程..." + stop_site + fi + + # 删除PM2配置 + su - "jingrow" -c " + export NVM_DIR=\"\$HOME/.nvm\" + [ -s \"\$NVM_DIR/nvm.sh\" ] && \. \"\$NVM_DIR/nvm.sh\" + pm2 delete '$SITE_NAME' 2>/dev/null || true + " + + # 删除traefik配置文件 + remove_traefik_website_config "$SITE_NAME" + + # 删除端口分配记录 + local port_file="$JSITE_BASE_DIR/site_port.json" + if [ -f "$port_file" ] && command -v jq &> /dev/null; then + jq "del(.$SITE_NAME)" "$port_file" > "${port_file}.tmp" && mv "${port_file}.tmp" "$port_file" + chown "jingrow:jingrow" "$port_file" 2>/dev/null || true + fi + + # 删除项目目录 + log_info "删除项目目录: $JSITE_BASE_DIR/$SITE_NAME" + rm -rf "$JSITE_BASE_DIR/$SITE_NAME" + + # 重启traefik以移除配置 + restart_traefik + + log_success "网站 $SITE_NAME 已删除" +} + +# 构建网站 +build_site() { + log_info "构建网站: $SITE_NAME" + + # 安装依赖 + log_info "安装依赖..." + if ! su - "jingrow" -c " + export NVM_DIR=\"\$HOME/.nvm\" + [ -s \"\$NVM_DIR/nvm.sh\" ] && \. \"\$NVM_DIR/nvm.sh\" + cd '$JSITE_BASE_DIR/$SITE_NAME' + npm install + "; then + log_error "依赖安装失败" + return 1 + fi + + # 构建项目 + log_info "构建项目..." + if ! su - "jingrow" -c " + export NVM_DIR=\"\$HOME/.nvm\" + [ -s \"\$NVM_DIR/nvm.sh\" ] && \. \"\$NVM_DIR/nvm.sh\" + cd '$JSITE_BASE_DIR/$SITE_NAME' + npm run build + "; then + log_error "项目构建失败" + return 1 + fi + + log_success "网站 $SITE_NAME 构建完成" +} + +# 启动网站 +start_site() { + local status=$(get_site_status "$SITE_NAME") + if [ "$status" = "running" ]; then + log_warning "网站 $SITE_NAME 已在运行中" + return 0 + fi + + # 获取分配的端口 + local port=$(get_or_assign_port "$SITE_NAME") + + log_info "启动网站: $SITE_NAME (端口: $port)" + + # 检查是否有构建产物 + if [ ! -d "$JSITE_BASE_DIR/$SITE_NAME/.next" ] && [ ! -d "$JSITE_BASE_DIR/$SITE_NAME/dist" ] && [ ! -d "$JSITE_BASE_DIR/$SITE_NAME/build" ]; then + log_warning "未找到构建产物,建议先执行构建: $0 --mode build --site-name $SITE_NAME" + fi + + # 使用PM2启动,设置端口环境变量 + if ! su - "jingrow" -c " + export NVM_DIR=\"\$HOME/.nvm\" + [ -s \"\$NVM_DIR/nvm.sh\" ] && \. \"\$NVM_DIR/nvm.sh\" + cd '$JSITE_BASE_DIR/$SITE_NAME' + PORT=$port pm2 start npm --name '$SITE_NAME' -- start + "; then + log_error "网站启动失败" + return 1 + fi + + # 等待启动完成 + sleep 3 + + local new_status=$(get_site_status "$SITE_NAME") + if [ "$new_status" = "running" ]; then + log_success "网站 $SITE_NAME 启动成功 (端口: $port)" + else + log_error "网站 $SITE_NAME 启动失败" + return 1 + fi +} + +# 停止网站 +stop_site() { + local status=$(get_site_status "$SITE_NAME") + if [ "$status" = "not_exists" ]; then + log_error "网站 $SITE_NAME 不存在" + return 1 + fi + + if [ "$status" = "stopped" ]; then + log_warning "网站 $SITE_NAME 已停止" + return 0 + fi + + log_info "停止网站: $SITE_NAME" + + if ! su - "jingrow" -c " + export NVM_DIR=\"\$HOME/.nvm\" + [ -s \"\$NVM_DIR/nvm.sh\" ] && \. \"\$NVM_DIR/nvm.sh\" + pm2 stop '$SITE_NAME' + "; then + log_error "网站停止失败" + return 1 + fi + + log_success "网站 $SITE_NAME 已停止" +} + +# 重启网站 +restart_site() { + local status=$(get_site_status "$SITE_NAME") + if [ "$status" = "not_exists" ]; then + log_error "网站 $SITE_NAME 不存在" + return 1 + fi + + log_info "重启网站: $SITE_NAME" + + if [ "$status" = "running" ]; then + if ! su - "jingrow" -c " + export NVM_DIR=\"\$HOME/.nvm\" + [ -s \"\$NVM_DIR/nvm.sh\" ] && \. \"\$NVM_DIR/nvm.sh\" + pm2 restart '$SITE_NAME' + "; then + log_error "网站重启失败" + return 1 fi else - log_warning "跳过项目依赖安装" + # 如果未运行,则启动 + start_site + return $? fi - # PM2安装和项目启动(在项目准备完成后) - if [ "$SKIP_PM2" = false ]; then - log_info "开始PM2安装和项目启动..." - install_pm2 - create_pm2_config - if ! start_project_with_pm2; then - log_error "PM2启动失败,部署终止" - log_info "请检查以下内容:" - log_info "1. 项目依赖是否正确安装" - log_info "2. package.json中是否有start脚本" - log_info "3. 项目端口是否被占用" - log_info "4. 查看PM2日志: pm2 logs $SITE_NAME" - exit 1 + log_success "网站 $SITE_NAME 重启成功" +} + +# 查看网站状态 +show_site_status() { + local status=$(get_site_status "$SITE_NAME") + + case "$status" in + "not_exists") + log_error "网站 $SITE_NAME 不存在" + return 1 + ;; + "running") + log_success "网站 $SITE_NAME 正在运行" + ;; + "stopped") + log_warning "网站 $SITE_NAME 已停止" + ;; + esac + + # 显示PM2详细状态 + if [ "$status" != "not_exists" ]; then + log_info "PM2状态:" + su - "jingrow" -c " + export NVM_DIR=\"\$HOME/.nvm\" + [ -s \"\$NVM_DIR/nvm.sh\" ] && \. \"\$NVM_DIR/nvm.sh\" + pm2 list | grep -E '(App name|$SITE_NAME)' || echo '未找到PM2进程' + " + fi +} + +# 列出所有网站 +list_sites() { + log_info "jsite网站列表:" + + if [ ! -d "$JSITE_BASE_DIR" ] || [ -z "$(ls -A "$JSITE_BASE_DIR" 2>/dev/null)" ]; then + echo " 暂无网站" + return 0 + fi + + for site_dir in "$JSITE_BASE_DIR"/*; do + if [ -d "$site_dir" ]; then + local site_name=$(basename "$site_dir") + local status=$(get_site_status "$site_name") + + case "$status" in + "running") + echo " - $site_name (运行中)" + ;; + "stopped") + echo " - $site_name (已停止)" + ;; + *) + echo " - $site_name (未知状态)" + ;; + esac fi - else - log_warning "跳过PM2安装和项目启动" + done +} + +# 查看网站日志 +show_site_logs() { + local status=$(get_site_status "$SITE_NAME") + if [ "$status" = "not_exists" ]; then + log_error "网站 $SITE_NAME 不存在" + return 1 fi - if [ "$SKIP_TRAEFIK" = false ]; then - install_traefik - create_traefik_website_config - start_traefik - else - log_warning "跳过Traefik安装和启动" - fi + log_info "网站 $SITE_NAME 的日志:" + su - "jingrow" -c " + export NVM_DIR=\"\$HOME/.nvm\" + [ -s \"\$NVM_DIR/nvm.sh\" ] && \. \"\$NVM_DIR/nvm.sh\" + pm2 logs '$SITE_NAME' + " +} + +# ======================================== +# 主函数 +# ======================================== + +# 主函数 +main() { + log_info "开始jsite管理脚本..." + echo "" + log_info "执行模式: $MODE" + log_info "目标网站: $SITE_NAME" + echo "" - show_deployment_info + # 检查用户和基础目录 + check_root + create_jingrow_user + check_jsite_base_dir - log_success "部署脚本执行完成!" + # 根据mode执行不同的任务 + case "$MODE" in + "deploy") + # 完整部署模式 + log_info "执行完整部署模式..." + echo "" + log_info "部署参数:" + echo " - 项目名称: $SITE_NAME" + echo " - Git仓库: $GIT_REPO" + echo " - Node.js版本: $NODE_VERSION" + echo " - 跳过Docker: $SKIP_DOCKER" + echo " - 跳过Traefik: $SKIP_TRAEFIK" + echo " - 跳过依赖安装: $SKIP_DEPENDENCIES" + echo " - 跳过PM2: $SKIP_PM2" + echo " - 强制更新: $FORCE_UPDATE" + echo "" + log_info "端口配置:" + echo " - 起始端口: $START_PORT" + echo " - 端口增量: $PORT_INCREMENT" + echo "" + log_info ".env文件参数:" + echo " - 网站URL: $SITE_URL" + echo " - 站点名称: $SITE_NAME" + echo " - 服务器URL: $SERVER_URL" + echo " - 重新验证令牌: $REVALIDATE_TOKEN" + echo " - API密钥: $API_KEY" + echo " - API密钥: $API_SECRET" + echo "" + + if [ "$SKIP_DOCKER" = false ]; then + install_docker + else + log_warning "跳过Docker安装" + fi + + install_nodejs + + if ! clone_jsite_project; then + log_error "项目克隆失败,部署终止" + exit 1 + fi + + create_env_file + + if [ "$SKIP_DEPENDENCIES" = false ]; then + if ! install_project_dependencies; then + log_error "项目依赖安装失败,部署终止" + exit 1 + fi + else + log_warning "跳过项目依赖安装" + fi + + # PM2安装和项目启动(在项目准备完成后) + if [ "$SKIP_PM2" = false ]; then + log_info "开始PM2安装和项目启动..." + install_pm2 + create_pm2_config + if ! start_project_with_pm2; then + log_error "PM2启动失败,部署终止" + log_info "请检查以下内容:" + log_info "1. 项目依赖是否正确安装" + log_info "2. package.json中是否有start脚本" + log_info "3. 项目端口是否被占用" + log_info "4. 查看PM2日志: pm2 logs $SITE_NAME" + exit 1 + fi + else + log_warning "跳过PM2安装和项目启动" + fi + + if [ "$SKIP_TRAEFIK" = false ]; then + install_traefik + create_traefik_website_config + start_traefik + else + log_warning "跳过Traefik安装和启动" + fi + + show_deployment_info + ;; + + "create") + # 创建新网站 + create_site + ;; + + "delete") + # 删除网站 + delete_site + ;; + + "build") + # 构建网站 + build_site + ;; + + "start") + # 启动网站 + start_site + ;; + + "stop") + # 停止网站 + stop_site + ;; + + "restart") + # 重启网站 + restart_site + ;; + + "status") + # 查看网站状态 + show_site_status + ;; + + "list") + # 列出所有网站 + list_sites + ;; + + "logs") + # 查看网站日志 + show_site_logs + ;; + + *) + log_error "不支持的mode: $MODE" + echo "支持的mode: deploy, create, delete, build, start, stop, restart, status, list, logs" + exit 1 + ;; + esac + + log_success "脚本执行完成!" } # 执行主函数 diff --git a/jsite.sh b/jsite.sh deleted file mode 100644 index 867a7a7..0000000 --- a/jsite.sh +++ /dev/null @@ -1,878 +0,0 @@ -#!/bin/bash - -# ======================================== -# jsite网站管理脚本 -# 功能:新建、删除、构建、启动、停止、重启jsite网站 -# ======================================== - -# 颜色定义 -RED='\033[0;31m' -GREEN='\033[0;32m' -YELLOW='\033[1;33m' -BLUE='\033[0;34m' -NC='\033[0m' # No Color - -# ======================================== -# 默认配置 -# ======================================== - -# 基础配置 -DEFAULT_GIT_REPO="http://git.jingrow.com:3000/jsite/jingrow" -JSITE_BASE_DIR="/home/jingrow/jsite" -TRAEFIK_CONFIG_DIR="/home/jingrow/traefik-docker/conf.d/website" - -# 默认配置参数 -SITE_NAME="jingrow" -START_PORT=3001 -PORT_INCREMENT=1 - -# 网络配置 -PUBLIC_IP="" # 公网IP地址 (用于内网IP不可用时) - -# .env文件参数 -SITE_URL="starrbud.com" -REVALIDATE_TOKEN="535bc122f3e364c" -SERVER_URL="https://admin.jingrow.com" -API_KEY="535bc122f3e364c" -API_SECRET="8629a3b12fc1cc2" - -# ======================================== -# 日志函数 -# ======================================== - -log_info() { - echo -e "${BLUE}[INFO]${NC} $1" -} - -log_success() { - echo -e "${GREEN}[SUCCESS]${NC} $1" -} - -log_warning() { - echo -e "${YELLOW}[WARNING]${NC} $1" >&2 -} - -log_error() { - echo -e "${RED}[ERROR]${NC} $1" >&2 -} - -# ======================================== -# 工具函数 -# ======================================== - -# 检查用户是否存在 -check_user() { - if ! id "jingrow" &>/dev/null; then - log_error "用户 jingrow 不存在,请先创建用户" - exit 1 - fi -} - -# 检查jsite基础目录 -check_base_dir() { - if [ ! -d "$JSITE_BASE_DIR" ]; then - log_info "创建jsite基础目录: $JSITE_BASE_DIR" - mkdir -p "$JSITE_BASE_DIR" - chown "jingrow:jingrow" "$JSITE_BASE_DIR" - fi -} - -# 检查网站目录是否存在 -check_site_exists() { - local site_name="$1" - [ -d "$JSITE_BASE_DIR/$site_name" ] -} - -# 获取网站状态 -get_site_status() { - local site_name="$1" - - if ! check_site_exists "$site_name"; then - echo "not_exists" - return - fi - - # 检查PM2进程状态 - local pm2_status=$(su - "jingrow" -c " - export NVM_DIR=\"\$HOME/.nvm\" - [ -s \"\$NVM_DIR/nvm.sh\" ] && \. \"\$NVM_DIR/nvm.sh\" - pm2 list 2>/dev/null | grep -w '$site_name' | grep -c 'online' || echo '0' - ") - - if [ "$pm2_status" != "0" ]; then - echo "running" - else - echo "stopped" - fi -} - -# 智能IP选择:优先使用内网IP,没有内网IP时使用公网IP -get_optimal_host_ip() { - local host_ip="" - - # 方法1: 尝试获取内网IP - local private_ip=$(ip route get 8.8.8.8 2>/dev/null | awk '{print $7}' | head -1) - if [ -n "$private_ip" ] && [ "$private_ip" != "8.8.8.8" ]; then - # 测试内网IP是否可达 - if ping -c 2 -W 1 "$private_ip" >/dev/null 2>&1; then - host_ip="$private_ip" - # 注意:这里不输出日志,避免污染返回值 - else - # 注意:这里不输出日志,避免污染返回值 - : - fi - fi - - # 方法2: 如果没有内网IP或内网IP不可达,使用公网IP - if [ -z "$host_ip" ]; then - if [ -n "$PUBLIC_IP" ]; then - host_ip="$PUBLIC_IP" - # 注意:这里不输出日志,避免污染返回值 - else - # 尝试自动获取公网IP - local auto_public_ip=$(curl -s --max-time 5 ifconfig.me 2>/dev/null || curl -s --max-time 5 ipinfo.io/ip 2>/dev/null) - if [ -n "$auto_public_ip" ]; then - host_ip="$auto_public_ip" - # 注意:这里不输出日志,避免污染返回值 - else - # 最后回退:使用第一个可用IP - host_ip=$(hostname -I | awk '{print $1}') - # 注意:这里不输出日志,避免污染返回值 - fi - fi - fi - - echo "$host_ip" -} - -# 显示帮助信息 -show_help() { - echo "用法: $0 <命令> [选项]" - echo "" - echo "命令:" - echo " create 新建网站" - echo " delete 删除网站" - echo " build 构建网站" - echo " start 启动网站" - echo " stop 停止网站" - echo " restart 重启网站" - echo " status 查看网站状态" - echo " list 列出所有网站" - echo " logs 查看网站日志" - echo "" - echo "create命令选项:" - echo " --site-name NAME 网站名称 (必需)" - echo " --git-repo URL Git仓库地址 (可选)" - echo " --site-url URL 网站URL (可选)" - echo "" - echo "全局选项:" - echo " -h, --help 显示此帮助信息" - echo "" - echo "示例:" - echo " $0 create --site-name mysite" - echo " $0 create --site-name mysite --git-repo http://git.example.com/project" - echo " $0 create --site-name mysite --site-url mysite.com" - echo " $0 create --site-name mysite --git-repo http://git.example.com/project --site-url mysite.com" - echo " $0 start mysite" - echo " $0 stop mysite" - echo " $0 restart mysite" - echo " $0 status mysite" - echo " $0 list" -} - -# 获取可用端口 -get_available_port() { - local site_name="$1" - local port_file="$JSITE_BASE_DIR/site_ports.json" - - # 如果端口文件不存在,返回起始端口 - if [ ! -f "$port_file" ]; then - echo "$START_PORT" - return 0 - fi - - # 检查网站是否已有分配的端口 - if command -v jq &> /dev/null; then - local existing_port=$(jq -r ".$site_name // empty" "$port_file" 2>/dev/null) - if [ -n "$existing_port" ] && [ "$existing_port" != "null" ] && [ "$existing_port" != "empty" ]; then - echo "$existing_port" - return 0 - fi - - # 找到最大端口号并加1 - local max_port=$(jq -r 'max(.[])' "$port_file" 2>/dev/null || echo "$START_PORT") - if [ -n "$max_port" ] && [ "$max_port" != "null" ]; then - echo $((max_port + PORT_INCREMENT)) - else - echo "$START_PORT" - fi - else - # 如果没有jq,使用简单逻辑 - echo "$START_PORT" - fi -} - -# 保存端口分配 -save_port_assignment() { - local site_name="$1" - local port="$2" - local port_file="$JSITE_BASE_DIR/site_ports.json" - - if command -v jq &> /dev/null; then - if [ -f "$port_file" ]; then - jq ". + {\"$site_name\": $port}" "$port_file" > "${port_file}.tmp" && mv "${port_file}.tmp" "$port_file" - else - echo "{\"$site_name\": $port}" > "$port_file" - fi - else - # 简单的JSON格式 - if [ -f "$port_file" ]; then - grep -v "\"$site_name\"" "$port_file" > "${port_file}.tmp" 2>/dev/null || true - sed -i "s/}$/,\"$site_name\": $port}/" "${port_file}.tmp" 2>/dev/null || echo "{\"$site_name\": $port}" > "${port_file}.tmp" - mv "${port_file}.tmp" "$port_file" - else - echo "{\"$site_name\": $port}" > "$port_file" - fi - fi - - chown "jingrow:jingrow" "$port_file" 2>/dev/null || true -} - -# 生成Host规则 -generate_host_rule() { - local domain="$1" - - # 移除协议前缀 - domain=$(echo "$domain" | sed -E 's|^https?://||') - # 移除端口号 - domain=$(echo "$domain" | sed -E 's|:[0-9]+$||') - - # 检查是否为一级域名 - if [[ "$domain" =~ ^[^.]+\.[^.]+$ ]]; then - echo "Host(\`$domain\`) || Host(\`www.$domain\`)" - else - echo "Host(\`$domain\`)" - fi -} - -# 创建.env文件 -create_env_file() { - local site_name="$1" - local port="$2" - - log_info "创建.env文件..." - - cd "$JSITE_BASE_DIR/$site_name" - - # 智能IP选择:优先使用内网IP,没有内网IP时使用公网IP(与traefik配置保持一致) - local host_ip=$(get_optimal_host_ip) - - # 输出IP选择结果 - if [[ "$host_ip" =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]]; then - log_success "使用IP地址: $host_ip" - else - log_warning "IP地址格式异常: $host_ip" - return 1 - fi - - # 构建PUBLIC_SITE_URL(优先使用内网IP,没有内网IP时使用公网IP) - local public_site_url="http://$host_ip:$port" - - # 检查.env文件是否已存在 - if [ -f "$JSITE_BASE_DIR/$site_name/.env" ]; then - log_warning ".env文件已存在,将会覆盖..." - fi - - # 创建.env文件 - cat > "$JSITE_BASE_DIR/$site_name/.env" << EOF -# 网站配置 -PUBLIC_SITE_URL=$public_site_url - -# 重新验证令牌 -REVALIDATE_TOKEN=$REVALIDATE_TOKEN - -# 项目配置 -JINGROW_SITE_NAME=$site_name - -# 服务器配置 -JINGROW_SERVER_URL=$SERVER_URL -JINGROW_API_KEY=$API_KEY -JINGROW_API_SECRET=$API_SECRET -EOF - - # 设置文件权限 - chown "jingrow:jingrow" "$JSITE_BASE_DIR/$site_name/.env" - chmod 600 "$JSITE_BASE_DIR/$site_name/.env" - - log_success ".env文件创建完成 (端口: $port, PUBLIC_SITE_URL: $public_site_url)" -} - -# 创建traefik网站配置文件 -create_traefik_config() { - local site_name="$1" - local domain="$2" - local port="$3" - - # 检查traefik配置目录是否存在 - if [ ! -d "$TRAEFIK_CONFIG_DIR" ]; then - log_warning "Traefik配置目录不存在: $TRAEFIK_CONFIG_DIR" - log_info "请先确保Traefik已正确安装" - return 1 - fi - - local config_file="$TRAEFIK_CONFIG_DIR/$site_name.yml" - local host_rule=$(generate_host_rule "$domain") - - # 获取本机IP地址 - local host_ip=$(get_optimal_host_ip) - - # 创建配置文件 - cat > "$config_file" << EOF -http: - routers: - main-https: - rule: &host_rule $host_rule - entryPoints: - - websecure - service: main-service - tls: - certResolver: myresolver - - main-http-redirect: - rule: *host_rule - entryPoints: - - web - middlewares: - - redirect-to-https - service: noop - - services: - main-service: - loadBalancer: - servers: - - url: "http://$host_ip:$port" - noop: - loadBalancer: - servers: - - url: "http://127.0.0.1:65535" # 占位用,无实际后端,仅用于HTTP跳转 - - middlewares: - redirect-to-https: - redirectScheme: - scheme: https - permanent: true -EOF - - chown "jingrow:jingrow" "$config_file" 2>/dev/null || true - chmod 644 "$config_file" - - log_success "Traefik配置文件已创建: $config_file" - log_info "域名: $domain -> $host_ip:$port" -} - -# 删除traefik网站配置文件 -remove_traefik_config() { - local site_name="$1" - local config_file="$TRAEFIK_CONFIG_DIR/$site_name.yml" - - if [ -f "$config_file" ]; then - rm -f "$config_file" - log_success "Traefik配置文件已删除: $config_file" - fi -} - -# 重启traefik服务以加载新配置 -restart_traefik() { - local traefik_dir="/home/jingrow/traefik-docker" - - if [ -d "$traefik_dir" ] && [ -f "$traefik_dir/docker-compose.yml" ]; then - log_info "重启Traefik服务以加载新配置..." - su - "jingrow" -c "cd '$traefik_dir' && docker compose restart" 2>/dev/null || { - log_warning "无法重启Traefik服务,请手动重启" - } - fi -} - -# ======================================== -# 核心功能函数 -# ======================================== - -# 解析create命令的参数 -parse_create_args() { - # 重置变量 - CREATE_SITE_NAME="" - CREATE_GIT_REPO="$DEFAULT_GIT_REPO" - CREATE_DOMAIN="$SITE_URL" - - # 解析参数 - while [[ $# -gt 0 ]]; do - case $1 in - --site-name) - CREATE_SITE_NAME="$2" - shift 2 - ;; - --git-repo) - CREATE_GIT_REPO="$2" - shift 2 - ;; - --site-url) - CREATE_DOMAIN="$2" - shift 2 - ;; - -h|--help) - show_help - exit 0 - ;; - *) - log_error "未知参数: $1" - echo "使用 $0 create --help 查看帮助" - exit 1 - ;; - esac - done - - # 验证必需参数 - if [ -z "$CREATE_SITE_NAME" ]; then - log_error "必需参数: --site-name" - echo "使用: $0 create --site-name <网站名称>" - exit 1 - fi -} - -# 新建网站 -create_site() { - local site_name="$CREATE_SITE_NAME" - local git_repo="$CREATE_GIT_REPO" - local domain="$CREATE_DOMAIN" - - log_info "开始创建网站: $site_name" - log_info "Git仓库: $git_repo" - log_info "域名: $domain" - - # 检查网站是否已存在 - if check_site_exists "$site_name"; then - log_error "网站 $site_name 已存在" - return 1 - fi - - # 获取并分配端口 - local port=$(get_available_port "$site_name") - - # 克隆项目 - log_info "克隆项目: $git_repo" - if ! su - "jingrow" -c "cd '$JSITE_BASE_DIR' && git clone '$git_repo' '$site_name'"; then - log_error "项目克隆失败" - return 1 - fi - - # 检查package.json是否存在 - if [ ! -f "$JSITE_BASE_DIR/$site_name/package.json" ]; then - log_error "package.json文件不存在,这可能不是一个有效的Node.js项目" - rm -rf "$JSITE_BASE_DIR/$site_name" - return 1 - fi - - # 保存端口分配 - save_port_assignment "$site_name" "$port" - - # 创建.env文件 - create_env_file "$site_name" "$port" - - # 创建traefik配置文件 - if create_traefik_config "$site_name" "$domain" "$port"; then - restart_traefik - fi - - log_success "网站 $site_name 创建成功" - log_info "项目路径: $JSITE_BASE_DIR/$site_name" - log_info "分配端口: $port" - log_info "绑定域名: $domain" - - # 提示下一步操作 - log_info "下一步操作:" - echo " 1. 构建项目: $0 build $site_name" - echo " 2. 启动项目: $0 start $site_name" -} - -# 删除网站 -delete_site() { - local site_name="$1" - - if [ -z "$site_name" ]; then - log_error "请指定网站名称" - return 1 - fi - - if ! check_site_exists "$site_name"; then - log_error "网站 $site_name 不存在" - return 1 - fi - - log_warning "即将删除网站: $site_name" - read -p "确认删除?(y/N): " confirm - - if [ "$confirm" != "y" ] && [ "$confirm" != "Y" ]; then - log_info "取消删除操作" - return 0 - fi - - # 停止PM2进程 - local status=$(get_site_status "$site_name") - if [ "$status" = "running" ]; then - log_info "停止运行中的进程..." - stop_site "$site_name" - fi - - # 删除PM2配置 - su - "jingrow" -c " - export NVM_DIR=\"\$HOME/.nvm\" - [ -s \"\$NVM_DIR/nvm.sh\" ] && \. \"\$NVM_DIR/nvm.sh\" - pm2 delete '$site_name' 2>/dev/null || true - " - - # 删除traefik配置文件 - remove_traefik_config "$site_name" - - # 删除端口分配记录 - local port_file="$JSITE_BASE_DIR/site_ports.json" - if [ -f "$port_file" ] && command -v jq &> /dev/null; then - jq "del(.$site_name)" "$port_file" > "${port_file}.tmp" && mv "${port_file}.tmp" "$port_file" - chown "jingrow:jingrow" "$port_file" 2>/dev/null || true - fi - - # 删除项目目录 - log_info "删除项目目录: $JSITE_BASE_DIR/$site_name" - rm -rf "$JSITE_BASE_DIR/$site_name" - - # 重启traefik以移除配置 - restart_traefik - - log_success "网站 $site_name 已删除" -} - -# 构建网站 -build_site() { - local site_name="$1" - - if [ -z "$site_name" ]; then - log_error "请指定网站名称" - return 1 - fi - - if ! check_site_exists "$site_name"; then - log_error "网站 $site_name 不存在" - return 1 - fi - - log_info "构建网站: $site_name" - - # 安装依赖 - log_info "安装依赖..." - if ! su - "jingrow" -c " - export NVM_DIR=\"\$HOME/.nvm\" - [ -s \"\$NVM_DIR/nvm.sh\" ] && \. \"\$NVM_DIR/nvm.sh\" - cd '$JSITE_BASE_DIR/$site_name' - npm install - "; then - log_error "依赖安装失败" - return 1 - fi - - # 构建项目 - log_info "构建项目..." - if ! su - "jingrow" -c " - export NVM_DIR=\"\$HOME/.nvm\" - [ -s \"\$NVM_DIR/nvm.sh\" ] && \. \"\$NVM_DIR/nvm.sh\" - cd '$JSITE_BASE_DIR/$site_name' - npm run build - "; then - log_error "项目构建失败" - return 1 - fi - - log_success "网站 $site_name 构建完成" -} - -# 启动网站 -start_site() { - local site_name="$1" - - if [ -z "$site_name" ]; then - log_error "请指定网站名称" - return 1 - fi - - if ! check_site_exists "$site_name"; then - log_error "网站 $site_name 不存在" - return 1 - fi - - local status=$(get_site_status "$site_name") - if [ "$status" = "running" ]; then - log_warning "网站 $site_name 已在运行中" - return 0 - fi - - # 获取分配的端口 - local port=$(get_available_port "$site_name") - - log_info "启动网站: $site_name (端口: $port)" - - # 检查是否有构建产物 - if [ ! -d "$JSITE_BASE_DIR/$site_name/.next" ] && [ ! -d "$JSITE_BASE_DIR/$site_name/dist" ] && [ ! -d "$JSITE_BASE_DIR/$site_name/build" ]; then - log_warning "未找到构建产物,建议先执行构建: $0 build $site_name" - fi - - # 使用PM2启动,设置端口环境变量 - if ! su - "jingrow" -c " - export NVM_DIR=\"\$HOME/.nvm\" - [ -s \"\$NVM_DIR/nvm.sh\" ] && \. \"\$NVM_DIR/nvm.sh\" - cd '$JSITE_BASE_DIR/$site_name' - PORT=$port pm2 start npm --name '$site_name' -- start - "; then - log_error "网站启动失败" - return 1 - fi - - # 等待启动完成 - sleep 3 - - local new_status=$(get_site_status "$site_name") - if [ "$new_status" = "running" ]; then - log_success "网站 $site_name 启动成功 (端口: $port)" - else - log_error "网站 $site_name 启动失败" - return 1 - fi -} - -# 停止网站 -stop_site() { - local site_name="$1" - - if [ -z "$site_name" ]; then - log_error "请指定网站名称" - return 1 - fi - - local status=$(get_site_status "$site_name") - if [ "$status" = "not_exists" ]; then - log_error "网站 $site_name 不存在" - return 1 - fi - - if [ "$status" = "stopped" ]; then - log_warning "网站 $site_name 已停止" - return 0 - fi - - log_info "停止网站: $site_name" - - if ! su - "jingrow" -c " - export NVM_DIR=\"\$HOME/.nvm\" - [ -s \"\$NVM_DIR/nvm.sh\" ] && \. \"\$NVM_DIR/nvm.sh\" - pm2 stop '$site_name' - "; then - log_error "网站停止失败" - return 1 - fi - - log_success "网站 $site_name 已停止" -} - -# 重启网站 -restart_site() { - local site_name="$1" - - if [ -z "$site_name" ]; then - log_error "请指定网站名称" - return 1 - fi - - local status=$(get_site_status "$site_name") - if [ "$status" = "not_exists" ]; then - log_error "网站 $site_name 不存在" - return 1 - fi - - log_info "重启网站: $site_name" - - if [ "$status" = "running" ]; then - if ! su - "jingrow" -c " - export NVM_DIR=\"\$HOME/.nvm\" - [ -s \"\$NVM_DIR/nvm.sh\" ] && \. \"\$NVM_DIR/nvm.sh\" - pm2 restart '$site_name' - "; then - log_error "网站重启失败" - return 1 - fi - else - # 如果未运行,则启动 - start_site "$site_name" - return $? - fi - - log_success "网站 $site_name 重启成功" -} - -# 查看网站状态 -show_site_status() { - local site_name="$1" - - if [ -z "$site_name" ]; then - log_error "请指定网站名称" - return 1 - fi - - local status=$(get_site_status "$site_name") - - case "$status" in - "not_exists") - log_error "网站 $site_name 不存在" - return 1 - ;; - "running") - log_success "网站 $site_name 正在运行" - ;; - "stopped") - log_warning "网站 $site_name 已停止" - ;; - esac - - # 显示PM2详细状态 - if [ "$status" != "not_exists" ]; then - log_info "PM2状态:" - su - "jingrow" -c " - export NVM_DIR=\"\$HOME/.nvm\" - [ -s \"\$NVM_DIR/nvm.sh\" ] && \. \"\$NVM_DIR/nvm.sh\" - pm2 list | grep -E '(App name|$site_name)' || echo '未找到PM2进程' - " - fi -} - -# 列出所有网站 -list_sites() { - log_info "jsite网站列表:" - - if [ ! -d "$JSITE_BASE_DIR" ] || [ -z "$(ls -A "$JSITE_BASE_DIR" 2>/dev/null)" ]; then - echo " 暂无网站" - return 0 - fi - - for site_dir in "$JSITE_BASE_DIR"/*; do - if [ -d "$site_dir" ]; then - local site_name=$(basename "$site_dir") - local status=$(get_site_status "$site_name") - - case "$status" in - "running") - echo " - $site_name (运行中)" - ;; - "stopped") - echo " - $site_name (已停止)" - ;; - *) - echo " - $site_name (未知状态)" - ;; - esac - fi - done -} - -# 查看网站日志 -show_site_logs() { - local site_name="$1" - - if [ -z "$site_name" ]; then - log_error "请指定网站名称" - return 1 - fi - - local status=$(get_site_status "$site_name") - if [ "$status" = "not_exists" ]; then - log_error "网站 $site_name 不存在" - return 1 - fi - - log_info "网站 $site_name 的日志:" - su - "jingrow" -c " - export NVM_DIR=\"\$HOME/.nvm\" - [ -s \"\$NVM_DIR/nvm.sh\" ] && \. \"\$NVM_DIR/nvm.sh\" - pm2 logs '$site_name' - " -} - -# ======================================== -# 主程序 -# ======================================== - -main() { - # 检查参数 - if [ $# -eq 0 ]; then - show_help - exit 1 - fi - - # 处理帮助选项 - case "$1" in - -h|--help) - show_help - exit 0 - ;; - esac - - # 检查用户和基础目录 - check_user - check_base_dir - - # 解析命令 - local command="$1" - shift # 移除命令,剩下的都是参数 - - case "$command" in - create) - parse_create_args "$@" - create_site - ;; - delete) - local site_name="$1" - delete_site "$site_name" - ;; - build) - local site_name="$1" - build_site "$site_name" - ;; - start) - local site_name="$1" - start_site "$site_name" - ;; - stop) - local site_name="$1" - stop_site "$site_name" - ;; - restart) - local site_name="$1" - restart_site "$site_name" - ;; - status) - local site_name="$1" - show_site_status "$site_name" - ;; - list) - list_sites - ;; - logs) - local site_name="$1" - show_site_logs "$site_name" - ;; - *) - log_error "未知命令: $command" - echo "" - show_help - exit 1 - ;; - esac -} - -# 执行主函数 -main "$@" \ No newline at end of file