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.

404 lines
12 KiB

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