diff --git a/package.json b/package.json index 8619acf..6710d28 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,8 @@ "test": "echo \"Error: no test specified\" && exit 1", "start": "peace-rc start", "build": "peace-rc build", - "pack": "rm -rf build && rm -f *.tar.gz && bash script/package-app.sh" + "pack": "rm -rf build && rm -f *.tar.gz && bash script/package-app.sh", + "pack:offline":"bash script/pack-remote.sh" }, "author": "附离", "license": "ISC", diff --git a/script/pack-remote.sh b/script/pack-remote.sh new file mode 100644 index 0000000..c15f27f --- /dev/null +++ b/script/pack-remote.sh @@ -0,0 +1,188 @@ +#!/bin/bash +# 远程打包脚本 - 在 Windows Git Bash 中运行 +# 功能: 上传代码到 Linux 服务器,在 Linux 上打包,然后下载离线包 + +# ===== 配置区域 - 请修改为你的 Linux 服务器信息 ===== +REMOTE_USER="root" +REMOTE_HOST="10.8.30.179" # 修改为你的服务器地址或IP +REMOTE_PORT="22" +REMOTE_WORK_DIR="/tmp/wuyuanbiaoba-pack" +# ======================================================== + +APP_NAME="wuyuanbiaoba-web" + +# 读取版本号 +if [ -f package.json ]; then + VERSION=$(grep '"version"' package.json | head -1 | sed -E 's/.*"version": *"([^"]+)".*/\1/') + [ -z "$VERSION" ] && VERSION="latest" +else + VERSION="latest" +fi + +DEPLOY_PACKAGE="${APP_NAME}-${VERSION}-offline.tar.gz" + +echo "================================================" +echo "🌐 远程 Linux 打包" +echo "================================================" +echo "目标服务器: $REMOTE_USER@$REMOTE_HOST" +echo "工作目录: $REMOTE_WORK_DIR" +echo "应用版本: $VERSION" +echo "" + +# 检查配置 +if [ "$REMOTE_HOST" = "your-linux-server.com" ]; then + echo "❌ 错误: 请先配置服务器信息" + echo "" + echo "编辑文件: script/pack-remote.sh" + echo "修改以下配置:" + echo " REMOTE_USER=\"root\"" + echo " REMOTE_HOST=\"192.168.1.100\" # 你的 Linux 服务器" + echo " REMOTE_PORT=\"22\"" + echo "" + exit 1 +fi + +# 检查 SSH 连接 +echo "🔍 检查服务器连接..." +if ! ssh -p $REMOTE_PORT -o ConnectTimeout=5 $REMOTE_USER@$REMOTE_HOST "echo '连接成功'" 2>/dev/null; then + echo "❌ 无法连接到服务器" + echo "" + echo "请检查:" + echo " 1. 服务器地址是否正确: $REMOTE_HOST" + echo " 2. SSH 端口是否正确: $REMOTE_PORT" + echo " 3. SSH 密钥是否配置" + echo " 4. 网络是否通畅" + echo "" + echo "测试连接命令:" + echo " ssh -p $REMOTE_PORT $REMOTE_USER@$REMOTE_HOST" + echo "" + echo "配置 SSH 免密登录:" + echo " ssh-keygen -t rsa -b 4096" + echo " ssh-copy-id -p $REMOTE_PORT $REMOTE_USER@$REMOTE_HOST" + echo "" + exit 1 +fi + +echo "✅ 服务器连接正常" +echo "" + +# 创建远程工作目录 +echo "📁 准备远程工作目录..." +ssh -p $REMOTE_PORT $REMOTE_USER@$REMOTE_HOST "mkdir -p $REMOTE_WORK_DIR" + +# 上传源代码(排除不必要的文件) +echo "" +echo "📤 打包源代码..." + +# 创建临时 tar 包(排除不必要的文件) +TEMP_TAR="/tmp/${APP_NAME}-source-$$.tar.gz" +tar -czf "$TEMP_TAR" \ + --exclude='node_modules' \ + --exclude='build' \ + --exclude='.git' \ + --exclude='*.tar.gz' \ + --exclude='.npm' \ + --exclude='.cache' \ + --exclude='.vscode' \ + --exclude='*.log' \ + ./ + +if [ $? -ne 0 ]; then + echo "❌ 打包失败" + exit 1 +fi + +SOURCE_SIZE=$(du -h "$TEMP_TAR" | cut -f1) +echo "✅ 源代码打包完成 ($SOURCE_SIZE)" + +echo "" +echo "📤 上传到服务器..." +echo " (这可能需要一些时间...)" + +scp -P $REMOTE_PORT "$TEMP_TAR" $REMOTE_USER@$REMOTE_HOST:$REMOTE_WORK_DIR/source.tar.gz + +if [ $? -ne 0 ]; then + echo "" + echo "❌ 上传失败" + rm -f "$TEMP_TAR" + exit 1 +fi + +echo "✅ 上传完成" + +# 删除本地临时文件 +rm -f "$TEMP_TAR" + +# 在服务器上解压 +echo "" +echo "📦 在服务器上解压源代码..." +ssh -p $REMOTE_PORT $REMOTE_USER@$REMOTE_HOST "cd $REMOTE_WORK_DIR && tar -xzf source.tar.gz && rm -f source.tar.gz" + +if [ $? -ne 0 ]; then + echo "❌ 解压失败" + exit 1 +fi + +echo "✅ 源代码准备完成" + +# 在远程服务器执行打包 +echo "" +echo "================================================" +echo "🔨 在 Linux 服务器上执行打包..." +echo "================================================" +echo "" + +ssh -p $REMOTE_PORT $REMOTE_USER@$REMOTE_HOST "cd $REMOTE_WORK_DIR && bash script/package-app-offline.sh" + +if [ $? -ne 0 ]; then + echo "" + echo "❌ 远程打包失败" + echo "" + echo "你可以手动登录服务器检查:" + echo " ssh -p $REMOTE_PORT $REMOTE_USER@$REMOTE_HOST" + echo " cd $REMOTE_WORK_DIR" + echo " bash script/package-app-offline.sh" + exit 1 +fi + +# 下载打包文件 +echo "" +echo "📥 下载打包文件到本地..." + +scp -P $REMOTE_PORT $REMOTE_USER@$REMOTE_HOST:$REMOTE_WORK_DIR/$DEPLOY_PACKAGE ./ + +if [ $? -ne 0 ]; then + echo "❌ 下载失败" + exit 1 +fi + +echo "" +echo "✅ 打包文件已下载到本地" + +# 清理远程临时文件(可选) +echo "" +read -p "🗑️ 是否清理远程临时文件? (y/n) " -n 1 -r +echo +if [[ $REPLY =~ ^[Yy]$ ]]; then + echo " 清理中..." + ssh -p $REMOTE_PORT $REMOTE_USER@$REMOTE_HOST "rm -rf $REMOTE_WORK_DIR" + echo " ✅ 已清理" +else + echo " 保留远程文件: $REMOTE_USER@$REMOTE_HOST:$REMOTE_WORK_DIR" +fi + +LOCAL_SIZE=$(du -h $DEPLOY_PACKAGE | cut -f1) + +echo "" +echo "================================================" +echo "🎉 远程打包完成!" +echo "================================================" +echo "" +echo "📦 本地文件: $DEPLOY_PACKAGE ($LOCAL_SIZE)" +echo "" +echo "📋 下一步:" +echo " 1. 将此文件上传到目标离线服务器" +echo " 2. 在服务器上解压并部署:" +echo " tar -xzf $DEPLOY_PACKAGE" +echo " sudo ./deploy.sh" +echo "" diff --git a/script/package-app-offline.sh b/script/package-app-offline.sh new file mode 100644 index 0000000..43fecb5 --- /dev/null +++ b/script/package-app-offline.sh @@ -0,0 +1,400 @@ +#!/bin/bash +# Linux 离线打包脚本 - 开发模式部署 + +APP_NAME="wuyuanbiaoba-web" + +# 读取版本号 +if [ -f package.json ]; then + VERSION=$(grep '"version"' package.json | head -1 | sed -E 's/.*"version": *"([^"]+)".*/\1/') + [ -z "$VERSION" ] && VERSION="latest" +else + VERSION="latest" +fi + +TARGET_DIR="./build" +DEPLOY_PACKAGE="${APP_NAME}-${VERSION}-offline.tar.gz" + +echo "================================================" +echo "🚀 Linux 离线打包" +echo "================================================" +echo "应用: $APP_NAME" +echo "版本: $VERSION" +echo "" + +# 检查必要工具 +check_command() { + if ! command -v $1 &> /dev/null; then + echo "❌ 错误: 未找到 $1 命令" + exit 1 + fi +} + +check_command "node" +check_command "npm" +check_command "tar" + +echo "✅ 环境检查通过" +echo " Node.js: $(node -v)" +echo " npm: $(npm -v)" +echo "" + +# 清理旧构建 +echo "🧹 清理旧构建..." +rm -rf $TARGET_DIR +rm -f *.tar.gz +mkdir -p $TARGET_DIR + +# 复制源代码 +echo "" +echo "📁 复制源代码..." + +REQUIRED_FILES=( + "package.json" + "package-lock.json" + "index.html" + "config.cjs" +) + +REQUIRED_DIRS=( + "client" + "server" + "script" +) + +# 复制文件 +for file in "${REQUIRED_FILES[@]}"; do + if [ -f "$file" ]; then + cp "$file" "$TARGET_DIR/" + echo " ✅ $file" + else + echo " ⚠️ 警告: 缺少 $file" + fi +done + +# 复制目录 +for dir in "${REQUIRED_DIRS[@]}"; do + if [ -d "$dir" ]; then + cp -r "$dir" "$TARGET_DIR/" + echo " ✅ $dir/" + else + echo " ⚠️ 警告: 缺少 $dir/" + fi +done + +# 复制 README(可选) +[ -f README.md ] && cp README.md "$TARGET_DIR/" && echo " ✅ README.md" + +# 安装依赖(包括 devDependencies,因为开发模式需要) +echo "" +echo "📦 安装依赖 (包括开发依赖)..." +echo " 这可能需要几分钟..." +echo "" + +cd $TARGET_DIR + +# 使用 npm install 安装所有依赖(不是 --production) +echo "安装依赖..." +npm install + +if [ $? -ne 0 ]; then + echo "❌ npm 安装失败" + exit 1 +fi + +cd .. + +echo "" +echo "✅ node_modules 安装完成" +echo " 大小: $(du -sh $TARGET_DIR/node_modules | cut -f1)" + +# 创建部署脚本(参照 package-app.sh 的逻辑) +echo "" +echo "📝 生成部署脚本..." + +cat > $TARGET_DIR/deploy.sh << 'DEPLOY_EOF' +#!/bin/bash +# 应用部署脚本(离线版本,开发模式) + +APP_NAME="wuyuanbiaoba-web" +INSTALL_DIR="/opt/$APP_NAME" +SERVICE_NAME="$APP_NAME.service" +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + +echo "================================================" +echo "🚀 部署 $APP_NAME (离线开发模式)" +echo "================================================" + +# Root 权限检查 +if [ "$EUID" -ne 0 ]; then + echo "❌ 错误: 需要 root 权限" + echo " 使用: sudo ./deploy.sh" + exit 1 +fi + +# systemd 检查 +if ! pidof systemd &>/dev/null; then + echo "❌ 错误: 系统不支持 systemd" + exit 1 +fi + +# 检查 Node.js 环境 +echo "" +echo "🔍 检查 Node.js 环境..." + +if ! command -v node &> /dev/null; then + echo "❌ 错误: 未安装 Node.js" + echo " 请先安装 Node.js 20 或更高版本" + exit 1 +fi + +if ! command -v npm &> /dev/null; then + echo "❌ 错误: 未安装 npm" + exit 1 +fi + +NODE_VERSION=$(node -v | cut -d'v' -f2 | cut -d'.' -f1) +if [ "$NODE_VERSION" -lt 20 ]; then + echo "❌ 错误: Node.js 版本过低 ($(node -v))" + echo " 需要 Node.js 20 或更高版本" + exit 1 +fi + +echo " ✅ Node.js: $(node -v)" +echo " ✅ npm: $(npm -v)" + +# 部署应用 +echo "" +echo "🛠️ 开始部署..." + +# 停止服务 +if systemctl is-active --quiet $SERVICE_NAME; then + echo " 停止服务..." + systemctl stop $SERVICE_NAME + sleep 2 +fi + +if systemctl is-enabled --quiet $SERVICE_NAME 2>/dev/null; then + echo " 禁用服务..." + systemctl disable $SERVICE_NAME 2>/dev/null +fi + +# 清理旧备份(保留7天内的) +if [ -d "/opt" ]; then + echo " 清理旧备份..." + find /opt -maxdepth 1 -name "${APP_NAME}_backup_*" -type d -mtime +7 -exec rm -rf {} \; 2>/dev/null || true +fi + +# 备份现有版本 +if [ -d "$INSTALL_DIR" ]; then + BACKUP_DIR="${INSTALL_DIR}_backup_$(date +%Y%m%d_%H%M%S)" + echo " 备份到: $BACKUP_DIR" + mv "$INSTALL_DIR" "$BACKUP_DIR" +fi + +# 创建安装目录 +mkdir -p $INSTALL_DIR + +# 复制应用文件 +echo " 复制文件..." + +# 创建文件清单,确保只复制打包时包含的文件 +REQUIRED_FILES=( + "package.json" + "package-lock.json" + "index.html" + "config.cjs" +) + +REQUIRED_DIRS=( + "client" + "server" + "script" + "node_modules" +) + +# 复制必需文件 +for file in "${REQUIRED_FILES[@]}"; do + if [ -f "$SCRIPT_DIR/$file" ]; then + cp "$SCRIPT_DIR/$file" "$INSTALL_DIR/" + echo " ✅ $file" + else + echo " ⚠️ 警告: 缺少 $file" + fi +done + +# 复制必需目录 +for dir in "${REQUIRED_DIRS[@]}"; do + if [ -d "$SCRIPT_DIR/$dir" ]; then + cp -r "$SCRIPT_DIR/$dir" "$INSTALL_DIR/" + echo " ✅ $dir/" + else + echo " ⚠️ 警告: 缺少 $dir" + fi +done + +# 复制可选文件 +[ -f "$SCRIPT_DIR/README.md" ] && cp "$SCRIPT_DIR/README.md" "$INSTALL_DIR/" && echo " ✅ README.md" + +# 验证关键文件 +echo "" +echo "🔍 验证部署..." + +MISSING=0 +REQUIRED_CHECK=( + "package.json" + "node_modules" + "server" + "client" + "index.html" + "config.cjs" +) + +for item in "${REQUIRED_CHECK[@]}"; do + if [ ! -e "$INSTALL_DIR/$item" ]; then + echo " ❌ 缺少: $item" + MISSING=1 + else + echo " ✅ $item" + fi +done + +if [ $MISSING -eq 1 ]; then + echo "" + echo "❌ 部署验证失败" + exit 1 +fi + +# 验证关键依赖 +if [ ! -d "$INSTALL_DIR/node_modules/@peace/react_client" ]; then + echo " ⚠️ 警告: 缺少关键依赖 @peace/react_client" +fi + +# 获取 npm 和 node 的完整路径 +NPM_PATH=$(which npm) +NODE_PATH=$(which node) + +echo "" +echo "⚙️ 配置服务..." +echo " Node: $NODE_PATH" +echo " npm: $NPM_PATH" + +# 设置 systemd 环境变量 +SYSTEMD_PATH="/usr/local/bin:/usr/local/sbin:/usr/sbin:/usr/bin:/sbin:/bin" + +# 创建启动脚本 +cat > $INSTALL_DIR/start.sh << STARTEOF +#!/bin/bash +cd $INSTALL_DIR +export NODE_ENV=development +export PATH=$SYSTEMD_PATH +exec $NPM_PATH start +STARTEOF + +chmod +x $INSTALL_DIR/start.sh + +# 创建 systemd 服务文件(开发模式) +cat > /etc/systemd/system/$SERVICE_NAME << SERVICE_EOF +[Unit] +Description=$APP_NAME Node.js Application +After=network.target + +[Service] +Type=simple +User=root +WorkingDirectory=$INSTALL_DIR +ExecStart=$INSTALL_DIR/start.sh +Restart=on-failure +RestartSec=10 +Environment=NODE_ENV=development +Environment=PATH=$SYSTEMD_PATH + +StandardOutput=journal +StandardError=journal + +[Install] +WantedBy=multi-user.target +SERVICE_EOF + +# 重新加载 systemd +systemctl daemon-reload + +# 启用并启动服务 +systemctl enable $SERVICE_NAME +systemctl start $SERVICE_NAME + +# 等待服务启动 +sleep 3 + +echo "" +echo "================================================" +echo "📊 部署结果" +echo "================================================" +systemctl status $SERVICE_NAME --no-pager + +# 清理部署目录(如果在 /tmp 下) +DEPLOY_DIR="$SCRIPT_DIR" +if [[ "$DEPLOY_DIR" == "/tmp/"* ]]; then + echo "" + echo "🗑️ 清理部署临时目录: $DEPLOY_DIR" + cd /tmp + rm -rf "$DEPLOY_DIR" +fi + +echo "" +echo "================================================" +echo "✅ 部署完成!" +echo "================================================" +echo "" +echo "📌 常用命令:" +echo " 状态: systemctl status $SERVICE_NAME" +echo " 日志: journalctl -u $SERVICE_NAME -f" +echo " 重启: systemctl restart $SERVICE_NAME" +echo " 停止: systemctl stop $SERVICE_NAME" +echo "" +DEPLOY_EOF + +chmod +x $TARGET_DIR/deploy.sh + +# 创建打包 +echo "" +echo "📦 创建离线部署包..." +echo "" + +cd $TARGET_DIR +tar -czf ../$DEPLOY_PACKAGE . 2>&1 | grep -v "tar: Removing leading" +cd .. + +if [ ! -f "$DEPLOY_PACKAGE" ]; then + echo "❌ 打包失败" + exit 1 +fi + +PACKAGE_SIZE=$(du -h $DEPLOY_PACKAGE | cut -f1) + +echo "" +echo "================================================" +echo "✅ 打包完成!" +echo "================================================" +echo "" +echo "📦 部署包信息:" +echo " 文件名: $DEPLOY_PACKAGE" +echo " 大小: $PACKAGE_SIZE" +echo " 位置: $(pwd)/$DEPLOY_PACKAGE" +echo "" +echo "📋 包含内容:" +echo " ✅ 应用源代码" +echo " ✅ node_modules (包括开发依赖)" +echo " ✅ 部署脚本" +echo "" +echo "🎯 部署要求:" +echo " - Node.js >= 20.0.0" +echo " - npm" +echo " - systemd" +echo "" +echo "🎯 部署步骤:" +echo " 1. 上传到目标服务器" +echo " 2. 解压: tar -xzf $DEPLOY_PACKAGE" +echo " 3. 部署: sudo ./deploy.sh" +echo "" +echo "💡 此包以开发模式运行,包含所有依赖" +echo ""