hackmd.js 3.7 KB

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