You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

153 lines
3.9 KiB

  1. const qs = require('querystring')
  2. const _ = require('lodash')
  3. const crypto = require('crypto')
  4. const path = require('path')
  5. const localeSegmentRegex = /^[A-Z]{2}(-[A-Z]{2})?$/i
  6. const localeFolderRegex = /^([a-z]{2}(?:-[a-z]{2})?\/)?(.*)/i
  7. // eslint-disable-next-line no-control-regex
  8. const unsafeCharsRegex = /[\x00-\x1f\x80-\x9f\\"|<>:*?]/
  9. const contentToExt = {
  10. markdown: 'md',
  11. asciidoc: 'adoc',
  12. html: 'html'
  13. }
  14. const extToContent = _.invert(contentToExt)
  15. /* global WIKI */
  16. module.exports = {
  17. /**
  18. * Parse raw url path and make it safe
  19. */
  20. parsePath (rawPath, opts = {}) {
  21. let pathObj = {
  22. locale: WIKI.config.lang.code,
  23. path: 'home',
  24. private: false,
  25. privateNS: '',
  26. explicitLocale: false
  27. }
  28. // Clean Path
  29. rawPath = _.trim(qs.unescape(rawPath))
  30. if (_.startsWith(rawPath, '/')) { rawPath = rawPath.substring(1) }
  31. rawPath = rawPath.replace(unsafeCharsRegex, '')
  32. if (rawPath === '') { rawPath = 'home' }
  33. rawPath = rawPath.replace(/\\/g, '').replace(/\/\//g, '').replace(/\.\.+/ig, '')
  34. // Extract Info
  35. let pathParts = _.filter(_.split(rawPath, '/'), p => {
  36. p = _.trim(p)
  37. return !_.isEmpty(p) && p !== '..' && p !== '.'
  38. })
  39. if (pathParts[0].length === 1) {
  40. pathParts.shift()
  41. }
  42. if (localeSegmentRegex.test(pathParts[0])) {
  43. pathObj.locale = pathParts[0]
  44. pathObj.explicitLocale = true
  45. pathParts.shift()
  46. }
  47. // Strip extension
  48. if (opts.stripExt && pathParts.length > 0) {
  49. const lastPart = _.last(pathParts)
  50. if (lastPart.indexOf('.') > 0) {
  51. pathParts.pop()
  52. const lastPartMeta = path.parse(lastPart)
  53. pathParts.push(lastPartMeta.name)
  54. }
  55. }
  56. pathObj.path = _.join(pathParts, '/')
  57. return pathObj
  58. },
  59. /**
  60. * Generate unique hash from page
  61. */
  62. generateHash(opts) {
  63. return crypto.createHash('sha1').update(`${opts.locale}|${opts.path}|${opts.privateNS}`).digest('hex')
  64. },
  65. /**
  66. * Inject Page Metadata
  67. */
  68. injectPageMetadata(page) {
  69. let meta = [
  70. ['title', page.title],
  71. ['description', page.description],
  72. ['published', page.isPublished.toString()],
  73. ['date', page.updatedAt],
  74. ['tags', page.tags ? page.tags.map(t => t.tag).join(', ') : ''],
  75. ['editor', page.editorKey],
  76. ['dateCreated', page.createdAt]
  77. ]
  78. switch (page.contentType) {
  79. case 'markdown':
  80. return '---\n' + meta.map(mt => `${mt[0]}: ${mt[1]}`).join('\n') + '\n---\n\n' + page.content
  81. case 'html':
  82. return '<!--\n' + meta.map(mt => `${mt[0]}: ${mt[1]}`).join('\n') + '\n-->\n\n' + page.content
  83. case 'json':
  84. return {
  85. ...page.content,
  86. _meta: _.fromPairs(meta)
  87. }
  88. default:
  89. return page.content
  90. }
  91. },
  92. /**
  93. * Check if path is a reserved path
  94. */
  95. isReservedPath(rawPath) {
  96. const firstSection = _.head(rawPath.split('/'))
  97. if (firstSection.length <= 1) {
  98. return true
  99. } else if (localeSegmentRegex.test(firstSection)) {
  100. return true
  101. } else if (
  102. _.some(WIKI.data.reservedPaths, p => {
  103. return p === firstSection
  104. })) {
  105. return true
  106. } else {
  107. return false
  108. }
  109. },
  110. /**
  111. * Get file extension from content type
  112. */
  113. getFileExtension(contentType) {
  114. return _.get(contentToExt, contentType, 'txt')
  115. },
  116. /**
  117. * Get content type from file extension
  118. */
  119. getContentType (filePath) {
  120. const ext = _.last(filePath.split('.'))
  121. return _.get(extToContent, ext, false)
  122. },
  123. /**
  124. * Get Page Meta object from disk path
  125. */
  126. getPagePath (filePath) {
  127. let fpath = filePath
  128. if (process.platform === 'win32') {
  129. fpath = filePath.replace(/\\/g, '/')
  130. }
  131. let meta = {
  132. locale: WIKI.config.lang.code,
  133. path: _.initial(fpath.split('.')).join('')
  134. }
  135. const result = localeFolderRegex.exec(meta.path)
  136. if (result[1]) {
  137. meta = {
  138. locale: result[1].replace('/', ''),
  139. path: result[2]
  140. }
  141. }
  142. return meta
  143. }
  144. }