common.js 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533
  1. const express = require('express')
  2. const router = express.Router()
  3. const pageHelper = require('../helpers/page')
  4. const _ = require('lodash')
  5. const CleanCSS = require('clean-css')
  6. const moment = require('moment')
  7. const path = require('path')
  8. const siteAssetsPath = path.resolve(WIKI.ROOTPATH, WIKI.config.dataPath, 'assets')
  9. /**
  10. * Robots.txt
  11. */
  12. router.get('/robots.txt', (req, res, next) => {
  13. res.type('text/plain')
  14. if (_.includes(WIKI.config.seo.robots, 'noindex')) {
  15. res.send('User-agent: *\nDisallow: /')
  16. } else {
  17. res.status(200).end()
  18. }
  19. })
  20. /**
  21. * Health Endpoint
  22. */
  23. router.get('/healthz', (req, res, next) => {
  24. if (WIKI.db.knex.client.pool.numFree() < 1 && WIKI.db.knex.client.pool.numUsed() < 1) {
  25. res.status(503).json({ ok: false }).end()
  26. } else {
  27. res.status(200).json({ ok: true }).end()
  28. }
  29. })
  30. /**
  31. * Site Asset
  32. */
  33. router.get('/_site/:siteId?/:resource', async (req, res, next) => {
  34. const site = req.params.siteId ? WIKI.sites[req.params.siteId] : await WIKI.db.sites.getSiteByHostname({ hostname: req.hostname })
  35. if (!site) {
  36. return res.status(404).send('Site Not Found')
  37. }
  38. switch (req.params.resource) {
  39. case 'logo': {
  40. if (site.config.assets.logo) {
  41. // TODO: Fetch from db if not in disk cache
  42. res.sendFile(path.join(siteAssetsPath, `logo-${site.id}.${site.config.assets.logoExt}`))
  43. } else {
  44. res.sendFile(path.join(WIKI.ROOTPATH, 'assets/_assets/logo-wikijs.svg'))
  45. }
  46. break
  47. }
  48. case 'favicon': {
  49. if (site.config.assets.favicon) {
  50. // TODO: Fetch from db if not in disk cache
  51. res.sendFile(path.join(siteAssetsPath, `favicon-${site.id}.${site.config.assets.faviconExt}`))
  52. } else {
  53. res.sendFile(path.join(WIKI.ROOTPATH, 'assets/_assets/logo-wikijs.svg'))
  54. }
  55. break
  56. }
  57. case 'loginbg': {
  58. if (site.config.assets.loginBg) {
  59. // TODO: Fetch from db if not in disk cache
  60. res.sendFile(path.join(siteAssetsPath, `loginbg-${site.id}.jpg`))
  61. } else {
  62. res.sendFile(path.join(WIKI.ROOTPATH, 'assets/_assets/bg/login.jpg'))
  63. }
  64. break
  65. }
  66. default: {
  67. return res.status(404).send('Invalid Site Resource')
  68. }
  69. }
  70. })
  71. /**
  72. * Asset Thumbnails / Download
  73. */
  74. router.get('/_thumb/:id.webp', async (req, res, next) => {
  75. const thumb = await WIKI.db.assets.getThumbnail({
  76. id: req.params.id
  77. })
  78. if (thumb) {
  79. // TODO: Check permissions
  80. switch (thumb.previewState) {
  81. case 'pending': {
  82. res.redirect('/_assets/illustrations/fileman-pending.svg')
  83. break
  84. }
  85. case 'ready': {
  86. res.set('Content-Type', 'image/webp')
  87. res.send(thumb.preview)
  88. break
  89. }
  90. case 'failed': {
  91. res.redirect('/_assets/illustrations/fileman-failed.svg')
  92. break
  93. }
  94. default: {
  95. return res.status(500).send('Invalid Thumbnail Preview State')
  96. }
  97. }
  98. } else {
  99. return res.sendStatus(404)
  100. }
  101. })
  102. // router.get(['/_admin', '/_admin/*'], (req, res, next) => {
  103. // if (!WIKI.auth.checkAccess(req.user, [
  104. // 'manage:system',
  105. // 'write:users',
  106. // 'manage:users',
  107. // 'write:groups',
  108. // 'manage:groups',
  109. // 'manage:navigation',
  110. // 'manage:theme',
  111. // 'manage:api'
  112. // ])) {
  113. // _.set(res.locals, 'pageMeta.title', 'Unauthorized')
  114. // return res.status(403).render('unauthorized', { action: 'view' })
  115. // }
  116. // _.set(res.locals, 'pageMeta.title', 'Admin')
  117. // res.render('admin')
  118. // })
  119. // /**
  120. // * Download Page / Version
  121. // */
  122. // router.get(['/d', '/d/*'], async (req, res, next) => {
  123. // const pageArgs = pageHelper.parsePath(req.path, { stripExt: true })
  124. // const versionId = (req.query.v) ? _.toSafeInteger(req.query.v) : 0
  125. // const page = await WIKI.db.pages.getPageFromDb({
  126. // path: pageArgs.path,
  127. // locale: pageArgs.locale,
  128. // userId: req.user.id,
  129. // isPrivate: false
  130. // })
  131. // pageArgs.tags = _.get(page, 'tags', [])
  132. // if (versionId > 0) {
  133. // if (!WIKI.auth.checkAccess(req.user, ['read:history'], pageArgs)) {
  134. // _.set(res.locals, 'pageMeta.title', 'Unauthorized')
  135. // return res.render('unauthorized', { action: 'downloadVersion' })
  136. // }
  137. // } else {
  138. // if (!WIKI.auth.checkAccess(req.user, ['read:source'], pageArgs)) {
  139. // _.set(res.locals, 'pageMeta.title', 'Unauthorized')
  140. // return res.render('unauthorized', { action: 'download' })
  141. // }
  142. // }
  143. // if (page) {
  144. // const fileName = _.last(page.path.split('/')) + '.' + pageHelper.getFileExtension(page.contentType)
  145. // res.attachment(fileName)
  146. // if (versionId > 0) {
  147. // const pageVersion = await WIKI.db.pageHistory.getVersion({ pageId: page.id, versionId })
  148. // res.send(pageHelper.injectPageMetadata(pageVersion))
  149. // } else {
  150. // res.send(pageHelper.injectPageMetadata(page))
  151. // }
  152. // } else {
  153. // res.status(404).end()
  154. // }
  155. // })
  156. // /**
  157. // * Create/Edit document
  158. // */
  159. // router.get(['/_edit', '/_edit/*'], async (req, res, next) => {
  160. // const pageArgs = pageHelper.parsePath(req.path, { stripExt: true })
  161. // const site = await WIKI.db.sites.getSiteByHostname({ hostname: req.hostname })
  162. // if (!site) {
  163. // throw new Error('INVALID_SITE')
  164. // }
  165. // if (pageArgs.path === '') {
  166. // return res.redirect(`/_edit/home`)
  167. // }
  168. // // if (WIKI.config.lang.namespacing && !pageArgs.explicitLocale) {
  169. // // return res.redirect(`/_edit/${pageArgs.locale}/${pageArgs.path}`)
  170. // // }
  171. // // req.i18n.changeLanguage(pageArgs.locale)
  172. // // -> Set Editor Lang
  173. // _.set(res, 'locals.siteConfig.lang', pageArgs.locale)
  174. // // _.set(res, 'locals.siteConfig.rtl', req.i18n.dir() === 'rtl')
  175. // // -> Check for reserved path
  176. // if (pageHelper.isReservedPath(pageArgs.path)) {
  177. // return next(new Error('Cannot create this page because it starts with a system reserved path.'))
  178. // }
  179. // // -> Get page data from DB
  180. // let page = await WIKI.db.pages.getPageFromDb({
  181. // siteId: site.id,
  182. // path: pageArgs.path,
  183. // locale: pageArgs.locale,
  184. // userId: req.user.id
  185. // })
  186. // pageArgs.tags = _.get(page, 'tags', [])
  187. // // -> Effective Permissions
  188. // const effectivePermissions = WIKI.auth.getEffectivePermissions(req, pageArgs)
  189. // const injectCode = {
  190. // css: '', // WIKI.config.theming.injectCSS,
  191. // head: '', // WIKI.config.theming.injectHead,
  192. // body: '' // WIKI.config.theming.injectBody
  193. // }
  194. // if (page) {
  195. // // -> EDIT MODE
  196. // if (!(effectivePermissions.pages.write || effectivePermissions.pages.manage)) {
  197. // _.set(res.locals, 'pageMeta.title', 'Unauthorized')
  198. // return res.render('unauthorized', { action: 'edit' })
  199. // }
  200. // // -> Get page tags
  201. // await page.$relatedQuery('tags')
  202. // page.tags = _.map(page.tags, 'tag')
  203. // // Handle missing extra field
  204. // page.extra = page.extra || { css: '', js: '' }
  205. // // -> Beautify Script CSS
  206. // if (!_.isEmpty(page.extra.css)) {
  207. // page.extra.css = new CleanCSS({ format: 'beautify' }).minify(page.extra.css).styles
  208. // }
  209. // _.set(res.locals, 'pageMeta.title', `Edit ${page.title}`)
  210. // _.set(res.locals, 'pageMeta.description', page.description)
  211. // page.mode = 'update'
  212. // page.isPublished = (page.isPublished === true || page.isPublished === 1) ? 'true' : 'false'
  213. // page.content = Buffer.from(page.content).toString('base64')
  214. // } else {
  215. // // -> CREATE MODE
  216. // if (!effectivePermissions.pages.write) {
  217. // _.set(res.locals, 'pageMeta.title', 'Unauthorized')
  218. // return res.render('unauthorized', { action: 'create' })
  219. // }
  220. // _.set(res.locals, 'pageMeta.title', `New Page`)
  221. // page = {
  222. // path: pageArgs.path,
  223. // localeCode: pageArgs.locale,
  224. // editorKey: null,
  225. // mode: 'create',
  226. // content: null,
  227. // title: null,
  228. // description: null,
  229. // updatedAt: new Date().toISOString(),
  230. // extra: {
  231. // css: '',
  232. // js: ''
  233. // }
  234. // }
  235. // }
  236. // res.render('editor', { page, injectCode, effectivePermissions })
  237. // })
  238. // /**
  239. // * History
  240. // */
  241. // router.get(['/h', '/h/*'], async (req, res, next) => {
  242. // const pageArgs = pageHelper.parsePath(req.path, { stripExt: true })
  243. // if (WIKI.config.lang.namespacing && !pageArgs.explicitLocale) {
  244. // return res.redirect(`/h/${pageArgs.locale}/${pageArgs.path}`)
  245. // }
  246. // req.i18n.changeLanguage(pageArgs.locale)
  247. // _.set(res, 'locals.siteConfig.lang', pageArgs.locale)
  248. // _.set(res, 'locals.siteConfig.rtl', req.i18n.dir() === 'rtl')
  249. // const page = await WIKI.db.pages.getPageFromDb({
  250. // path: pageArgs.path,
  251. // locale: pageArgs.locale,
  252. // userId: req.user.id,
  253. // isPrivate: false
  254. // })
  255. // if (!page) {
  256. // _.set(res.locals, 'pageMeta.title', 'Page Not Found')
  257. // return res.status(404).render('notfound', { action: 'history' })
  258. // }
  259. // pageArgs.tags = _.get(page, 'tags', [])
  260. // const effectivePermissions = WIKI.auth.getEffectivePermissions(req, pageArgs)
  261. // if (!effectivePermissions.history.read) {
  262. // _.set(res.locals, 'pageMeta.title', 'Unauthorized')
  263. // return res.render('unauthorized', { action: 'history' })
  264. // }
  265. // if (page) {
  266. // _.set(res.locals, 'pageMeta.title', page.title)
  267. // _.set(res.locals, 'pageMeta.description', page.description)
  268. // res.render('history', { page, effectivePermissions })
  269. // } else {
  270. // res.redirect(`/${pageArgs.path}`)
  271. // }
  272. // })
  273. // /**
  274. // * Page ID redirection
  275. // */
  276. // router.get(['/i', '/i/:id'], async (req, res, next) => {
  277. // const pageId = _.toSafeInteger(req.params.id)
  278. // if (pageId <= 0) {
  279. // return res.redirect('/')
  280. // }
  281. // const page = await WIKI.db.pages.query().column(['path', 'localeCode', 'isPrivate', 'privateNS']).findById(pageId)
  282. // if (!page) {
  283. // _.set(res.locals, 'pageMeta.title', 'Page Not Found')
  284. // return res.status(404).render('notfound', { action: 'view' })
  285. // }
  286. // if (!WIKI.auth.checkAccess(req.user, ['read:pages'], {
  287. // locale: page.localeCode,
  288. // path: page.path,
  289. // private: page.isPrivate,
  290. // privateNS: page.privateNS,
  291. // explicitLocale: false,
  292. // tags: page.tags
  293. // })) {
  294. // _.set(res.locals, 'pageMeta.title', 'Unauthorized')
  295. // return res.render('unauthorized', { action: 'view' })
  296. // }
  297. // if (WIKI.config.lang.namespacing) {
  298. // return res.redirect(`/${page.localeCode}/${page.path}`)
  299. // } else {
  300. // return res.redirect(`/${page.path}`)
  301. // }
  302. // })
  303. // /**
  304. // * Source
  305. // */
  306. // router.get(['/s', '/s/*'], async (req, res, next) => {
  307. // const pageArgs = pageHelper.parsePath(req.path, { stripExt: true })
  308. // const versionId = (req.query.v) ? _.toSafeInteger(req.query.v) : 0
  309. // const page = await WIKI.db.pages.getPageFromDb({
  310. // path: pageArgs.path,
  311. // locale: pageArgs.locale,
  312. // userId: req.user.id,
  313. // isPrivate: false
  314. // })
  315. // pageArgs.tags = _.get(page, 'tags', [])
  316. // if (WIKI.config.lang.namespacing && !pageArgs.explicitLocale) {
  317. // return res.redirect(`/s/${pageArgs.locale}/${pageArgs.path}`)
  318. // }
  319. // // -> Effective Permissions
  320. // const effectivePermissions = WIKI.auth.getEffectivePermissions(req, pageArgs)
  321. // _.set(res, 'locals.siteConfig.lang', pageArgs.locale)
  322. // _.set(res, 'locals.siteConfig.rtl', req.i18n.dir() === 'rtl')
  323. // if (versionId > 0) {
  324. // if (!effectivePermissions.history.read) {
  325. // _.set(res.locals, 'pageMeta.title', 'Unauthorized')
  326. // return res.render('unauthorized', { action: 'sourceVersion' })
  327. // }
  328. // } else {
  329. // if (!effectivePermissions.source.read) {
  330. // _.set(res.locals, 'pageMeta.title', 'Unauthorized')
  331. // return res.render('unauthorized', { action: 'source' })
  332. // }
  333. // }
  334. // if (page) {
  335. // if (versionId > 0) {
  336. // const pageVersion = await WIKI.db.pageHistory.getVersion({ pageId: page.id, versionId })
  337. // _.set(res.locals, 'pageMeta.title', pageVersion.title)
  338. // _.set(res.locals, 'pageMeta.description', pageVersion.description)
  339. // res.render('source', {
  340. // page: {
  341. // ...page,
  342. // ...pageVersion
  343. // },
  344. // effectivePermissions
  345. // })
  346. // } else {
  347. // _.set(res.locals, 'pageMeta.title', page.title)
  348. // _.set(res.locals, 'pageMeta.description', page.description)
  349. // res.render('source', { page, effectivePermissions })
  350. // }
  351. // } else {
  352. // res.redirect(`/${pageArgs.path}`)
  353. // }
  354. // })
  355. // /**
  356. // * Tags
  357. // */
  358. // router.get(['/t', '/t/*'], (req, res, next) => {
  359. // _.set(res.locals, 'pageMeta.title', 'Tags')
  360. // res.render('tags')
  361. // })
  362. /**
  363. * User Avatar
  364. */
  365. router.get('/_user/:uid/avatar', async (req, res, next) => {
  366. if (!WIKI.auth.checkAccess(req.user, ['read:pages'])) {
  367. return res.sendStatus(403)
  368. }
  369. const av = await WIKI.db.users.getUserAvatarData(req.params.uid)
  370. if (av) {
  371. res.set('Content-Type', 'image/jpeg')
  372. return res.send(av)
  373. }
  374. return res.sendStatus(404)
  375. })
  376. // /**
  377. // * View document / asset
  378. // */
  379. // router.get('/*', async (req, res, next) => {
  380. // const stripExt = _.some(WIKI.data.pageExtensions, ext => _.endsWith(req.path, `.${ext}`))
  381. // const pageArgs = pageHelper.parsePath(req.path, { stripExt })
  382. // const isPage = (stripExt || pageArgs.path.indexOf('.') === -1)
  383. // const site = await WIKI.db.sites.getSiteByHostname({ hostname: req.hostname })
  384. // if (!site) {
  385. // throw new Error('INVALID_SITE')
  386. // }
  387. // if (isPage) {
  388. // // if (WIKI.config.lang.namespacing && !pageArgs.explicitLocale) {
  389. // // return res.redirect(`/${pageArgs.locale}/${pageArgs.path}`)
  390. // // }
  391. // // req.i18n.changeLanguage(pageArgs.locale)
  392. // try {
  393. // // -> Get Page from cache
  394. // const page = await WIKI.db.pages.getPage({
  395. // siteId: site.id,
  396. // path: pageArgs.path,
  397. // locale: pageArgs.locale,
  398. // userId: req.user.id
  399. // })
  400. // pageArgs.tags = _.get(page, 'tags', [])
  401. // // -> Effective Permissions
  402. // const effectivePermissions = WIKI.auth.getEffectivePermissions(req, pageArgs)
  403. // // -> Check User Access
  404. // if (!effectivePermissions.pages.read) {
  405. // if (req.user.id === WIKI.auth.guest.id) {
  406. // res.cookie('loginRedirect', req.path, {
  407. // maxAge: 15 * 60 * 1000
  408. // })
  409. // }
  410. // if (pageArgs.path === 'home' && req.user.id === WIKI.auth.guest.id) {
  411. // return res.redirect('/login')
  412. // }
  413. // return res.redirect(`/_error/unauthorized?from=${req.path}`)
  414. // }
  415. // _.set(res, 'locals.siteConfig.lang', pageArgs.locale)
  416. // // _.set(res, 'locals.siteConfig.rtl', req.i18n.dir() === 'rtl')
  417. // if (page) {
  418. // _.set(res.locals, 'pageMeta.title', page.title)
  419. // _.set(res.locals, 'pageMeta.description', page.description)
  420. // // -> Check Publishing State
  421. // let pageIsPublished = page.isPublished
  422. // if (pageIsPublished && !_.isEmpty(page.publishStartDate)) {
  423. // pageIsPublished = moment(page.publishStartDate).isSameOrBefore()
  424. // }
  425. // if (pageIsPublished && !_.isEmpty(page.publishEndDate)) {
  426. // pageIsPublished = moment(page.publishEndDate).isSameOrAfter()
  427. // }
  428. // if (!pageIsPublished && !effectivePermissions.pages.write) {
  429. // _.set(res.locals, 'pageMeta.title', 'Unauthorized')
  430. // return res.status(403).render('unauthorized', {
  431. // action: 'view'
  432. // })
  433. // }
  434. // // -> Render view
  435. // res.sendFile(path.join(WIKI.ROOTPATH, 'assets/index.html'))
  436. // } else if (pageArgs.path === 'home') {
  437. // res.redirect('/_welcome')
  438. // } else {
  439. // _.set(res.locals, 'pageMeta.title', 'Page Not Found')
  440. // if (effectivePermissions.pages.write) {
  441. // res.status(404).render('new', { path: pageArgs.path, locale: pageArgs.locale })
  442. // } else {
  443. // res.status(404).render('notfound', { action: 'view' })
  444. // }
  445. // }
  446. // } catch (err) {
  447. // next(err)
  448. // }
  449. // } else {
  450. // if (!WIKI.auth.checkAccess(req.user, ['read:assets'], pageArgs)) {
  451. // return res.sendStatus(403)
  452. // }
  453. // await WIKI.db.assets.getAsset(pageArgs.path, res)
  454. // }
  455. // })
  456. router.get('/*', (req, res, next) => {
  457. res.sendFile(path.join(WIKI.ROOTPATH, 'assets/index.html'))
  458. })
  459. module.exports = router