diff --git a/install_jsite.sh b/install_jsite.sh index f076e3f..ed2e7c2 100755 --- a/install_jsite.sh +++ b/install_jsite.sh @@ -34,6 +34,11 @@ SKIP_DOCKER=false SKIP_TRAEFIK=false SKIP_DEPENDENCIES=false FORCE_UPDATE=false +SKIP_PM2=false + +# 端口管理参数 +START_PORT=3001 +PORT_INCREMENT=1 # .env文件参数 SITE_URL="http://192.168.2.200:3001" @@ -54,8 +59,13 @@ show_help() { echo " --skip-docker 跳过Docker安装" echo " --skip-traefik 跳过Traefik安装和启动" echo " --skip-dependencies 跳过项目依赖安装" + echo " --skip-pm2 跳过PM2安装和启动" echo " --force-update 强制更新项目(如果已存在)" echo "" + echo "端口配置:" + echo " --start-port PORT 起始端口 (默认: 3001)" + echo " --port-increment INCREMENT 端口增量 (默认: 1)" + echo "" echo ".env文件配置:" echo " --site-url URL 网站URL (默认: http://192.168.2.200:3001)" echo " --revalidate-token TOKEN 重新验证令牌 (默认: 535bc122f3e364c)" @@ -71,6 +81,7 @@ show_help() { echo " $0 --skip-docker --skip-traefik" echo " $0 --force-update" echo " $0 --site-url http://example.com --site-name myproject" + echo " $0 --start-port 3005 --port-increment 10" } # 解析命令行参数 @@ -101,10 +112,22 @@ parse_arguments() { SKIP_DEPENDENCIES=true shift ;; + --skip-pm2) + SKIP_PM2=true + shift + ;; --force-update) FORCE_UPDATE=true shift ;; + --start-port) + START_PORT="$2" + shift 2 + ;; + --port-increment) + PORT_INCREMENT="$2" + shift 2 + ;; --site-url) SITE_URL="$2" shift 2 @@ -222,6 +245,103 @@ install_nodejs() { log_success "npm版本: $NPM_VERSION" } +# 2.5. 安装PM2 +install_pm2() { + log_info "开始安装PM2..." + + # 检查PM2是否已安装 + PM2_VERSION=$(su - jingrow -c 'export NVM_DIR="$HOME/.nvm" && [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" && pm2 -v 2>/dev/null || echo "not_installed"') + + if [ "$PM2_VERSION" != "not_installed" ]; then + log_warning "PM2已安装,版本: $PM2_VERSION" + else + log_info "安装PM2..." + su - jingrow -c " + export NVM_DIR=\"\$HOME/.nvm\" + [ -s \"\$NVM_DIR/nvm.sh\" ] && \. \"\$NVM_DIR/nvm.sh\" + npm install -g pm2 + " + log_success "PM2安装完成" + fi + + # 设置PM2开机自启 + su - jingrow -c " + export NVM_DIR=\"\$HOME/.nvm\" + [ -s \"\$NVM_DIR/nvm.sh\" ] && \. \"\$NVM_DIR/nvm.sh\" + pm2 startup + " + log_success "PM2开机自启配置完成" +} + +# 2.6. 端口管理函数 +get_available_port() { + local project_name="$1" + local base_port="$START_PORT" + local increment="$PORT_INCREMENT" + + # 检查项目是否已有分配的端口 + local port_file="/home/jingrow/jsite/port_assignments.json" + + if [ -f "$port_file" ]; then + # 如果项目已存在,返回已分配的端口 + local existing_port=$(jq -r ".$project_name // empty" "$port_file" 2>/dev/null || echo "") + if [ -n "$existing_port" ] && [ "$existing_port" != "null" ] && [ "$existing_port" != "empty" ]; then + echo "$existing_port" + return 0 + fi + fi + + # 查找可用端口 + local port="$base_port" + local max_attempts=1000 + local attempts=0 + + while [ $attempts -lt $max_attempts ]; do + # 检查端口是否被占用 + if ! netstat -tuln 2>/dev/null | grep -q ":$port "; then + # 检查端口是否已被分配 + if [ -f "$port_file" ]; then + local used_ports=$(jq -r '.[]' "$port_file" 2>/dev/null || echo "") + if ! echo "$used_ports" | grep -q "^$port$"; then + echo "$port" + return 0 + fi + else + echo "$port" + return 0 + fi + fi + port=$((port + increment)) + attempts=$((attempts + 1)) + done + + log_error "无法找到可用端口,已尝试 $max_attempts 次" + return 1 +} + +# 2.7. 保存端口分配 +save_port_assignment() { + local project_name="$1" + local port="$2" + + local port_file="/home/jingrow/jsite/port_assignments.json" + + # 创建或更新端口分配文件 + if [ -f "$port_file" ]; then + # 更新现有文件 + jq ". + {\"$project_name\": $port}" "$port_file" > "${port_file}.tmp" && mv "${port_file}.tmp" "$port_file" + else + # 创建新文件 + echo "{\"$project_name\": $port}" > "$port_file" + fi + + # 设置文件权限 + chown jingrow:jingrow "$port_file" + chmod 644 "$port_file" + + log_success "端口 $port 已分配给项目 $project_name" +} + # 3. 克隆jsite项目 clone_jsite_project() { log_info "开始克隆jsite项目: $PROJECT_NAME..." @@ -259,6 +379,13 @@ create_env_file() { cd /home/jingrow/jsite/$PROJECT_NAME + # 获取项目端口 + local project_port=$(get_available_port "$PROJECT_NAME") + save_port_assignment "$PROJECT_NAME" "$project_port" + + # 更新SITE_URL以使用分配的端口 + local site_url_with_port=$(echo "$SITE_URL" | sed "s|:[0-9]*|:$project_port|") + # 检查.env文件是否已存在 if [ -f "/home/jingrow/jsite/$PROJECT_NAME/.env" ]; then if [ "$FORCE_UPDATE" = true ]; then @@ -271,7 +398,7 @@ create_env_file() { # 创建.env文件 cat > "/home/jingrow/jsite/$PROJECT_NAME/.env" << EOF -PUBLIC_SITE_URL=$SITE_URL +PUBLIC_SITE_URL=$site_url_with_port REVALIDATE_TOKEN=$REVALIDATE_TOKEN @@ -280,13 +407,116 @@ JINGROW_SITE_NAME=$SITE_NAME JINGROW_SERVER_URL=$SERVER_URL JINGROW_API_KEY=$API_KEY JINGROW_API_SECRET=$API_SECRET + +# 项目端口配置 +PORT=$project_port EOF # 设置文件权限 chown jingrow:jingrow "/home/jingrow/jsite/$PROJECT_NAME/.env" chmod 600 "/home/jingrow/jsite/$PROJECT_NAME/.env" - log_success ".env文件创建完成" + log_success ".env文件创建完成 (端口: $project_port)" +} + +# 4.5. 创建PM2配置文件 +create_pm2_config() { + log_info "创建PM2配置文件..." + + cd /home/jingrow/jsite/$PROJECT_NAME + + # 获取项目端口 + local project_port=$(get_available_port "$PROJECT_NAME") + + # 创建logs目录 + mkdir -p "/home/jingrow/jsite/$PROJECT_NAME/logs" + + # 创建ecosystem.config.js文件 + cat > "/home/jingrow/jsite/$PROJECT_NAME/ecosystem.config.js" << EOF +module.exports = { + apps: [{ + name: '$PROJECT_NAME', + script: 'npm', + args: 'start', + cwd: '/home/jingrow/jsite/$PROJECT_NAME', + instances: 1, + autorestart: true, + watch: false, + max_memory_restart: '1G', + env: { + NODE_ENV: 'production', + PORT: $project_port + }, + env_file: '.env', + log_file: './logs/combined.log', + out_file: './logs/out.log', + error_file: './logs/error.log', + log_date_format: 'YYYY-MM-DD HH:mm:ss Z', + merge_logs: true, + time: true + }] +}; +EOF + + # 设置文件权限 + chown -R jingrow:jingrow "/home/jingrow/jsite/$PROJECT_NAME/ecosystem.config.js" + chown -R jingrow:jingrow "/home/jingrow/jsite/$PROJECT_NAME/logs" + + log_success "PM2配置文件创建完成 (端口: $project_port)" +} + +# 4.6. 用PM2启动项目 +start_project_with_pm2() { + log_info "使用PM2启动项目: $PROJECT_NAME..." + + cd /home/jingrow/jsite/$PROJECT_NAME + + # 检查项目是否已经在PM2中运行 + PM2_STATUS=$(su - jingrow -c " + export NVM_DIR=\"\$HOME/.nvm\" + [ -s \"\$NVM_DIR/nvm.sh\" ] && \. \"\$NVM_DIR/nvm.sh\" + pm2 list | grep -c '$PROJECT_NAME' || echo '0' + ") + + if [ "$PM2_STATUS" != "0" ]; then + if [ "$FORCE_UPDATE" = true ]; then + log_warning "项目已在PM2中运行,重新启动..." + su - jingrow -c " + export NVM_DIR=\"\$HOME/.nvm\" + [ -s \"\$NVM_DIR/nvm.sh\" ] && \. \"\$NVM_DIR/nvm.sh\" + cd /home/jingrow/jsite/$PROJECT_NAME + pm2 delete $PROJECT_NAME 2>/dev/null || true + pm2 start ecosystem.config.js + " + else + log_warning "项目已在PM2中运行,跳过启动" + return + fi + else + su - jingrow -c " + export NVM_DIR=\"\$HOME/.nvm\" + [ -s \"\$NVM_DIR/nvm.sh\" ] && \. \"\$NVM_DIR/nvm.sh\" + cd /home/jingrow/jsite/$PROJECT_NAME + pm2 start ecosystem.config.js + " + fi + + # 保存PM2配置 + su - jingrow -c " + export NVM_DIR=\"\$HOME/.nvm\" + [ -s \"\$NVM_DIR/nvm.sh\" ] && \. \"\$NVM_DIR/nvm.sh\" + pm2 save + " + + # 显示PM2状态 + log_info "PM2项目状态:" + su - jingrow -c " + export NVM_DIR=\"\$HOME/.nvm\" + [ -s \"\$NVM_DIR/nvm.sh\" ] && \. \"\$NVM_DIR/nvm.sh\" + pm2 list + " + + log_success "项目已使用PM2启动完成" } # 5. 安装traefik @@ -442,8 +672,11 @@ show_deployment_info() { echo " - 用户: jingrow" echo " - jsite目录: /home/jingrow/jsite" echo " - 项目目录: /home/jingrow/jsite/$PROJECT_NAME" + echo " - 项目端口: $(get_available_port "$PROJECT_NAME")" echo " - Traefik目录: /home/jingrow/traefik" echo " - Traefik管理界面: http://localhost:8080" + echo " - PM2配置文件: /home/jingrow/jsite/$PROJECT_NAME/ecosystem.config.js" + echo " - PM2日志目录: /home/jingrow/jsite/$PROJECT_NAME/logs" echo "" log_info ".env文件配置:" echo " - PUBLIC_SITE_URL: $SITE_URL" @@ -453,10 +686,20 @@ show_deployment_info() { echo " - JINGROW_API_KEY: $API_KEY" echo " - JINGROW_API_SECRET: $API_SECRET" echo "" + log_info "PM2管理命令:" + echo " - 查看状态: pm2 list" + echo " - 查看日志: pm2 logs $PROJECT_NAME" + echo " - 重启项目: pm2 restart $PROJECT_NAME" + echo " - 停止项目: pm2 stop $PROJECT_NAME" + echo " - 删除项目: pm2 delete $PROJECT_NAME" + echo " - 监控界面: pm2 monit" + echo "" log_info "下一步操作:" echo " 1. 进入项目目录: cd /home/jingrow/jsite/$PROJECT_NAME" - echo " 2. 启动开发服务器: npm run dev" - echo " 3. 访问Traefik管理界面: http://localhost:8080" + echo " 2. 查看PM2状态: pm2 list" + echo " 3. 查看项目日志: pm2 logs $PROJECT_NAME" + echo " 4. 访问Traefik管理界面: http://localhost:8080" + echo " 5. 访问项目: $SITE_URL" echo "" log_warning "注意:请确保防火墙允许80、443、8080端口访问" } @@ -472,8 +715,13 @@ main() { 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" @@ -493,9 +741,29 @@ main() { fi install_nodejs + + if [ "$SKIP_PM2" = false ]; then + install_pm2 + else + log_warning "跳过PM2安装" + fi + clone_jsite_project create_env_file + if [ "$SKIP_DEPENDENCIES" = false ]; then + install_project_dependencies + else + log_warning "跳过项目依赖安装" + fi + + if [ "$SKIP_PM2" = false ]; then + create_pm2_config + start_project_with_pm2 + else + log_warning "跳过PM2配置和启动" + fi + if [ "$SKIP_TRAEFIK" = false ]; then install_traefik start_traefik @@ -503,12 +771,6 @@ main() { log_warning "跳过Traefik安装和启动" fi - if [ "$SKIP_DEPENDENCIES" = false ]; then - install_project_dependencies - else - log_warning "跳过项目依赖安装" - fi - show_deployment_info log_success "部署脚本执行完成!"