Преглед изворни кода

Merge pull request #15 from hakumizuki/feat/g2g-receive

feat: G2G Receive
Haku Mizuki пре 3 година
родитељ
комит
bd1b9be3eb

+ 4 - 1
packages/app/src/client/services/g2g-transfer.ts

@@ -9,8 +9,11 @@ export const useGenerateTransferKeyWithThrottle = (): {transferKey: string, gene
   const [transferKey, setTransferKey] = useState('');
 
   const generateTransferKeyWithThrottle = useMemo(() => {
+    const href = document.location.href;
+    const url = new URL(href);
+
     return throttle(1000, async() => {
-      const response = await apiv3Post('/g2g-transfer/generate-key');
+      const response = await apiv3Post('/g2g-transfer/generate-key', { appSiteUrl: url.origin });
       const { transferKey } = response.data;
       setTransferKey(transferKey);
     });

+ 36 - 21
packages/app/src/server/routes/apiv3/g2g-transfer.ts

@@ -20,12 +20,17 @@ import ErrorV3 from '../../models/vo/error-apiv3';
 import { generateOverwriteParams } from './import';
 import { ApiV3Response } from './interfaces/apiv3-response';
 
+interface AuthorizedRequest extends Request {
+  user?: any
+}
+
 const logger = loggerFactory('growi:routes:apiv3:transfer');
 
 const validator = {
   transfer: [
     body('transferKey').isString().withMessage('transferKey is required'),
     body('collections').isArray().withMessage('collections is required'),
+    body('optionsMap').isObject().withMessage('optionsMap is required'),
   ],
 };
 
@@ -129,15 +134,25 @@ module.exports = (crowi: Crowi): Router => {
   const pushRouter = express.Router();
 
   // Auto import
-  receiveRouter.post('/', uploads.single('file'), verifyAndExtractTransferKey, async(req: Request & { transferKey: TransferKey }, res: ApiV3Response) => {
+  // eslint-disable-next-line max-len
+  receiveRouter.post('/', uploads.single('transferDataZipFile'), /* verifyAndExtractTransferKey, */ async(req: Request & { transferKey: TransferKey, operatorUserId: string }, res: ApiV3Response) => {
     const { file } = req;
 
     const zipFile = importService.getFile(file.filename);
     let data;
 
-    // ぶちこみ
+    const { collections: strCollections, optionsMap: strOptionsMap, operatorUserId } = req.body;
 
-    const { collections, optionsMap } = req.body;
+    let collections;
+    let optionsMap;
+    try {
+      collections = JSON.parse(strCollections);
+      optionsMap = JSON.parse(strOptionsMap);
+    }
+    catch (err) {
+      logger.error(err);
+      return res.apiv3Err(new ErrorV3('Failed to parse body.', 'parse_failed'), 500);
+    }
 
     /*
      * unzip, parse
@@ -188,7 +203,7 @@ module.exports = (crowi: Crowi): Router => {
       importSettings.jsonFileName = fileName;
 
       // generate overwrite params
-      importSettings.overwriteParams = generateOverwriteParams(collectionName, req, options);
+      importSettings.overwriteParams = generateOverwriteParams(collectionName, operatorUserId, options);
 
       importSettingsMap[collectionName] = importSettings;
     });
@@ -198,7 +213,7 @@ module.exports = (crowi: Crowi): Router => {
      */
     try {
       importService.import(collections, importSettingsMap);
-      const parameters = { action: SupportedAction.ACTION_ADMIN_GROWI_DATA_IMPORTED };
+      // const parameters = { action: SupportedAction.ACTION_ADMIN_GROWI_DATA_IMPORTED };
       // activityEvent.emit('update', res.locals.activity._id, parameters);
     }
     catch (err) {
@@ -206,7 +221,7 @@ module.exports = (crowi: Crowi): Router => {
       // adminEvent.emit('onErrorForImport', { message: err.message });
     }
 
-    // ここまで
+    // -----
 
     try {
       data = await growiBridgeService.parseZipFile(zipFile);
@@ -288,7 +303,7 @@ module.exports = (crowi: Crowi): Router => {
   // Auto export
   // TODO: Use socket to send progress info to the client
   // eslint-disable-next-line max-len
-  pushRouter.post('/transfer', accessTokenParser, loginRequiredStrictly, adminRequired, validator.transfer, apiV3FormValidator, async(req: Request, res: ApiV3Response) => {
+  pushRouter.post('/transfer', accessTokenParser, loginRequiredStrictly, adminRequired, validator.transfer, apiV3FormValidator, async(req: AuthorizedRequest, res: ApiV3Response) => {
     const { transferKey: transferKeyString, collections, optionsMap } = req.body;
 
     // Parse transfer key
@@ -303,25 +318,25 @@ module.exports = (crowi: Crowi): Router => {
 
     // Ask growi info
     // TODO: Ask progress as well
-    let toGROWIInfo: IDataGROWIInfo;
-    try {
-      toGROWIInfo = await g2gTransferPusherService.askGROWIInfo(tk);
-    }
-    catch (err) {
-      logger.error(err);
-      return res.apiv3Err(new ErrorV3('GROWI is incompatible to transfer data.', 'growi_incompatible_to_transfer'));
-    }
+    // let toGROWIInfo: IDataGROWIInfo;
+    // try {
+    //   toGROWIInfo = await g2gTransferPusherService.askGROWIInfo(tk);
+    // }
+    // catch (err) {
+    //   logger.error(err);
+    //   return res.apiv3Err(new ErrorV3('Error occurred while asking GROWI growi info.', 'failed_to_ask_growi_info'));
+    // }
 
     // Check if can transfer
-    const canTransfer = await g2gTransferPusherService.canTransfer(toGROWIInfo);
-    if (!canTransfer) {
-      logger.debug('Could not transfer.');
-      return res.apiv3Err(new ErrorV3('GROWI is incompatible to transfer data.', 'growi_incompatible_to_transfer'));
-    }
+    // const canTransfer = await g2gTransferPusherService.canTransfer(toGROWIInfo);
+    // if (!canTransfer) {
+    //   logger.debug('Could not transfer.');
+    //   return res.apiv3Err(new ErrorV3('GROWI is incompatible to transfer data.', 'growi_incompatible_to_transfer'));
+    // }
 
     // Start transfer
     try {
-      await g2gTransferPusherService.startTransfer(tk, collections, optionsMap);
+      await g2gTransferPusherService.startTransfer(tk, req.user, collections, optionsMap);
     }
     catch (err) {
       logger.error(err);

+ 12 - 10
packages/app/src/server/routes/apiv3/import.js

@@ -1,10 +1,12 @@
-import mongoose from 'mongoose';
-
 import { SupportedAction } from '~/interfaces/activity';
 import loggerFactory from '~/utils/logger';
 
 import { generateAddActivityMiddleware } from '../../middlewares/add-activity';
 
+import overwriteParamsAttachmentFilesChunks from './overwrite-params/attachmentFiles.chunks';
+import overwriteParamsPages from './overwrite-params/pages';
+import overwriteParamsRevisions from './overwrite-params/revisions';
+
 
 const logger = loggerFactory('growi:routes:apiv3:import'); // eslint-disable-line no-unused-vars
 
@@ -52,23 +54,23 @@ const router = express.Router();
 /**
  * generate overwrite params with overwrite-params/* modules
  * @param {string} collectionName
- * @param {object} req Request Object
+ * @param {string} operatorUserId Operator user id
  * @param {GrowiArchiveImportOption} options GrowiArchiveImportOption instance
  */
-export const generateOverwriteParams = (collectionName, req, options) => {
+export const generateOverwriteParams = (collectionName, operatorUserId, options) => {
   switch (collectionName) {
     case 'pages':
-      return require('./overwrite-params/pages')(req, options);
+      return overwriteParamsPages(operatorUserId, options);
     case 'revisions':
-      return require('./overwrite-params/revisions')(req, options);
+      return overwriteParamsRevisions(operatorUserId, options);
     case 'attachmentFiles.chunks':
-      return require('./overwrite-params/attachmentFiles.chunks')(req, options);
+      return overwriteParamsAttachmentFilesChunks(operatorUserId, options);
     default:
       return {};
   }
 };
 
-module.exports = (crowi) => {
+export default function route(crowi) {
   const { growiBridgeService, importService, socketIoService } = crowi;
   const accessTokenParser = require('../../middlewares/access-token-parser')(crowi);
   const loginRequired = require('../../middlewares/login-required')(crowi);
@@ -283,7 +285,7 @@ module.exports = (crowi) => {
       importSettings.jsonFileName = fileName;
 
       // generate overwrite params
-      importSettings.overwriteParams = generateOverwriteParams(collectionName, req, options);
+      importSettings.overwriteParams = generateOverwriteParams(collectionName, req.user._id, options);
 
       importSettingsMap[collectionName] = importSettings;
     });
@@ -385,4 +387,4 @@ module.exports = (crowi) => {
   });
 
   return router;
-};
+}

+ 2 - 1
packages/app/src/server/routes/apiv3/index.js

@@ -6,6 +6,7 @@ import * as loginFormValidator from '../../middlewares/login-form-validator';
 import * as registerFormValidator from '../../middlewares/register-form-validator';
 
 import g2gTransfer from './g2g-transfer';
+import importRoute from './import';
 import pageListing from './page-listing';
 import * as userActivation from './user-activation';
 
@@ -33,7 +34,7 @@ module.exports = (crowi, app, isInstalled) => {
   routerForAdmin.use('/users', require('./users')(crowi));
   routerForAdmin.use('/user-groups', require('./user-group')(crowi));
   routerForAdmin.use('/export', require('./export')(crowi));
-  routerForAdmin.use('/import', require('./import')(crowi));
+  routerForAdmin.use('/import', importRoute(crowi));
   routerForAdmin.use('/search', require('./search')(crowi));
   routerForAdmin.use('/security-setting', require('./security-setting')(crowi));
   routerForAdmin.use('/mongo', require('./mongo')(crowi));

+ 3 - 3
packages/app/src/server/routes/apiv3/overwrite-params/attachmentFiles.chunks.js

@@ -5,13 +5,13 @@ class AttachmentFilesChunksOverwriteParamsFactory {
 
   /**
    * generate overwrite params object
-   * @param {object} req
+   * @param {string} operatorUserId
    * @param {ImportOptionForPages} option
    * @return object
    *  key: property name
    *  value: any value or a function `(value, { document, schema, propertyName }) => { return newValue }`
    */
-  static generate(req, option) {
+  static generate(operatorUserId, option) {
     const params = {};
 
     // Date
@@ -29,4 +29,4 @@ class AttachmentFilesChunksOverwriteParamsFactory {
 
 }
 
-module.exports = (req, option) => AttachmentFilesChunksOverwriteParamsFactory.generate(req, option);
+module.exports = (operatorUserId, option) => AttachmentFilesChunksOverwriteParamsFactory.generate(operatorUserId, option);

+ 5 - 6
packages/app/src/server/routes/apiv3/overwrite-params/pages.js

@@ -1,6 +1,5 @@
-const mongoose = require('mongoose');
-const { format } = require('date-fns');
 const { pagePathUtils } = require('@growi/core');
+const mongoose = require('mongoose');
 
 const { isTopPage } = pagePathUtils;
 
@@ -17,17 +16,17 @@ class PageOverwriteParamsFactory {
 
   /**
    * generate overwrite params object
-   * @param {object} req
+   * @param {string} operatorUserId
    * @param {ImportOptionForPages} option
    * @return object
    *  key: property name
    *  value: any value or a function `(value, { document, schema, propertyName }) => { return newValue }`
    */
-  static generate(req, option) {
+  static generate(operatorUserId, option) {
     const params = {};
 
     if (option.isOverwriteAuthorWithCurrentUser) {
-      const userId = ObjectId(req.user._id);
+      const userId = ObjectId(operatorUserId);
       params.creator = userId;
       params.lastUpdateUser = userId;
     }
@@ -71,4 +70,4 @@ class PageOverwriteParamsFactory {
 
 }
 
-module.exports = (req, option) => PageOverwriteParamsFactory.generate(req, option);
+module.exports = (operatorUserId, option) => PageOverwriteParamsFactory.generate(operatorUserId, option);

+ 4 - 4
packages/app/src/server/routes/apiv3/overwrite-params/revisions.js

@@ -9,17 +9,17 @@ class RevisionOverwriteParamsFactory {
 
   /**
    * generate overwrite params object
-   * @param {object} req
+   * @param {string} operatorUserId
    * @param {ImportOptionForPages} option
    * @return object
    *  key: property name
    *  value: any value or a function `(value, { document, schema, propertyName }) => { return newValue }`
    */
-  static generate(req, option) {
+  static generate(operatorUserId, option) {
     const params = {};
 
     if (option.isOverwriteAuthorWithCurrentUser) {
-      const userId = ObjectId(req.user._id);
+      const userId = ObjectId(operatorUserId);
       params.author = userId;
     }
 
@@ -28,4 +28,4 @@ class RevisionOverwriteParamsFactory {
 
 }
 
-module.exports = (req, option) => RevisionOverwriteParamsFactory.generate(req, option);
+module.exports = (operatorUserId, option) => RevisionOverwriteParamsFactory.generate(operatorUserId, option);

+ 6 - 4
packages/app/src/server/service/g2g-transfer.ts

@@ -47,7 +47,7 @@ interface Pusher {
    * @param {string[]} collections Collection name string array
    * @param {any} optionsMap Options map
    */
-  startTransfer(tk: TransferKey, collections: string[], optionsMap: any): Promise<void>
+  startTransfer(tk: TransferKey, user: any, collections: string[], optionsMap: any): Promise<void>
 }
 
 interface Receiver {
@@ -113,7 +113,7 @@ export class G2GTransferPusherService implements Pusher {
 
   public async transferAttachments(): Promise<void> { return }
 
-  public async startTransfer(tk: TransferKey, collections: string[], optionsMap: any): Promise<void> {
+  public async startTransfer(tk: TransferKey, user: any, collections: string[], optionsMap: any): Promise<void> {
     const { appUrl, key } = tk;
 
     let zipFileStream: ReadStream;
@@ -136,8 +136,9 @@ export class G2GTransferPusherService implements Pusher {
 
       const appTitle = this.crowi.appService.getAppTitle();
       form.append('transferDataZipFile', zipFileStream, `${appTitle}-${Date.now}.growi.zip`);
-      form.append('collections', collections);
-      form.append('optionsMap', optionsMap);
+      form.append('collections', JSON.stringify(collections));
+      form.append('optionsMap', JSON.stringify(optionsMap));
+      form.append('operatorUserId', user._id.toString());
       await rawAxios.post('/_api/v3/g2g-transfer/', form, {
         baseURL: appUrl.origin,
         headers: {
@@ -147,6 +148,7 @@ export class G2GTransferPusherService implements Pusher {
       });
     }
     catch (errs) {
+      logger.error(errs);
       if (!Array.isArray(errs)) {
         // TODO: socker.emit(failed_to_transfer);
         return;