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.

189 lines
5.4 KiB

  1. const fs = require('fs-extra')
  2. const http = require('http')
  3. const https = require('https')
  4. const { ApolloServer } = require('apollo-server-express')
  5. const Promise = require('bluebird')
  6. const _ = require('lodash')
  7. /* global WIKI */
  8. module.exports = {
  9. servers: {
  10. graph: null,
  11. http: null,
  12. https: null
  13. },
  14. connections: new Map(),
  15. le: null,
  16. /**
  17. * Start HTTP Server
  18. */
  19. async startHTTP () {
  20. WIKI.logger.info(`HTTP Server on port: [ ${WIKI.config.port} ]`)
  21. this.servers.http = http.createServer(WIKI.app)
  22. this.servers.graph.installSubscriptionHandlers(this.servers.http)
  23. this.servers.http.listen(WIKI.config.port, WIKI.config.bindIP)
  24. this.servers.http.on('error', (error) => {
  25. if (error.syscall !== 'listen') {
  26. throw error
  27. }
  28. switch (error.code) {
  29. case 'EACCES':
  30. WIKI.logger.error('Listening on port ' + WIKI.config.port + ' requires elevated privileges!')
  31. return process.exit(1)
  32. case 'EADDRINUSE':
  33. WIKI.logger.error('Port ' + WIKI.config.port + ' is already in use!')
  34. return process.exit(1)
  35. default:
  36. throw error
  37. }
  38. })
  39. this.servers.http.on('listening', () => {
  40. WIKI.logger.info('HTTP Server: [ RUNNING ]')
  41. })
  42. this.servers.http.on('connection', conn => {
  43. let connKey = `http:${conn.remoteAddress}:${conn.remotePort}`
  44. this.connections.set(connKey, conn)
  45. conn.on('close', () => {
  46. this.connections.delete(connKey)
  47. })
  48. })
  49. },
  50. /**
  51. * Start HTTPS Server
  52. */
  53. async startHTTPS () {
  54. if (WIKI.config.ssl.provider === 'letsencrypt') {
  55. this.le = require('./letsencrypt')
  56. await this.le.init()
  57. }
  58. WIKI.logger.info(`HTTPS Server on port: [ ${WIKI.config.ssl.port} ]`)
  59. const tlsOpts = {}
  60. try {
  61. if (WIKI.config.ssl.format === 'pem') {
  62. tlsOpts.key = WIKI.config.ssl.inline ? WIKI.config.ssl.key : fs.readFileSync(WIKI.config.ssl.key)
  63. tlsOpts.cert = WIKI.config.ssl.inline ? WIKI.config.ssl.cert : fs.readFileSync(WIKI.config.ssl.cert)
  64. } else {
  65. tlsOpts.pfx = WIKI.config.ssl.inline ? WIKI.config.ssl.pfx : fs.readFileSync(WIKI.config.ssl.pfx)
  66. }
  67. if (!_.isEmpty(WIKI.config.ssl.passphrase)) {
  68. tlsOpts.passphrase = WIKI.config.ssl.passphrase
  69. }
  70. if (!_.isEmpty(WIKI.config.ssl.dhparam)) {
  71. tlsOpts.dhparam = WIKI.config.ssl.dhparam
  72. }
  73. } catch (err) {
  74. WIKI.logger.error('Failed to setup HTTPS server parameters:')
  75. WIKI.logger.error(err)
  76. return process.exit(1)
  77. }
  78. this.servers.https = https.createServer(tlsOpts, WIKI.app)
  79. this.servers.graph.installSubscriptionHandlers(this.servers.https)
  80. this.servers.https.listen(WIKI.config.ssl.port, WIKI.config.bindIP)
  81. this.servers.https.on('error', (error) => {
  82. if (error.syscall !== 'listen') {
  83. throw error
  84. }
  85. switch (error.code) {
  86. case 'EACCES':
  87. WIKI.logger.error('Listening on port ' + WIKI.config.ssl.port + ' requires elevated privileges!')
  88. return process.exit(1)
  89. case 'EADDRINUSE':
  90. WIKI.logger.error('Port ' + WIKI.config.ssl.port + ' is already in use!')
  91. return process.exit(1)
  92. default:
  93. throw error
  94. }
  95. })
  96. this.servers.https.on('listening', () => {
  97. WIKI.logger.info('HTTPS Server: [ RUNNING ]')
  98. })
  99. this.servers.https.on('connection', conn => {
  100. let connKey = `https:${conn.remoteAddress}:${conn.remotePort}`
  101. this.connections.set(connKey, conn)
  102. conn.on('close', () => {
  103. this.connections.delete(connKey)
  104. })
  105. })
  106. },
  107. /**
  108. * Start GraphQL Server
  109. */
  110. async startGraphQL () {
  111. const graphqlSchema = require('../graph')
  112. this.servers.graph = new ApolloServer({
  113. ...graphqlSchema,
  114. context: ({ req, res }) => ({ req, res }),
  115. subscriptions: {
  116. onConnect: (connectionParams, webSocket) => {
  117. },
  118. path: '/graphql-subscriptions'
  119. }
  120. })
  121. this.servers.graph.applyMiddleware({ app: WIKI.app, cors: false })
  122. },
  123. /**
  124. * Close all active connections
  125. */
  126. closeConnections (mode = 'all') {
  127. for (const [key, conn] of this.connections) {
  128. if (mode !== `all` && key.indexOf(`${mode}:`) !== 0) {
  129. continue
  130. }
  131. conn.destroy()
  132. this.connections.delete(key)
  133. }
  134. if (mode === 'all') {
  135. this.connections.clear()
  136. }
  137. },
  138. /**
  139. * Stop all servers
  140. */
  141. async stopServers () {
  142. this.closeConnections()
  143. if (this.servers.http) {
  144. await Promise.fromCallback(cb => { this.servers.http.close(cb) })
  145. this.servers.http = null
  146. }
  147. if (this.servers.https) {
  148. await Promise.fromCallback(cb => { this.servers.https.close(cb) })
  149. this.servers.https = null
  150. }
  151. this.servers.graph = null
  152. },
  153. /**
  154. * Restart Server
  155. */
  156. async restartServer (srv = 'https') {
  157. this.closeConnections(srv)
  158. switch (srv) {
  159. case 'http':
  160. if (this.servers.http) {
  161. await Promise.fromCallback(cb => { this.servers.http.close(cb) })
  162. this.servers.http = null
  163. }
  164. this.startHTTP()
  165. break
  166. case 'https':
  167. if (this.servers.https) {
  168. await Promise.fromCallback(cb => { this.servers.https.close(cb) })
  169. this.servers.https = null
  170. }
  171. this.startHTTPS()
  172. break
  173. default:
  174. throw new Error('Cannot restart server: Invalid designation')
  175. }
  176. }
  177. }