master.js 7.95 KB
Newer Older
1 2 3 4 5 6 7
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')
const favicon = require('serve-favicon')
8
const fs = require('fs-extra')
9
const http = require('http')
Nick's avatar
Nick committed
10
const https = require('https')
11
const path = require('path')
Nick's avatar
Nick committed
12
const _ = require('lodash')
13
const { ApolloServer } = require('apollo-server-express')
NGPixel's avatar
NGPixel committed
14
// const oauth2orize = require('oauth2orize')
15

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

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

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

  // ----------------------------------------
29
  // Load middlewares
30 31
  // ----------------------------------------

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

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

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

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

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

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

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

64 65 66 67
  // ----------------------------------------
  // OAuth2 Server
  // ----------------------------------------

NGPixel's avatar
NGPixel committed
68
  // const OAuth2Server = oauth2orize.createServer()
69

70 71 72 73 74
  // ----------------------------------------
  // Passport Authentication
  // ----------------------------------------

  app.use(cookieParser())
75
  app.use(WIKI.auth.passport.initialize())
76
  app.use(WIKI.auth.authenticate)
77 78 79 80 81 82 83 84 85 86

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

  app.use(mw.seo)

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

88
  app.set('views', path.join(WIKI.SERVERPATH, 'views'))
89
  app.set('view engine', 'pug')
NGPixel's avatar
NGPixel committed
90

91 92
  app.use(bodyParser.json({ limit: '1mb' }))
  app.use(bodyParser.urlencoded({ extended: false, limit: '1mb' }))
NGPixel's avatar
NGPixel committed
93

94 95 96 97
  // ----------------------------------------
  // Localization
  // ----------------------------------------

98
  WIKI.lang.attachMiddleware(app)
99

100 101 102
  // ----------------------------------------
  // View accessible data
  // ----------------------------------------
NGPixel's avatar
NGPixel committed
103

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

NGPixel's avatar
NGPixel committed
116 117 118 119 120
  // ----------------------------------------
  // HMR (Dev Mode Only)
  // ----------------------------------------

  if (global.DEV) {
121 122
    app.use(global.WP_DEV.devMiddleware)
    app.use(global.WP_DEV.hotMiddleware)
NGPixel's avatar
NGPixel committed
123 124
  }

125
  // ----------------------------------------
126 127 128 129 130 131
  // Apollo Server (GraphQL)
  // ----------------------------------------

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

      },
      path: '/graphql-subscriptions'
    }
139 140 141 142 143
  })
  apolloServer.applyMiddleware({ app })

  // ----------------------------------------
  // Routing
144
  // ----------------------------------------
NGPixel's avatar
NGPixel committed
145

146
  app.use('/', ctrl.auth)
147
  app.use('/', ctrl.common)
NGPixel's avatar
NGPixel committed
148

149 150 151
  // ----------------------------------------
  // Error handling
  // ----------------------------------------
NGPixel's avatar
NGPixel committed
152

153
  app.use((req, res, next) => {
154 155 156 157
    var err = new Error('Not Found')
    err.status = 404
    next(err)
  })
NGPixel's avatar
NGPixel committed
158

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

168
  // ----------------------------------------
169
  // HTTP/S server
170 171
  // ----------------------------------------

NGPixel's avatar
NGPixel committed
172 173
  let srvConnections = {}

174
  app.set('port', WIKI.config.port)
Nick's avatar
Nick committed
175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196
  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)
197 198 199 200 201 202 203 204

    // 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
205 206 207 208
  } else {
    WIKI.logger.info(`HTTP Server on port: [ ${WIKI.config.port} ]`)
    WIKI.server = http.createServer(app)
  }
209
  apolloServer.installSubscriptionHandlers(WIKI.server)
210

211
  WIKI.server.listen(WIKI.config.port, WIKI.config.bindIP)
212
  WIKI.server.on('error', (error) => {
213
    if (error.syscall !== 'listen') {
NGPixel's avatar
NGPixel committed
214
      throw error
215 216 217 218 219
    }

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

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

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

      // 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
267 268 269
    } else {
      WIKI.logger.info('HTTP Server: [ RUNNING ]')
    }
270
  })
NGPixel's avatar
NGPixel committed
271

272 273
  WIKI.server.destroy = (cb) => {
    WIKI.server.close(cb)
NGPixel's avatar
NGPixel committed
274 275 276
    for (let key in srvConnections) {
      srvConnections[key].destroy()
    }
277 278 279 280

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

283
  return true
NGPixel's avatar
NGPixel committed
284
}