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.

307 lines
9.0 KiB

8 years ago
  1. 'use strict'
  2. /* global db, lang, rights, winston */
  3. var express = require('express')
  4. var router = express.Router()
  5. const Promise = require('bluebird')
  6. const validator = require('validator')
  7. const _ = require('lodash')
  8. const axios = require('axios')
  9. const path = require('path')
  10. const fs = Promise.promisifyAll(require('fs-extra'))
  11. const os = require('os')
  12. const filesize = require('filesize.js')
  13. /**
  14. * Admin
  15. */
  16. router.get('/', (req, res) => {
  17. res.redirect('/admin/profile')
  18. })
  19. router.get('/profile', (req, res) => {
  20. if (res.locals.isGuest) {
  21. return res.render('error-forbidden')
  22. }
  23. res.render('pages/admin/profile', { adminTab: 'profile' })
  24. })
  25. router.post('/profile', (req, res) => {
  26. if (res.locals.isGuest) {
  27. return res.render('error-forbidden')
  28. }
  29. return db.User.findById(req.user.id).then((usr) => {
  30. usr.name = _.trim(req.body.name)
  31. if (usr.provider === 'local' && req.body.password !== '********') {
  32. let nPwd = _.trim(req.body.password)
  33. if (nPwd.length < 6) {
  34. return Promise.reject(new Error('New Password too short!'))
  35. } else {
  36. return db.User.hashPassword(nPwd).then((pwd) => {
  37. usr.password = pwd
  38. return usr.save()
  39. })
  40. }
  41. } else {
  42. return usr.save()
  43. }
  44. }).then(() => {
  45. return res.json({ msg: 'OK' })
  46. }).catch((err) => {
  47. res.status(400).json({ msg: err.message })
  48. })
  49. })
  50. router.get('/stats', (req, res) => {
  51. if (res.locals.isGuest) {
  52. return res.render('error-forbidden')
  53. }
  54. Promise.all([
  55. db.Entry.count(),
  56. db.UplFile.count(),
  57. db.User.count()
  58. ]).spread((totalEntries, totalUploads, totalUsers) => {
  59. return res.render('pages/admin/stats', {
  60. totalEntries, totalUploads, totalUsers, adminTab: 'stats'
  61. }) || true
  62. }).catch((err) => {
  63. throw err
  64. })
  65. })
  66. router.get('/users', (req, res) => {
  67. if (!res.locals.rights.manage) {
  68. return res.render('error-forbidden')
  69. }
  70. db.User.find({})
  71. .select('-password -rights')
  72. .sort('name email')
  73. .exec().then((usrs) => {
  74. res.render('pages/admin/users', { adminTab: 'users', usrs })
  75. })
  76. })
  77. router.get('/users/:id', (req, res) => {
  78. if (!res.locals.rights.manage) {
  79. return res.render('error-forbidden')
  80. }
  81. if (!validator.isMongoId(req.params.id)) {
  82. return res.render('error-forbidden')
  83. }
  84. db.User.findById(req.params.id)
  85. .select('-password -providerId')
  86. .exec().then((usr) => {
  87. let usrOpts = {
  88. canChangeEmail: (usr.email !== 'guest' && usr.provider === 'local' && usr.email !== req.app.locals.appconfig.admin),
  89. canChangeName: (usr.email !== 'guest'),
  90. canChangePassword: (usr.email !== 'guest' && usr.provider === 'local'),
  91. canChangeRole: (usr.email !== 'guest' && !(usr.provider === 'local' && usr.email === req.app.locals.appconfig.admin)),
  92. canBeDeleted: (usr.email !== 'guest' && !(usr.provider === 'local' && usr.email === req.app.locals.appconfig.admin))
  93. }
  94. res.render('pages/admin/users-edit', { adminTab: 'users', usr, usrOpts })
  95. }).catch(err => { // eslint-disable-line handle-callback-err
  96. return res.status(404).end() || true
  97. })
  98. })
  99. /**
  100. * Create / Authorize a new user
  101. */
  102. router.post('/users/create', (req, res) => {
  103. if (!res.locals.rights.manage) {
  104. return res.status(401).json({ msg: 'Unauthorized' })
  105. }
  106. let nUsr = {
  107. email: _.toLower(_.trim(req.body.email)),
  108. provider: _.trim(req.body.provider),
  109. password: req.body.password,
  110. name: _.trim(req.body.name)
  111. }
  112. if (!validator.isEmail(nUsr.email)) {
  113. return res.status(400).json({ msg: 'Invalid email address' })
  114. } else if (!validator.isIn(nUsr.provider, ['local', 'google', 'windowslive', 'facebook', 'github', 'slack'])) {
  115. return res.status(400).json({ msg: 'Invalid provider' })
  116. } else if (nUsr.provider === 'local' && !validator.isLength(nUsr.password, { min: 6 })) {
  117. return res.status(400).json({ msg: 'Password too short or missing' })
  118. } else if (nUsr.provider === 'local' && !validator.isLength(nUsr.name, { min: 2 })) {
  119. return res.status(400).json({ msg: 'Name is missing' })
  120. }
  121. db.User.findOne({ email: nUsr.email, provider: nUsr.provider }).then(exUsr => {
  122. if (exUsr) {
  123. return res.status(400).json({ msg: 'User already exists!' }) || true
  124. }
  125. let pwdGen = (nUsr.provider === 'local') ? db.User.hashPassword(nUsr.password) : Promise.resolve(true)
  126. return pwdGen.then(nPwd => {
  127. if (nUsr.provider !== 'local') {
  128. nUsr.password = ''
  129. nUsr.name = '-- pending --'
  130. } else {
  131. nUsr.password = nPwd
  132. }
  133. nUsr.rights = [{
  134. role: 'read',
  135. path: '/',
  136. exact: false,
  137. deny: false
  138. }]
  139. return db.User.create(nUsr).then(() => {
  140. return res.json({ ok: true })
  141. })
  142. }).catch(err => {
  143. winston.warn(err)
  144. return res.status(500).json({ msg: err })
  145. })
  146. }).catch(err => {
  147. winston.warn(err)
  148. return res.status(500).json({ msg: err })
  149. })
  150. })
  151. router.post('/users/:id', (req, res) => {
  152. if (!res.locals.rights.manage) {
  153. return res.status(401).json({ msg: lang.t('errors:unauthorized') })
  154. }
  155. if (!validator.isMongoId(req.params.id)) {
  156. return res.status(400).json({ msg: lang.t('errors:invaliduserid') })
  157. }
  158. return db.User.findById(req.params.id).then((usr) => {
  159. usr.name = _.trim(req.body.name)
  160. usr.rights = JSON.parse(req.body.rights)
  161. if (usr.provider === 'local' && req.body.password !== '********') {
  162. let nPwd = _.trim(req.body.password)
  163. if (nPwd.length < 6) {
  164. return Promise.reject(new Error(lang.t('errors:newpasswordtooshort')))
  165. } else {
  166. return db.User.hashPassword(nPwd).then((pwd) => {
  167. usr.password = pwd
  168. return usr.save()
  169. })
  170. }
  171. } else {
  172. return usr.save()
  173. }
  174. }).then((usr) => {
  175. // Update guest rights for future requests
  176. if (usr.provider === 'local' && usr.email === 'guest') {
  177. rights.guest = usr
  178. }
  179. return usr
  180. }).then(() => {
  181. return res.json({ msg: 'OK' })
  182. }).catch((err) => {
  183. res.status(400).json({ msg: err.message })
  184. })
  185. })
  186. /**
  187. * Delete / Deauthorize a user
  188. */
  189. router.delete('/users/:id', (req, res) => {
  190. if (!res.locals.rights.manage) {
  191. return res.status(401).json({ msg: lang.t('errors:unauthorized') })
  192. }
  193. if (!validator.isMongoId(req.params.id)) {
  194. return res.status(400).json({ msg: lang.t('errors:invaliduserid') })
  195. }
  196. return db.User.findByIdAndRemove(req.params.id).then(() => {
  197. return res.json({ ok: true })
  198. }).catch((err) => {
  199. res.status(500).json({ ok: false, msg: err.message })
  200. })
  201. })
  202. router.get('/settings', (req, res) => {
  203. if (!res.locals.rights.manage) {
  204. return res.render('error-forbidden')
  205. }
  206. res.render('pages/admin/settings', { adminTab: 'settings' })
  207. })
  208. router.get('/system', (req, res) => {
  209. if (!res.locals.rights.manage) {
  210. return res.render('error-forbidden')
  211. }
  212. let hostInfo = {
  213. cpus: os.cpus(),
  214. hostname: os.hostname(),
  215. nodeversion: process.version,
  216. os: `${os.type()} (${os.platform()}) ${os.release()} ${os.arch()}`,
  217. totalmem: filesize(os.totalmem()),
  218. cwd: process.cwd()
  219. }
  220. fs.readJsonAsync(path.join(ROOTPATH, 'package.json')).then(packageObj => {
  221. axios.get('https://api.github.com/repos/Requarks/wiki/releases/latest').then(resp => {
  222. let sysversion = {
  223. current: 'v' + packageObj.version,
  224. latest: resp.data.tag_name,
  225. latestPublishedAt: resp.data.published_at
  226. }
  227. res.render('pages/admin/system', { adminTab: 'system', hostInfo, sysversion })
  228. }).catch(err => {
  229. winston.warn(err)
  230. res.render('pages/admin/system', { adminTab: 'system', hostInfo, sysversion: { current: 'v' + packageObj.version } })
  231. })
  232. })
  233. })
  234. router.post('/system/install', (req, res) => {
  235. if (!res.locals.rights.manage) {
  236. return res.render('error-forbidden')
  237. }
  238. // let sysLib = require(path.join(ROOTPATH, 'libs/system.js'))
  239. // sysLib.install('v1.0-beta.7')
  240. res.status(400).send('Sorry, Upgrade/Re-Install via the web UI is not yet ready. You must use the npm upgrade method in the meantime.').end()
  241. })
  242. router.get('/theme', (req, res) => {
  243. if (!res.locals.rights.manage) {
  244. return res.render('error-forbidden')
  245. }
  246. res.render('pages/admin/theme', { adminTab: 'theme' })
  247. })
  248. router.post('/theme', (req, res) => {
  249. if (res.locals.isGuest) {
  250. return res.render('error-forbidden')
  251. }
  252. if (!validator.isIn(req.body.primary, appdata.colors)) {
  253. return res.status(406).json({ msg: 'Primary color is invalid.' })
  254. } else if (!validator.isIn(req.body.alt, appdata.colors)) {
  255. return res.status(406).json({ msg: 'Alternate color is invalid.' })
  256. } else if (!validator.isIn(req.body.footer, appdata.colors)) {
  257. return res.status(406).json({ msg: 'Footer color is invalid.' })
  258. }
  259. appconfig.theme.primary = req.body.primary
  260. appconfig.theme.alt = req.body.alt
  261. appconfig.theme.footer = req.body.footer
  262. appconfig.theme.code.dark = req.body.codedark === 'true'
  263. appconfig.theme.code.colorize = req.body.codecolorize === 'true'
  264. return res.json({ msg: 'OK' })
  265. })
  266. module.exports = router