wikijs-fork/server/setup.js

452 lines
14 KiB
JavaScript
Raw Normal View History

2017-10-08 21:21:36 +00:00
const path = require('path')
const { v4: uuid } = require('uuid')
const bodyParser = require('body-parser')
const compression = require('compression')
const express = require('express')
const favicon = require('serve-favicon')
const http = require('http')
const Promise = require('bluebird')
const fs = require('fs-extra')
const _ = require('lodash')
const crypto = Promise.promisifyAll(require('crypto'))
const pem2jwk = require('pem-jwk').pem2jwk
const semver = require('semver')
2017-02-26 23:06:20 +00:00
/* global WIKI */
2017-02-26 23:06:20 +00:00
2017-10-08 21:21:36 +00:00
module.exports = () => {
WIKI.config.site = {
path: '',
title: 'Wiki.js'
}
WIKI.system = require('./core/system')
2017-02-26 23:06:20 +00:00
// ----------------------------------------
// Define Express App
// ----------------------------------------
2017-10-08 21:21:36 +00:00
let app = express()
2017-02-26 23:06:20 +00:00
app.use(compression())
// ----------------------------------------
// Public Assets
// ----------------------------------------
app.use(favicon(path.join(WIKI.ROOTPATH, 'assets', 'favicon.ico')))
app.use('/_assets', express.static(path.join(WIKI.ROOTPATH, 'assets')))
2017-02-26 23:06:20 +00:00
// ----------------------------------------
// View Engine Setup
// ----------------------------------------
app.set('views', path.join(WIKI.SERVERPATH, 'views'))
2017-02-26 23:06:20 +00:00
app.set('view engine', 'pug')
app.use(bodyParser.json())
app.use(bodyParser.urlencoded({ extended: false }))
app.locals.config = WIKI.config
app.locals.data = WIKI.data
2017-02-26 23:06:20 +00:00
app.locals._ = require('lodash')
2020-01-26 04:12:57 +00:00
app.locals.devMode = WIKI.devMode
2017-02-26 23:06:20 +00:00
2018-01-28 02:40:51 +00:00
// ----------------------------------------
// HMR (Dev Mode Only)
// ----------------------------------------
if (global.DEV) {
app.use(global.WP_DEV.devMiddleware)
app.use(global.WP_DEV.hotMiddleware)
2018-01-28 02:40:51 +00:00
}
2017-02-26 23:06:20 +00:00
// ----------------------------------------
// Controllers
// ----------------------------------------
app.get('*', async (req, res) => {
let packageObj = await fs.readJson(path.join(WIKI.ROOTPATH, 'package.json'))
2019-02-02 06:17:09 +00:00
res.render('setup', { packageObj })
2017-02-26 23:06:20 +00:00
})
2017-03-25 21:03:09 +00:00
/**
* Finalize
2017-03-25 21:03:09 +00:00
*/
app.post('/finalize', async (req, res) => {
try {
2018-05-28 18:46:55 +00:00
// Set config
2019-01-07 03:03:34 +00:00
_.set(WIKI.config, 'auth', {
audience: 'urn:wiki.js',
tokenExpiration: '30m',
tokenRenewal: '14d'
})
2018-12-24 06:03:10 +00:00
_.set(WIKI.config, 'company', '')
_.set(WIKI.config, 'features', {
featurePageRatings: true,
featurePageComments: true,
featurePersonalWikis: true
})
2018-05-28 18:46:55 +00:00
_.set(WIKI.config, 'graphEndpoint', 'https://graph.requarks.io')
2019-10-06 18:55:22 +00:00
_.set(WIKI.config, 'host', req.body.siteUrl)
2018-12-24 06:03:10 +00:00
_.set(WIKI.config, 'lang', {
code: 'en',
autoUpdate: true,
namespacing: false,
namespaces: []
})
_.set(WIKI.config, 'logo', {
hasLogo: false,
logoIsSquare: false
})
_.set(WIKI.config, 'mail', {
senderName: '',
senderEmail: '',
host: '',
port: 465,
name: '',
2018-12-24 06:03:10 +00:00
secure: true,
verifySSL: true,
2018-12-24 06:03:10 +00:00
user: '',
pass: '',
useDKIM: false,
dkimDomainName: '',
dkimKeySelector: '',
dkimPrivateKey: ''
})
_.set(WIKI.config, 'seo', {
description: '',
robots: ['index', 'follow'],
analyticsService: '',
analyticsId: ''
2018-12-24 06:03:10 +00:00
})
2018-05-28 18:46:55 +00:00
_.set(WIKI.config, 'sessionSecret', (await crypto.randomBytesAsync(32)).toString('hex'))
2018-12-24 06:03:10 +00:00
_.set(WIKI.config, 'telemetry', {
2019-01-07 03:03:34 +00:00
isEnabled: req.body.telemetry === true,
2019-02-02 06:17:09 +00:00
clientId: uuid()
2018-12-24 06:03:10 +00:00
})
_.set(WIKI.config, 'theming', {
theme: 'default',
2019-08-05 03:53:21 +00:00
darkMode: false,
iconset: 'mdi',
injectCSS: '',
injectHead: '',
injectBody: ''
2018-12-24 06:03:10 +00:00
})
_.set(WIKI.config, 'title', 'Wiki.js')
2019-02-02 06:17:09 +00:00
// Init Telemetry
WIKI.kernel.initTelemetry()
2020-04-17 00:38:28 +00:00
// WIKI.telemetry.sendEvent('setup', 'install-start')
2019-02-02 06:17:09 +00:00
// Basic checks
if (!semver.satisfies(process.version, '>=10.12')) {
throw new Error('Node.js 10.12.x or later required!')
2019-02-02 06:17:09 +00:00
}
// Create directory structure
WIKI.logger.info('Creating data directories...')
await fs.ensureDir(path.resolve(WIKI.ROOTPATH, WIKI.config.dataPath))
await fs.emptyDir(path.resolve(WIKI.ROOTPATH, WIKI.config.dataPath, 'cache'))
await fs.ensureDir(path.resolve(WIKI.ROOTPATH, WIKI.config.dataPath, 'uploads'))
2019-02-02 06:17:09 +00:00
2018-12-03 02:42:43 +00:00
// Generate certificates
WIKI.logger.info('Generating certificates...')
const certs = crypto.generateKeyPairSync('rsa', {
modulusLength: 2048,
publicKeyEncoding: {
type: 'pkcs1',
format: 'pem'
},
privateKeyEncoding: {
type: 'pkcs1',
format: 'pem',
cipher: 'aes-256-cbc',
passphrase: WIKI.config.sessionSecret
}
})
2018-12-24 06:03:10 +00:00
_.set(WIKI.config, 'certs', {
jwk: pem2jwk(certs.publicKey),
public: certs.publicKey,
private: certs.privateKey
})
2018-12-03 02:42:43 +00:00
2017-12-17 04:41:16 +00:00
// Save config to DB
WIKI.logger.info('Persisting config to DB...')
await WIKI.configSvc.saveToDb([
2019-01-07 03:03:34 +00:00
'auth',
2018-12-24 06:03:10 +00:00
'certs',
'company',
'features',
'graphEndpoint',
2018-12-24 06:03:10 +00:00
'host',
'lang',
2018-12-24 06:03:10 +00:00
'logo',
'mail',
'seo',
'sessionSecret',
'telemetry',
2018-06-11 03:23:09 +00:00
'theming',
'uploads',
2018-12-24 06:03:10 +00:00
'title'
2020-04-20 00:26:26 +00:00
], false)
2017-12-17 04:41:16 +00:00
2019-10-26 03:39:58 +00:00
// Truncate tables (reset from previous failed install)
await WIKI.models.locales.query().where('code', '!=', 'x').del()
await WIKI.models.navigation.query().truncate()
switch (WIKI.config.db.type) {
case 'postgres':
await WIKI.models.knex.raw('TRUNCATE groups, users CASCADE')
break
case 'mysql':
case 'mariadb':
await WIKI.models.groups.query().where('id', '>', 0).del()
await WIKI.models.users.query().where('id', '>', 0).del()
await WIKI.models.knex.raw('ALTER TABLE `groups` AUTO_INCREMENT = 1')
await WIKI.models.knex.raw('ALTER TABLE `users` AUTO_INCREMENT = 1')
break
case 'mssql':
await WIKI.models.groups.query().del()
await WIKI.models.users.query().del()
await WIKI.models.knex.raw(`
IF EXISTS (SELECT * FROM sys.identity_columns WHERE OBJECT_NAME(OBJECT_ID) = 'groups' AND last_value IS NOT NULL)
DBCC CHECKIDENT ([groups], RESEED, 0)
`)
await WIKI.models.knex.raw(`
IF EXISTS (SELECT * FROM sys.identity_columns WHERE OBJECT_NAME(OBJECT_ID) = 'users' AND last_value IS NOT NULL)
DBCC CHECKIDENT ([users], RESEED, 0)
`)
break
case 'sqlite':
await WIKI.models.groups.query().truncate()
await WIKI.models.users.query().truncate()
break
2019-10-26 03:39:58 +00:00
}
2018-05-20 22:50:51 +00:00
// Create default locale
WIKI.logger.info('Installing default locale...')
await WIKI.models.locales.query().insert({
2018-05-20 22:50:51 +00:00
code: 'en',
2019-01-20 18:10:06 +00:00
strings: {},
2018-05-20 22:50:51 +00:00
isRTL: false,
name: 'English',
nativeName: 'English'
})
// Create default groups
WIKI.logger.info('Creating default groups...')
const adminGroup = await WIKI.models.groups.query().insert({
name: 'Administrators',
permissions: JSON.stringify(['manage:system']),
2018-12-30 18:14:05 +00:00
pageRules: JSON.stringify([]),
isSystem: true
})
const guestGroup = await WIKI.models.groups.query().insert({
name: 'Guests',
2019-01-12 23:33:30 +00:00
permissions: JSON.stringify(['read:pages', 'read:assets', 'read:comments']),
2018-12-30 18:14:05 +00:00
pageRules: JSON.stringify([
2019-01-12 23:33:30 +00:00
{ id: 'guest', roles: ['read:pages', 'read:assets', 'read:comments'], match: 'START', deny: false, path: '', locales: [] }
2018-12-30 18:14:05 +00:00
]),
isSystem: true
})
if (adminGroup.id !== 1 || guestGroup.id !== 2) {
throw new Error('Incorrect groups auto-increment configuration! Should start at 0 and increment by 1. Contact your database administrator.')
}
// Load local authentication strategy
await WIKI.models.authentication.query().insert({
key: 'local',
config: {},
selfRegistration: false,
isEnabled: true,
domainWhitelist: {v: []},
autoEnrollGroups: {v: []},
order: 0,
strategyKey: 'local',
displayName: 'Local'
})
2018-05-28 18:46:55 +00:00
// Load editors + enable default
await WIKI.models.editors.refreshEditorsFromDisk()
await WIKI.models.editors.query().patch({ isEnabled: true }).where('key', 'markdown')
2018-05-28 18:46:55 +00:00
2018-09-01 19:15:44 +00:00
// Load loggers
await WIKI.models.loggers.refreshLoggersFromDisk()
// Load renderers
await WIKI.models.renderers.refreshRenderersFromDisk()
2018-09-01 19:15:44 +00:00
// Load search engines + enable default
await WIKI.models.searchEngines.refreshSearchEnginesFromDisk()
await WIKI.models.searchEngines.query().patch({ isEnabled: true }).where('key', 'db')
2020-04-17 00:38:28 +00:00
// WIKI.telemetry.sendEvent('setup', 'install-loadedmodules')
2019-02-02 06:17:09 +00:00
2018-06-26 02:04:47 +00:00
// Load storage targets
await WIKI.models.storage.refreshTargetsFromDisk()
2018-06-26 02:04:47 +00:00
2017-12-17 04:41:16 +00:00
// Create root administrator
WIKI.logger.info('Creating root administrator...')
const adminUser = await WIKI.models.users.query().insert({
email: req.body.adminEmail.toLowerCase(),
2017-12-17 04:41:16 +00:00
provider: 'local',
password: req.body.adminPassword,
2017-12-17 04:41:16 +00:00
name: 'Administrator',
2018-05-20 22:50:51 +00:00
locale: 'en',
2018-05-28 18:46:55 +00:00
defaultEditor: 'markdown',
tfaIsActive: false,
isActive: true,
isVerified: true
2017-12-17 04:41:16 +00:00
})
await adminUser.$relatedQuery('groups').relate(adminGroup.id)
2017-12-17 04:41:16 +00:00
// Create Guest account
2018-05-28 18:46:55 +00:00
WIKI.logger.info('Creating guest account...')
const guestUser = await WIKI.models.users.query().insert({
provider: 'local',
email: 'guest@example.com',
name: 'Guest',
password: '',
locale: 'en',
defaultEditor: 'markdown',
tfaIsActive: false,
isSystem: true,
isActive: true,
isVerified: true
})
await guestUser.$relatedQuery('groups').relate(guestGroup.id)
if (adminUser.id !== 1 || guestUser.id !== 2) {
2019-10-26 03:39:58 +00:00
throw new Error('Incorrect users auto-increment configuration! Should start at 0 and increment by 1. Contact your database administrator.')
}
2018-10-22 02:25:17 +00:00
// Create site nav
WIKI.logger.info('Creating default site navigation')
await WIKI.models.navigation.query().insert({
key: 'site',
2018-11-03 05:02:08 +00:00
config: [
2018-10-22 02:25:17 +00:00
{
locale: 'en',
items: [
{
id: uuid(),
icon: 'mdi-home',
kind: 'link',
label: 'Home',
target: '/',
targetType: 'home',
visibilityMode: 'all',
visibilityGroups: null
}
]
2018-10-22 02:25:17 +00:00
}
2018-11-03 05:02:08 +00:00
]
2018-10-22 02:25:17 +00:00
})
WIKI.logger.info('Setup is complete!')
2020-04-17 00:38:28 +00:00
// WIKI.telemetry.sendEvent('setup', 'install-completed')
res.json({
ok: true,
redirectPath: '/',
redirectPort: WIKI.config.port
}).end()
2020-04-17 00:38:28 +00:00
if (WIKI.config.telemetry.isEnabled) {
await WIKI.telemetry.sendInstanceEvent('INSTALL')
}
2018-05-28 18:46:55 +00:00
WIKI.config.setup = false
WIKI.logger.info('Stopping Setup...')
WIKI.server.destroy(() => {
WIKI.logger.info('Setup stopped. Starting Wiki.js...')
_.delay(() => {
WIKI.kernel.bootMaster()
}, 1000)
})
} catch (err) {
2019-10-26 03:39:58 +00:00
try {
await WIKI.models.knex('settings').truncate()
} catch (err) {}
2019-02-02 06:17:09 +00:00
WIKI.telemetry.sendError(err)
res.json({ ok: false, error: err.message })
}
2017-03-25 21:03:09 +00:00
})
2017-02-26 23:06:20 +00:00
// ----------------------------------------
// Error handling
// ----------------------------------------
app.use(function (req, res, next) {
const err = new Error('Not Found')
2017-02-26 23:06:20 +00:00
err.status = 404
next(err)
})
app.use(function (err, req, res, next) {
res.status(err.status || 500)
res.send({
message: err.message,
error: WIKI.IS_DEBUG ? err : {}
2017-02-26 23:06:20 +00:00
})
WIKI.logger.error(err.message)
WIKI.telemetry.sendError(err)
2017-02-26 23:06:20 +00:00
})
// ----------------------------------------
// Start HTTP server
// ----------------------------------------
2019-07-01 02:37:01 +00:00
WIKI.logger.info(`Starting HTTP server on port ${WIKI.config.port}...`)
2017-02-26 23:06:20 +00:00
app.set('port', WIKI.config.port)
WIKI.logger.info(`HTTP Server on port: [ ${WIKI.config.port} ]`)
WIKI.server = http.createServer(app)
2018-09-01 03:42:14 +00:00
WIKI.server.listen(WIKI.config.port, WIKI.config.bindIP)
2017-03-29 02:29:53 +00:00
var openConnections = []
WIKI.server.on('connection', (conn) => {
2017-03-29 02:29:53 +00:00
let key = conn.remoteAddress + ':' + conn.remotePort
openConnections[key] = conn
conn.on('close', () => {
2019-03-19 18:46:10 +00:00
openConnections.splice(key, 1)
2017-03-29 02:29:53 +00:00
})
})
WIKI.server.destroy = (cb) => {
WIKI.server.close(cb)
2017-03-29 02:29:53 +00:00
for (let key in openConnections) {
openConnections[key].destroy()
}
}
WIKI.server.on('error', (error) => {
2017-02-26 23:06:20 +00:00
if (error.syscall !== 'listen') {
throw error
}
switch (error.code) {
case 'EACCES':
WIKI.logger.error('Listening on port ' + WIKI.config.port + ' requires elevated privileges!')
2017-04-05 02:04:17 +00:00
return process.exit(1)
2017-02-26 23:06:20 +00:00
case 'EADDRINUSE':
WIKI.logger.error('Port ' + WIKI.config.port + ' is already in use!')
2017-04-05 02:04:17 +00:00
return process.exit(1)
2017-02-26 23:06:20 +00:00
default:
throw error
}
})
WIKI.server.on('listening', () => {
2018-05-28 18:46:55 +00:00
WIKI.logger.info('HTTP Server: [ RUNNING ]')
2019-01-07 03:03:34 +00:00
WIKI.logger.info('🔻🔻🔻🔻🔻🔻🔻🔻🔻🔻🔻🔻🔻🔻🔻🔻🔻🔻🔻🔻🔻🔻🔻🔻🔻🔻🔻🔻🔻')
WIKI.logger.info('')
WIKI.logger.info(`Browse to http://YOUR-SERVER-IP:${WIKI.config.port}/ to complete setup!`)
2019-01-07 03:03:34 +00:00
WIKI.logger.info('')
WIKI.logger.info('🔺🔺🔺🔺🔺🔺🔺🔺🔺🔺🔺🔺🔺🔺🔺🔺🔺🔺🔺🔺🔺🔺🔺🔺🔺🔺🔺🔺🔺')
2017-02-26 23:06:20 +00:00
})
}