setup.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396
  1. const path = require('path')
  2. const uuid = require('uuid/v4')
  3. /* global WIKI */
  4. module.exports = () => {
  5. WIKI.config.site = {
  6. path: '',
  7. title: 'Wiki.js'
  8. }
  9. WIKI.system = require('./core/system')
  10. // ----------------------------------------
  11. // Load modules
  12. // ----------------------------------------
  13. const bodyParser = require('body-parser')
  14. const compression = require('compression')
  15. const express = require('express')
  16. const favicon = require('serve-favicon')
  17. const http = require('http')
  18. const Promise = require('bluebird')
  19. const fs = require('fs-extra')
  20. const _ = require('lodash')
  21. const cfgHelper = require('./helpers/config')
  22. const crypto = Promise.promisifyAll(require('crypto'))
  23. const pem2jwk = require('pem-jwk').pem2jwk
  24. const semver = require('semver')
  25. // ----------------------------------------
  26. // Define Express App
  27. // ----------------------------------------
  28. let app = express()
  29. app.use(compression())
  30. // ----------------------------------------
  31. // Public Assets
  32. // ----------------------------------------
  33. app.use(favicon(path.join(WIKI.ROOTPATH, 'assets', 'favicon.ico')))
  34. app.use(express.static(path.join(WIKI.ROOTPATH, 'assets')))
  35. // ----------------------------------------
  36. // View Engine Setup
  37. // ----------------------------------------
  38. app.set('views', path.join(WIKI.SERVERPATH, 'views'))
  39. app.set('view engine', 'pug')
  40. app.use(bodyParser.json())
  41. app.use(bodyParser.urlencoded({ extended: false }))
  42. app.locals.config = WIKI.config
  43. app.locals.data = WIKI.data
  44. app.locals._ = require('lodash')
  45. // ----------------------------------------
  46. // HMR (Dev Mode Only)
  47. // ----------------------------------------
  48. if (global.DEV) {
  49. app.use(global.WP_DEV.devMiddleware)
  50. app.use(global.WP_DEV.hotMiddleware)
  51. }
  52. // ----------------------------------------
  53. // Controllers
  54. // ----------------------------------------
  55. app.get('*', async (req, res) => {
  56. let packageObj = await fs.readJson(path.join(WIKI.ROOTPATH, 'package.json'))
  57. res.render('setup', {
  58. packageObj,
  59. telemetryClientID: WIKI.telemetry.cid
  60. })
  61. })
  62. /**
  63. * Finalize
  64. */
  65. app.post('/finalize', async (req, res) => {
  66. WIKI.telemetry.sendEvent('setup', 'finalize')
  67. try {
  68. // Basic checks
  69. if (!semver.satisfies(process.version, '>=10.14')) {
  70. throw new Error('Node.js 10.14.x or later required!')
  71. }
  72. // Upgrade from WIKI.js 1.x?
  73. if (req.body.upgrade) {
  74. await WIKI.system.upgradeFromMongo({
  75. mongoCnStr: cfgHelper.parseConfigValue(req.body.upgMongo)
  76. })
  77. }
  78. // Create directory structure
  79. WIKI.logger.info('Creating data directories...')
  80. const dataPath = path.join(process.cwd(), 'data')
  81. await fs.ensureDir(dataPath)
  82. await fs.emptyDir(path.join(dataPath, 'cache'))
  83. await fs.ensureDir(path.join(dataPath, 'uploads'))
  84. // Set config
  85. _.set(WIKI.config, 'company', '')
  86. _.set(WIKI.config, 'defaultEditor', 'markdown')
  87. _.set(WIKI.config, 'features', {
  88. featurePageRatings: true,
  89. featurePageComments: true,
  90. featurePersonalWikis: true
  91. })
  92. _.set(WIKI.config, 'graphEndpoint', 'https://graph.requarks.io')
  93. _.set(WIKI.config, 'host', 'http://')
  94. _.set(WIKI.config, 'lang', {
  95. code: 'en',
  96. autoUpdate: true,
  97. namespacing: false,
  98. namespaces: []
  99. })
  100. _.set(WIKI.config, 'logo', {
  101. hasLogo: false,
  102. logoIsSquare: false
  103. })
  104. _.set(WIKI.config, 'mail', {
  105. senderName: '',
  106. senderEmail: '',
  107. host: '',
  108. port: 465,
  109. secure: true,
  110. user: '',
  111. pass: '',
  112. useDKIM: false,
  113. dkimDomainName: '',
  114. dkimKeySelector: '',
  115. dkimPrivateKey: ''
  116. })
  117. _.set(WIKI.config, 'public', false)
  118. _.set(WIKI.config, 'seo', {
  119. description: '',
  120. robots: ['index', 'follow'],
  121. analyticsService: '',
  122. analyticsId: ''
  123. })
  124. _.set(WIKI.config, 'sessionSecret', (await crypto.randomBytesAsync(32)).toString('hex'))
  125. _.set(WIKI.config, 'telemetry', {
  126. isEnabled: req.body.telemetry === 'true',
  127. clientId: WIKI.telemetry.cid
  128. })
  129. _.set(WIKI.config, 'theming', {
  130. theme: 'default',
  131. darkMode: false
  132. })
  133. _.set(WIKI.config, 'title', 'Wiki.js')
  134. // Generate certificates
  135. WIKI.logger.info('Generating certificates...')
  136. const certs = crypto.generateKeyPairSync('rsa', {
  137. modulusLength: 2048,
  138. publicKeyEncoding: {
  139. type: 'pkcs1',
  140. format: 'pem'
  141. },
  142. privateKeyEncoding: {
  143. type: 'pkcs1',
  144. format: 'pem',
  145. cipher: 'aes-256-cbc',
  146. passphrase: WIKI.config.sessionSecret
  147. }
  148. })
  149. _.set(WIKI.config, 'certs', {
  150. jwk: pem2jwk(certs.publicKey),
  151. public: certs.publicKey,
  152. private: certs.privateKey
  153. })
  154. // Save config to DB
  155. WIKI.logger.info('Persisting config to DB...')
  156. await WIKI.configSvc.saveToDb([
  157. 'certs',
  158. 'company',
  159. 'defaultEditor',
  160. 'features',
  161. 'graphEndpoint',
  162. 'host',
  163. 'lang',
  164. 'logo',
  165. 'mail',
  166. 'public',
  167. 'seo',
  168. 'sessionSecret',
  169. 'telemetry',
  170. 'theming',
  171. 'title'
  172. ])
  173. // Create default locale
  174. WIKI.logger.info('Installing default locale...')
  175. await WIKI.models.locales.query().insert({
  176. code: 'en',
  177. strings: require('./locales/default.json'),
  178. isRTL: false,
  179. name: 'English',
  180. nativeName: 'English'
  181. })
  182. // Create default groups
  183. WIKI.logger.info('Creating default groups...')
  184. const adminGroup = await WIKI.models.groups.query().insert({
  185. name: 'Administrators',
  186. permissions: JSON.stringify(['manage:system']),
  187. pageRules: JSON.stringify([]),
  188. isSystem: true
  189. })
  190. const guestGroup = await WIKI.models.groups.query().insert({
  191. name: 'Guests',
  192. permissions: JSON.stringify(['read:pages']),
  193. pageRules: JSON.stringify([
  194. { id: 'guest', roles: ['READ', 'AS_READ', 'CM_READ'], match: 'START', deny: false, path: '', locales: [] }
  195. ]),
  196. isSystem: true
  197. })
  198. // Load authentication strategies + enable local
  199. await WIKI.models.authentication.refreshStrategiesFromDisk()
  200. await WIKI.models.authentication.query().patch({ isEnabled: true }).where('key', 'local')
  201. // Load editors + enable default
  202. await WIKI.models.editors.refreshEditorsFromDisk()
  203. await WIKI.models.editors.query().patch({ isEnabled: true }).where('key', 'markdown')
  204. // Load loggers
  205. await WIKI.models.loggers.refreshLoggersFromDisk()
  206. // Load renderers
  207. await WIKI.models.renderers.refreshRenderersFromDisk()
  208. // Load search engines + enable default
  209. await WIKI.models.searchEngines.refreshSearchEnginesFromDisk()
  210. await WIKI.models.searchEngines.query().patch({ isEnabled: true }).where('key', 'db')
  211. // Load storage targets
  212. await WIKI.models.storage.refreshTargetsFromDisk()
  213. // Create root administrator
  214. WIKI.logger.info('Creating root administrator...')
  215. await WIKI.models.users.query().delete().where({
  216. providerKey: 'local',
  217. email: req.body.adminEmail
  218. })
  219. const adminUser = await WIKI.models.users.query().insert({
  220. email: req.body.adminEmail,
  221. provider: 'local',
  222. password: req.body.adminPassword,
  223. name: 'Administrator',
  224. locale: 'en',
  225. defaultEditor: 'markdown',
  226. tfaIsActive: false,
  227. isActive: true,
  228. isVerified: true
  229. })
  230. await adminUser.$relatedQuery('groups').relate(adminGroup.id)
  231. // Create Guest account
  232. WIKI.logger.info('Creating guest account...')
  233. await WIKI.models.users.query().delete().where({
  234. providerKey: 'local',
  235. email: 'guest@example.com'
  236. })
  237. const guestUser = await WIKI.models.users.query().insert({
  238. provider: 'local',
  239. email: 'guest@example.com',
  240. name: 'Guest',
  241. password: '',
  242. locale: 'en',
  243. defaultEditor: 'markdown',
  244. tfaIsActive: false,
  245. isSystem: true,
  246. isActive: true,
  247. isVerified: true
  248. })
  249. await guestUser.$relatedQuery('groups').relate(guestGroup.id)
  250. // Create site nav
  251. WIKI.logger.info('Creating default site navigation')
  252. await WIKI.models.navigation.query().delete().where({ key: 'site' })
  253. await WIKI.models.navigation.query().insert({
  254. key: 'site',
  255. config: [
  256. {
  257. id: uuid(),
  258. icon: 'home',
  259. kind: 'link',
  260. label: 'Home',
  261. target: '/',
  262. targetType: 'home'
  263. }
  264. ]
  265. })
  266. WIKI.logger.info('Setup is complete!')
  267. res.json({
  268. ok: true,
  269. redirectPath: '/',
  270. redirectPort: WIKI.config.port
  271. }).end()
  272. WIKI.config.setup = false
  273. WIKI.logger.info('Stopping Setup...')
  274. WIKI.server.destroy(() => {
  275. WIKI.logger.info('Setup stopped. Starting Wiki.js...')
  276. _.delay(() => {
  277. WIKI.kernel.bootMaster()
  278. }, 1000)
  279. })
  280. } catch (err) {
  281. res.json({ ok: false, error: err.message })
  282. }
  283. })
  284. // ----------------------------------------
  285. // Error handling
  286. // ----------------------------------------
  287. app.use(function (req, res, next) {
  288. var err = new Error('Not Found')
  289. err.status = 404
  290. next(err)
  291. })
  292. app.use(function (err, req, res, next) {
  293. res.status(err.status || 500)
  294. res.send({
  295. message: err.message,
  296. error: WIKI.IS_DEBUG ? err : {}
  297. })
  298. WIKI.logger.error(err.message)
  299. WIKI.telemetry.sendError(err)
  300. })
  301. // ----------------------------------------
  302. // Start HTTP server
  303. // ----------------------------------------
  304. WIKI.logger.info(`HTTP Server on port: [ ${WIKI.config.port} ]`)
  305. app.set('port', WIKI.config.port)
  306. WIKI.server = http.createServer(app)
  307. WIKI.server.listen(WIKI.config.port, WIKI.config.bindIP)
  308. var openConnections = []
  309. WIKI.server.on('connection', (conn) => {
  310. let key = conn.remoteAddress + ':' + conn.remotePort
  311. openConnections[key] = conn
  312. conn.on('close', () => {
  313. delete openConnections[key]
  314. })
  315. })
  316. WIKI.server.destroy = (cb) => {
  317. WIKI.server.close(cb)
  318. for (let key in openConnections) {
  319. openConnections[key].destroy()
  320. }
  321. }
  322. WIKI.server.on('error', (error) => {
  323. if (error.syscall !== 'listen') {
  324. throw error
  325. }
  326. switch (error.code) {
  327. case 'EACCES':
  328. WIKI.logger.error('Listening on port ' + WIKI.config.port + ' requires elevated privileges!')
  329. return process.exit(1)
  330. case 'EADDRINUSE':
  331. WIKI.logger.error('Port ' + WIKI.config.port + ' is already in use!')
  332. return process.exit(1)
  333. default:
  334. throw error
  335. }
  336. })
  337. WIKI.server.on('listening', () => {
  338. WIKI.logger.info('HTTP Server: [ RUNNING ]')
  339. WIKI.logger.info('========================================')
  340. WIKI.logger.info(`Browse to http://localhost:${WIKI.config.port}/`)
  341. WIKI.logger.info('========================================')
  342. })
  343. }