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.

432 lines
13 KiB

  1. const express = require('express')
  2. const router = express.Router()
  3. const pageHelper = require('../helpers/page')
  4. const _ = require('lodash')
  5. /* global WIKI */
  6. const tmplCreateRegex = /^[0-9]+(,[0-9]+)?$/
  7. /**
  8. * Robots.txt
  9. */
  10. router.get('/robots.txt', (req, res, next) => {
  11. res.type('text/plain')
  12. if (_.includes(WIKI.config.seo.robots, 'noindex')) {
  13. res.send('User-agent: *\nDisallow: /')
  14. } else {
  15. res.status(200).end()
  16. }
  17. })
  18. /**
  19. * Health Endpoint
  20. */
  21. router.get('/healthz', (req, res, next) => {
  22. if (WIKI.models.knex.client.pool.numFree() < 1 && WIKI.models.knex.client.pool.numUsed() < 1) {
  23. res.status(503).json({ ok: false }).end()
  24. } else {
  25. res.status(200).json({ ok: true }).end()
  26. }
  27. })
  28. /**
  29. * Administration
  30. */
  31. router.get(['/a', '/a/*'], (req, res, next) => {
  32. _.set(res.locals, 'pageMeta.title', 'Admin')
  33. res.render('admin')
  34. })
  35. /**
  36. * Download Page / Version
  37. */
  38. router.get(['/d', '/d/*'], async (req, res, next) => {
  39. const pageArgs = pageHelper.parsePath(req.path, { stripExt: true })
  40. const versionId = (req.query.v) ? _.toSafeInteger(req.query.v) : 0
  41. const page = await WIKI.models.pages.getPageFromDb({
  42. path: pageArgs.path,
  43. locale: pageArgs.locale,
  44. userId: req.user.id,
  45. isPrivate: false
  46. })
  47. pageArgs.tags = _.get(page, 'tags', [])
  48. if (versionId > 0) {
  49. if (!WIKI.auth.checkAccess(req.user, ['read:history'], pageArgs)) {
  50. _.set(res.locals, 'pageMeta.title', 'Unauthorized')
  51. return res.render('unauthorized', { action: 'downloadVersion' })
  52. }
  53. } else {
  54. if (!WIKI.auth.checkAccess(req.user, ['read:source'], pageArgs)) {
  55. _.set(res.locals, 'pageMeta.title', 'Unauthorized')
  56. return res.render('unauthorized', { action: 'download' })
  57. }
  58. }
  59. if (page) {
  60. const fileName = _.last(page.path.split('/')) + '.' + pageHelper.getFileExtension(page.contentType)
  61. res.attachment(fileName)
  62. if (versionId > 0) {
  63. const pageVersion = await WIKI.models.pageHistory.getVersion({ pageId: page.id, versionId })
  64. res.send(pageHelper.injectPageMetadata(pageVersion))
  65. } else {
  66. res.send(pageHelper.injectPageMetadata(page))
  67. }
  68. } else {
  69. res.status(404).end()
  70. }
  71. })
  72. /**
  73. * Create/Edit document
  74. */
  75. router.get(['/e', '/e/*'], async (req, res, next) => {
  76. const pageArgs = pageHelper.parsePath(req.path, { stripExt: true })
  77. if (WIKI.config.lang.namespacing && !pageArgs.explicitLocale) {
  78. return res.redirect(`/e/${pageArgs.locale}/${pageArgs.path}`)
  79. }
  80. // -> Set Editor Lang
  81. _.set(res, 'locals.siteConfig.lang', pageArgs.locale)
  82. _.set(res, 'locals.siteConfig.rtl', req.i18n.dir() === 'rtl')
  83. // -> Check for reserved path
  84. if (pageHelper.isReservedPath(pageArgs.path)) {
  85. return next(new Error('Cannot create this page because it starts with a system reserved path.'))
  86. }
  87. // -> Get page data from DB
  88. let page = await WIKI.models.pages.getPageFromDb({
  89. path: pageArgs.path,
  90. locale: pageArgs.locale,
  91. userId: req.user.id,
  92. isPrivate: false
  93. })
  94. pageArgs.tags = _.get(page, 'tags', [])
  95. const injectCode = {
  96. css: WIKI.config.theming.injectCSS,
  97. head: WIKI.config.theming.injectHead,
  98. body: WIKI.config.theming.injectBody
  99. }
  100. if (page) {
  101. // -> EDIT MODE
  102. if (!WIKI.auth.checkAccess(req.user, ['write:pages', 'manage:pages'], pageArgs)) {
  103. _.set(res.locals, 'pageMeta.title', 'Unauthorized')
  104. return res.render('unauthorized', { action: 'edit' })
  105. }
  106. // -> Get page tags
  107. await page.$relatedQuery('tags')
  108. page.tags = _.map(page.tags, 'tag')
  109. _.set(res.locals, 'pageMeta.title', `Edit ${page.title}`)
  110. _.set(res.locals, 'pageMeta.description', page.description)
  111. page.mode = 'update'
  112. page.isPublished = (page.isPublished === true || page.isPublished === 1) ? 'true' : 'false'
  113. page.content = Buffer.from(page.content).toString('base64')
  114. } else {
  115. // -> CREATE MODE
  116. if (!WIKI.auth.checkAccess(req.user, ['write:pages'], pageArgs)) {
  117. _.set(res.locals, 'pageMeta.title', 'Unauthorized')
  118. return res.render('unauthorized', { action: 'create' })
  119. }
  120. _.set(res.locals, 'pageMeta.title', `New Page`)
  121. page = {
  122. path: pageArgs.path,
  123. localeCode: pageArgs.locale,
  124. editorKey: null,
  125. mode: 'create',
  126. content: null,
  127. title: null,
  128. description: null,
  129. updatedAt: new Date().toISOString()
  130. }
  131. // -> From Template
  132. if (req.query.from && tmplCreateRegex.test(req.query.from)) {
  133. let tmplPageId = 0
  134. let tmplVersionId = 0
  135. if (req.query.from.indexOf(',')) {
  136. const q = req.query.from.split(',')
  137. tmplPageId = _.toSafeInteger(q[0])
  138. tmplVersionId = _.toSafeInteger(q[1])
  139. } else {
  140. tmplPageId = _.toSafeInteger(req.query.from)
  141. }
  142. if (tmplVersionId > 0) {
  143. // -> From Page Version
  144. const pageVersion = await WIKI.models.pageHistory.getVersion({ pageId: tmplPageId, versionId: tmplVersionId })
  145. if (!pageVersion) {
  146. _.set(res.locals, 'pageMeta.title', 'Page Not Found')
  147. return res.status(404).render('notfound', { action: 'template' })
  148. }
  149. if (!WIKI.auth.checkAccess(req.user, ['read:history'], { path: pageVersion.path, locale: pageVersion.locale })) {
  150. _.set(res.locals, 'pageMeta.title', 'Unauthorized')
  151. return res.render('unauthorized', { action: 'sourceVersion' })
  152. }
  153. page.content = Buffer.from(pageVersion.content).toString('base64')
  154. page.editorKey = pageVersion.editor
  155. page.title = pageVersion.title
  156. page.description = pageVersion.description
  157. } else {
  158. // -> From Page Live
  159. const pageOriginal = await WIKI.models.pages.query().findById(tmplPageId)
  160. if (!pageOriginal) {
  161. _.set(res.locals, 'pageMeta.title', 'Page Not Found')
  162. return res.status(404).render('notfound', { action: 'template' })
  163. }
  164. if (!WIKI.auth.checkAccess(req.user, ['read:source'], { path: pageOriginal.path, locale: pageOriginal.locale })) {
  165. _.set(res.locals, 'pageMeta.title', 'Unauthorized')
  166. return res.render('unauthorized', { action: 'source' })
  167. }
  168. page.content = Buffer.from(pageOriginal.content).toString('base64')
  169. page.editorKey = pageOriginal.editorKey
  170. page.title = pageOriginal.title
  171. page.description = pageOriginal.description
  172. }
  173. }
  174. }
  175. res.render('editor', { page, injectCode })
  176. })
  177. /**
  178. * History
  179. */
  180. router.get(['/h', '/h/*'], async (req, res, next) => {
  181. const pageArgs = pageHelper.parsePath(req.path, { stripExt: true })
  182. if (WIKI.config.lang.namespacing && !pageArgs.explicitLocale) {
  183. return res.redirect(`/h/${pageArgs.locale}/${pageArgs.path}`)
  184. }
  185. _.set(res, 'locals.siteConfig.lang', pageArgs.locale)
  186. _.set(res, 'locals.siteConfig.rtl', req.i18n.dir() === 'rtl')
  187. const page = await WIKI.models.pages.getPageFromDb({
  188. path: pageArgs.path,
  189. locale: pageArgs.locale,
  190. userId: req.user.id,
  191. isPrivate: false
  192. })
  193. if (!page) {
  194. _.set(res.locals, 'pageMeta.title', 'Page Not Found')
  195. return res.status(404).render('notfound', { action: 'history' })
  196. }
  197. pageArgs.tags = _.get(page, 'tags', [])
  198. if (!WIKI.auth.checkAccess(req.user, ['read:history'], pageArgs)) {
  199. _.set(res.locals, 'pageMeta.title', 'Unauthorized')
  200. return res.render('unauthorized', { action: 'history' })
  201. }
  202. if (page) {
  203. _.set(res.locals, 'pageMeta.title', page.title)
  204. _.set(res.locals, 'pageMeta.description', page.description)
  205. res.render('history', { page })
  206. } else {
  207. res.redirect(`/${pageArgs.path}`)
  208. }
  209. })
  210. /**
  211. * Page ID redirection
  212. */
  213. router.get(['/i', '/i/:id'], async (req, res, next) => {
  214. const pageId = _.toSafeInteger(req.params.id)
  215. if (pageId <= 0) {
  216. return res.redirect('/')
  217. }
  218. const page = await WIKI.models.pages.query().column(['path', 'localeCode', 'isPrivate', 'privateNS']).findById(pageId)
  219. if (!page) {
  220. _.set(res.locals, 'pageMeta.title', 'Page Not Found')
  221. return res.status(404).render('notfound', { action: 'view' })
  222. }
  223. if (!WIKI.auth.checkAccess(req.user, ['read:pages'], {
  224. locale: page.localeCode,
  225. path: page.path,
  226. private: page.isPrivate,
  227. privateNS: page.privateNS,
  228. explicitLocale: false,
  229. tags: page.tags
  230. })) {
  231. _.set(res.locals, 'pageMeta.title', 'Unauthorized')
  232. return res.render('unauthorized', { action: 'view' })
  233. }
  234. if (WIKI.config.lang.namespacing) {
  235. return res.redirect(`/${page.localeCode}/${page.path}`)
  236. } else {
  237. return res.redirect(`/${page.path}`)
  238. }
  239. })
  240. /**
  241. * Profile
  242. */
  243. router.get(['/p', '/p/*'], (req, res, next) => {
  244. _.set(res.locals, 'pageMeta.title', 'User Profile')
  245. res.render('profile')
  246. })
  247. /**
  248. * Source
  249. */
  250. router.get(['/s', '/s/*'], async (req, res, next) => {
  251. const pageArgs = pageHelper.parsePath(req.path, { stripExt: true })
  252. const versionId = (req.query.v) ? _.toSafeInteger(req.query.v) : 0
  253. const page = await WIKI.models.pages.getPageFromDb({
  254. path: pageArgs.path,
  255. locale: pageArgs.locale,
  256. userId: req.user.id,
  257. isPrivate: false
  258. })
  259. pageArgs.tags = _.get(page, 'tags', [])
  260. if (WIKI.config.lang.namespacing && !pageArgs.explicitLocale) {
  261. return res.redirect(`/s/${pageArgs.locale}/${pageArgs.path}`)
  262. }
  263. _.set(res, 'locals.siteConfig.lang', pageArgs.locale)
  264. _.set(res, 'locals.siteConfig.rtl', req.i18n.dir() === 'rtl')
  265. if (versionId > 0) {
  266. if (!WIKI.auth.checkAccess(req.user, ['read:history'], pageArgs)) {
  267. _.set(res.locals, 'pageMeta.title', 'Unauthorized')
  268. return res.render('unauthorized', { action: 'sourceVersion' })
  269. }
  270. } else {
  271. if (!WIKI.auth.checkAccess(req.user, ['read:source'], pageArgs)) {
  272. _.set(res.locals, 'pageMeta.title', 'Unauthorized')
  273. return res.render('unauthorized', { action: 'source' })
  274. }
  275. }
  276. if (page) {
  277. if (versionId > 0) {
  278. const pageVersion = await WIKI.models.pageHistory.getVersion({ pageId: page.id, versionId })
  279. _.set(res.locals, 'pageMeta.title', pageVersion.title)
  280. _.set(res.locals, 'pageMeta.description', pageVersion.description)
  281. res.render('source', {
  282. page: {
  283. ...page,
  284. ...pageVersion
  285. }
  286. })
  287. } else {
  288. _.set(res.locals, 'pageMeta.title', page.title)
  289. _.set(res.locals, 'pageMeta.description', page.description)
  290. res.render('source', { page })
  291. }
  292. } else {
  293. res.redirect(`/${pageArgs.path}`)
  294. }
  295. })
  296. /**
  297. * Tags
  298. */
  299. router.get(['/t', '/t/*'], (req, res, next) => {
  300. _.set(res.locals, 'pageMeta.title', 'Tags')
  301. res.render('tags')
  302. })
  303. /**
  304. * View document / asset
  305. */
  306. router.get('/*', async (req, res, next) => {
  307. const stripExt = _.some(WIKI.data.pageExtensions, ext => _.endsWith(req.path, `.${ext}`))
  308. const pageArgs = pageHelper.parsePath(req.path, { stripExt })
  309. const isPage = (stripExt || pageArgs.path.indexOf('.') === -1)
  310. if (isPage) {
  311. if (WIKI.config.lang.namespacing && !pageArgs.explicitLocale) {
  312. return res.redirect(`/${pageArgs.locale}/${pageArgs.path}`)
  313. }
  314. req.i18n.changeLanguage(pageArgs.locale)
  315. try {
  316. const page = await WIKI.models.pages.getPage({
  317. path: pageArgs.path,
  318. locale: pageArgs.locale,
  319. userId: req.user.id,
  320. isPrivate: false
  321. })
  322. pageArgs.tags = _.get(page, 'tags', [])
  323. if (!WIKI.auth.checkAccess(req.user, ['read:pages'], pageArgs)) {
  324. if (req.user.id === 2) {
  325. res.cookie('loginRedirect', req.path, {
  326. maxAge: 15 * 60 * 1000
  327. })
  328. }
  329. if (pageArgs.path === 'home' && req.user.id === 2) {
  330. return res.redirect('/login')
  331. }
  332. _.set(res.locals, 'pageMeta.title', 'Unauthorized')
  333. return res.status(403).render('unauthorized', {
  334. action: 'view'
  335. })
  336. }
  337. _.set(res, 'locals.siteConfig.lang', pageArgs.locale)
  338. _.set(res, 'locals.siteConfig.rtl', req.i18n.dir() === 'rtl')
  339. if (page) {
  340. _.set(res.locals, 'pageMeta.title', page.title)
  341. _.set(res.locals, 'pageMeta.description', page.description)
  342. const sidebar = await WIKI.models.navigation.getTree({ cache: true, locale: pageArgs.locale })
  343. const injectCode = {
  344. css: WIKI.config.theming.injectCSS,
  345. head: WIKI.config.theming.injectHead,
  346. body: WIKI.config.theming.injectBody
  347. }
  348. if (req.query.legacy || req.get('user-agent').indexOf('Trident') >= 0) {
  349. if (_.isString(page.toc)) {
  350. page.toc = JSON.parse(page.toc)
  351. }
  352. res.render('legacy/page', { page, sidebar, injectCode, isAuthenticated: req.user && req.user.id !== 2 })
  353. } else {
  354. res.render('page', { page, sidebar, injectCode })
  355. }
  356. } else if (pageArgs.path === 'home') {
  357. _.set(res.locals, 'pageMeta.title', 'Welcome')
  358. res.render('welcome', { locale: pageArgs.locale })
  359. } else {
  360. _.set(res.locals, 'pageMeta.title', 'Page Not Found')
  361. if (WIKI.auth.checkAccess(req.user, ['write:pages'], pageArgs)) {
  362. res.status(404).render('new', { path: pageArgs.path, locale: pageArgs.locale })
  363. } else {
  364. res.status(404).render('notfound', { action: 'view' })
  365. }
  366. }
  367. } catch (err) {
  368. next(err)
  369. }
  370. } else {
  371. if (!WIKI.auth.checkAccess(req.user, ['read:assets'], pageArgs)) {
  372. return res.sendStatus(403)
  373. }
  374. await WIKI.models.assets.getAsset(pageArgs.path, res)
  375. }
  376. })
  377. module.exports = router