整合營銷服務商

          電腦端+手機端+微信端=數據同步管理

          免費咨詢熱線:

          學習KOA框架

          學習KOA框架

          . Context 對象

          Koa 提供一個 Context 對象,表示一次對話的上下文(包括 HTTP 請求和 HTTP 回復)。通過加工這個對象,就可以控制返回給用戶的內容。

          Context.response.body屬性就是發送給用戶的內容

          const Koa=require('koa');
          const app=new Koa();
          const main=ctx=> {
           ctx.response.body='Hello World';
          };
          app.use(main);
          app.listen(3000);
          復制代碼
          

          上面代碼中,main函數用來設置ctx.response.body。然后,使用app.use方法加載main函數。

          你可能已經猜到了,ctx.response代表 HTTP Response。同樣地,ctx.request代表 HTTP Request。

          2. 路由

          原生路由用起來不太方便,我們可以使用封裝好的koa-route模塊

          const route=require('koa-route');
          const about=ctx=> {
           ctx.response.type='html';
           ctx.response.body='<a href="/">Index Page</a>';
          };
          const main=ctx=> {
           ctx.response.body='Hello World';
          };
          app.use(route.get('/', main));
          app.use(route.get('/about', about));
          復制代碼
          

          3. 靜態資源

          如果網站提供靜態資源(圖片、字體、樣式表、腳本......),為它們一個個寫路由就很麻煩,也沒必要。koa-static模塊封裝了這部分的請求。

          const path=require('path');
          const serve=require('koa-static');
          const main=serve(path.join(__dirname));
          app.use(main);
          復制代碼
          

          4. 中間件

          const logger=(ctx, next)=> {
          	 console.log(`${Date.now()} ${ctx.request.method} ${ctx.request.url}`);
           next();
          }
          app.use(logger);
          復制代碼
          

          像上面代碼中的logger函數就叫做"中間件"(middleware),因為它處在 HTTP Request 和 HTTP Response 中間,用來實現某種中間功能。app.use()用來加載中間件。

          基本上,Koa 所有的功能都是通過中間件實現的,前面例子里面的main也是中間件。每個中間件默認接受兩個參數,第一個參數是 Context 對象,第二個參數是next函數。只要調用next函數,就可以把執行權轉交給下一個中間件

          5. express和koa中間件對比

          express中間件是一個接一個的順序執行 koa中間件是按照圓圈循環進行,即從外層到內層,又從內層回到外層來結束。

          • koa2的中間件是通過 async await 實現的,中間件執行順序是“洋蔥圈”模型。 中間件之間通過next函數聯系,當一個中間件調用 next() 后,會將控制權交給下一個中間件, 直到下一個中間件不再執行 next() 后, 將會沿路折返,將控制權依次交換給前一個中間件。
          • 與 koa2 中間件不同的是,express中間件一個接一個的順序執行, 通常會將 response 響應寫在最后一個中間件中 主要特點: app.use 用來注冊中間件 遇到 http 請求,根據 path 和 method 判斷觸發哪些中間件 實現 next 機制,即上一個中間件會通過 next 觸發下一個中間件


          者:liu6

          轉發鏈接:https://juejin.im/post/5e6e088ff265da57434bd2b1

          錄結構

          ├── bin # 命令行工具命令
          │   ├── start.js # zuodeploy start 執行入口
          │   └── zuodeploy.js # zuodeploy 命令入口,在 package.json 的 bin 屬性中配置
          ├── docImages # README.md 文檔圖片 
          ├── frontend # 客戶端頁面/前端操作頁面(koa-static 靜態服務指定目錄)
          │   └── index.html # Vue + ElementUI + axios + socket.io
          ├── server # 服務端
          │   ├── utils
          │   │   ├── logger.js # log4js 
          │   │   └── runCmd.js # node child_process spawn(執行 shell 腳本、pm2 服務開啟)
          │   └── index.js # 主服務(koa 接口、靜態服務 + socket + 執行 shell 腳本)
          ├── .eslintrc.cjs # eslint 配置文件 + prettier
          ├── args.json # 用于 pm2 改造后,跨文件傳遞端口、密碼參數
          ├── CHANGELOG.md # release 版本功能迭代記錄
          ├── deploy-master.sh # 用于測試,當前目錄開啟服務偶,點擊部署按鈕,執行該腳本
          ├── index.js # zuodeploy start 執行文件,用于執行 pm2 start server/index.js 主服務 
          ├── package.json # 項目描述文件,npm 包名、版本號、cli 命令名稱、
          ├── publish.sh # npm publish(npm包) 發布腳本
          └── README.md # 使用文檔
          

          前后端技術棧、相關依賴

          • 前端/客戶端
            • 靜態 html + css,非前端工程化,庫都以 cdn 形式引入,通過庫以 UMD 打包方式暴露的全局變量使用
            • vue3,MVVM 框架,不用操作 dom
            • element-plus,基礎表單樣式統一、美化
            • axios,請求接口
            • socket.io,接收實時部署 log
          • 服務端
            • 普通接口,可能需要等完全部署好后,才能拿到結果
            • 基于 Node.js 技術棧,無數據庫
            • commander,用于生成的命令 zuodeploy 運行時幫助文檔、提示,zuodeploy start 執行入口
            • prompts,參照 vue-create,引導用戶輸入端口、密碼
            • koa,http 服務端, 提供接口、靜態服務運行容器(類似 nginx、tomcat 等)
            • koa-bodyparser,用于解析 post 請求參數(login 鑒權接口需要)
            • koa-router,用于不同接口(路徑,比如 /login, /deploy等)執行不同的方法
            • koa-session,用于接口鑒權,防止他人獲取到部署接口后瘋狂請求部署
            • koa-static,靜態服務器,類似 nginx 啟動靜態服務
            • socket.io,socket 服務端,當 git pull, npm run build 部署時間較長時,實時發送 log 到前端
            • log4js,帶時間戳的 log 輸出
            • pm2,直接執行,當 terminal 結束服務會被關掉,用 pm2 以后臺方式靜默執行

          基礎功能實現思路

          最初目標:前端頁面點擊部署按鈕,可以直接讓服務器執行部署,并將部署 log 返回給前端

          怎么去實現?

          • 1.要有一個前端頁面,給出 部署 按鈕,日志顯示區域。
          • 2.前端頁面與服務器交互,必須要有一個服務端 server
            • 2.1 提供接口,前端頁面點擊部署,請求該接口,知道什么時候要執行部署,
            • 2.2 后端接口接收到請求后,怎么執行部署任務,
            • 2.3 shell 腳本執行的 log,怎么搜集并發送給前端。同上,spawn 支持 log 輸出

          技術棧確定:

          • 1.Vue + ElementUI 基本頁面布局+基本邏輯,axios 請求接口數據
          • 2.使用 node 技術棧來提供 服務端 server
            • 2.1 使用 koa/koa-router 實現接口
            • 2.2 部署一般是執行 shell 腳本,node 使用內置子進程 spawn 可以執行 shell 腳本文件、跑 terminal 下運行的命令操作
            • 2.3 spawn 執行時,子進程 stdout, stderr 可以獲取到腳本執行 log,收集后返回給前端

          考慮到前端頁面的部署問題,可以與 koa server 服務放到一起,使用 koa-static 開啟靜態文件服務,支持前端頁面訪問

          這里不使用前端工程化 @vue/cli ,直接使用靜態 html,通過 cdn 引入 vue 等

          1.客戶端 Vue+ElementUI+axios

          前端服務我們放到 frontend/index.html,koa-static 靜態服務直接指向 frontend 目錄就可以訪問頁面了

          核心代碼如下:

          注意:cdn 鏈接都是 // 相對路徑,需要使用 http 服務打開頁面,不能以普通的 File 文件形式打開!可以等到后面 koa 寫好后,開啟服務再訪問

          <head>
            <title>zuo-deploy</title>
            <!-- 導入樣式 -->
            <link rel="stylesheet" href="//unpkg.com/element-plus/dist/index.css" />
            <!-- 導入 Vue 3 -->
            <script src="//unpkg.com/vue@next"></script>
            <!-- 導入組件庫 -->
            <script src="//unpkg.com/element-plus"></script>
            <script src="https://unpkg.com/axios/dist/axios.min.js"></script>
          </head>
          
          <body>
            <div id="app" style="margin:0 20px;">
              <el-button type="primary" @click="deploy">部署</el-button>
              <div>
                <p>部署日志:</p>
                <div class="text-log-wrap">
                  <pre>{{ deployLog }}</pre>
                </div>
              </div>
            </div>
            <script>
              const app={
                data() {
                  return {
                    deployLog: '點擊按鈕進行部署',
                  }
                },
                methods: {
                  deploy() {
                    this.deployLog='后端部署中,請稍等...'
                    axios.post('/deploy')
                      .then((res)=> {
                        // 部署完成,返回 log
                        console.log(res.data);
                        this.deployLog=res.data.msg
                      })
                      .catch(function (err) {
                        console.log(err);
                      })
                  }
                }
              }
          
              Vue.createApp(app).use(ElementPlus).mount('#app')
            </script>
          </body>
          

          2.服務端koa+koa-router+koa-static

          koa 開啟 http server,寫 deploy 接口處理。koa-static 開啟靜態服務

          // server/index.js
          const Koa=require("koa");
          const KoaStatic=require("koa-static");
          const KoaRouter=require("koa-router");
          const path=require("path");
          
          const app=new Koa();
          const router=new KoaRouter();
          
          router.post("/deploy", async (ctx)=> {
            // 執行部署腳本
            let execFunc=()=> {};
            try {
              let res=await execFunc();
              ctx.body={
                code: 0,
                msg: res,
              };
            } catch (e) {
              ctx.body={
                code: -1,
                msg: e.message,
              };
            }
          });
          
          app.use(new KoaStatic(path.resolve(__dirname, "../frontend")));
          app.use(router.routes()).use(router.allowedMethods());
          app.listen(7777, ()=> console.log(`服務監聽 ${7777} 端口`));
          

          將項目跑起來

          1. 在當前項目目錄,執行 npm init 初始化 package.json
          2. npm install koa koa-router koa-static --save 安裝依賴包
          3. node server/index.js 運行項目,注意如果 7777 端口被占用,需要換一個端口

          訪問 http:// 127.0.0.1:7777 就可以訪問頁面,點擊部署就可以請求成功了

          3.Node執行shell腳本并輸出log到前端

          node 內置模塊 child_process 下 spawn 執行 terminal 命令,包括執行 shell 腳本的 sh 腳本文件.sh 命令

          下來看一個 demo,新建一個 testExecShell 測試目錄,測試效果

          // testExecShell/runCmd.js
          const { spawn }=require('child_process');
          const ls=spawn('ls', ['-lh', '/usr']); // 執行 ls -lh /usr 命令
          
          ls.stdout.on('data', (data)=> {
            // ls 產生的 terminal log 在這里 console
            console.log(`stdout: ${data}`);
          });
          
          ls.stderr.on('data', (data)=> {
            // 如果發生錯誤,錯誤從這里輸出
            console.error(`stderr: ${data}`);
          });
          
          ls.on('close', (code)=> {
            // 執行完成后正常退出就是 0 
            console.log(`child process exited with code ${code}`);
          });
          

          運行 node testExecShell/runCmd.js 就可以使用 node 執行 ls \-lh /usr,并通過 ls.stdout 接收到 log 信息并打印

          回到正題,這里需要執行 shell 腳本,可以將 ls \-lh /usr 替換為 sh 腳本文件.sh 即可。下面來試試

          // testExecShell/runShell.js
          const { spawn }=require('child_process');
          const child=spawn('sh', ['testExecShell/deploy.sh']); // 執行 sh deploy.sh 命令
          
          child.stdout.on('data', (data)=> {
            // shell 執行的 log 在這里搜集,可以通過接口返回給前端
            console.log(`stdout: ${data}`);
          });
          
          child.stderr.on('data', (data)=> {
            // 如果發生錯誤,錯誤從這里輸出
            console.error(`stderr: ${data}`);
          });
          
          child.on('close', (code)=> {
            // 執行完成后正常退出就是 0 
            console.log(`child process exited with code ${code}`);
          });
          

          創建執行的 shell 腳本,可以先 sh estExecShell/deploy.sh 試試是否有可執行,如果沒執行權限,就添加(chmod +x 文件名)

          # /testExecShell/deploy.sh
          echo '執行 pwd'
          pwd
          echo '執行 git pull'
          git pull
          

          運行 node testExecShell/runShell.js 就可以讓 node 執行 deploy.sh 腳本了,如下圖

          參考:child\_process \- Node.js 內置模塊筆記[2]

          4.deploy接口集成執行shell腳本功能

          修改之前的 deploy 接口,加一個 runCmd 方法,執行當前目錄的 deploy.sh 部署腳本,完成后接口將執行 log 響應給前端

          // 新建 server/indexExecShell.js,將 server/index.js 內容拷貝進來,并做如下修改
          const rumCmd=()=> {
            return new Promise((resolve, reject)=> {
              const { spawn }=require('child_process');
              const child=spawn('sh', ['deploy.sh']); // 執行 sh deploy.sh 命令
          
              let msg=''
              child.stdout.on('data', (data)=> {
                // shell 執行的 log 在這里搜集,可以通過接口返回給前端
                console.log(`stdout: ${data}`);
                // 普通接口僅能返回一次,需要把 log 都搜集到一次,在 end 時 返回給前端
                msg +=`${data}`
              });
          
              child.stdout.on('end', (data)=> {
                resolve(msg) // 執行完畢后,接口 resolve,返回給前端
              });
          
              child.stderr.on('data', (data)=> {
                // 如果發生錯誤,錯誤從這里輸出
                console.error(`stderr: ${data}`);
                msg +=`${data}`
              });
          
              child.on('close', (code)=> {
                // 執行完成后正常退出就是 0 
                console.log(`child process exited with code ${code}`);
              });
            })
          }
          
          router.post("/deploy", async (ctx)=> {
            try {
              let res=await rumCmd(); // 執行部署腳本
              ctx.body={
                code: 0,
                msg: res,
              };
            } catch (e) {
              ctx.body={
                code: -1,
                msg: e.message,
              };
            }
          });
          

          修改完成后,運行 node server/indexExecShell.js 開啟最新的服務,點擊部署,接口執行正常,如下圖

          執行的是當前目錄的 deploy.sh,沒有對應的文件。將上面 testExeclShell/deploy.sh 放到當前目錄再點擊部署

          這樣自動化部署基礎功能基本就完成了。

          功能優化

          1.使用 socket 實時輸出 log

          上面的例子中,普通接口需要等部署腳本執行完成后再響應給前端,如果腳本中包含 git pull、npm run build 等耗時較長的命令,就會導致前端頁面一直沒log信息,如下圖

          測試 shell

          echo '執行 pwd'
          pwd
          echo '執行 git pull'
          git pull
          git clone git@github.com:zuoxiaobai/zuo11.com.git # 耗時較長的命令
          echo '部署完成'
          

          這里我們改造下,使用 socket.io[3] 來實時將部署 log 發送給前端

          socket.io 分為客戶端、服務端兩個部分

          客戶端代碼

          <!-- frontend/indexSocket.html -->
          <script src="https://cdn.socket.io/4.4.1/socket.io.min.js"></script>
          <script>
            // vue mounted 鉤子里面鏈接 socket 服務端
            mounted() {
              this.socket=io() // 鏈接到 socket 服務器,發一個 http 請求,成功后轉 101 ws 協議
              // 訂閱部署日志,拿到日志,就一點點 push 到數組,顯示到前端
              this.socket.on('deploy-log', (msg)=> {
                console.log(msg)
                this.msgList.push(msg)
              })
            },  
          </script>
          

          后端 koa 中引入 socket.io 代碼

          // server/indexSoket.js
          // npm install socket.io --save
          const app=new Koa();
          const router=new KoaRouter();
          
          // 開啟 socket 服務
          let socketList=[];
          const server=require("http").Server(app.callback());
          const socketIo=require("socket.io")(server);
          socketIo.on("connection", (socket)=> {
            socketList.push(socket);
            console.log("a user connected"); // 前端調用 io(),即可連接成功
          });
          // 返回的 socketIo 對象可以用來給前端廣播消息
          
          runCmd() {
            // 部分核心代碼
            let msg=''
            child.stdout.on('data', (data)=> {
              // shell 執行的 log 在這里搜集,可以通過接口返回給前端
              console.log(`stdout: ${data}`);
              socketIo.emit('deploy-log', `${data}`) //socket 實時發送給前端
              // 普通接口僅能返回一次,需要把 log 都搜集到一次,在 end 時 返回給前端
              msg +=`${data}`
            });
            // ...
            child.stderr.on('data', (data)=> {
              // 如果發生錯誤,錯誤從這里輸出
              console.error(`stderr: ${data}`);
              socketIo.emit('deploy-log', `${data}`) // socket 實時發送給前端
              msg +=`${data}`
            });
          }
          // app.listen 需要改為上面加入了 socket 服務的 server 對象
          server.listen(7777, ()=> console.log(`服務監聽 ${7777} 端口`));
          

          我們在之前的 demo 中加入上面的代碼,即可完成 socket 改造,node server/indexSocket.js,打開 127.0.0.1:7777/indexSocket.html,點擊部署,即可看到如下效果。完成 demo 訪問地址[4]

          相關問題

          1. 關于 http 轉 ws 協議,我們可以通過打開 F12 NetWork 面板看前端的 socket 相關連接步驟
          • GET http://127.0.0.1:7777/socket.io/?EIO=4&transport=polling&t=Nz5mBZk 獲取 sid
          • POST http://127.0.0.1:7777/socket.io/?EIO=4&transport=polling&t=Nz5mBaY&sid=DKQAS0fxzXUutg0wAAAG
          • GET http://127.0.0.1:7777/socket.io/?EIO=4&transport=polling&t=Nz5mBav&sid=DKQAS0fxzXUutg0wAAAG
          • ws://127.0.0.1:7777/socket.io/?EIO=4&transport=websocket&sid=DKQAS0fxzXUutg0wAAAG

          ws 這個里面可以看到 socket 傳的數據

          1. http 請求成功狀態碼一般是 200, ws Status Code 為 101 Switching Protocols

          2.部署接口添加鑒權

          上面只是用接口實現的功能,并沒有加權限控制,任何人知道接口地址后,可以通過 postman 請求該接口,觸發部署。如下圖

          為了安全起見,我們這里為接口添加鑒權,前端增加一個輸入密碼登錄的功能。這里使用 koa-session 來鑒權,只有登錄態才能請求成功

          // server/indexAuth.js
          // npm install koa-session koa-bodyparser --save
          // ..
          const session=require("koa-session");
          const bodyParser=require("koa-bodyparser"); // post 請求參數解析
          const app=new Koa();
          const router=new KoaRouter();
          
          app.use(bodyParser()); // 處理 post 請求參數
          
          // 集成 session
          app.keys=[`自定義安全字符串`]; // 'some secret hurr'
          const CONFIG={
            key: "koa:sess" /** (string) cookie key (default is koa:sess) */,
            /** (number || 'session') maxAge in ms (default is 1 days) */
            /** 'session' will result in a cookie that expires when session/browser is closed */
            /** Warning: If a session cookie is stolen, this cookie will never expire */
            maxAge: 0.5 * 3600 * 1000, // 0.5h
            overwrite: true /** (boolean) can overwrite or not (default true) */,
            httpOnly: true /** (boolean) httpOnly or not (default true) */,
            signed: true /** (boolean) signed or not (default true) */,
            rolling: false /** (boolean) Force a session identifier cookie to be set on every response. The expiration is reset to the original maxAge, resetting the expiration countdown. (default is false) */,
            renew: false /** (boolean) renew session when session is nearly expired, so we can always keep user logged in. (default is false)*/,
          };
          app.use(session(CONFIG, app));
          
          router.post("/login", async (ctx)=> {
            let code=0;
            let msg="登錄成功";
            let { password }=ctx.request.body;
            if (password===`888888`) { // 888888 為設置的密碼
              ctx.session.isLogin=true;
            } else {
              code=-1;
              msg="密碼錯誤";
            }
            ctx.body={
              code,
              msg,
            };
          });
          
          router.post("/deploy", async (ctx)=> {
            if (!ctx.session.isLogin) {
              ctx.body={
                code: -2,
                msg: "未登錄",
              };
              return;
            }
            // 有登錄態,執行部署
          })
          

          前端相關改動,加一個密碼輸入框、一個登錄按鈕

          <!-- frontend/indexAuth.html 注意id="app"包裹 -->
          <div class="login-area">
            <div v-if="!isLogin">
              <el-input v-model="password" type="password" style="width: 200px;"></el-input>
               
              <el-button type="primary" @click="login">登錄</el-button>
            </div>
            <div v-else>已登錄</div>
          </div>
          <script>
          data() {
            return {
              isLogin: false,
              password: ''
            }
          },
          methods: {
            login() {
              if (!this.password) {
                this.$message.warning('請輸入密碼')
                return
              }
              axios.post('/login', { password: this.password })
                .then((response)=> {
                  console.log(response.data);
                  let { code, msg }=response.data
                  if (code===0) {
                    this.isLogin=true
                  } else {
                    this.$message.error(msg)
                  }
                })
                .catch(function (err) {
                  console.log(err);
                  this.$message.error(err.message)
                })
            }
          }
          </script>
          

          node server/indexAuth.js,打開 127.0.0.1:7777/indexAuth.html,登錄成功之后才能部署

          3.封裝成一個npm包cli工具

          為什么封裝成 npm 包,使用命令行工具開啟服務。主要是簡單易用,如果不使用命令行工具形式,需要三步:

          1. 先下載代碼到服務器
          2. npm install
          3. node index.js 或者 pm2 start index.js -n xxx 開啟服務

          改成 npm 包命令行工具形式只需要下面兩步,而且更節省時間

          1. npm install zuo-deploy pm2 -g
          2. 運行 zuodeploy start 會自動使用 pm2 開啟服務

          下面先來看一個簡單的例子,創建一個 npm 包并上傳到 npm 官方庫步驟

          • 需要有 npm 賬號,如果沒有可以到 www.npmjs.com/[5] 注冊一個,我的用戶名是 'guoqzuo'
          • 創建一個文件夾,用于存放 npm 包內容,比如 npmPackage
          • 在該目錄下,運行 npm init 初始化一個 package.json,輸入的 name 就是 npm 包名,這里我設置 name 為 'zuoxiaobai-test'
          • 包名有兩種形式,普通包 vue-cli,作用域包 @vue/cli,區別參見 npm包前面加\@是什么意思\(vue-cli與\@vue/cli的區別\)[6]
          • 一般默認入口為 index.js,暴露出一個變量、一個方法
          // index.js
          module.exports={
            name: '寫一個npm包',
            doSomething() {
              console.log('這個npm暴露一個方法')
            }
          }
          
          • 這樣就可以直接發布了,創建一個 publish 腳本,并執行(linux 下 chmod +x publish.sh;./publish.sh;)
          # publish.sh
          npm config set registry=https://registry.npmjs.org
          npm login # 登陸 ,如果有 OTP, 郵箱會接收到驗證碼,輸入即可
          # 登錄成功后,短時間內會保存狀態,可以直接 npm pubish
          npm publish # 可能會提示名稱已存在,換個名字,獲取使用作用域包(@xxx/xxx)
          npm config set registry=https://registry.npm.taobao.org # 還原淘寶鏡像
          

          到 npmjs.org 搜索對應包就可以看到了

          使用該 npm 包,創建 testNpm/index.js

          const packageInfo=require('zuoxiaobai-test')
          
          console.log(packageInfo) 
          packageInfo.doSomething()
          

          在 testNpm 目錄下 npm init 初始化 package.json,再 npm install zuoxiaobai-test --save; 再 node index.js,執行情況如下圖,調用 npm 包正常

          這樣我們就知道怎么寫一個 npm 包,并上傳到 npm 官方庫了。

          下面,我們來看怎么在 npm 包中集成 cli 命令。舉個例子:在 npm install @vue/cli \-g 后,會在環境變量中添加一個 vue 命令。使用 vue create xx 可初始化一個項目。一般這種形式就是 cli 工具。

          一般在 package.json 中有一個 bin 屬性,用于創建該 npm 包的自定義命令

          // package.json
          "bin": {
              "zuodeploy": "./bin/zuodeploy.js"
            },
          

          上的配置意思是:全局安裝 npm install xx -g 后,生成 zuodeploy 命令,運行該命令時,會執行 bin/zuodeploy.js

          本地開發時,配置好后,在當前目錄下運行 sudo npm link 即可將 zuodeploy 命令鏈接到本地的環境變量里。任何 terminal 里面運行 zuodeploy 都會執行當前項目下的這個文件。解除可以使用 npm unlink

          一般 cli 都會使用 commander 來生成幫助文檔,管理指令邏輯,代碼如下

          // bin/zuodeploy.js
          #!/usr/bin/env node
          
          const { program }=require("commander");
          const prompts=require("prompts");
          
          program.version(require("../package.json").version);
          
          program
            .command("start")
            .description("開啟部署監聽服務") // description + action 可防止查找 command拼接文件
            .action(async ()=> {
              const args=await prompts([
                {
                  type: "number",
                  name: "port",
                  initial: 7777,
                  message: "請指定部署服務監聽端口:",
                  validate: (value)=>
                    value !=="" && (value < 3000 || value > 10000)
                      ? `端口號必須在 3000 - 10000 之間`
                      : true,
                },
                {
                  type: "password",
                  name: "password",
                  initial: "888888",
                  message: "請設置登錄密碼(默認:888888)",
                  validate: (value)=> (value.length < 6 ? `密碼需要 6 位以上` : true),
                },
              ]);
              require("./start")(args); // args 為 { port: 7777, password: '888888' }
            });
          
          program.parse();
          

          使用 commander 可以快速管理、生成幫助文檔,分配具體指令的執行邏輯

          上面的代碼中,指定了 start 指令,zuodeploy start 執行時會先通過 prompts 以詢問的方式搜集參數,再執行 bin/start.js

          在 start.js 中,我么可以將 server/index.js 的代碼全部拷貝過去即可完成 zuodeploy start 開啟服務,點擊部署的功能

          4.穩定性提高-pm2改造

          為了提升穩定性,我們可以在 start.js 中以代碼的方式執行 pm2 src/index.js 這樣服務更穩定可靠,另外可以再加入 log4js 輸出帶時間戳的 log,這樣有利于排查問題。

          • 具體代碼參考:zuo-deploy -github[7]
          • 所有測試 demo 地址: zuo-deploy 實現 demo - fedemo -github[4]

          最后

          將上面零碎的知識點匯聚到一起就是 zuo-deploy 的實現,代碼寫的比較隨意,歡迎 star、fork、提改進 PR!


          主站蜘蛛池模板: 国产免费一区二区三区免费视频| 亚洲色精品vr一区二区三区| 精品日产一区二区三区手机| 亚洲熟妇成人精品一区| 加勒比精品久久一区二区三区| 日本免费一区二区三区 | 婷婷亚洲综合一区二区| 久久99久久无码毛片一区二区| 亚洲av无码一区二区乱子伦as| 亚洲国产高清在线一区二区三区| 日本一区二区三区久久| 无码人妻精品一区二区三区不卡| 亚洲国产精品综合一区在线| 色久综合网精品一区二区| 亚洲一区二区三区香蕉| 日韩aⅴ人妻无码一区二区| 一区二区三区四区精品视频| 国产一区二区免费| 国产精品一区二区久久不卡| 三上悠亚国产精品一区| 精品人妻少妇一区二区三区| 中文字幕一区二区三区视频在线| 国产成人高清视频一区二区 | 亚洲乱码一区av春药高潮 | 在线观看国产一区| 亚洲一区二区三区AV无码| 国产午夜精品一区二区三区嫩草| AV怡红院一区二区三区| 国产一区二区三区在线视頻 | 日本精品一区二区三区在线视频| 视频在线观看一区| 亚洲一区二区三区无码中文字幕 | 精品福利一区二区三区免费视频| AV无码精品一区二区三区| 国模大胆一区二区三区| 无码国产亚洲日韩国精品视频一区二区三区 | 亚洲Av永久无码精品一区二区 | 亚洲一区二区视频在线观看| 国产av成人一区二区三区| 日韩精品无码一区二区三区不卡| 国产精品视频一区麻豆|