2
0

index.ts 2.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475
  1. import { SCOPE } from '@growi/core/dist/interfaces';
  2. import type { AccessTokenParser } from '@growi/core/dist/interfaces/server';
  3. import type { NextFunction, Request, Response } from 'express';
  4. import { query, validationResult } from 'express-validator';
  5. import { FilterXSS } from 'xss';
  6. import type { LsxApiOptions } from '../interfaces/api';
  7. import { listPages } from './routes/list-pages';
  8. const loginRequiredFallback = (_req: Request, res: Response) => {
  9. return res.status(403).send('login required');
  10. };
  11. const filterXSS = new FilterXSS();
  12. const lsxValidator = [
  13. query('pagePath').notEmpty().isString(),
  14. query('offset').optional().isInt().toInt(),
  15. query('limit').optional().isInt().toInt(),
  16. query('options')
  17. .optional()
  18. .customSanitizer((options) => {
  19. try {
  20. const jsonData: LsxApiOptions =
  21. typeof options === 'string' ? JSON.parse(options) : options;
  22. for (const key in jsonData) {
  23. jsonData[key] = filterXSS.process(jsonData[key]);
  24. }
  25. return jsonData;
  26. } catch {
  27. throw new Error('Invalid JSON format in options');
  28. }
  29. }),
  30. query('options.*').optional().isString(),
  31. ];
  32. const paramValidator = (req: Request, res: Response, next: NextFunction) => {
  33. const errObjArray = validationResult(req);
  34. if (errObjArray.isEmpty()) {
  35. return next();
  36. }
  37. const errs = errObjArray.array().map((err) => {
  38. return new Error(`Invalid lsx parameter: ${err.param}: ${err.msg}`);
  39. });
  40. res.status(400).json({ errors: errs.map((err) => err.message) });
  41. };
  42. const middleware = (crowi: any, app: any): void => {
  43. const loginRequired = crowi.loginRequiredFactory(
  44. crowi,
  45. true,
  46. loginRequiredFallback,
  47. );
  48. const accessTokenParser: AccessTokenParser = crowi.accessTokenParser;
  49. // Use a callback to get excludedPaths at request time, not at server startup.
  50. // This ensures config changes are reflected without server restart.
  51. const getExcludedPaths = () => crowi.pageService.getExcludedPathsBySystem();
  52. app.get(
  53. '/_api/lsx',
  54. accessTokenParser([SCOPE.READ.FEATURES.PAGE], { acceptLegacy: true }),
  55. loginRequired,
  56. lsxValidator,
  57. paramValidator,
  58. listPages({ getExcludedPaths }),
  59. );
  60. };
  61. export default middleware;