page.js 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826
  1. import { pagePathUtils, AllSubscriptionStatusType, SubscriptionStatusType } from '@growi/core';
  2. import { SupportedAction, SupportedTargetModel } from '~/interfaces/activity';
  3. import { generateAddActivityMiddleware } from '~/server/middlewares/add-activity';
  4. import Subscription from '~/server/models/subscription';
  5. import UserGroup from '~/server/models/user-group';
  6. import loggerFactory from '~/utils/logger';
  7. import { apiV3FormValidator } from '../../middlewares/apiv3-form-validator';
  8. const logger = loggerFactory('growi:routes:apiv3:page'); // eslint-disable-line no-unused-vars
  9. const express = require('express');
  10. const { body, query, param } = require('express-validator');
  11. const router = express.Router();
  12. const { convertToNewAffiliationPath, isTopPage } = pagePathUtils;
  13. const ErrorV3 = require('../../models/vo/error-apiv3');
  14. /**
  15. * @swagger
  16. * tags:
  17. * name: Page
  18. */
  19. /**
  20. * @swagger
  21. *
  22. * components:
  23. * schemas:
  24. * Page:
  25. * description: Page
  26. * type: object
  27. * properties:
  28. * _id:
  29. * type: string
  30. * description: page ID
  31. * example: 5e07345972560e001761fa63
  32. * __v:
  33. * type: number
  34. * description: DB record version
  35. * example: 0
  36. * commentCount:
  37. * type: number
  38. * description: count of comments
  39. * example: 3
  40. * createdAt:
  41. * type: string
  42. * description: date created at
  43. * example: 2010-01-01T00:00:00.000Z
  44. * creator:
  45. * $ref: '#/components/schemas/User'
  46. * extended:
  47. * type: object
  48. * description: extend data
  49. * example: {}
  50. * grant:
  51. * type: number
  52. * description: grant
  53. * example: 1
  54. * grantedUsers:
  55. * type: array
  56. * description: granted users
  57. * items:
  58. * type: string
  59. * description: user ID
  60. * example: ["5ae5fccfc5577b0004dbd8ab"]
  61. * lastUpdateUser:
  62. * $ref: '#/components/schemas/User'
  63. * liker:
  64. * type: array
  65. * description: granted users
  66. * items:
  67. * type: string
  68. * description: user ID
  69. * example: []
  70. * path:
  71. * type: string
  72. * description: page path
  73. * example: /
  74. * revision:
  75. * type: string
  76. * description: page revision
  77. * seenUsers:
  78. * type: array
  79. * description: granted users
  80. * items:
  81. * type: string
  82. * description: user ID
  83. * example: ["5ae5fccfc5577b0004dbd8ab"]
  84. * status:
  85. * type: string
  86. * description: status
  87. * enum:
  88. * - 'wip'
  89. * - 'published'
  90. * - 'deleted'
  91. * - 'deprecated'
  92. * example: published
  93. * updatedAt:
  94. * type: string
  95. * description: date updated at
  96. * example: 2010-01-01T00:00:00.000Z
  97. *
  98. * LikeParams:
  99. * description: LikeParams
  100. * type: object
  101. * properties:
  102. * pageId:
  103. * type: string
  104. * description: page ID
  105. * example: 5e07345972560e001761fa63
  106. * bool:
  107. * type: boolean
  108. * description: boolean for like status
  109. *
  110. * PageInfo:
  111. * description: PageInfo
  112. * type: object
  113. * required:
  114. * - sumOfLikers
  115. * - likerIds
  116. * - sumOfSeenUsers
  117. * - seenUserIds
  118. * properties:
  119. * isLiked:
  120. * type: boolean
  121. * description: Whether the page is liked by the logged in user
  122. * sumOfLikers:
  123. * type: number
  124. * description: Number of users who have liked the page
  125. * likerIds:
  126. * type: array
  127. * items:
  128. * type: string
  129. * description: Ids of users who have liked the page
  130. * example: ["5e07345972560e001761fa63"]
  131. * sumOfSeenUsers:
  132. * type: number
  133. * description: Number of users who have seen the page
  134. * seenUserIds:
  135. * type: array
  136. * items:
  137. * type: string
  138. * description: Ids of users who have seen the page
  139. * example: ["5e07345972560e001761fa63"]
  140. *
  141. * PageParams:
  142. * description: PageParams
  143. * type: object
  144. * required:
  145. * - pageId
  146. * properties:
  147. * pageId:
  148. * type: string
  149. * description: page ID
  150. * example: 5e07345972560e001761fa63
  151. */
  152. module.exports = (crowi) => {
  153. const accessTokenParser = require('../../middlewares/access-token-parser')(crowi);
  154. const loginRequired = require('../../middlewares/login-required')(crowi, true);
  155. const loginRequiredStrictly = require('../../middlewares/login-required')(crowi);
  156. const certifySharedPage = require('../../middlewares/certify-shared-page')(crowi);
  157. const addActivity = generateAddActivityMiddleware(crowi);
  158. const globalNotificationService = crowi.getGlobalNotificationService();
  159. const socketIoService = crowi.socketIoService;
  160. const { Page, GlobalNotificationSetting, Bookmark } = crowi.models;
  161. const { pageService, exportService } = crowi;
  162. const activityEvent = crowi.event('activity');
  163. const validator = {
  164. getPage: [
  165. query('pageId').optional().isString(),
  166. query('path').optional().isString(),
  167. query('findAll').optional().isBoolean(),
  168. ],
  169. likes: [
  170. body('pageId').isString(),
  171. body('bool').isBoolean(),
  172. ],
  173. info: [
  174. query('pageId').isMongoId().withMessage('pageId is required'),
  175. ],
  176. isGrantNormalized: [
  177. query('pageId').isMongoId().withMessage('pageId is required'),
  178. ],
  179. applicableGrant: [
  180. query('pageId').isMongoId().withMessage('pageId is required'),
  181. ],
  182. updateGrant: [
  183. param('pageId').isMongoId().withMessage('pageId is required'),
  184. body('grant').isInt().withMessage('grant is required'),
  185. body('grantedGroup').optional().isMongoId().withMessage('grantedGroup must be a mongo id'),
  186. ],
  187. export: [
  188. query('format').isString().isIn(['md', 'pdf']),
  189. query('revisionId').isString(),
  190. ],
  191. archive: [
  192. body('rootPagePath').isString(),
  193. body('isCommentDownload').isBoolean(),
  194. body('isAttachmentFileDownload').isBoolean(),
  195. body('isSubordinatedPageDownload').isBoolean(),
  196. body('fileType').isString().isIn(['pdf', 'markdown']),
  197. body('hierarchyType').isString().isIn(['allSubordinatedPage', 'decideHierarchy']),
  198. body('hierarchyValue').isNumeric(),
  199. ],
  200. exist: [
  201. query('fromPath').isString(),
  202. query('toPath').isString(),
  203. ],
  204. subscribe: [
  205. body('pageId').isString(),
  206. body('status').isIn(AllSubscriptionStatusType),
  207. ],
  208. subscribeStatus: [
  209. query('pageId').isString(),
  210. ],
  211. };
  212. /**
  213. * @swagger
  214. *
  215. * /page:
  216. * get:
  217. * tags: [Page]
  218. * operationId: getPage
  219. * summary: /page
  220. * description: get page by pagePath or pageId
  221. * parameters:
  222. * - name: pageId
  223. * in: query
  224. * description: page id
  225. * schema:
  226. * $ref: '#/components/schemas/Page/properties/_id'
  227. * - name: path
  228. * in: query
  229. * description: page path
  230. * schema:
  231. * $ref: '#/components/schemas/Page/properties/path'
  232. * responses:
  233. * 200:
  234. * description: Page data
  235. * content:
  236. * application/json:
  237. * schema:
  238. * $ref: '#/components/schemas/Page'
  239. */
  240. router.get('/', certifySharedPage, accessTokenParser, loginRequired, validator.getPage, apiV3FormValidator, async(req, res) => {
  241. const { user } = req;
  242. const { pageId, path, findAll } = req.query;
  243. if (pageId == null && path == null) {
  244. return res.apiv3Err(new ErrorV3('Either parameter of path or pageId is required.', 'invalid-request'));
  245. }
  246. let page;
  247. let pages;
  248. try {
  249. if (pageId != null) { // prioritized
  250. page = await Page.findByIdAndViewer(pageId, user);
  251. }
  252. else if (!findAll) {
  253. page = await Page.findByPathAndViewer(path, user, null, true);
  254. }
  255. else {
  256. pages = await Page.findByPathAndViewer(path, user, null, false);
  257. }
  258. }
  259. catch (err) {
  260. logger.error('get-page-failed', err);
  261. return res.apiv3Err(err, 500);
  262. }
  263. if (page == null && (pages == null || pages.length === 0)) {
  264. return res.apiv3Err('Page is not found', 404);
  265. }
  266. if (page != null) {
  267. try {
  268. page.initLatestRevisionField();
  269. // populate
  270. page = await page.populateDataToShowRevision();
  271. }
  272. catch (err) {
  273. logger.error('populate-page-failed', err);
  274. return res.apiv3Err(err, 500);
  275. }
  276. }
  277. return res.apiv3({ page, pages });
  278. });
  279. /**
  280. * @swagger
  281. *
  282. * /page/likes:
  283. * put:
  284. * tags: [Page]
  285. * summary: /page/likes
  286. * description: Update liked status
  287. * operationId: updateLikedStatus
  288. * requestBody:
  289. * content:
  290. * application/json:
  291. * schema:
  292. * $ref: '#/components/schemas/LikeParams'
  293. * responses:
  294. * 200:
  295. * description: Succeeded to update liked status.
  296. * content:
  297. * application/json:
  298. * schema:
  299. * $ref: '#/components/schemas/Page'
  300. */
  301. router.put('/likes', accessTokenParser, loginRequiredStrictly, addActivity, validator.likes, apiV3FormValidator, async(req, res) => {
  302. const { pageId, bool: isLiked } = req.body;
  303. let page;
  304. try {
  305. page = await Page.findByIdAndViewer(pageId, req.user);
  306. if (page == null) {
  307. return res.apiv3Err(`Page '${pageId}' is not found or forbidden`);
  308. }
  309. if (isLiked) {
  310. page = await page.like(req.user);
  311. }
  312. else {
  313. page = await page.unlike(req.user);
  314. }
  315. }
  316. catch (err) {
  317. logger.error('update-like-failed', err);
  318. return res.apiv3Err(err, 500);
  319. }
  320. const result = { page };
  321. result.seenUser = page.seenUsers;
  322. const parameters = {
  323. targetModel: SupportedTargetModel.MODEL_PAGE,
  324. target: page,
  325. action: isLiked ? SupportedAction.ACTION_PAGE_LIKE : SupportedAction.ACTION_PAGE_UNLIKE,
  326. };
  327. activityEvent.emit('update', res.locals.activity._id, parameters, page);
  328. res.apiv3({ result });
  329. if (isLiked) {
  330. try {
  331. // global notification
  332. await globalNotificationService.fire(GlobalNotificationSetting.EVENT.PAGE_LIKE, page, req.user);
  333. }
  334. catch (err) {
  335. logger.error('Like notification failed', err);
  336. }
  337. }
  338. });
  339. /**
  340. * @swagger
  341. *
  342. * /page/info:
  343. * get:
  344. * tags: [Page]
  345. * summary: /page/info
  346. * description: Retrieve current page info
  347. * operationId: getPageInfo
  348. * requestBody:
  349. * content:
  350. * application/json:
  351. * schema:
  352. * $ref: '#/components/schemas/PageParams'
  353. * responses:
  354. * 200:
  355. * description: Successfully retrieved current page info.
  356. * content:
  357. * application/json:
  358. * schema:
  359. * $ref: '#/components/schemas/PageInfo'
  360. * 500:
  361. * description: Internal server error.
  362. */
  363. router.get('/info', certifySharedPage, loginRequired, validator.info, apiV3FormValidator, async(req, res) => {
  364. const { user, isSharedPage } = req;
  365. const { pageId } = req.query;
  366. try {
  367. const pageWithMeta = await pageService.findPageAndMetaDataByViewer(pageId, null, user, true, isSharedPage);
  368. if (pageWithMeta == null) {
  369. return res.apiv3Err(`Page '${pageId}' is not found or forbidden`);
  370. }
  371. return res.apiv3(pageWithMeta.meta);
  372. }
  373. catch (err) {
  374. logger.error('get-page-info', err);
  375. return res.apiv3Err(err, 500);
  376. }
  377. });
  378. /**
  379. * @swagger
  380. *
  381. * /page/is-grant-normalized:
  382. * get:
  383. * tags: [Page]
  384. * summary: /page/info
  385. * description: Retrieve current page's isGrantNormalized value
  386. * operationId: getIsGrantNormalized
  387. * parameters:
  388. * - name: pageId
  389. * in: query
  390. * description: page id
  391. * schema:
  392. * $ref: '#/components/schemas/Page/properties/_id'
  393. * responses:
  394. * 200:
  395. * description: Successfully retrieved current isGrantNormalized.
  396. * content:
  397. * application/json:
  398. * schema:
  399. * type: object
  400. * properties:
  401. * isGrantNormalized:
  402. * type: boolean
  403. * 400:
  404. * description: Bad request. Page is unreachable or empty.
  405. * 500:
  406. * description: Internal server error.
  407. */
  408. router.get('/is-grant-normalized', loginRequiredStrictly, validator.isGrantNormalized, apiV3FormValidator, async(req, res) => {
  409. const { pageId } = req.query;
  410. const Page = crowi.model('Page');
  411. const page = await Page.findByIdAndViewer(pageId, req.user, null, false);
  412. if (page == null) {
  413. return res.apiv3Err(new ErrorV3('Page is unreachable or empty.', 'page_unreachable_or_empty'), 400);
  414. }
  415. const {
  416. path, grant, grantedUsers, grantedGroup,
  417. } = page;
  418. let isGrantNormalized;
  419. try {
  420. isGrantNormalized = await crowi.pageGrantService.isGrantNormalized(req.user, path, grant, grantedUsers, grantedGroup, false, false);
  421. }
  422. catch (err) {
  423. logger.error('Error occurred while processing isGrantNormalized.', err);
  424. return res.apiv3Err(err, 500);
  425. }
  426. const currentPageUserGroup = await UserGroup.findOne({ _id: grantedGroup });
  427. const currentPageGrant = {
  428. grant,
  429. grantedGroup: currentPageUserGroup != null
  430. ? {
  431. id: currentPageUserGroup._id,
  432. name: currentPageUserGroup.name,
  433. }
  434. : null,
  435. };
  436. // page doesn't have parent page
  437. if (page.parent == null) {
  438. const grantData = {
  439. isForbidden: false,
  440. currentPageGrant,
  441. parentPageGrant: null,
  442. };
  443. return res.apiv3({ isGrantNormalized, grantData });
  444. }
  445. const parentPage = await Page.findByIdAndViewer(page.parent, req.user, null, false);
  446. // user isn't allowed to see parent's grant
  447. if (parentPage == null) {
  448. const grantData = {
  449. isForbidden: true,
  450. currentPageGrant,
  451. parentPageGrant: null,
  452. };
  453. return res.apiv3({ isGrantNormalized, grantData });
  454. }
  455. const parentPageUserGroup = await UserGroup.findOne({ _id: parentPage.grantedGroup });
  456. const parentPageGrant = {
  457. grant: parentPage.grant,
  458. grantedGroup: parentPageUserGroup != null
  459. ? {
  460. id: parentPageUserGroup._id,
  461. name: parentPageUserGroup.name,
  462. }
  463. : null,
  464. };
  465. const grantData = {
  466. isForbidden: false,
  467. currentPageGrant,
  468. parentPageGrant,
  469. };
  470. return res.apiv3({ isGrantNormalized, grantData });
  471. });
  472. router.get('/applicable-grant', loginRequiredStrictly, validator.applicableGrant, apiV3FormValidator, async(req, res) => {
  473. const { pageId } = req.query;
  474. const Page = crowi.model('Page');
  475. const page = await Page.findByIdAndViewer(pageId, req.user, null);
  476. if (page == null) {
  477. return res.apiv3Err(new ErrorV3('Page is unreachable or empty.', 'page_unreachable_or_empty'), 400);
  478. }
  479. let data;
  480. try {
  481. data = await crowi.pageGrantService.calcApplicableGrantData(page, req.user);
  482. }
  483. catch (err) {
  484. logger.error('Error occurred while processing calcApplicableGrantData.', err);
  485. return res.apiv3Err(err, 500);
  486. }
  487. return res.apiv3(data);
  488. });
  489. router.put('/:pageId/grant', loginRequiredStrictly, validator.updateGrant, apiV3FormValidator, async(req, res) => {
  490. const { pageId } = req.params;
  491. const { grant, grantedGroup } = req.body;
  492. const Page = crowi.model('Page');
  493. const page = await Page.findByIdAndViewer(pageId, req.user, null, false);
  494. if (page == null) {
  495. return res.apiv3Err(new ErrorV3('Page is unreachable or empty.', 'page_unreachable_or_empty'), 400);
  496. }
  497. let data;
  498. try {
  499. const shouldUseV4Process = false;
  500. const grantData = { grant, grantedGroup };
  501. data = await Page.updateGrant(page, req.user, grantData, shouldUseV4Process);
  502. }
  503. catch (err) {
  504. logger.error('Error occurred while processing calcApplicableGrantData.', err);
  505. return res.apiv3Err(err, 500);
  506. }
  507. return res.apiv3(data);
  508. });
  509. /**
  510. * @swagger
  511. *
  512. * /pages/export:
  513. * get:
  514. * tags: [Export]
  515. * description: return page's markdown
  516. * responses:
  517. * 200:
  518. * description: Return page's markdown
  519. */
  520. router.get('/export/:pageId', loginRequiredStrictly, validator.export, async(req, res) => {
  521. const { pageId } = req.params;
  522. const { format, revisionId = null } = req.query;
  523. let revision;
  524. try {
  525. const Page = crowi.model('Page');
  526. const page = await Page.findByIdAndViewer(pageId, req.user);
  527. if (page == null) {
  528. const isPageExist = await Page.count({ _id: pageId }) > 0;
  529. if (isPageExist) {
  530. // This page exists but req.user has not read permission
  531. return res.apiv3Err(new ErrorV3(`Haven't the right to see the page ${pageId}.`), 403);
  532. }
  533. return res.apiv3Err(new ErrorV3(`Page ${pageId} is not exist.`), 404);
  534. }
  535. const revisionIdForFind = revisionId || page.revision;
  536. const Revision = crowi.model('Revision');
  537. revision = await Revision.findById(revisionIdForFind);
  538. }
  539. catch (err) {
  540. logger.error('Failed to get page data', err);
  541. return res.apiv3Err(err, 500);
  542. }
  543. const fileName = revision.id;
  544. let stream;
  545. try {
  546. stream = exportService.getReadStreamFromRevision(revision, format);
  547. }
  548. catch (err) {
  549. logger.error('Failed to create readStream', err);
  550. return res.apiv3Err(err, 500);
  551. }
  552. res.set({
  553. 'Content-Disposition': `attachment;filename*=UTF-8''${fileName}.${format}`,
  554. });
  555. const parameters = {
  556. ip: req.ip,
  557. endpoint: req.originalUrl,
  558. action: SupportedAction.ACTION_PAGE_EXPORT,
  559. user: req.user?._id,
  560. snapshot: {
  561. username: req.user?.username,
  562. },
  563. };
  564. await crowi.activityService.createActivity(parameters);
  565. return stream.pipe(res);
  566. });
  567. /**
  568. * @swagger
  569. *
  570. * /page/exist-paths:
  571. * get:
  572. * tags: [Page]
  573. * summary: /page/exist-paths
  574. * description: Get already exist paths
  575. * operationId: getAlreadyExistPaths
  576. * parameters:
  577. * - name: fromPath
  578. * in: query
  579. * description: old parent path
  580. * schema:
  581. * type: string
  582. * - name: toPath
  583. * in: query
  584. * description: new parent path
  585. * schema:
  586. * type: string
  587. * responses:
  588. * 200:
  589. * description: Succeeded to retrieve pages.
  590. * content:
  591. * application/json:
  592. * schema:
  593. * properties:
  594. * existPaths:
  595. * type: object
  596. * description: Paths are already exist in DB
  597. * 500:
  598. * description: Internal server error.
  599. */
  600. router.get('/exist-paths', loginRequired, validator.exist, apiV3FormValidator, async(req, res) => {
  601. const { fromPath, toPath } = req.query;
  602. try {
  603. const fromPage = await Page.findByPath(fromPath, true);
  604. if (fromPage == null) {
  605. return res.apiv3Err(new ErrorV3('fromPage is Null'), 400);
  606. }
  607. const fromPageDescendants = await Page.findManageableListWithDescendants(fromPage, req.user, {}, true);
  608. const toPathDescendantsArray = fromPageDescendants.map((subordinatedPage) => {
  609. return convertToNewAffiliationPath(fromPath, toPath, subordinatedPage.path);
  610. });
  611. const existPages = await Page.findListByPathsArray(toPathDescendantsArray);
  612. const existPaths = existPages.map(page => page.path);
  613. return res.apiv3({ existPaths });
  614. }
  615. catch (err) {
  616. logger.error('Failed to get exist path', err);
  617. return res.apiv3Err(err, 500);
  618. }
  619. });
  620. // TODO GW-2746 bulk export pages
  621. // /**
  622. // * @swagger
  623. // *
  624. // * /page/archive:
  625. // * post:
  626. // * tags: [Page]
  627. // * summary: /page/archive
  628. // * description: create page archive
  629. // * requestBody:
  630. // * content:
  631. // * application/json:
  632. // * schema:
  633. // * properties:
  634. // * rootPagePath:
  635. // * type: string
  636. // * description: path of the root page
  637. // * isCommentDownload:
  638. // * type: boolean
  639. // * description: whether archive data contains comments
  640. // * isAttachmentFileDownload:
  641. // * type: boolean
  642. // * description: whether archive data contains attachments
  643. // * isSubordinatedPageDownload:
  644. // * type: boolean
  645. // * description: whether archive data children pages
  646. // * fileType:
  647. // * type: string
  648. // * description: file type of archive data(.md, .pdf)
  649. // * hierarchyType:
  650. // * type: string
  651. // * description: method of select children pages archive data contains('allSubordinatedPage', 'decideHierarchy')
  652. // * hierarchyValue:
  653. // * type: number
  654. // * description: depth of hierarchy(use when hierarchyType is 'decideHierarchy')
  655. // * responses:
  656. // * 200:
  657. // * description: create page archive
  658. // * content:
  659. // * application/json:
  660. // * schema:
  661. // * $ref: '#/components/schemas/Page'
  662. // */
  663. // router.post('/archive', accessTokenParser, loginRequired, validator.archive, apiV3FormValidator, async(req, res) => {
  664. // const PageArchive = crowi.model('PageArchive');
  665. // const {
  666. // rootPagePath,
  667. // isCommentDownload,
  668. // isAttachmentFileDownload,
  669. // fileType,
  670. // } = req.body;
  671. // const owner = req.user._id;
  672. // const numOfPages = 1; // TODO 最終的にzipファイルに取り込むページ数を入れる
  673. // const createdPageArchive = PageArchive.create({
  674. // owner,
  675. // fileType,
  676. // rootPagePath,
  677. // numOfPages,
  678. // hasComment: isCommentDownload,
  679. // hasAttachment: isAttachmentFileDownload,
  680. // });
  681. // console.log(createdPageArchive);
  682. // return res.apiv3({ });
  683. // });
  684. // router.get('/count-children-pages', accessTokenParser, loginRequired, async(req, res) => {
  685. // // TO DO implement correct number at another task
  686. // const { pageId } = req.query;
  687. // console.log(pageId);
  688. // const dummy = 6;
  689. // return res.apiv3({ dummy });
  690. // });
  691. /**
  692. * @swagger
  693. *
  694. * /page/subscribe:
  695. * put:
  696. * tags: [Page]
  697. * summary: /page/subscribe
  698. * description: Update subscription status
  699. * operationId: updateSubscriptionStatus
  700. * requestBody:
  701. * content:
  702. * application/json:
  703. * schema:
  704. * properties:
  705. * pageId:
  706. * $ref: '#/components/schemas/Page/properties/_id'
  707. * responses:
  708. * 200:
  709. * description: Succeeded to update subscription status.
  710. * content:
  711. * application/json:
  712. * schema:
  713. * $ref: '#/components/schemas/Page'
  714. * 500:
  715. * description: Internal server error.
  716. */
  717. router.put('/subscribe', accessTokenParser, loginRequiredStrictly, addActivity, validator.subscribe, apiV3FormValidator, async(req, res) => {
  718. const { pageId, status } = req.body;
  719. const userId = req.user._id;
  720. try {
  721. const subscription = await Subscription.subscribeByPageId(userId, pageId, status);
  722. const parameters = {};
  723. if (SubscriptionStatusType.SUBSCRIBE === status) {
  724. Object.assign(parameters, { action: SupportedAction.ACTION_PAGE_SUBSCRIBE });
  725. }
  726. else if (SubscriptionStatusType.UNSUBSCRIBE === status) {
  727. Object.assign(parameters, { action: SupportedAction.ACTION_PAGE_UNSUBSCRIBE });
  728. }
  729. if ('action' in parameters) {
  730. activityEvent.emit('update', res.locals.activity._id, parameters);
  731. }
  732. return res.apiv3({ subscription });
  733. }
  734. catch (err) {
  735. logger.error('Failed to update subscribe status', err);
  736. return res.apiv3Err(err, 500);
  737. }
  738. });
  739. return router;
  740. };