Taichi Masuyama 3 лет назад
Родитель
Сommit
a53c25482b

+ 5 - 0
packages/app/src/interfaces/transfer-key.ts

@@ -0,0 +1,5 @@
+export interface ITransferKey<ID = string> {
+  _id: ID
+  expireAt: Date
+  value: string,
+}

+ 20 - 0
packages/app/src/server/models/transfer-key.ts

@@ -0,0 +1,20 @@
+import { getOrCreateModel } from '@growi/core';
+import { Schema } from 'mongoose';
+
+import { ITransferKey } from '~/interfaces/transfer-key';
+
+import loggerFactory from '../../utils/logger';
+
+const logger = loggerFactory('growi:models:transfer-key');
+
+const schema = new Schema<ITransferKey>({
+  expireAt: { type: Date, default: () => new Date(), expires: '30m' },
+  value: { type: String, unique: true },
+}, {
+  timestamps: {
+    createdAt: true,
+    updatedAt: false,
+  },
+});
+
+export default getOrCreateModel('TransferKey', schema);

+ 79 - 0
packages/app/src/server/routes/apiv3/g2g-transfer.ts

@@ -0,0 +1,79 @@
+import express, { NextFunction, Request, Router } from 'express';
+import { Types as MongooseTypes } from 'mongoose';
+
+import TransferKeyModel from '~/server/models/transfer-key';
+import loggerFactory from '~/utils/logger';
+import { TransferKey } from '~/utils/vo/transfer-key';
+
+import Crowi from '../../crowi';
+import { apiV3FormValidator } from '../../middlewares/apiv3-form-validator';
+import ErrorV3 from '../../models/vo/error-apiv3';
+
+import { ApiV3Response } from './interfaces/apiv3-response';
+
+const logger = loggerFactory('growi:routes:apiv3:transfer');
+
+/*
+ * Routes
+ */
+export default (crowi: Crowi): Router => {
+  const isInstalled = crowi.configManager?.getConfig('crowi', 'app:installed');
+
+  const accessTokenParser = require('../../middlewares/access-token-parser')(crowi);
+  const adminRequired = require('../../middlewares/admin-required')(crowi);
+
+  const adminRequiredIfInstalled = (req: Request, res: ApiV3Response, next: NextFunction) => {
+    if (!isInstalled) {
+      next();
+      return;
+    }
+
+    return adminRequired(req, res, next);
+  };
+
+  const appSiteUrlRequiredIfNotInstalled = (req: Request, res: ApiV3Response, next: NextFunction) => {
+    if (!isInstalled && req.body.appSiteUrl != null) {
+      next();
+      return;
+    }
+
+    if (crowi.configManager?.getConfig('crowi', 'app:siteUrl') != null || req.body.appSiteUrl != null) {
+      next();
+      return;
+    }
+
+    return res.apiv3Err(new ErrorV3('Body param "appSiteUrl" is required when GROWI is NOT installed yet'), 400);
+  };
+
+  const router = express.Router();
+
+  // TODO: Refactor process as TransferService methods
+  router.post('/generate-key', accessTokenParser, adminRequiredIfInstalled, appSiteUrlRequiredIfNotInstalled, async(req: Request, res: ApiV3Response) => {
+    const appSiteUrl = req.body.appSiteUrl ?? crowi.configManager?.getConfig('crowi', 'app:siteUrl');
+
+    const uuid = new MongooseTypes.ObjectId().toString();
+
+    // Generate transfer key string
+    let transferKeyString: string;
+    try {
+      transferKeyString = TransferKey.generateKeyString(new URL(appSiteUrl), uuid);
+    }
+    catch (err) {
+      logger.error(err);
+      return res.apiv3Err(new ErrorV3('appSiteUrl may be wrong', 'failed_to_generate_key_string'));
+    }
+
+    // Save TransferKey document
+    try {
+      await TransferKeyModel.create({ _id: uuid, appSiteUrl, value: transferKeyString });
+    }
+    catch (err) {
+      logger.error(err);
+      return res.apiv3Err(new ErrorV3('Error occurred while generating transfer key.', 'failed_to_generate_key'));
+    }
+
+    return res.apiv3({ transferKey: transferKeyString });
+  });
+
+  return router;
+};

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

@@ -2,6 +2,7 @@ import loggerFactory from '~/utils/logger';
 
 import injectUserRegistrationOrderByTokenMiddleware from '../../middlewares/inject-user-registration-order-by-token-middleware';
 
+import g2gTransfer from './g2g-transfer';
 import pageListing from './page-listing';
 import * as userActivation from './user-activation';
 
@@ -36,6 +37,7 @@ module.exports = (crowi) => {
   routerForAdmin.use('/slack-integration-settings', require('./slack-integration-settings')(crowi));
   routerForAdmin.use('/slack-integration-legacy-settings', require('./slack-integration-legacy-settings')(crowi));
   routerForAdmin.use('/activity', require('./activity')(crowi));
+  routerForAdmin.use('/g2g-transfer', g2gTransfer(crowi));
 
   // auth
   routerForAuth.use('/logout', require('./logout')(crowi));