3 Commits 7c11b917b2 ... 29eeb72a14

Autor SHA1 Nachricht Datum
  快乐的梦鱼 29eeb72a14 ⚙️ 升级更新脚本 vor 4 Wochen
  快乐的梦鱼 f5ac0c297e Merge branch 'main' of http://47.100.2.119:3000/imengyu/wenbao-website into main vor 4 Wochen
  快乐的梦鱼 4e4d2deb82 📦 升级更新脚本 vor 4 Wochen

+ 29 - 27
scripts/UpdateScript/app.mjs

@@ -16,6 +16,8 @@ import {
   logout,
   checkLogged,
 } from './auth.mjs';
+import consola from 'consola';
+import { logError } from './utils.mjs';
 
 // 版本相关
 //========================================
@@ -41,7 +43,7 @@ export async function viewVersion(type) {
         data = await axiosInstance.get('/version/' + type);
     }
     catch (e) {
-      console.error('Failed to load version info', e);
+      logError('Failed to load version info', e);
       return;
     }
     const table = new Table({
@@ -74,9 +76,9 @@ export async function getVersion(action, type) {
           status: 1,
           config: "{}",
         });
-        console.log('Add version success');
+        consola.success('Add version success');
       } catch (e) {
-        console.error('Failed to add version', e);
+        logError('Failed to add version', e);
       }
       break;
     }
@@ -88,9 +90,9 @@ export async function getVersion(action, type) {
         return;
       try {
         await axiosInstance.delete('/version/' + versionId);
-        console.log('删除版本成功');
+        consola.success('删除版本成功');
       } catch (e) {
-        console.error('删除版本失败', e);
+        logError('删除版本失败', e);
       }
       break;
     }
@@ -118,9 +120,9 @@ export async function getVersion(action, type) {
         await axiosInstance.put('/version/' + versionId, {
           status: state
         });
-        console.log('设置状态成功');
+        consola.success('设置状态成功');
       } catch (e) {
-        console.error('设置状态失败', e);
+        logError('设置状态失败', e);
       }
       break;
     }
@@ -131,9 +133,9 @@ export async function getVersion(action, type) {
         await axiosInstance.put('/version/' + versionId, {
           config: config,
         });
-        console.log('设置配置成功');
+        consola.success('设置配置成功');
       } catch (e) {
-        console.error('设置配置失败', e);
+        logError('设置配置失败', e);
       }
       break;
     }
@@ -144,9 +146,9 @@ export async function getVersion(action, type) {
 
       try {
         await axiosInstance.post('/update/active/app', { versionId, updateId, isNext });
-        console.log('成功');
+        consola.success('成功');
       } catch (e) {
-        console.error('失败', e);
+        logError('失败', e);
       }
       break;
     }
@@ -156,14 +158,14 @@ export async function getVersion(action, type) {
 
       try {
         await axiosInstance.post('/update/active/web', { versionId, updateId });
-        console.log('成功');
+        consola.success('成功');
       } catch (e) {
-        console.error('失败', e);
+        logError('失败', e);
       }
       break;
     }
     default:
-      console.error('未知参数', action);
+      logError('未知参数', action);
       break;
   }
 }
@@ -176,7 +178,7 @@ export async function selectVersion(defaultVersionId = null) {
     appId: config.appId 
   }))).data;
   if (data.length === 0) {
-    console.error('没有版本');
+    logError('没有版本,请在管理后台创建 appId ' + config.appId + ' 的版本');
     return;
   }
   const versionId = (await select({
@@ -194,7 +196,7 @@ export async function selectUpdate() {
   const { versionId } = await selectVersion();
   const data = (await axiosInstance.get('/version/update?search=' + JSON.stringify({ versionId }))).data;
   if (data.length === 0) {
-    console.error('没有更新');
+    logError('没有更新');
     return;
   }
   const resultId = (await select({
@@ -225,7 +227,7 @@ export async function postUpdate(type, options) {
       break;
     }
     default:
-      console.error('Unknow type', type);
+      logError('Unknow type', type);
       break;
   }
 }
@@ -250,11 +252,11 @@ export async function deprecateOrDeleteUpdate(updateId) {
   })) === 0;
 
   if (deprecate && currentUpdateInfo.type !== 1) {
-    console.log('只有Web更新允许弃用');
+    consola.warn('只有Web更新允许弃用');
     return;
   }
   if (currentUpdateInfo.status === 0) {
-    console.log(`当前状态 ${stateConstant[currentUpdateInfo.status]} 无法弃用`);
+    consola.warn(`当前状态 ${stateConstant[currentUpdateInfo.status]} 无法弃用`);
     return;
   }
 
@@ -268,9 +270,9 @@ export async function deprecateOrDeleteUpdate(updateId) {
 
   try {
     await axiosInstance.post(`/update/${deprecate ? 'deprecate' : 'delete'}`, { updateId });
-    console.log(`${deprecate ? '弃用' : '删除'} 成功`);
+    consola.success(`${deprecate ? '弃用' : '删除'} 成功`);
   } catch (e) {
-    console.error(`操作失败`, e);
+    logError(`操作失败`, e);
   }
 }
 export async function viewUpdate(updateId) {
@@ -281,12 +283,12 @@ export async function viewUpdate(updateId) {
   try {
     currentUpdateInfo = (await axiosInstance.get('/update/' + updateId)).data;
   } catch (e) {
-    console.error('加载更新信息失败', updateId);
+    logError('加载更新信息失败', updateId);
   }
   try {
     currentVersionInfo = (await axiosInstance.get('/version/' + currentUpdateInfo.versionId)).data;
   } catch (e) {
-    console.error('加载版本信息失败', currentUpdateInfo.versionId);
+    logError('加载版本信息失败', currentUpdateInfo.versionId);
   }
 
   const table = new Table({
@@ -349,7 +351,7 @@ export async function getUpdate(action, type, all, options) {
 
     if (type !== 'all' && all !== 'all' && data.data.length > 10) {
       data.data = data.data.slice(0, 10);
-      console.log('filter!', all);
+      consola.info('filter!', all);
     }
 
     data.data.forEach((d) => {
@@ -378,7 +380,7 @@ export async function getUpdate(action, type, all, options) {
         break;
       }
       default: 
-        console.error('Unknow action', action);
+        logError('Unknow action', action);
         break;
     }
   }
@@ -386,8 +388,8 @@ export async function getUpdate(action, type, all, options) {
 export async function testVersion(version) {
   try {
     const data = await axiosInstance.get('/update-get-info?version=' + version);
-    console.log('版本信息', data.data);
+    consola.info('版本信息', data.data);
   } catch(e) {
-    console.log('获取失败', e);
+    logError('获取失败', e);
   }
 }

+ 145 - 20
scripts/UpdateScript/auth.mjs

@@ -10,8 +10,10 @@ import { dirname } from 'node:path';
 import { fileURLToPath } from 'node:url';
 import axios from 'axios';
 import md5 from 'md5';
-import { password } from '@inquirer/prompts';
+import { password, input } from '@inquirer/prompts';
 import { config } from './postConfig.mjs';
+import consola from 'consola';
+import { logError } from './utils.mjs';
 
 // 基础配置
 // ========================================
@@ -26,6 +28,7 @@ export const constant = {
 
 let currentData = {
   token: '',
+  refreshToken: '',
   identifier: '',
 };
 
@@ -38,6 +41,10 @@ function getErrorMessage(e) {
   return e instanceof Error ? e.message : (typeof e === 'object' ? e : '' + e);
 }
 
+async function saveCurrentData() {
+  await writeFile(constant.TokenSave, JSON.stringify(currentData));
+}
+
 // Axios 实例
 // ========================================
 
@@ -64,16 +71,75 @@ axiosInstance.interceptors.request.use((value) => {
   return value;
 });
 
+let refreshPromise = null;
+
+async function tryRefreshToken() {
+  if (!currentData.refreshToken) return false;
+  if (refreshPromise) return refreshPromise;
+  refreshPromise = (async () => {
+    try {
+      const res = await axios.post(
+        `${constant.ServerUrl}auth/refresh?identifier=${currentData.identifier}`,
+        { refreshToken: currentData.refreshToken },
+        { responseType: 'json', validateStatus: () => true }
+      );
+      if (res.data && res.data.success) {
+        currentData.token = {
+          authName: res.data.data.authName,
+          authKey: res.data.data.authKey,
+        };
+        if (res.data.data.refreshToken)
+          currentData.refreshToken = res.data.data.refreshToken;
+        await saveCurrentData();
+        return true;
+      } else
+        return false;
+    } catch {
+      // 刷新失败
+    }
+    return false;
+  })();
+  try {
+    return await refreshPromise;
+  } finally {
+    refreshPromise = null;
+  }
+}
+
 axiosInstance.interceptors.response.use((value) => {
-  if (value.data.success) return value.data;
-  else return Promise.reject(value.data);
+  if (value.data.success) 
+    return value.data;
+  const err = value.data;
+  err.__config = value.config;
+  return Promise.reject(err);
+}, (error) => {
+  return Promise.reject(error);
+});
+
+axiosInstance.interceptors.response.use(null, async (error) => {
+  const code = error?.code;
+  if (
+    (code === 40102 || code === 40103) &&
+    currentData.refreshToken &&
+    !error.__retried
+  ) {
+    const refreshed = await tryRefreshToken();
+    if (refreshed) {
+      const cfg = error.__config || error.config;
+      if (cfg) {
+        error.__retried = true;
+        return axiosInstance.request(cfg);
+      }
+    }
+  }
+  return Promise.reject(error);
 });
 
 // 认证初始化
 // ========================================
 
 /**
- * 初始化认证模块,从本地加载 token
+ * 初始化认证模块,从本地加载 token,并检查登录状态
  * @returns {Promise<void>}
  */
 export async function initAuth() {
@@ -87,6 +153,33 @@ export async function initAuth() {
     // 无 token 文件时使用默认值
   }
   initIdentifier();
+
+  if (currentData.token) {
+    try {
+      const res = await axiosInstance.get('/user/user-info');
+      if (res?.data) {
+        consola.info(`当前登录用户: ${res.data.name || res.data.user} (ID: ${res.data.id})`);
+        return;
+      }
+    } catch {
+      // 登录已过期,尝试刷新 token
+    }
+    if (currentData.refreshToken) {
+      consola.info('尝试刷新 token');
+      try {
+        const res = await tryRefreshToken();
+        if (res) {
+          consola.success('刷新登录成功');
+          return;
+        }
+      } catch (e) {
+        logError('刷新 token 失败', e);
+      }
+    }
+  }
+
+  consola.warn('未登录,进入登录界面');
+  await interactiveLogin();
 }
 
 /**
@@ -100,14 +193,52 @@ export function getCurrentData() {
 // ========================================
 
 /**
+ * 执行登录请求并保存 token
+ * @param {string} user 用户名
+ * @param {string} pass 密码
+ */
+async function doLogin(user, pass) {
+  const res = await axiosInstance.post('/auth?rember=true', {
+    method: 'key',
+    key: `${user}@${md5(pass)}`,
+  });
+  currentData.token = {
+    authName: res.data.authName,
+    authKey: res.data.authKey,
+  };
+  if (res.data.refreshToken)
+    currentData.refreshToken = res.data.refreshToken;
+  await saveCurrentData();
+}
+
+/**
+ * 交互式登录,循环直至登录成功或用户取消
+ */
+async function interactiveLogin() {
+  while (true) {
+    try {
+      const user = await input({ message: '输入用户名' });
+      const pass = await password({ message: '输入密码' });
+      await doLogin(user, pass);
+      consola.success('登录成功');
+      return;
+    } catch (e) {
+      if (e?.name === 'ExitPromptError' || e?.name === 'CancelPromptError')
+        return;
+      logError('登录失败', getErrorMessage(e));
+    }
+  }
+}
+
+/**
  * 检查登录状态
  */
 export async function checkLogged() {
   try {
     await axiosInstance.get('/auth');
-    console.log('已登录');
+    consola.success('已登录');
   } catch (e) {
-    console.error('获取状态失败:', getErrorMessage(e));
+    logError('获取状态失败:', getErrorMessage(e));
   }
 }
 
@@ -117,19 +248,12 @@ export async function checkLogged() {
  */
 export async function login(user) {
   try {
+    const username = user || await input({ message: '输入用户名' });
     const pass = await password({ message: '输入密码' });
-    const res = await axiosInstance.post('/auth?rember=true', {
-      method: 'key',
-      key: `${user}@${md5(pass)}`,
-    });
-    currentData.token = {
-      authName: res.data.authName,
-      authKey: res.data.authKey,
-    };
-    await writeFile(constant.TokenSave, JSON.stringify(currentData));
-    console.log('登录成功');
+    await doLogin(username, pass);
+    consola.success('登录成功');
   } catch (e) {
-    console.error('登录失败', getErrorMessage(e));
+    logError('登录失败', getErrorMessage(e));
   }
 }
 
@@ -138,11 +262,12 @@ export async function login(user) {
  */
 export async function logout() {
   currentData.token = '';
-  await writeFile(constant.TokenSave, JSON.stringify(currentData));
+  currentData.refreshToken = '';
+  await saveCurrentData();
   try {
     await axiosInstance.delete('/auth');
-    console.log('退出登录成功');
+    consola.success('退出登录成功');
   } catch (e) {
-    console.error('退出登录失败', getErrorMessage(e));
+    logError('退出登录失败', getErrorMessage(e));
   }
 }

+ 15 - 3
scripts/UpdateScript/index.mjs

@@ -7,6 +7,7 @@
 import { program } from 'commander';
 import { login, checkLogged, logout, initAuth } from './auth.mjs';
 import { testVersion, getVersion, getUpdate } from './app.mjs';
+import { logError } from './utils.mjs';
 
 //程序入口
 //============================================
@@ -46,6 +47,17 @@ function start() {
 
 initAuth().then(start);
 
-process.on('unhandledRejection', (reason, p) => {
-  console.error('Promise: ', p, 'Reason: ', reason)
-})
+process.on('unhandledRejection', (reason) => {
+  let reasonStr = '';
+  if (typeof reason === 'string') {
+    reasonStr = reason;
+  } else if (typeof reason === 'object') {
+    if (reason.message) {
+      reasonStr = reason.message;
+    }
+  } else {
+    reasonStr = reason.toString();
+  }
+  logError('UnhandledRejection:', reasonStr)
+})
+

+ 40 - 37
scripts/UpdateScript/postUpdate.mjs

@@ -11,9 +11,10 @@ import OSS from 'ali-oss';
 import cliProgress from 'cli-progress';
 import { selectVersion } from './app.mjs';
 import { config } from './postConfig.mjs';
-import { readFileRange, compressZip, execAsync } from './utils.mjs';
+import { readFileRange, compressZip, execAsync, logError } from './utils.mjs';
 import { dirname } from 'node:path';
 import { fileURLToPath } from 'node:url';
+import consola from 'consola';
 
 //基础配置
 //========================================
@@ -46,17 +47,19 @@ async function saveConfig(postConfig) {
 }
 
 async function getUpdateInfo(postConfig) {
+  let lastUseGit = false;
   let updateInfo = await input({ message: '输入更新信息', default: postConfig.lastUpdateInfo });
   if (updateInfo === 'git') {
-    console.log('开始获取git提交信息');
+    consola.info('开始获取git提交信息');
+    lastUseGit = true;
     updateInfo = await execAsync('git log -1 --pretty=format:"%h %s"');
-    console.log('使用git提交信息 "' + updateInfo + '" 作为更新信息');
+    consola.info('使用git提交信息 "' + updateInfo + '" 作为更新信息');
   }
-  return updateInfo;
+  return { updateInfo, lastUseGit };
 }
 export async function uploadMulitPartLarge(axiosInstance, fileInfo, filePath) {
 
-  console.log('开始分片上传');
+  consola.start('开始分片上传');
 
   const multuploadInfo = (await axiosInstance.post('/update/update-large-token', {
     fileSize: fileInfo.size,
@@ -94,7 +97,7 @@ async function aliOSSMultipartUpload(client, fileName, uploadFile, progressCallb
   const partSize = 10 * 1024 * 1024; // 分片大小,这里使用10MB
   const partCount = Math.ceil(fileInfo.size / partSize); // 计算总分片数
   
-  console.log(`开始分片上传到阿里OSS,文件大小: ${(fileInfo.size / 1024 / 1024).toFixed(2)}MB,分片数: ${partCount}`);
+  consola.start(`开始分片上传到阿里OSS,文件大小: ${(fileInfo.size / 1024 / 1024).toFixed(2)}MB,分片数: ${partCount}`);
   
   try {
     // 初始化分片上传
@@ -123,19 +126,19 @@ async function aliOSSMultipartUpload(client, fileName, uploadFile, progressCallb
         progressCallback(progress);
       }
       
-      console.log(`已上传分片 ${i + 1}/${partCount} (${(progress * 100).toFixed(2)}%)`);
+      consola.info(`已上传分片 ${i + 1}/${partCount} (${(progress * 100).toFixed(2)}%)`);
     }
     
     // 完成分片上传
     await client.completeMultipartUpload(fileName, uploadId, parts);
     
-    console.log('阿里OSS分片上传完成');
+    consola.success('阿里OSS分片上传完成');
     
     // 获取上传后的文件URL
     const url = await client.generateObjectUrl(fileName);
     return url;
   } catch (error) {
-    console.error('阿里OSS分片上传失败:', error);
+    logError('阿里OSS分片上传失败:', error);
     throw error;
   }
 }
@@ -146,7 +149,7 @@ async function aliOSSMultipartUpload(client, fileName, uploadFile, progressCallb
 export async function postAppUpdate(axiosInstance, param) {
   const postConfig = await getConfig();
   const { versionId, versionName } = await selectVersion(postConfig.lastVersion);
-  const updateInfo = await getUpdateInfo(postConfig);
+  const { updateInfo, lastUseGit } = await getUpdateInfo(postConfig);
 
   const serverConfig = await axiosInstance.post('/update/update-post', { 
     config: { 
@@ -159,7 +162,7 @@ export async function postAppUpdate(axiosInstance, param) {
   });
 
   postConfig.lastVersion = versionId;
-  postConfig.lastUpdateInfo = updateInfo;
+  postConfig.lastUpdateInfo = lastUseGit ? 'git': updateInfo;
   postConfig.lastTodaySubVersion++;
 
   const updateSource = (await select({
@@ -201,15 +204,15 @@ export async function postAppUpdate(axiosInstance, param) {
         updateInfo,
         versionCode: versionCode
       })).data;
-      console.log('上传成功');
-      console.log('新更新ID: ' + result.updateId);
+      consola.success('上传成功');
+      consola.info('新更新ID: ' + result.updateId);
     } catch (e) {
-      console.error('上传失败', e);
+      logError('上传失败', e);
     }
     return;
   }
   else {
-    console.error('错误的选择');
+    logError('错误的选择');
     return;
   }
 
@@ -218,11 +221,11 @@ export async function postAppUpdate(axiosInstance, param) {
   try {
     await access(uploadFile, constants.R_OK)
   } catch {
-    console.error(`Failed to access ${uploadFile}, did you created it?`);
+    logError(`Failed to access ${uploadFile}, did you created it?`);
     return;
   }
 
-  console.log('开始上传');
+  consola.start('开始上传');
 
   //小于8mb则小文件上传,否则使用阿里OSS上传
   const fileInfo = await stat(uploadFile);
@@ -246,10 +249,10 @@ export async function postAppUpdate(axiosInstance, param) {
       const result = (await axiosInstance.post('/update/update-post', formData, {
         headers: { 'Content-Type': 'multipart/form-data' }
       })).data;
-      console.log('上传成功');
-      console.log('新更新ID: ' + result.updateId);
+      consola.success('上传成功');
+      consola.info('新更新ID: ' + result.updateId);
     } catch (e) {
-      console.error('上传失败', e);
+      logError('上传失败', e);
     }
   } else {
 
@@ -281,7 +284,7 @@ export async function postAppUpdate(axiosInstance, param) {
       
       //小于96mb则直接上传,否则分片上传
 
-      console.log('Start upload to ali oss');
+      consola.start('Start upload to ali oss');
 
       let returnUrl = '';
       if (fileInfo.size < 96 * 1024 * 1024) {
@@ -302,9 +305,9 @@ export async function postAppUpdate(axiosInstance, param) {
         ossPublic: returnUrl,
       };
 
-      console.log('Upload to ali oss done');
+      consola.success('Upload to ali oss done');
     } else {
-      console.error('错误的 uploadAppType 配置 ' + serverConfig.uploadAppType);
+      logError('错误的 uploadAppType 配置 ' + serverConfig.uploadAppType);
     }
   
     try {
@@ -323,10 +326,10 @@ export async function postAppUpdate(axiosInstance, param) {
           fileName
         },
       })).data;
-      console.log('上传成功');
-      console.log('新更新ID: ' + result.updateId);
+      consola.success('上传成功');
+      consola.info('新更新ID: ' + result.updateId);
     } catch (e) {
-      console.error('上传失败', e);
+      logError('上传失败', e);
     }
   }
 }
@@ -340,10 +343,10 @@ export async function postWebUpdate(axiosInstance, param) {
   
   const postConfig = await getConfig();
   const { versionId, versionName } = await selectVersion(postConfig.lastVersion);
-  const updateInfo = await getUpdateInfo(postConfig);
+  const { updateInfo, lastUseGit } = await getUpdateInfo(postConfig);
 
   postConfig.lastVersion = versionId;
-  postConfig.lastUpdateInfo = updateInfo;
+  postConfig.lastUpdateInfo = lastUseGit ? 'git': updateInfo;
   postConfig.lastTodaySubVersion++;
 
   await axiosInstance.post('/update/update-post', { config: { type: 1, test: true, versionId, uploadWebConfig: config.uploadWebConfig, submitKey: config.submitKey } });
@@ -357,9 +360,9 @@ export async function postWebUpdate(axiosInstance, param) {
   await saveConfig(postConfig);
   
   if (!skipBuild && config.buildWebCommand) {
-    console.log('正在执行构建...');
+    consola.start('正在执行构建...');
     await execAsync(config.buildWebCommand);
-    console.log('构建完成');
+    consola.success('构建完成');
   }
 
   const distDir = path.resolve(__dirname, config.buildWebOutDir);
@@ -367,7 +370,7 @@ export async function postWebUpdate(axiosInstance, param) {
   try {
     await access(distDir, constants.R_OK)
   } catch {
-    console.error(`Failed to access ${distDir}`);
+    logError(`Failed to access ${distDir}`);
     return;
   }
 
@@ -375,11 +378,11 @@ export async function postWebUpdate(axiosInstance, param) {
   const skipFiles = config?.buildWebOptions?.skipFiles ?? [];
 
   if (!skipBuild) {
-    console.log('开始压缩zip...');
+    consola.start('开始压缩zip...');
     await compressZip(distDir, outputPath, skipFiles);
   }
 
-  console.log('开始上传zip');
+  consola.start('开始上传zip');
 
   let success = false;
 
@@ -407,15 +410,15 @@ export async function postWebUpdate(axiosInstance, param) {
     const result = (await axiosInstance.post('/update/update-post', formData, {
       headers: { 'Content-Type': 'multipart/form-data' }
     })).data;
-    console.log('上传成功');
-    console.log('新更新ID: ' + result.updateId);
+    consola.success('上传成功');
+    consola.info('新更新ID: ' + result.updateId);
     success = true;
   } catch (e) {
-    console.error('上传失败', e);
+    logError('上传失败', e);
   }
   if (!success || noDelete) 
     return;
     
-  console.log('删除zip');
+  consola.info('删除zip');
   await unlink(outputPath);
 }

+ 37 - 7
scripts/UpdateScript/utils.mjs

@@ -1,7 +1,8 @@
 import fs from 'fs';
-import archiver from 'archiver';
+import { ZipArchive } from 'archiver';
 import { exec } from 'child_process';
 import { readdir, stat } from 'fs/promises';
+import consola from 'consola';
 
 export function readFileRange(file, start, length) {
   return new Promise((resolve, reject) => {
@@ -37,9 +38,15 @@ export function pad(num, n) {
   }
   return strNum;
 }
+
+/**
+ * 异步执行命令
+ * @param {string} command 命令
+ * @returns {Promise<string>} 命令执行结果
+ */
 export function execAsync(command) {
   return new Promise((resolve, reject) => {
-    console.log('[CMD] ' + command);
+    consola.info('[CMD] ' + command);
     exec(command, (error, stdout, stderr) => {
       if (error) {
         reject(error);
@@ -49,6 +56,29 @@ export function execAsync(command) {
     });
   })
 }
+
+/**
+ * 记录错误信息
+ * @param {string} message 错误信息
+ * @param {*} e 错误对象
+ */
+export function logError(message, e) {
+  consola.error(message, e || '');
+}
+
+/**
+ * 获取错误信息
+ * @param {*} e 错误对象
+ * @returns {string} 错误信息
+ */
+export function getErrorMessage(e) {
+  if (e instanceof Error) 
+    return e.message;
+  if (typeof e === 'object' && e.message)
+    return e.message;
+  return '' + e;
+}
+
 /**
  * 压缩zip
  * @param {string} dir 压缩目录
@@ -58,7 +88,7 @@ export function execAsync(command) {
  */
 export async function compressZip(dir, targetFilePath, skipFiles) {
   const output = fs.createWriteStream(targetFilePath);
-  const archive = archiver('zip', {
+  const archive = new ZipArchive({
     zlib: { level: 9 } // Sets the compression level.
   });
   archive.pipe(output);
@@ -82,7 +112,7 @@ export async function compressZip(dir, targetFilePath, skipFiles) {
       const subPath = subPrefix + '/' + file;
       if (checkPathSkip(subPath))
         continue;
-      console.log('File: ' + subPath);
+      consola.info('File: ' + subPath);
       const filestat = await stat(dir + subPath);
       if (filestat.isDirectory()) {
         await loopDir(dir + subPath, subPath);
@@ -93,12 +123,12 @@ export async function compressZip(dir, targetFilePath, skipFiles) {
   }
   await loopDir(dir, '')
 
-  console.log('等待压缩zip...');
+  consola.start('等待压缩zip...');
 
   const waitArchive = new Promise((resolve, reject) => {
     output.on('close', function() {
-      console.log(archive.pointer() + ' total bytes');
-      console.log('archiver has been finalized and the output file descriptor has closed.');
+      consola.info(archive.pointer() + ' total bytes');
+      consola.success('archiver has been finalized and the output file descriptor has closed.');
       resolve();
     });
     archive.on('error', function(err) {