master.js 8.06 KB
Newer Older
1 2 3 4 5 6
const autoload = require('auto-load')
const bodyParser = require('body-parser')
const compression = require('compression')
const cookieParser = require('cookie-parser')
const cors = require('cors')
const express = require('express')
Nick's avatar
Nick committed
7
const session = require('express-session')
8
const KnexSessionStore = require('connect-session-knex')(session)
9
const favicon = require('serve-favicon')
10
const fs = require('fs-extra')
11
const http = require('http')
Nick's avatar
Nick committed
12
const https = require('https')
13
const path = require('path')
Nick's avatar
Nick committed
14
const _ = require('lodash')
15
const { ApolloServer } = require('apollo-server-express')
16

17
/* global WIKI */
NGPixel's avatar
NGPixel committed
18

NGPixel's avatar
NGPixel committed
19
module.exports = async () => {
20
  // ----------------------------------------
21
  // Load core modules
22 23
  // ----------------------------------------

24 25
  WIKI.auth = require('./core/auth').init()
  WIKI.lang = require('./core/localization').init()
26
  WIKI.mail = require('./core/mail').init()
27
  WIKI.system = require('./core/system').init()
28 29

  // ----------------------------------------
30
  // Load middlewares
31 32
  // ----------------------------------------

33 34
  var mw = autoload(path.join(WIKI.SERVERPATH, '/middlewares'))
  var ctrl = autoload(path.join(WIKI.SERVERPATH, '/controllers'))
35 36 37 38 39 40

  // ----------------------------------------
  // Define Express App
  // ----------------------------------------

  const app = express()
41
  WIKI.app = app
42 43 44 45 46 47 48
  app.use(compression())

  // ----------------------------------------
  // Security
  // ----------------------------------------

  app.use(mw.security)
49 50
  app.use(cors(WIKI.config.cors))
  app.options('*', cors(WIKI.config.cors))
51 52 53
  if (WIKI.config.trustProxy) {
    app.enable('trust proxy')
  }
54 55 56 57 58

  // ----------------------------------------
  // Public Assets
  // ----------------------------------------

59 60
  app.use(favicon(path.join(WIKI.ROOTPATH, 'assets', 'favicon.ico')))
  app.use(express.static(path.join(WIKI.ROOTPATH, 'assets'), {
61 62 63 64 65 66 67 68 69
    index: false,
    maxAge: '7d'
  }))

  // ----------------------------------------
  // Passport Authentication
  // ----------------------------------------

  app.use(cookieParser())
Nick's avatar
Nick committed
70 71 72
  app.use(session({
    secret: WIKI.config.sessionSecret,
    resave: false,
73 74 75 76
    saveUninitialized: false,
    store: new KnexSessionStore({
      knex: WIKI.models.knex
    })
Nick's avatar
Nick committed
77
  }))
78
  app.use(WIKI.auth.passport.initialize())
79
  app.use(WIKI.auth.authenticate)
80 81 82 83 84 85 86 87 88 89

  // ----------------------------------------
  // SEO
  // ----------------------------------------

  app.use(mw.seo)

  // ----------------------------------------
  // View Engine Setup
  // ----------------------------------------
NGPixel's avatar
NGPixel committed
90

91
  app.set('views', path.join(WIKI.SERVERPATH, 'views'))
92
  app.set('view engine', 'pug')
NGPixel's avatar
NGPixel committed
93

94 95
  app.use(bodyParser.json({ limit: '1mb' }))
  app.use(bodyParser.urlencoded({ extended: false, limit: '1mb' }))
NGPixel's avatar
NGPixel committed
96

97 98 99 100
  // ----------------------------------------
  // Localization
  // ----------------------------------------

101
  WIKI.lang.attachMiddleware(app)
102

103 104 105
  // ----------------------------------------
  // View accessible data
  // ----------------------------------------
NGPixel's avatar
NGPixel committed
106

107
  app.locals.basedir = WIKI.ROOTPATH
108 109
  app.locals._ = require('lodash')
  app.locals.moment = require('moment')
110
  app.locals.moment.locale(WIKI.config.lang.code)
111
  app.locals.config = WIKI.config
112 113 114 115 116 117
  app.locals.pageMeta = {
    title: '',
    description: WIKI.config.description,
    image: '',
    url: '/'
  }
NGPixel's avatar
NGPixel committed
118

NGPixel's avatar
NGPixel committed
119 120 121 122 123
  // ----------------------------------------
  // HMR (Dev Mode Only)
  // ----------------------------------------

  if (global.DEV) {
124 125
    app.use(global.WP_DEV.devMiddleware)
    app.use(global.WP_DEV.hotMiddleware)
NGPixel's avatar
NGPixel committed
126 127
  }

128
  // ----------------------------------------
129 130 131 132 133 134
  // Apollo Server (GraphQL)
  // ----------------------------------------

  const graphqlSchema = require('./graph')
  const apolloServer = new ApolloServer({
    ...graphqlSchema,
135 136 137 138 139 140 141
    context: ({ req, res }) => ({ req, res }),
    subscriptions: {
      onConnect: (connectionParams, webSocket) => {

      },
      path: '/graphql-subscriptions'
    }
142 143 144 145 146
  })
  apolloServer.applyMiddleware({ app })

  // ----------------------------------------
  // Routing
147
  // ----------------------------------------
NGPixel's avatar
NGPixel committed
148

149
  app.use('/', ctrl.auth)
150
  app.use('/', ctrl.upload)
151
  app.use('/', ctrl.common)
NGPixel's avatar
NGPixel committed
152

153 154 155
  // ----------------------------------------
  // Error handling
  // ----------------------------------------
NGPixel's avatar
NGPixel committed
156

157
  app.use((req, res, next) => {
158 159 160 161
    var err = new Error('Not Found')
    err.status = 404
    next(err)
  })
NGPixel's avatar
NGPixel committed
162

163
  app.use((err, req, res, next) => {
164
    res.status(err.status || 500)
165
    _.set(res.locals, 'pageMeta.title', 'Error')
166 167
    res.render('error', {
      message: err.message,
168
      error: WIKI.IS_DEBUG ? err : {}
169
    })
NGPixel's avatar
NGPixel committed
170 171
  })

172
  // ----------------------------------------
173
  // HTTP/S server
174 175
  // ----------------------------------------

NGPixel's avatar
NGPixel committed
176 177
  let srvConnections = {}

178
  app.set('port', WIKI.config.port)
Nick's avatar
Nick committed
179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200
  if (WIKI.config.ssl.enabled) {
    WIKI.logger.info(`HTTPS Server on port: [ ${WIKI.config.port} ]`)
    const tlsOpts = {}
    try {
      if (WIKI.config.ssl.format === 'pem') {
        tlsOpts.key = fs.readFileSync(WIKI.config.ssl.key)
        tlsOpts.cert = fs.readFileSync(WIKI.config.ssl.cert)
      } else {
        tlsOpts.pfx = fs.readFileSync(WIKI.config.ssl.pfx)
      }
      if (!_.isEmpty(WIKI.config.ssl.passphrase)) {
        tlsOpts.passphrase = WIKI.config.ssl.passphrase
      }
      if (!_.isEmpty(WIKI.config.ssl.dhparam)) {
        tlsOpts.dhparam = WIKI.config.ssl.dhparam
      }
    } catch (err) {
      WIKI.logger.error('Failed to setup HTTPS server parameters:')
      WIKI.logger.error(err)
      return process.exit(1)
    }
    WIKI.server = https.createServer(tlsOpts, app)
201 202 203 204 205 206 207 208

    // HTTP Redirect Server
    if (WIKI.config.ssl.redirectNonSSLPort) {
      WIKI.serverAlt = http.createServer((req, res) => {
        res.writeHead(301, { 'Location': 'https://' + req.headers['host'] + req.url })
        res.end()
      })
    }
Nick's avatar
Nick committed
209 210 211 212
  } else {
    WIKI.logger.info(`HTTP Server on port: [ ${WIKI.config.port} ]`)
    WIKI.server = http.createServer(app)
  }
213
  apolloServer.installSubscriptionHandlers(WIKI.server)
214

215
  WIKI.server.listen(WIKI.config.port, WIKI.config.bindIP)
216
  WIKI.server.on('error', (error) => {
217
    if (error.syscall !== 'listen') {
NGPixel's avatar
NGPixel committed
218
      throw error
219 220 221 222 223
    }

    // handle specific listen errors with friendly messages
    switch (error.code) {
      case 'EACCES':
224
        WIKI.logger.error('Listening on port ' + WIKI.config.port + ' requires elevated privileges!')
225 226
        return process.exit(1)
      case 'EADDRINUSE':
227
        WIKI.logger.error('Port ' + WIKI.config.port + ' is already in use!')
228 229 230 231 232
        return process.exit(1)
      default:
        throw error
    }
  })
NGPixel's avatar
NGPixel committed
233

234
  WIKI.server.on('connection', conn => {
NGPixel's avatar
NGPixel committed
235 236 237 238 239 240 241
    let key = `${conn.remoteAddress}:${conn.remotePort}`
    srvConnections[key] = conn
    conn.on('close', function() {
      delete srvConnections[key]
    })
  })

242
  WIKI.server.on('listening', () => {
Nick's avatar
Nick committed
243 244
    if (WIKI.config.ssl.enabled) {
      WIKI.logger.info('HTTPS Server: [ RUNNING ]')
245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270

      // Start HTTP Redirect Server
      if (WIKI.config.ssl.redirectNonSSLPort) {
        WIKI.serverAlt.listen(WIKI.config.ssl.redirectNonSSLPort, WIKI.config.bindIP)

        WIKI.serverAlt.on('error', (error) => {
          if (error.syscall !== 'listen') {
            throw error
          }

          switch (error.code) {
            case 'EACCES':
              WIKI.logger.error('(HTTP Redirect) Listening on port ' + WIKI.config.port + ' requires elevated privileges!')
              return process.exit(1)
            case 'EADDRINUSE':
              WIKI.logger.error('(HTTP Redirect) Port ' + WIKI.config.port + ' is already in use!')
              return process.exit(1)
            default:
              throw error
          }
        })

        WIKI.serverAlt.on('listening', () => {
          WIKI.logger.info('HTTP Server: [ RUNNING in redirect mode ]')
        })
      }
Nick's avatar
Nick committed
271 272 273
    } else {
      WIKI.logger.info('HTTP Server: [ RUNNING ]')
    }
274
  })
NGPixel's avatar
NGPixel committed
275

276 277
  WIKI.server.destroy = (cb) => {
    WIKI.server.close(cb)
NGPixel's avatar
NGPixel committed
278 279 280
    for (let key in srvConnections) {
      srvConnections[key].destroy()
    }
281 282 283 284

    if (WIKI.config.ssl.enabled && WIKI.config.ssl.redirectNonSSLPort) {
      WIKI.serverAlt.close(cb)
    }
NGPixel's avatar
NGPixel committed
285 286
  }

287
  return true
NGPixel's avatar
NGPixel committed
288
}