Yuki Takei 1 год назад
Родитель
Сommit
2edc34b6c1
24 измененных файлов с 125 добавлено и 122 удалено
  1. 5 5
      apps/app/src/features/external-user-group/server/routes/apiv3/external-user-group-relation.ts
  2. 2 1
      apps/app/src/server/middlewares/access-token-parser.js
  3. 1 1
      apps/app/src/server/models/serializers/bookmark-serializer.js
  4. 1 1
      apps/app/src/server/models/serializers/page-serializer.js
  5. 1 1
      apps/app/src/server/models/serializers/revision-serializer.js
  6. 1 1
      apps/app/src/server/models/serializers/user-group-relation-serializer.js
  7. 11 10
      apps/app/src/server/routes/apiv3/activity.ts
  8. 4 5
      apps/app/src/server/routes/apiv3/attachment.js
  9. 2 1
      apps/app/src/server/routes/apiv3/bookmarks.js
  10. 1 1
      apps/app/src/server/routes/apiv3/forgot-password.js
  11. 31 17
      apps/app/src/server/routes/apiv3/in-app-notification.ts
  12. 2 3
      apps/app/src/server/routes/apiv3/page/create-page.ts
  13. 3 3
      apps/app/src/server/routes/apiv3/page/update-page.ts
  14. 2 2
      apps/app/src/server/routes/apiv3/pages/index.js
  15. 2 3
      apps/app/src/server/routes/apiv3/revisions.js
  16. 2 3
      apps/app/src/server/routes/apiv3/user-group-relation.js
  17. 6 9
      apps/app/src/server/routes/apiv3/user-group.js
  18. 7 9
      apps/app/src/server/routes/apiv3/users.js
  19. 1 3
      apps/app/src/server/routes/attachment/api.js
  20. 3 2
      apps/app/src/server/routes/comment.js
  21. 6 12
      apps/app/src/server/service/search-delegator/private-legacy-pages.ts
  22. 1 1
      apps/app/src/server/service/search.ts
  23. 1 0
      packages/remark-attachment-refs/package.json
  24. 29 28
      packages/remark-attachment-refs/src/server/routes/refs.ts

+ 5 - 5
apps/app/src/features/external-user-group/server/routes/apiv3/external-user-group-relation.ts

@@ -1,19 +1,19 @@
 import { ErrorV3 } from '@growi/core/dist/models';
-import { Router, Request } from 'express';
+import type { Router, Request } from 'express';
 
-import { IExternalUserGroupRelationHasId } from '~/features/external-user-group/interfaces/external-user-group';
+import type { IExternalUserGroupRelationHasId } from '~/features/external-user-group/interfaces/external-user-group';
 import ExternalUserGroupRelation from '~/features/external-user-group/server/models/external-user-group-relation';
-import Crowi from '~/server/crowi';
+import type Crowi from '~/server/crowi';
+import { serializeUserGroupRelationSecurely } from '~/server/models/serializers';
+import type { ApiV3Response } from '~/server/routes/apiv3/interfaces/apiv3-response';
 import loggerFactory from '~/utils/logger';
 
-import { ApiV3Response } from '../../../../../server/routes/apiv3/interfaces/apiv3-response';
 
 const logger = loggerFactory('growi:routes:apiv3:user-group-relation'); // eslint-disable-line no-unused-vars
 
 const express = require('express');
 const { query } = require('express-validator');
 
-const { serializeUserGroupRelationSecurely } = require('~/server/models/serializers/user-group-relation-serializer');
 
 const router = express.Router();
 

+ 2 - 1
apps/app/src/server/middlewares/access-token-parser.js

@@ -1,6 +1,7 @@
+import { serializeUserSecurely } from '@growi/core/dist/models/serializers';
+
 import loggerFactory from '~/utils/logger';
 
-const { serializeUserSecurely } = require('../models/serializers/user-serializer');
 
 const logger = loggerFactory('growi:middleware:access-token-parser');
 

+ 1 - 1
apps/app/src/server/models/serializers/bookmark-serializer.js

@@ -1,4 +1,4 @@
-const { serializePageSecurely } = require('./page-serializer');
+import { serializePageSecurely } from './page-serializer';
 
 function serializeInsecurePageAttributes(bookmark) {
   if (bookmark.page != null && bookmark.page._id != null) {

+ 1 - 1
apps/app/src/server/models/serializers/page-serializer.js

@@ -1,4 +1,4 @@
-const { serializeUserSecurely } = require('./user-serializer');
+import { serializeUserSecurely } from '@growi/core/dist/models/serializers';
 
 function depopulate(page, attributeName) {
   // revert the ObjectID

+ 1 - 1
apps/app/src/server/models/serializers/revision-serializer.js

@@ -1,4 +1,4 @@
-const { serializeUserSecurely } = require('./user-serializer');
+import { serializeUserSecurely } from '@growi/core/dist/models/serializers';
 
 function serializeInsecureUserAttributes(revision) {
   if (revision.author != null && revision.author._id != null) {

+ 1 - 1
apps/app/src/server/models/serializers/user-group-relation-serializer.js

@@ -1,4 +1,4 @@
-const { serializeUserSecurely } = require('./user-serializer');
+import { serializeUserSecurely } from '@growi/core/dist/models/serializers';
 
 function serializeInsecureUserAttributes(userGroupRelation) {
   if (userGroupRelation.relatedUser != null && userGroupRelation.relatedUser._id != null) {

+ 11 - 10
apps/app/src/server/routes/apiv3/activity.ts

@@ -1,16 +1,17 @@
+import { serializeUserSecurely } from '@growi/core/dist/models/serializers';
 import { parseISO, addMinutes, isValid } from 'date-fns';
-import express, { Request, Router } from 'express';
+import type { Request, Router } from 'express';
+import express from 'express';
 import { query } from 'express-validator';
 
-import { IActivity, ISearchFilter } from '~/interfaces/activity';
+import type { IActivity, ISearchFilter } from '~/interfaces/activity';
 import Activity from '~/server/models/activity';
 import loggerFactory from '~/utils/logger';
 
-import Crowi from '../../crowi';
+import type Crowi from '../../crowi';
 import { apiV3FormValidator } from '../../middlewares/apiv3-form-validator';
-import { serializeUserSecurely } from '../../models/serializers/user-serializer';
 
-import { ApiV3Response } from './interfaces/apiv3-response';
+import type { ApiV3Response } from './interfaces/apiv3-response';
 
 
 const logger = loggerFactory('growi:routes:apiv3:activity');
@@ -103,12 +104,12 @@ module.exports = (crowi: Crowi): Router => {
         },
       );
 
-      const User = crowi.model('User');
       const serializedDocs = paginateResult.docs.map((doc: IActivity) => {
-        if (doc.user != null && doc.user instanceof User) {
-          doc.user = serializeUserSecurely(doc.user);
-        }
-        return doc;
+        const { user, ...rest } = doc;
+        return {
+          user: serializeUserSecurely(user),
+          ...rest,
+        };
       });
 
       const serializedPaginationResult = {

+ 4 - 5
apps/app/src/server/routes/apiv3/attachment.js

@@ -1,10 +1,13 @@
 import { ErrorV3 } from '@growi/core/dist/models';
+import { serializeUserSecurely } from '@growi/core/dist/models/serializers';
+import express from 'express';
 import multer from 'multer';
 import autoReap from 'multer-autoreap';
 
 import { SupportedAction } from '~/interfaces/activity';
 import { AttachmentType } from '~/server/interfaces/attachment';
-import { Attachment } from '~/server/models';
+import { Attachment } from '~/server/models/attachment';
+import { serializePageSecurely, serializeRevisionSecurely } from '~/server/models/serializers';
 import loggerFactory from '~/utils/logger';
 
 import { generateAddActivityMiddleware } from '../../middlewares/add-activity';
@@ -14,16 +17,12 @@ import { excludeReadOnlyUser } from '../../middlewares/exclude-read-only-user';
 
 
 const logger = loggerFactory('growi:routes:apiv3:attachment'); // eslint-disable-line no-unused-vars
-const express = require('express');
 
 const router = express.Router();
 const {
   query, param, body,
 } = require('express-validator');
 
-const { serializePageSecurely } = require('../../models/serializers/page-serializer');
-const { serializeRevisionSecurely } = require('../../models/serializers/revision-serializer');
-const { serializeUserSecurely } = require('../../models/serializers/user-serializer');
 
 /**
  * @swagger

+ 2 - 1
apps/app/src/server/routes/apiv3/bookmarks.js

@@ -1,3 +1,5 @@
+import { serializeUserSecurely } from '@growi/core/dist/models/serializers';
+
 import { SupportedAction, SupportedTargetModel } from '~/interfaces/activity';
 import { generateAddActivityMiddleware } from '~/server/middlewares/add-activity';
 import { serializeBookmarkSecurely } from '~/server/models/serializers/bookmark-serializer';
@@ -12,7 +14,6 @@ const logger = loggerFactory('growi:routes:apiv3:bookmarks'); // eslint-disable-
 const express = require('express');
 const { body, query, param } = require('express-validator');
 
-const { serializeUserSecurely } = require('../../models/serializers/user-serializer');
 
 const router = express.Router();
 

+ 1 - 1
apps/app/src/server/routes/apiv3/forgot-password.js

@@ -1,4 +1,5 @@
 import { ErrorV3 } from '@growi/core/dist/models';
+import { serializeUserSecurely } from '@growi/core/dist/models/serializers';
 import { format, subSeconds } from 'date-fns';
 
 import { SupportedAction } from '~/interfaces/activity';
@@ -17,7 +18,6 @@ const logger = loggerFactory('growi:routes:apiv3:forgotPassword'); // eslint-dis
 const express = require('express');
 const { body } = require('express-validator');
 
-const { serializeUserSecurely } = require('../../models/serializers/user-serializer');
 
 const router = express.Router();
 

+ 31 - 17
apps/app/src/server/routes/apiv3/in-app-notification.ts

@@ -1,12 +1,14 @@
+import { serializeUserSecurely } from '@growi/core/dist/models/serializers';
+import express from 'express';
+
 import { SupportedAction } from '~/interfaces/activity';
+import type { CrowiRequest } from '~/interfaces/crowi-request';
 import { generateAddActivityMiddleware } from '~/server/middlewares/add-activity';
 
+import type { IInAppNotification } from '../../../interfaces/in-app-notification';
 
-import { IInAppNotification } from '../../../interfaces/in-app-notification';
-
-const express = require('express');
+import type { ApiV3Response } from './interfaces/apiv3-response';
 
-const { serializeUserSecurely } = require('../../models/serializers/user-serializer');
 
 const router = express.Router();
 
@@ -22,14 +24,18 @@ module.exports = (crowi) => {
 
   const activityEvent = crowi.event('activity');
 
-  router.get('/list', accessTokenParser, loginRequiredStrictly, async(req, res) => {
-    const user = req.user;
+  router.get('/list', accessTokenParser, loginRequiredStrictly, async(req: CrowiRequest, res: ApiV3Response) => {
+    // user must be set by loginRequiredStrictly
+    // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
+    const user = req.user!;
 
-    const limit = parseInt(req.query.limit) || 10;
+    const limit = req.query.limit != null
+      ? parseInt(req.query.limit.toString()) || 10
+      : 10;
 
     let offset = 0;
-    if (req.query.offset) {
-      offset = parseInt(req.query.offset, 10);
+    if (req.query.offset != null) {
+      offset = parseInt(req.query.offset.toString(), 10);
     }
 
     const queryOptions = {
@@ -73,10 +79,13 @@ module.exports = (crowi) => {
     return res.apiv3(serializedPaginationResult);
   });
 
-  router.get('/status', accessTokenParser, loginRequiredStrictly, async(req, res) => {
-    const userId = req.user._id;
+  router.get('/status', accessTokenParser, loginRequiredStrictly, async(req: CrowiRequest, res: ApiV3Response) => {
+    // user must be set by loginRequiredStrictly
+    // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
+    const user = req.user!;
+
     try {
-      const count = await inAppNotificationService.getUnreadCountByUser(userId);
+      const count = await inAppNotificationService.getUnreadCountByUser(user._id);
       return res.apiv3({ count });
     }
     catch (err) {
@@ -84,7 +93,7 @@ module.exports = (crowi) => {
     }
   });
 
-  router.post('/read', accessTokenParser, loginRequiredStrictly, async(req, res) => {
+  router.post('/read', accessTokenParser, loginRequiredStrictly, async(req: CrowiRequest, res: ApiV3Response) => {
     const user = req.user;
 
     try {
@@ -96,8 +105,11 @@ module.exports = (crowi) => {
     }
   });
 
-  router.post('/open', accessTokenParser, loginRequiredStrictly, async(req, res) => {
-    const user = req.user;
+  router.post('/open', accessTokenParser, loginRequiredStrictly, async(req: CrowiRequest, res: ApiV3Response) => {
+    // user must be set by loginRequiredStrictly
+    // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
+    const user = req.user!;
+
     const id = req.body.id;
 
     try {
@@ -110,8 +122,10 @@ module.exports = (crowi) => {
     }
   });
 
-  router.put('/all-statuses-open', accessTokenParser, loginRequiredStrictly, addActivity, async(req, res) => {
-    const user = req.user;
+  router.put('/all-statuses-open', accessTokenParser, loginRequiredStrictly, addActivity, async(req: CrowiRequest, res: ApiV3Response) => {
+    // user must be set by loginRequiredStrictly
+    // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
+    const user = req.user!;
 
     try {
       await inAppNotificationService.updateAllNotificationsAsOpened(user);

+ 2 - 3
apps/app/src/server/routes/apiv3/page/create-page.ts

@@ -16,11 +16,10 @@ import { subscribeRuleNames } from '~/interfaces/in-app-notification';
 import type { IOptionsForCreate } from '~/interfaces/page';
 import type Crowi from '~/server/crowi';
 import { generateAddActivityMiddleware } from '~/server/middlewares/add-activity';
-import {
-  GlobalNotificationSettingEvent, serializePageSecurely, serializeRevisionSecurely,
-} from '~/server/models';
+import { GlobalNotificationSettingEvent } from '~/server/models/GlobalNotificationSetting';
 import type { PageDocument, PageModel } from '~/server/models/page';
 import PageTagRelation from '~/server/models/page-tag-relation';
+import { serializePageSecurely, serializeRevisionSecurely } from '~/server/models/serializers';
 import { configManager } from '~/server/service/config-manager';
 import { getTranslation } from '~/server/service/i18next';
 import loggerFactory from '~/utils/logger';

+ 3 - 3
apps/app/src/server/routes/apiv3/page/update-page.ts

@@ -3,6 +3,7 @@ import type {
   IPage, IRevisionHasId, IUserHasId,
 } from '@growi/core';
 import { ErrorV3 } from '@growi/core/dist/models';
+import { serializeUserSecurely } from '@growi/core/dist/models/serializers';
 import type { Request, RequestHandler } from 'express';
 import type { ValidationChain } from 'express-validator';
 import { body } from 'express-validator';
@@ -13,10 +14,9 @@ import { type IApiv3PageUpdateParams, PageUpdateErrorCode } from '~/interfaces/a
 import type { IOptionsForUpdate } from '~/interfaces/page';
 import type Crowi from '~/server/crowi';
 import { generateAddActivityMiddleware } from '~/server/middlewares/add-activity';
-import {
-  GlobalNotificationSettingEvent, serializePageSecurely, serializeRevisionSecurely, serializeUserSecurely,
-} from '~/server/models';
+import { GlobalNotificationSettingEvent } from '~/server/models/GlobalNotificationSetting';
 import type { PageDocument, PageModel } from '~/server/models/page';
+import { serializePageSecurely, serializeRevisionSecurely } from '~/server/models/serializers';
 import { preNotifyService } from '~/server/service/pre-notify';
 import { normalizeLatestRevisionIfBroken } from '~/server/service/revision/normalize-latest-revision-if-broken';
 import { getYjsService } from '~/server/service/yjs';

+ 2 - 2
apps/app/src/server/routes/apiv3/pages/index.js

@@ -1,6 +1,7 @@
 
 import { PageGrant } from '@growi/core';
 import { ErrorV3 } from '@growi/core/dist/models';
+import { serializeUserSecurely } from '@growi/core/dist/models/serializers';
 import { isCreatablePage, isTrashPage, isUserPage } from '@growi/core/dist/utils/page-path-utils';
 import { normalizePath, addHeadingSlash } from '@growi/core/dist/utils/path-utils';
 import express from 'express';
@@ -8,7 +9,7 @@ import { body, query } from 'express-validator';
 
 import { SupportedTargetModel, SupportedAction } from '~/interfaces/activity';
 import { subscribeRuleNames } from '~/interfaces/in-app-notification';
-import { GlobalNotificationSettingEvent } from '~/server/models';
+import { GlobalNotificationSettingEvent } from '~/server/models/GlobalNotificationSetting';
 import PageTagRelation from '~/server/models/page-tag-relation';
 import { preNotifyService } from '~/server/service/pre-notify';
 import loggerFactory from '~/utils/logger';
@@ -17,7 +18,6 @@ import { generateAddActivityMiddleware } from '../../../middlewares/add-activity
 import { apiV3FormValidator } from '../../../middlewares/apiv3-form-validator';
 import { excludeReadOnlyUser } from '../../../middlewares/exclude-read-only-user';
 import { serializePageSecurely } from '../../../models/serializers/page-serializer';
-import { serializeUserSecurely } from '../../../models/serializers/user-serializer';
 import { isV5ConversionError } from '../../../models/vo/v5-conversion-error';
 
 

+ 2 - 3
apps/app/src/server/routes/apiv3/revisions.js

@@ -1,4 +1,6 @@
 import { ErrorV3 } from '@growi/core/dist/models';
+import { serializeUserSecurely } from '@growi/core/dist/models/serializers';
+import express from 'express';
 
 import { Revision } from '~/server/models/revision';
 import { normalizeLatestRevisionIfBroken } from '~/server/service/revision/normalize-latest-revision-if-broken';
@@ -8,11 +10,8 @@ import { apiV3FormValidator } from '../../middlewares/apiv3-form-validator';
 
 const logger = loggerFactory('growi:routes:apiv3:pages');
 
-const express = require('express');
 const { query, param } = require('express-validator');
 
-const { serializeUserSecurely } = require('../../models/serializers/user-serializer');
-
 const router = express.Router();
 
 /**

+ 2 - 3
apps/app/src/server/routes/apiv3/user-group-relation.js

@@ -1,15 +1,14 @@
 import { ErrorV3 } from '@growi/core/dist/models';
+import express from 'express';
 
+import { serializeUserGroupRelationSecurely } from '~/server/models/serializers';
 import UserGroupRelation from '~/server/models/user-group-relation';
 import loggerFactory from '~/utils/logger';
 
 const logger = loggerFactory('growi:routes:apiv3:user-group-relation'); // eslint-disable-line no-unused-vars
 
-const express = require('express');
 const { query } = require('express-validator');
 
-const { serializeUserGroupRelationSecurely } = require('../../models/serializers/user-group-relation-serializer');
-
 const router = express.Router();
 
 const validator = {};

+ 6 - 9
apps/app/src/server/routes/apiv3/user-group.js

@@ -1,12 +1,17 @@
 import { GroupType } from '@growi/core';
 import { ErrorV3 } from '@growi/core/dist/models';
-
+import { serializeUserSecurely } from '@growi/core/dist/models/serializers';
+import express from 'express';
+import {
+  body, param, query, sanitizeQuery,
+} from 'express-validator';
 
 import { SupportedAction } from '~/interfaces/activity';
 import { serializeUserGroupRelationSecurely } from '~/server/models/serializers/user-group-relation-serializer';
 import UserGroup from '~/server/models/user-group';
 import UserGroupRelation from '~/server/models/user-group-relation';
 import { excludeTestIdsFromTargetIds } from '~/server/util/compare-objectId';
+import { toPagingLimit, toPagingOffset } from '~/server/util/express-validator/sanitizer';
 import { generalXssFilter } from '~/services/general-xss-filter';
 import loggerFactory from '~/utils/logger';
 
@@ -16,16 +21,8 @@ import { apiV3FormValidator } from '../../middlewares/apiv3-form-validator';
 
 const logger = loggerFactory('growi:routes:apiv3:user-group'); // eslint-disable-line no-unused-vars
 
-const express = require('express');
-
 const router = express.Router();
 
-const { body, param, query } = require('express-validator');
-const { sanitizeQuery } = require('express-validator');
-
-const { serializeUserSecurely } = require('../../models/serializers/user-serializer');
-const { toPagingLimit, toPagingOffset } = require('../../util/express-validator/sanitizer');
-
 
 /**
  * @swagger

+ 7 - 9
apps/app/src/server/routes/apiv3/users.js

@@ -1,11 +1,18 @@
 
+import path from 'path';
+
 import { ErrorV3 } from '@growi/core/dist/models';
+import { serializeUserSecurely } from '@growi/core/dist/models/serializers';
 import { userHomepagePath } from '@growi/core/dist/utils/page-path-utils';
+import express from 'express';
+import { body, query } from 'express-validator';
+import { isEmail } from 'validator';
 
 import ExternalUserGroupRelation from '~/features/external-user-group/server/models/external-user-group-relation';
 import { SupportedAction } from '~/interfaces/activity';
 import Activity from '~/server/models/activity';
 import ExternalAccount from '~/server/models/external-account';
+import { serializePageSecurely } from '~/server/models/serializers';
 import UserGroupRelation from '~/server/models/user-group-relation';
 import { configManager } from '~/server/service/config-manager';
 import { deleteCompletelyUserHomeBySystem } from '~/server/service/page/delete-completely-user-home-by-system';
@@ -16,17 +23,8 @@ import { apiV3FormValidator } from '../../middlewares/apiv3-form-validator';
 
 const logger = loggerFactory('growi:routes:apiv3:users');
 
-const path = require('path');
-
-const express = require('express');
-
 const router = express.Router();
 
-const { body, query } = require('express-validator');
-const { isEmail } = require('validator');
-
-const { serializePageSecurely } = require('../../models/serializers/page-serializer');
-const { serializeUserSecurely } = require('../../models/serializers/user-serializer');
 
 const PAGE_ITEMS = 50;
 

+ 1 - 3
apps/app/src/server/routes/attachment/api.js

@@ -1,11 +1,9 @@
-import { isCreatablePage, isUserPage } from '@growi/core/dist/utils/page-path-utils';
 
 import { SupportedAction } from '~/interfaces/activity';
 import { AttachmentType } from '~/server/interfaces/attachment';
 import loggerFactory from '~/utils/logger';
 
-import { Attachment, serializePageSecurely, serializeRevisionSecurely } from '../../models';
-
+import { Attachment } from '../../models/attachment';
 /* eslint-disable no-use-before-define */
 
 

+ 3 - 2
apps/app/src/server/routes/comment.js

@@ -1,9 +1,11 @@
 
+import { serializeUserSecurely } from '@growi/core/dist/models/serializers';
+
 import { Comment, CommentEvent, commentEvent } from '~/features/comment/server';
 import { SupportedAction, SupportedTargetModel, SupportedEventModel } from '~/interfaces/activity';
 import loggerFactory from '~/utils/logger';
 
-import { GlobalNotificationSettingEvent } from '../models';
+import { GlobalNotificationSettingEvent } from '../models/GlobalNotificationSetting';
 import { preNotifyService } from '../service/pre-notify';
 
 /**
@@ -12,7 +14,6 @@ import { preNotifyService } from '../service/pre-notify';
  *    name: Comments
  */
 
-const { serializeUserSecurely } = require('../models/serializers/user-serializer');
 
 /**
  * @swagger

+ 6 - 12
apps/app/src/server/service/search-delegator/private-legacy-pages.ts

@@ -2,14 +2,14 @@ import type { IPage } from '@growi/core';
 import mongoose from 'mongoose';
 
 import { SearchDelegatorName } from '~/interfaces/named-query';
-import { ISearchResult } from '~/interfaces/search';
-import { PageModel, PageDocument, PageQueryBuilder } from '~/server/models/page';
+import type { ISearchResult } from '~/interfaces/search';
+import type { PageModel, PageDocument, PageQueryBuilder } from '~/server/models/page';
+import { serializePageSecurely } from '~/server/models/serializers';
 
-import {
+import type {
   QueryTerms, MongoTermsKey,
   SearchableData, SearchDelegator, UnavailableTermsKey, MongoQueryTerms,
 } from '../../interfaces/search';
-import { serializeUserSecurely } from '../../models/serializers/user-serializer';
 
 
 const AVAILABLE_KEYS = ['match', 'not_match', 'prefix', 'not_prefix'];
@@ -47,21 +47,15 @@ class PrivateLegacyPagesDelegator implements SearchDelegator<IPage, MongoTermsKe
 
     const total = await countQueryBuilder.query.count();
 
-    const _pages: PageDocument[] = await findQueryBuilder
+    const pages: PageDocument[] = await findQueryBuilder
       .addConditionToPagenate(offset, limit)
       .query
       .populate('creator')
       .populate('lastUpdateUser')
       .exec();
 
-    const pages = _pages.map((page) => {
-      page.creator = serializeUserSecurely(page.creator);
-      page.lastUpdateUser = serializeUserSecurely(page.lastUpdateUser);
-      return page;
-    });
-
     return {
-      data: pages,
+      data: pages.map(page => serializePageSecurely(page)),
       meta: {
         total,
         hitsCount: pages.length,

+ 1 - 1
apps/app/src/server/service/search.ts

@@ -1,4 +1,5 @@
 import type { IPageHasId } from '@growi/core';
+import { serializeUserSecurely } from '@growi/core/dist/models/serializers';
 import mongoose from 'mongoose';
 import { FilterXSS } from 'xss';
 
@@ -13,7 +14,6 @@ import type {
 } from '../interfaces/search';
 import NamedQuery from '../models/named-query';
 import type { PageModel } from '../models/page';
-import { serializeUserSecurely } from '../models/serializers/user-serializer';
 import { SearchError } from '../models/vo/search-error';
 import { hasIntersection } from '../util/compare-objectId';
 

+ 1 - 0
packages/remark-attachment-refs/package.json

@@ -49,6 +49,7 @@
     "@growi/ui": "link:../ui",
     "axios": "^0.24.0",
     "bunyan": "^1.8.15",
+    "express": "^4.19.2",
     "hast-util-select": "^5.0.5",
     "mongoose": "^6.11.3",
     "swr": "^2.0.3",

+ 29 - 28
packages/remark-attachment-refs/src/server/routes/refs.ts

@@ -1,33 +1,37 @@
-import type { IAttachment } from '@growi/core';
+import type { IPage, IUser, IAttachment } from '@growi/core';
+import { serializeAttachmentSecurely } from '@growi/core/dist/models/serializers';
 import { OptionParser } from '@growi/core/dist/remark-plugins';
-import { model } from 'mongoose';
+import type { Request } from 'express';
+import { Router } from 'express';
+import type { Model, HydratedDocument } from 'mongoose';
+import mongoose, { model, Types } from 'mongoose';
 
 import loggerFactory from '../../utils/logger';
 
 const logger = loggerFactory('growi:remark-attachment-refs:routes:refs');
 
 
+type RequestWithUser = Request & { user: HydratedDocument<IUser> };
+
+
 const loginRequiredFallback = (req, res) => {
   return res.status(403).send('login required');
 };
 
 // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
 export const routesFactory = (crowi): any => {
-  const express = crowi.require('express');
-  const mongoose = crowi.require('mongoose');
 
   const loginRequired = crowi.require('../middlewares/login-required')(crowi, true, loginRequiredFallback);
   const accessTokenParser = crowi.require('../middlewares/access-token-parser')(crowi);
-  const { serializeUserSecurely } = crowi.require('../models/serializers/user-serializer');
 
-  const router = express.Router();
+  const router = Router();
 
-  const ObjectId = mongoose.Types.ObjectId;
+  const ObjectId = Types.ObjectId;
 
-  const User = crowi.model('User');
-  const Page = crowi.model('Page');
+  const Page = mongoose.model <HydratedDocument<IPage>, Model<any> & any>('Page');
 
-  const { PageQueryBuilder } = Page;
+  // eslint-disable-next-line @typescript-eslint/no-explicit-any
+  const { PageQueryBuilder } = Page as any;
 
   function generateRegexp(expression: string): RegExp {
     // https://regex101.com/r/uOrwqt/2
@@ -78,7 +82,7 @@ export const routesFactory = (crowi): any => {
   /**
    * return an Attachment model
    */
-  router.get('/ref', accessTokenParser, loginRequired, async(req, res) => {
+  router.get('/ref', accessTokenParser, loginRequired, async(req: RequestWithUser, res) => {
     const user = req.user;
     const { pagePath, fileNameOrId } = req.query;
 
@@ -96,9 +100,10 @@ export const routesFactory = (crowi): any => {
     }
 
     // convert ObjectId
+    // eslint-disable-next-line @typescript-eslint/no-explicit-any
     const orConditions: any[] = [{ originalName: fileNameOrId }];
-    if (ObjectId.isValid(fileNameOrId)) {
-      orConditions.push({ _id: ObjectId(fileNameOrId) });
+    if (fileNameOrId != null && ObjectId.isValid(fileNameOrId.toString())) {
+      orConditions.push({ _id: new ObjectId(fileNameOrId.toString()) });
     }
 
     const Attachment = model<IAttachment>('Attachment');
@@ -125,19 +130,16 @@ export const routesFactory = (crowi): any => {
       return;
     }
 
-    // serialize User data
-    attachment.creator = serializeUserSecurely(attachment.creator);
-
-    res.status(200).send({ attachment });
+    res.status(200).send({ attachment: serializeAttachmentSecurely(attachment) });
   });
 
   /**
    * return a list of Attachment
    */
-  router.get('/refs', accessTokenParser, loginRequired, async(req, res) => {
+  router.get('/refs', accessTokenParser, loginRequired, async(req: RequestWithUser, res) => {
     const user = req.user;
     const { prefix, pagePath } = req.query;
-    const options = JSON.parse(req.query.options);
+    const options = JSON.parse(req.query.options?.toString() ?? '');
 
     // check either 'prefix' or 'pagePath ' is specified
     if (prefix == null && pagePath == null) {
@@ -149,11 +151,17 @@ export const routesFactory = (crowi): any => {
     let regex;
     const regexOptionValue = options.regexp || options.regex;
     if (regexOptionValue != null) {
+      // check the length to avoid ReDoS
+      if (regexOptionValue.length > 400) {
+        res.status(400).send('the length of the \'regex\' option is too long.');
+        return;
+      }
+
       try {
         regex = generateRegexp(regexOptionValue);
       }
       catch (err) {
-        res.status(400).send(`the 'regex=${options.regex}' option is invalid as RegExp.`);
+        res.status(400).send('the \'regex\' option is invalid as RegExp.');
         return;
       }
     }
@@ -207,14 +215,7 @@ export const routesFactory = (crowi): any => {
       .populate('creator')
       .exec();
 
-    // serialize User data
-    attachments.forEach((doc) => {
-      if (doc.creator != null && doc.creator instanceof User) {
-        doc.creator = serializeUserSecurely(doc.creator);
-      }
-    });
-
-    res.status(200).send({ attachments });
+    res.status(200).send({ attachments: attachments.map(attachment => serializeAttachmentSecurely(attachment)) });
   });
 
   return router;