jam411 3 лет назад
Родитель
Сommit
307b3d3b55
1 измененных файлов с 41 добавлено и 24 удалено
  1. 41 24
      packages/app/src/server/service/plugin.ts

+ 41 - 24
packages/app/src/server/service/plugin.ts

@@ -4,7 +4,6 @@ import path from 'path';
 import { GrowiThemeMetadata, ViteManifest } from '@growi/core';
 // eslint-disable-next-line no-restricted-imports
 import axios from 'axios';
-import { plugins } from 'handsontable';
 import mongoose from 'mongoose';
 import streamToPromise from 'stream-to-promise';
 import unzipper from 'unzipper';
@@ -81,27 +80,27 @@ export class PluginService implements IPluginService {
   }
 
   async install(origin: GrowiPluginOrigin): Promise<string> {
-    try {
     // download
-      const ghUrl = new URL(origin.url);
-      const ghPathname = ghUrl.pathname;
-      // TODO: Branch names can be specified.
-      const ghBranch = 'main';
-
-      const match = ghPathname.match(githubReposIdPattern);
-      if (ghUrl.hostname !== 'github.com' || match == null) {
-        throw new Error('GitHub repository URL is invalid.');
-      }
+    const ghUrl = new URL(origin.url);
+    const ghPathname = ghUrl.pathname;
+    // TODO: Branch names can be specified.
+    const ghBranch = 'main';
+
+    const match = ghPathname.match(githubReposIdPattern);
+    if (ghUrl.hostname !== 'github.com' || match == null) {
+      throw new Error('GitHub repository URL is invalid.');
+    }
 
-      const ghOrganizationName = match[1];
-      const ghReposName = match[2];
-      const installedPath = `${ghOrganizationName}/${ghReposName}`;
+    const ghOrganizationName = match[1];
+    const ghReposName = match[2];
+    const installedPath = `${ghOrganizationName}/${ghReposName}`;
 
-      // download github repository to local file system
-      await this.downloadPluginRepository(ghOrganizationName, ghReposName, ghBranch);
+    try {
+      // clean up old plugin data from file system and documents
+      await this.cleanUp(ghOrganizationName, ghReposName, ghBranch);
 
-      // delete old document
-      await this.deleteOldPluginDocument(installedPath);
+      // download github repository to file system
+      await this.downloadPluginRepository(ghOrganizationName, ghReposName, ghBranch);
 
       // save plugin metadata
       const plugins = await PluginService.detectPlugins(origin, installedPath);
@@ -110,12 +109,34 @@ export class PluginService implements IPluginService {
       return plugins[0].meta.name;
     }
     catch (err) {
+      await this.cleanUp(ghOrganizationName, ghReposName, ghBranch);
       logger.error(err);
       throw err;
     }
   }
 
-  private async deleteOldPluginDocument(path: string): Promise<void> {
+  private async cleanUp(ghOrganizationName: string, ghReposName: string, ghBranch: string): Promise<void> {
+    const zipFilePath = path.join(pluginStoringPath, `${ghBranch}.zip`);
+    const unzippedPath = path.join(pluginStoringPath, ghOrganizationName);
+    const installedPath = `${ghOrganizationName}/${ghReposName}`;
+
+    try {
+      // delete zip file if remain
+      if (fs.existsSync(zipFilePath)) await fs.promises.rm(zipFilePath);
+      // delete unzipped folder if remain
+      if (fs.existsSync(`${unzippedPath}/${ghReposName}-${ghBranch}`)) await fs.promises.rm(`${unzippedPath}/${ghReposName}-${ghBranch}`, { recursive: true });
+      // delete renamed folder if remain
+      if (fs.existsSync(`${unzippedPath}/${ghReposName}`)) await fs.promises.rm(`${unzippedPath}/${ghReposName}`, { recursive: true });
+      // delete plugin documents
+      await this.deleteOldPluginDocuments(installedPath);
+    }
+    catch (err) {
+      logger.error(err);
+      throw new Error('Failed to clean up plugin data.');
+    }
+  }
+
+  private async deleteOldPluginDocuments(path: string): Promise<void> {
     const GrowiPlugin = mongoose.model<GrowiPlugin>('GrowiPlugin');
     await GrowiPlugin.deleteMany({ installedPath: path });
   }
@@ -157,10 +178,9 @@ export class PluginService implements IPluginService {
       try {
         const stream = fs.createReadStream(zipFilePath);
         const unzipStream = stream.pipe(unzipper.Extract({ path: unzippedPath }));
-        const deleteZipFile = (path: fs.PathLike) => fs.unlinkSync(path);
 
         await streamToPromise(unzipStream);
-        deleteZipFile(zipFilePath);
+        await fs.promises.rm(zipFilePath);
       }
       catch (err) {
         logger.error(err);
@@ -170,9 +190,6 @@ export class PluginService implements IPluginService {
 
     const renamePath = async(oldPath: fs.PathLike, newPath: fs.PathLike) => {
       try {
-        // if repository already exists, delete old repository before rename path
-        if (fs.existsSync(newPath)) await fs.promises.rm(newPath, { recursive: true });
-        // rename repository
         fs.renameSync(oldPath, newPath);
       }
       catch (err) {