revisions.js 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218
  1. import { ErrorV3 } from '@growi/core';
  2. import loggerFactory from '~/utils/logger';
  3. import { apiV3FormValidator } from '../../middlewares/apiv3-form-validator';
  4. const logger = loggerFactory('growi:routes:apiv3:pages');
  5. const express = require('express');
  6. const { query, param } = require('express-validator');
  7. const { serializeUserSecurely } = require('../../models/serializers/user-serializer');
  8. const router = express.Router();
  9. /**
  10. * @swagger
  11. * tags:
  12. * name: Revisions
  13. */
  14. /**
  15. * @swagger
  16. *
  17. * components:
  18. * schemas:
  19. * Revision:
  20. * description: Revision
  21. * type: object
  22. * properties:
  23. * _id:
  24. * type: string
  25. * description: revision ID
  26. * example: 5e0734e472560e001761fa68
  27. * __v:
  28. * type: number
  29. * description: DB record version
  30. * example: 0
  31. * author:
  32. * $ref: '#/components/schemas/User/properties/_id'
  33. * body:
  34. * type: string
  35. * description: content body
  36. * example: |
  37. * # test
  38. *
  39. * test
  40. * format:
  41. * type: string
  42. * description: format
  43. * example: markdown
  44. * path:
  45. * type: string
  46. * description: path
  47. * example: /user/alice/test
  48. * createdAt:
  49. * type: string
  50. * description: date created at
  51. * example: 2010-01-01T00:00:00.000Z
  52. */
  53. module.exports = (crowi) => {
  54. const certifySharedPage = require('../../middlewares/certify-shared-page')(crowi);
  55. const accessTokenParser = require('../../middlewares/access-token-parser')(crowi);
  56. const loginRequired = require('../../middlewares/login-required')(crowi, true);
  57. const {
  58. Revision,
  59. Page,
  60. User,
  61. } = crowi.models;
  62. const validator = {
  63. retrieveRevisions: [
  64. query('pageId').isMongoId().withMessage('pageId is required'),
  65. query('offset').if(value => value != null).isInt({ min: 0 }).withMessage('offset must be int'),
  66. query('limit').if(value => value != null).isInt({ max: 100 }).withMessage('You should set less than 100 or not to set limit.'),
  67. ],
  68. retrieveRevisionById: [
  69. query('pageId').isMongoId().withMessage('pageId is required'),
  70. param('id').isMongoId().withMessage('id is required'),
  71. ],
  72. };
  73. /**
  74. * @swagger
  75. *
  76. * /revisions/list:
  77. * get:
  78. * tags: [Revisions]
  79. * description: Get revisions by page id
  80. * parameters:
  81. * - in: query
  82. * name: pageId
  83. * schema:
  84. * type: string
  85. * description: page id
  86. * - in: query
  87. * name: page
  88. * description: selected page number
  89. * schema:
  90. * type: number
  91. * - in: query
  92. * name: limit
  93. * description: page item limit
  94. * schema:
  95. * type: number
  96. * responses:
  97. * 200:
  98. * description: Return revisions belong to page
  99. *
  100. */
  101. router.get('/list', certifySharedPage, accessTokenParser, loginRequired, validator.retrieveRevisions, apiV3FormValidator, async(req, res) => {
  102. const pageId = req.query.pageId;
  103. const limit = req.query.limit || await crowi.configManager.getConfig('crowi', 'customize:showPageLimitationS') || 10;
  104. const { isSharedPage } = req;
  105. const offset = req.query.offset || 0;
  106. // check whether accessible
  107. if (!isSharedPage && !(await Page.isAccessiblePageByViewer(pageId, req.user))) {
  108. return res.apiv3Err(new ErrorV3('Current user is not accessible to this page.', 'forbidden-page'), 403);
  109. }
  110. try {
  111. const page = await Page.findOne({ _id: pageId });
  112. const queryOpts = {
  113. offset,
  114. sort: { createdAt: -1 },
  115. populate: 'author',
  116. pagination: false,
  117. };
  118. if (limit > 0) {
  119. queryOpts.limit = limit;
  120. queryOpts.pagination = true;
  121. }
  122. const paginateResult = await Revision.paginate(
  123. { pageId: page._id },
  124. queryOpts,
  125. );
  126. paginateResult.docs.forEach((doc) => {
  127. if (doc.author != null && doc.author instanceof User) {
  128. doc.author = serializeUserSecurely(doc.author);
  129. }
  130. });
  131. const result = {
  132. revisions: paginateResult.docs,
  133. totalCount: paginateResult.totalDocs,
  134. offset: paginateResult.offset,
  135. };
  136. return res.apiv3(result);
  137. }
  138. catch (err) {
  139. const msg = 'Error occurred in getting revisions by poge id';
  140. logger.error('Error', err);
  141. return res.apiv3Err(new ErrorV3(msg, 'faild-to-find-revisions'), 500);
  142. }
  143. });
  144. /**
  145. * @swagger
  146. *
  147. * /revisions/{id}:
  148. * get:
  149. * tags: [Revisions]
  150. * description: Get one revision by id
  151. * parameters:
  152. * - in: query
  153. * name: pageId
  154. * required: true
  155. * description: page id
  156. * schema:
  157. * type: string
  158. * - in: path
  159. * name: id
  160. * required: true
  161. * description: revision id
  162. * schema:
  163. * type: string
  164. * responses:
  165. * 200:
  166. * description: Return revision
  167. *
  168. */
  169. router.get('/:id', certifySharedPage, accessTokenParser, loginRequired, validator.retrieveRevisionById, apiV3FormValidator, async(req, res) => {
  170. const revisionId = req.params.id;
  171. const pageId = req.query.pageId;
  172. const { isSharedPage } = req;
  173. // check whether accessible
  174. if (!isSharedPage && !(await Page.isAccessiblePageByViewer(pageId, req.user))) {
  175. return res.apiv3Err(new ErrorV3('Current user is not accessible to this page.', 'forbidden-page'), 403);
  176. }
  177. try {
  178. const revision = await Revision.findById(revisionId).populate('author');
  179. if (revision.author != null && revision.author instanceof User) {
  180. revision.author = serializeUserSecurely(revision.author);
  181. }
  182. return res.apiv3({ revision });
  183. }
  184. catch (err) {
  185. const msg = 'Error occurred in getting revision data by id';
  186. logger.error('Error', err);
  187. return res.apiv3Err(new ErrorV3(msg, 'faild-to-find-revision'), 500);
  188. }
  189. });
  190. return router;
  191. };