2
0
mizozobu 6 жил өмнө
parent
commit
2ad7f5c90f

+ 9 - 2
src/server/routes/apiv3/export.js

@@ -86,10 +86,17 @@ module.exports = (crowi) => {
       const { collections } = req.body;
       const { collections } = req.body;
       // get model for collection
       // get model for collection
       const models = collections.map(collectionName => exportService.getModelFromCollectionName(collectionName));
       const models = collections.map(collectionName => exportService.getModelFromCollectionName(collectionName));
-      // export into json
-      const jsonFiles = await exportService.exportMultipleCollectionsToJsons(models);
+
+      const [metaJson, jsonFiles] = await Promise.all([
+        exportService.createMetaJson(),
+        exportService.exportMultipleCollectionsToJsons(models),
+      ]);
+
       // zip json
       // zip json
       const configs = jsonFiles.map((jsonFile) => { return { from: jsonFile, as: path.basename(jsonFile) } });
       const configs = jsonFiles.map((jsonFile) => { return { from: jsonFile, as: path.basename(jsonFile) } });
+      // add meta.json in zip
+      configs.push({ from: metaJson, as: path.basename(metaJson) });
+      // exec zip
       const zipFile = await exportService.zipFiles(configs);
       const zipFile = await exportService.zipFiles(configs);
 
 
       // TODO: use res.apiv3
       // TODO: use res.apiv3

+ 63 - 0
src/server/service/export.js

@@ -8,6 +8,7 @@ const toArrayIfNot = require('../../lib/util/toArrayIfNot');
 class ExportService {
 class ExportService {
 
 
   constructor(crowi) {
   constructor(crowi) {
+    this.crowi = crowi;
     this.appService = crowi.appService;
     this.appService = crowi.appService;
     this.baseDir = path.join(crowi.tmpDir, 'downloads');
     this.baseDir = path.join(crowi.tmpDir, 'downloads');
     this.zipFileName = 'GROWI.zip';
     this.zipFileName = 'GROWI.zip';
@@ -74,6 +75,31 @@ class ExportService {
     return status;
     return status;
   }
   }
 
 
+  /**
+   * create meta.json
+   *
+   * @memberOf ExportService
+   * @return {string} path to meta.json
+   */
+  async createMetaJson() {
+    const metaJson = this.getMetaJson();
+    const writeStream = fs.createWriteStream(metaJson, { encoding: this.encoding });
+
+    const metaData = {
+      version: this.crowi.version,
+      url: this.appService.getSiteUrl(),
+      passwordSeed: this.crowi.env.PASSWORD_SEED,
+      exportedAt: new Date(),
+    };
+
+    writeStream.write(JSON.stringify(metaData));
+    writeStream.close();
+
+    await streamToPromise(writeStream);
+
+    return metaJson;
+  }
+
   /**
   /**
    * dump a collection into json
    * dump a collection into json
    *
    *
@@ -126,8 +152,16 @@ class ExportService {
     return file;
     return file;
   }
   }
 
 
+  /**
+   * export multiple collections
+   *
+   * @memberOf ExportService
+   * @param {number} models number of items exported
+   * @return {Array.<string>} paths to json files created
+   */
   async exportMultipleCollectionsToJsons(models) {
   async exportMultipleCollectionsToJsons(models) {
     const jsonFiles = await Promise.all(models.map(Model => this.exportCollectionToJson(Model)));
     const jsonFiles = await Promise.all(models.map(Model => this.exportCollectionToJson(Model)));
+
     return jsonFiles;
     return jsonFiles;
   }
   }
 
 
@@ -229,6 +263,35 @@ class ExportService {
     return zipFile;
     return zipFile;
   }
   }
 
 
+  /**
+   * get the absolute path to the zip file
+   *
+   * @memberOf ImportService
+   * @param {boolean} [validate=false] boolean to check if the file exists
+   * @return {string} absolute path to meta.json
+   */
+  getMetaJson(validate = false) {
+    const jsonFile = path.join(this.baseDir, this.metaFileName);
+
+    if (validate) {
+      try {
+        fs.accessSync(jsonFile);
+      }
+      catch (err) {
+        if (err.code === 'ENOENT') {
+          logger.error(`${jsonFile} does not exist`, err);
+        }
+        else {
+          logger.error(err);
+        }
+
+        throw err;
+      }
+    }
+
+    return jsonFile;
+  }
+
   /**
   /**
    * get a model from collection name
    * get a model from collection name
    *
    *

+ 2 - 2
src/server/service/import.js

@@ -143,7 +143,7 @@ class ImportService {
    *
    *
    * @memberOf ImportService
    * @memberOf ImportService
    * @param {string} zipFile path to zip file
    * @param {string} zipFile path to zip file
-   * @return {object} meta{object} and files{array<object>}
+   * @return {object} meta{object} and files{Array.<object>}
    */
    */
   async unzip(zipFile) {
   async unzip(zipFile) {
     const readStream = fs.createReadStream(zipFile);
     const readStream = fs.createReadStream(zipFile);
@@ -183,7 +183,7 @@ class ImportService {
    *
    *
    * @memberOf ImportService
    * @memberOf ImportService
    * @param {object} unorderedBulkOp result of Model.collection.initializeUnorderedBulkOp()
    * @param {object} unorderedBulkOp result of Model.collection.initializeUnorderedBulkOp()
-   * @return {{nInserted: number, failed: string[]}} number of docuemnts inserted and failed
+   * @return {{nInserted: number, failed: Array.<string>}} number of docuemnts inserted and failed
    */
    */
   async execUnorderedBulkOpSafely(unorderedBulkOp) {
   async execUnorderedBulkOpSafely(unorderedBulkOp) {
     // keep the number of documents inserted and failed for logger
     // keep the number of documents inserted and failed for logger