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.

426 lines
12 KiB

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