servers.js 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225
  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 _ = require('lodash')
  6. const io = require('socket.io')
  7. const { ApolloServerPluginLandingPageGraphQLPlayground, ApolloServerPluginLandingPageProductionDefault } = require('apollo-server-core')
  8. const { graphqlUploadExpress } = require('graphql-upload')
  9. module.exports = {
  10. graph: null,
  11. http: null,
  12. https: null,
  13. ws: null,
  14. connections: new Map(),
  15. le: null,
  16. /**
  17. * Initialize HTTP Server
  18. */
  19. async initHTTP () {
  20. WIKI.logger.info(`HTTP Server on port: [ ${WIKI.config.port} ]`)
  21. this.http = http.createServer(WIKI.app)
  22. this.http.on('error', (error) => {
  23. if (error.syscall !== 'listen') {
  24. throw error
  25. }
  26. switch (error.code) {
  27. case 'EACCES':
  28. WIKI.logger.error('Listening on port ' + WIKI.config.port + ' requires elevated privileges!')
  29. return process.exit(1)
  30. case 'EADDRINUSE':
  31. WIKI.logger.error('Port ' + WIKI.config.port + ' is already in use!')
  32. return process.exit(1)
  33. default:
  34. throw error
  35. }
  36. })
  37. this.http.on('listening', () => {
  38. WIKI.logger.info('HTTP Server: [ RUNNING ]')
  39. })
  40. this.http.on('connection', conn => {
  41. let connKey = `http:${conn.remoteAddress}:${conn.remotePort}`
  42. this.connections.set(connKey, conn)
  43. conn.on('close', () => {
  44. this.connections.delete(connKey)
  45. })
  46. })
  47. },
  48. /**
  49. * Start HTTP Server
  50. */
  51. async startHTTP () {
  52. this.http.listen(WIKI.config.port, WIKI.config.bindIP)
  53. },
  54. /**
  55. * Initialize HTTPS Server
  56. */
  57. async initHTTPS () {
  58. if (WIKI.config.ssl.provider === 'letsencrypt') {
  59. this.le = require('./letsencrypt')
  60. await this.le.init()
  61. }
  62. WIKI.logger.info(`HTTPS Server on port: [ ${WIKI.config.ssl.port} ]`)
  63. const tlsOpts = {}
  64. try {
  65. if (WIKI.config.ssl.format === 'pem') {
  66. tlsOpts.key = WIKI.config.ssl.inline ? WIKI.config.ssl.key : fs.readFileSync(WIKI.config.ssl.key)
  67. tlsOpts.cert = WIKI.config.ssl.inline ? WIKI.config.ssl.cert : fs.readFileSync(WIKI.config.ssl.cert)
  68. } else {
  69. tlsOpts.pfx = WIKI.config.ssl.inline ? WIKI.config.ssl.pfx : fs.readFileSync(WIKI.config.ssl.pfx)
  70. }
  71. if (!_.isEmpty(WIKI.config.ssl.passphrase)) {
  72. tlsOpts.passphrase = WIKI.config.ssl.passphrase
  73. }
  74. if (!_.isEmpty(WIKI.config.ssl.dhparam)) {
  75. tlsOpts.dhparam = WIKI.config.ssl.dhparam
  76. }
  77. } catch (err) {
  78. WIKI.logger.error('Failed to setup HTTPS server parameters:')
  79. WIKI.logger.error(err)
  80. return process.exit(1)
  81. }
  82. this.https = https.createServer(tlsOpts, WIKI.app)
  83. this.https.listen(WIKI.config.ssl.port, WIKI.config.bindIP)
  84. this.https.on('error', (error) => {
  85. if (error.syscall !== 'listen') {
  86. throw error
  87. }
  88. switch (error.code) {
  89. case 'EACCES':
  90. WIKI.logger.error('Listening on port ' + WIKI.config.ssl.port + ' requires elevated privileges!')
  91. return process.exit(1)
  92. case 'EADDRINUSE':
  93. WIKI.logger.error('Port ' + WIKI.config.ssl.port + ' is already in use!')
  94. return process.exit(1)
  95. default:
  96. throw error
  97. }
  98. })
  99. this.https.on('listening', () => {
  100. WIKI.logger.info('HTTPS Server: [ RUNNING ]')
  101. })
  102. this.https.on('connection', conn => {
  103. let connKey = `https:${conn.remoteAddress}:${conn.remotePort}`
  104. this.connections.set(connKey, conn)
  105. conn.on('close', () => {
  106. this.connections.delete(connKey)
  107. })
  108. })
  109. },
  110. /**
  111. * Start HTTPS Server
  112. */
  113. async startHTTPS () {
  114. this.https.listen(WIKI.config.ssl.port, WIKI.config.bindIP)
  115. },
  116. /**
  117. * Start GraphQL Server
  118. */
  119. async startGraphQL () {
  120. const graphqlSchema = require('../graph')
  121. this.graph = new ApolloServer({
  122. schema: graphqlSchema,
  123. csrfPrevention: true,
  124. cache: 'bounded',
  125. context: ({ req, res }) => ({ req, res }),
  126. plugins: [
  127. process.env.NODE_ENV === 'development' ? ApolloServerPluginLandingPageGraphQLPlayground({
  128. footer: false
  129. }) : ApolloServerPluginLandingPageProductionDefault({
  130. footer: false
  131. })
  132. // ApolloServerPluginDrainHttpServer({ httpServer: this.http })
  133. // ...(this.https && ApolloServerPluginDrainHttpServer({ httpServer: this.https }))
  134. ]
  135. })
  136. await this.graph.start()
  137. WIKI.app.use(graphqlUploadExpress())
  138. this.graph.applyMiddleware({ app: WIKI.app, cors: false, path: '/_graphql' })
  139. },
  140. /**
  141. * Start Socket.io WebSocket Server
  142. */
  143. async initWebSocket() {
  144. if (this.https) {
  145. this.ws = new io.Server(this.https, {
  146. path: '/_ws/',
  147. serveClient: false
  148. })
  149. WIKI.logger.info(`WebSocket Server attached to HTTPS Server [ OK ]`)
  150. } else {
  151. this.ws = new io.Server(this.http, {
  152. path: '/_ws/',
  153. serveClient: false,
  154. cors: true // TODO: dev only, replace with app settings once stable
  155. })
  156. WIKI.logger.info(`WebSocket Server attached to HTTP Server [ OK ]`)
  157. }
  158. },
  159. /**
  160. * Close all active connections
  161. */
  162. closeConnections (mode = 'all') {
  163. for (const [key, conn] of this.connections) {
  164. if (mode !== `all` && key.indexOf(`${mode}:`) !== 0) {
  165. continue
  166. }
  167. conn.destroy()
  168. this.connections.delete(key)
  169. }
  170. if (mode === 'all') {
  171. this.connections.clear()
  172. }
  173. },
  174. /**
  175. * Stop all servers
  176. */
  177. async stopServers () {
  178. this.closeConnections()
  179. if (this.http) {
  180. await new Promise(resolve => this.http.close(resolve))
  181. this.http = null
  182. }
  183. if (this.https) {
  184. await new Promise(resolve => this.https.close(resolve))
  185. this.https = null
  186. }
  187. this.graph = null
  188. },
  189. /**
  190. * Restart Server
  191. */
  192. async restartServer (srv = 'https') {
  193. this.closeConnections(srv)
  194. switch (srv) {
  195. case 'http':
  196. if (this.http) {
  197. await new Promise(resolve => this.http.close(resolve))
  198. this.http = null
  199. }
  200. this.initHTTP()
  201. this.startHTTP()
  202. break
  203. case 'https':
  204. if (this.https) {
  205. await new Promise(resolve => this.https.close(resolve))
  206. this.https = null
  207. }
  208. this.initHTTPS()
  209. this.startHTTPS()
  210. break
  211. default:
  212. throw new Error('Cannot restart server: Invalid designation')
  213. }
  214. }
  215. }