ad97cdf444
Found via `codespell -q 3 -L optio,promisses,usera`
167 lines
5.2 KiB
JavaScript
167 lines
5.2 KiB
JavaScript
/* istanbul ignore file */
|
|
'use strict'
|
|
|
|
const http = require( 'http' )
|
|
const url = require( 'url' )
|
|
const express = require( 'express' )
|
|
const compression = require( 'compression' )
|
|
const appRoot = require( 'app-root-path' )
|
|
const logger = require( 'morgan-debug' )
|
|
const bodyParser = require( 'body-parser' )
|
|
const path = require( 'path' )
|
|
const debug = require( 'debug' )
|
|
|
|
const Sentry = require( '@sentry/node' )
|
|
const Tracing = require( '@sentry/tracing' )
|
|
const Logging = require( `${appRoot}/logging` )
|
|
const { startup: MatStartup } = require( `${appRoot}/logging/matomoHelper` )
|
|
const prometheusClient = require( 'prom-client' )
|
|
|
|
const { ApolloServer, ForbiddenError } = require( 'apollo-server-express' )
|
|
|
|
require( 'dotenv' ).config( { path: `${appRoot}/.env` } )
|
|
|
|
const { contextApiTokenHelper } = require( './modules/shared' )
|
|
const knex = require( './db/knex' )
|
|
|
|
let graphqlServer
|
|
|
|
/**
|
|
* Initialises the express application together with the graphql server middleware.
|
|
* @return {[type]} an express application and the graphql server
|
|
*/
|
|
exports.init = async ( ) => {
|
|
const app = express( )
|
|
|
|
Logging( app )
|
|
MatStartup()
|
|
|
|
// Initialise prometheus metrics
|
|
prometheusClient.register.clear()
|
|
prometheusClient.collectDefaultMetrics()
|
|
|
|
// Moves things along automatically on restart.
|
|
// Should perhaps be done manually?
|
|
await knex.migrate.latest( )
|
|
|
|
if ( process.env.NODE_ENV !== 'test' ) {
|
|
app.use( logger( 'speckle', 'dev', {} ) )
|
|
}
|
|
|
|
if ( process.env.COMPRESSION ) {
|
|
app.use( compression( ) )
|
|
}
|
|
|
|
app.use( bodyParser.json( { limit: '100mb' } ) )
|
|
app.use( bodyParser.urlencoded( { limit: '100mb', extended: false } ) )
|
|
|
|
const { init, graph } = require( './modules' )
|
|
|
|
// Initialise default modules, including rest api handlers
|
|
await init( app )
|
|
|
|
// Initialise graphql server
|
|
const metricConnectCounter = new prometheusClient.Counter( { name: 'speckle_server_apollo_connect', help: 'Number of connects' } )
|
|
const metricConnectedClients = new prometheusClient.Gauge( { name: 'speckle_server_apollo_clients', help: 'Number of currently connected clients' } )
|
|
graphqlServer = new ApolloServer( {
|
|
...graph( ),
|
|
context: contextApiTokenHelper,
|
|
subscriptions: {
|
|
onConnect: ( connectionParams, webSocket, context ) => {
|
|
metricConnectCounter.inc()
|
|
metricConnectedClients.inc()
|
|
try {
|
|
if ( connectionParams.Authorization || connectionParams.authorization || connectionParams.headers.Authorization ) {
|
|
let header = connectionParams.Authorization || connectionParams.authorization || connectionParams.headers.Authorization
|
|
let token = header.split( ' ' )[ 1 ]
|
|
return { token: token }
|
|
}
|
|
} catch ( e ) {
|
|
throw new ForbiddenError( 'You need a token to subscribe' )
|
|
}
|
|
},
|
|
onDisconnect: ( webSocket, context ) => {
|
|
metricConnectedClients.dec()
|
|
// debug( `speckle:debug` )( 'ws on disconnect connect event' )
|
|
},
|
|
},
|
|
plugins: [
|
|
require( `${appRoot}/logging/apolloPlugin` )
|
|
],
|
|
tracing: process.env.NODE_ENV === 'development',
|
|
introspection: true,
|
|
playground: true
|
|
} )
|
|
|
|
graphqlServer.applyMiddleware( { app: app } )
|
|
|
|
// Expose prometheus metrics
|
|
app.get( '/metrics', async ( req, res ) => {
|
|
try {
|
|
res.set( 'Content-Type', prometheusClient.register.contentType )
|
|
res.end( await prometheusClient.register.metrics() )
|
|
} catch ( ex ) {
|
|
res.status( 500 ).end( ex )
|
|
}
|
|
} )
|
|
|
|
// Trust X-Forwarded-* headers (for https protocol detection)
|
|
app.enable( 'trust proxy' )
|
|
|
|
return { app, graphqlServer }
|
|
}
|
|
|
|
|
|
/**
|
|
* Starts a http server, hoisting the express app to it.
|
|
* @param {[type]} app [description]
|
|
* @return {[type]} [description]
|
|
*/
|
|
exports.startHttp = async ( app, customPortOverride ) => {
|
|
let bindAddress = process.env.BIND_ADDRESS || '127.0.0.1'
|
|
let port = process.env.PORT || 3000
|
|
|
|
if ( customPortOverride ) port = customPortOverride
|
|
|
|
app.set( 'port', port )
|
|
|
|
let frontendHost = process.env.FRONTEND_HOST || 'localhost'
|
|
let frontendPort = process.env.FRONTEND_PORT || 8080
|
|
|
|
// Handles frontend proxying:
|
|
// Dev mode -> proxy form the local webpack server
|
|
if ( process.env.NODE_ENV === 'development' ) {
|
|
const { createProxyMiddleware } = require( 'http-proxy-middleware' )
|
|
const frontendProxy = createProxyMiddleware( { target: `http://${frontendHost}:${frontendPort}`, changeOrigin: true, ws: false, logLevel: 'silent' } )
|
|
app.use( '/', frontendProxy )
|
|
|
|
debug( 'speckle:startup' )( '✨ Proxying frontend (dev mode):' )
|
|
debug( 'speckle:startup' )( `👉 main application: http://localhost:${port}/` )
|
|
|
|
}
|
|
|
|
// Production mode
|
|
else {
|
|
bindAddress = process.env.BIND_ADDRESS || '0.0.0.0'
|
|
}
|
|
|
|
let server = http.createServer( app )
|
|
|
|
// Final apollo server setup
|
|
graphqlServer.installSubscriptionHandlers( server )
|
|
graphqlServer.applyMiddleware( { app: app } )
|
|
|
|
app.use( Sentry.Handlers.errorHandler( ) )
|
|
|
|
server.on( 'listening', ( ) => {
|
|
debug( 'speckle:startup' )( `🚀 My name is Speckle Server, and I'm running at ${server.address().address}:${server.address().port}` )
|
|
} )
|
|
|
|
server.listen( port, bindAddress )
|
|
|
|
server.keepAliveTimeout = 61 * 1000
|
|
server.headersTimeout = 65 * 1000
|
|
|
|
return { server }
|
|
}
|