hackmd.js 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135
  1. const logger = require('@alias/logger')('growi:routes:hackmd');
  2. const path = require('path');
  3. const swig = require('swig-templates');
  4. const axios = require('axios');
  5. const ApiResponse = require('../util/apiResponse');
  6. module.exports = function(crowi, app) {
  7. const Page = crowi.models.Page;
  8. // load GROWI agent script for HackMD
  9. const manifest = require(path.join(crowi.publicDir, 'manifest.json'));
  10. const agentScriptPath = path.join(crowi.publicDir, manifest['js/agent-for-hackmd.js']);
  11. // generate swig template
  12. const agentScriptContentTpl = swig.compileFile(agentScriptPath);
  13. /**
  14. * GET /_hackmd/load-agent
  15. *
  16. * loadAgent action
  17. * This should be access from HackMD and send agent script
  18. *
  19. * @param {object} req
  20. * @param {object} res
  21. */
  22. const loadAgent = function(req, res) {
  23. const origin = `${req.protocol}://${req.get('host')}`;
  24. const styleFilePath = origin + manifest['styles/style-hackmd.css'];
  25. // generate definitions to replace
  26. const definitions = {
  27. origin,
  28. styleFilePath,
  29. };
  30. // inject
  31. const script = agentScriptContentTpl(definitions);
  32. res.set('Content-Type', 'application/javascript');
  33. res.send(script);
  34. };
  35. const validateForApi = async function(req, res, next) {
  36. // validate process.env.HACKMD_URI
  37. const hackmdUri = process.env.HACKMD_URI;
  38. if (hackmdUri == null) {
  39. return res.json(ApiResponse.error('HackMD for GROWI has not been setup'));
  40. }
  41. // validate pageId
  42. const pageId = req.body.pageId;
  43. if (pageId == null) {
  44. return res.json(ApiResponse.error('pageId required'));
  45. }
  46. // validate page
  47. const page = await Page.findOne({ _id: pageId });
  48. if (page == null) {
  49. return res.json(ApiResponse.error(`Page('${pageId}') does not exist`));
  50. }
  51. req.page = page;
  52. next();
  53. };
  54. /**
  55. * POST /_api/hackmd.integrate
  56. *
  57. * Create page on HackMD and start to integrate
  58. * @param {object} req
  59. * @param {object} res
  60. */
  61. const integrate = async function(req, res) {
  62. const hackmdUri = process.env.HACKMD_URI;
  63. let page = req.page;
  64. try {
  65. if (page.pageIdOnHackmd == null) {
  66. page = await createNewPageOnHackmdAndRegister(hackmdUri, page);
  67. }
  68. else {
  69. page = await Page.syncRevisionToHackmd(page);
  70. }
  71. const data = {
  72. pageIdOnHackmd: page.pageIdOnHackmd,
  73. revisionIdHackmdSynced: page.revisionHackmdSynced,
  74. hasDraftOnHackmd: page.hasDraftOnHackmd,
  75. };
  76. return res.json(ApiResponse.success(data));
  77. }
  78. catch (err) {
  79. return res.json(ApiResponse.error(err));
  80. }
  81. };
  82. async function createNewPageOnHackmdAndRegister(hackmdUri, page) {
  83. // access to HackMD and create page
  84. const response = await axios.get(`${hackmdUri}/new`);
  85. logger.debug('HackMD responds', response);
  86. // extract page id on HackMD
  87. const pagePathOnHackmd = response.request.path; // e.g. '/NC7bSRraT1CQO1TO7wjCPw'
  88. const pageIdOnHackmd = pagePathOnHackmd.substr(1); // strip the head '/'
  89. return Page.registerHackmdPage(page, pageIdOnHackmd);
  90. }
  91. /**
  92. * POST /_api/hackmd.saveOnHackmd
  93. *
  94. * receive when save operation triggered on HackMD
  95. * !! This will be invoked many time from many people !!
  96. *
  97. * @param {object} req
  98. * @param {object} res
  99. */
  100. const saveOnHackmd = async function(req, res) {
  101. const page = req.page;
  102. const bodyOnHackmd = req.body.bodyOnHackmd;
  103. try {
  104. await Page.updateIfHackmdHasDraft(page, bodyOnHackmd);
  105. return res.json(ApiResponse.success());
  106. }
  107. catch (err) {
  108. return res.json(ApiResponse.error(err));
  109. }
  110. };
  111. return {
  112. loadAgent,
  113. validateForApi,
  114. integrate,
  115. saveOnHackmd,
  116. };
  117. };