Просмотр исходного кода

refactor download features using stream

jam411 3 лет назад
Родитель
Сommit
1ad0a562f9

+ 1 - 0
packages/app/package.json

@@ -186,6 +186,7 @@
     "sticky-events": "^3.4.11",
     "stream-to-promise": "^3.0.0",
     "string-width": "=4.2.2",
+    "superagent": "^8.0.6",
     "superjson": "^1.9.1",
     "swagger-jsdoc": "^6.1.0",
     "swig-templates": "^2.0.2",

+ 1 - 2
packages/app/src/server/routes/apiv3/plugins-extension.ts

@@ -16,11 +16,10 @@ module.exports = (crowi: Crowi) => {
     }
 
     try {
-      await pluginService.install(crowi, req.body.pluginInstallerForm);
+      await pluginService.install(req.body.pluginInstallerForm);
       return res.apiv3({});
     }
     catch (err) {
-      // TODO: error handling
       return res.apiv3Err(err, 400);
     }
   });

+ 50 - 37
packages/app/src/server/service/plugin.ts

@@ -1,15 +1,14 @@
-import { execSync } from 'child_process';
 import fs from 'fs';
 import path from 'path';
 
 import mongoose from 'mongoose';
+import request from 'superagent';
+import unzipper from 'unzipper';
 
-import type { GrowiPlugin, GrowiPluginMeta, GrowiPluginOrigin } from '~/interfaces/plugin';
+import type { GrowiPlugin, GrowiPluginOrigin } from '~/interfaces/plugin';
 import loggerFactory from '~/utils/logger';
 import { resolveFromRoot } from '~/utils/project-dir-utils';
 
-// eslint-disable-next-line import/no-cycle
-import Crowi from '../crowi';
 
 const logger = loggerFactory('growi:plugins:plugin-utils');
 
@@ -21,22 +20,7 @@ const githubReposIdPattern = new RegExp(/^\/([^/]+)\/([^/]+)$/);
 
 export class PluginService {
 
-  crowi: any;
-
-  growiBridgeService: any;
-
-  baseDir: any;
-
-  getFile:any;
-
-  constructor(crowi) {
-    this.crowi = crowi;
-    this.growiBridgeService = crowi.growiBridgeService;
-    this.baseDir = path.join(crowi.tmpDir, 'plugins');
-    this.getFile = this.growiBridgeService.getFile.bind(this);
-  }
-
-  async install(crowi: Crowi, origin: GrowiPluginOrigin): Promise<void> {
+  async install(origin: GrowiPluginOrigin): Promise<void> {
     // download
     const ghUrl = new URL(origin.url);
     const ghPathname = ghUrl.pathname;
@@ -49,12 +33,8 @@ export class PluginService {
     const ghOrganizationName = match[1];
     const ghReposName = match[2];
 
-    try {
-      await this.downloadZipFile(`${ghUrl.href}/archive/refs/heads/main.zip`, ghOrganizationName, ghReposName);
-    }
-    catch (err) {
-      console.log('downloadZipFile error', err);
-    }
+    // download github repository to local file system
+    await this.download(`${ghUrl.href}/archive/refs/heads/main.zip`, ghOrganizationName, ghReposName);
 
     // save plugin metadata
     const installedPath = `${ghOrganizationName}/${ghReposName}`;
@@ -64,18 +44,52 @@ export class PluginService {
     return;
   }
 
-  async downloadZipFile(url: string, ghOrganizationName: string, ghReposName: string): Promise<void> {
+  async download(url: string, ghOrganizationName: string, ghReposName: string): Promise<void> {
 
-    const downloadTargetPath = pluginStoringPath;
-    const zipFilePath = path.join(downloadTargetPath, 'main.zip');
-    const unzipTargetPath = path.join(pluginStoringPath, ghOrganizationName);
+    const zipFilePath = path.join(pluginStoringPath, 'main.zip');
+    const unzipFolderPath = path.join(pluginStoringPath, ghOrganizationName);
+    const unzippedFolderPath = `${unzipFolderPath}/${ghReposName}-main`;
+    const newFolderPath = `${unzipFolderPath}/${ghReposName}`;
 
-    const stdout1 = execSync(`wget ${url} -O ${zipFilePath}`);
-    const stdout2 = execSync(`mkdir -p ${ghOrganizationName}`);
-    const stdout3 = execSync(`rm -rf ${ghOrganizationName}/${ghReposName}`);
-    const stdout4 = execSync(`unzip ${zipFilePath} -d ${unzipTargetPath}`);
-    const stdout5 = execSync(`mv ${unzipTargetPath}/${ghReposName}-main ${unzipTargetPath}/${ghReposName}`);
-    const stdout6 = execSync(`rm ${zipFilePath}`);
+    const deleteFile = (path: fs.PathLike) => {
+      fs.unlink(path, (err) => {
+        if (err) throw err;
+      });
+    };
+
+    const downloadRepository = () => {
+      const writeStream = fs.createWriteStream(zipFilePath);
+
+      return new Promise<void>((resolve, reject) => {
+        request
+          .get(url)
+          .pipe(writeStream)
+          .on('close', () => writeStream.close())
+          .on('finish', () => resolve())
+          .on('error', (error: any) => reject(error));
+      });
+    };
+
+    const unzip = () => {
+      const stream = fs.createReadStream(zipFilePath);
+
+      return new Promise<void>((resolve, reject) => {
+        stream.pipe(unzipper.Extract({ path: unzipFolderPath }))
+          .on('finish', () => {
+            deleteFile(zipFilePath);
+            resolve();
+          })
+          .on('error', (error: any) => reject(error));
+      });
+    };
+
+    const rename = async() => {
+      fs.renameSync(unzippedFolderPath, newFolderPath);
+    };
+
+    await downloadRepository();
+    await unzip();
+    await rename();
 
     return;
   }
@@ -135,5 +149,4 @@ export class PluginService {
     return [];
   }
 
-
 }