UNPKG

@xrengine/server-core

Version:

Shared components for XREngine server

203 lines (175 loc) 6.11 kB
// Do not delete json and urlencoded, they are used even if some IDEs show them as unused import express, { errorHandler, json, rest, urlencoded } from '@feathersjs/express' import { feathers } from '@feathersjs/feathers' import * as k8s from '@kubernetes/client-node' import compress from 'compression' import cors from 'cors' import { EventEmitter } from 'events' // Do not delete, this is used even if some IDEs show it as unused import swagger from 'feathers-swagger' import sync from 'feathers-sync' import helmet from 'helmet' import path from 'path' import { isDev } from '@xrengine/common/src/config' import { pipe } from '@xrengine/common/src/utils/pipe' import { Engine } from '@xrengine/engine/src/ecs/classes/Engine' import { createEngine, initializeNode, setupEngineActionSystems } from '@xrengine/engine/src/initializeEngine' import { Application, ServerTypeMode } from '../declarations' import appConfig from './appconfig' import config from './appconfig' import { createDefaultStorageProvider, createIPFSStorageProvider } from './media/storageprovider/storageprovider' import sequelize from './sequelize' import { elasticOnlyLogger, logger } from './ServerLogger' import services from './services' import authentication from './user/authentication' import primus from './util/primus' require('fix-esm').register() export const configureOpenAPI = () => (app: Application) => { app.configure( swagger({ docsPath: '/openapi', docsJsonPath: '/openapi.json', uiIndex: path.join(process.cwd() + '/openapi.html'), // TODO: Relate to server config, don't hardcode this here specs: { info: { title: 'XREngine API Surface', description: 'APIs for the XREngine application', version: '1.0.0' }, schemes: ['https'], securityDefinitions: { bearer: { type: 'apiKey', in: 'header', name: 'authorization' } }, security: [{ bearer: [] }] } }) ) return app } export const configurePrimus = (instanceserver = false) => (app: Application) => { const origin = [ 'https://' + appConfig.server.clientHost, 'capacitor://' + appConfig.server.clientHost, 'ionic://' + appConfig.server.clientHost ] if (!instanceserver) origin.push('https://localhost:3001') app.configure( primus( { transformer: 'websockets', origins: origin, methods: ['OPTIONS', 'GET', 'POST'], headers: '*', credentials: true }, (primus) => { primus.use((message, socket, next) => { ;(message as any).feathers.socketQuery = message.query ;(message as any).socketQuery = message.query next() }) } ) ) return app } export const configureRedis = () => (app: Application) => { if (appConfig.redis.enabled) { app.configure( sync({ uri: appConfig.redis.password ? `redis://:${appConfig.redis.password}@${appConfig.redis.address}:${appConfig.redis.port}` : `redis://${appConfig.redis.address}:${appConfig.redis.port}` }) ) app.sync.ready.then(() => { logger.info('Feathers-sync started.') }) } return app } export const configureK8s = () => (app: Application) => { if (appConfig.kubernetes.enabled) { const kc = new k8s.KubeConfig() kc.loadFromDefault() app.k8AgonesClient = kc.makeApiClient(k8s.CustomObjectsApi) app.k8DefaultClient = kc.makeApiClient(k8s.CoreV1Api) app.k8AppsClient = kc.makeApiClient(k8s.AppsV1Api) app.k8BatchClient = kc.makeApiClient(k8s.BatchV1Api) } return app } export const serverPipe = pipe(configureOpenAPI(), configurePrimus(), configureRedis(), configureK8s()) as ( app: Application ) => Application export const createFeathersExpressApp = ( serverMode: ServerTypeMode = 'API', configurationPipe = serverPipe ): Application => { createDefaultStorageProvider() if (appConfig.ipfs.enabled) { createIPFSStorageProvider() } if (!appConfig.db.forceRefresh) { createEngine() Engine.instance.publicPath = config.client.dist setupEngineActionSystems() initializeNode() } const app = express(feathers()) as Application app.serverMode = serverMode app.set('nextReadyEmitter', new EventEmitter()) // Feathers authentication-oauth will only append the port in production, but then it will also // hard-code http as the protocol, so manually mashing host + port together if in local. app.set( 'host', appConfig.server.local ? appConfig.server.hostname + ':' + appConfig.server.port : appConfig.server.hostname ) app.set('port', appConfig.server.port) app.set('paginate', appConfig.server.paginate) app.set('authentication', appConfig.authentication) configurationPipe(app) // Feathers authentication-oauth will use http for its redirect_uri if this is 'dev'. // Doesn't appear anything else uses it. app.set('env', 'production') app.configure(sequelize) // Enable security, CORS, compression, favicon and body parsing app.use(helmet()) app.use( cors({ origin: true, credentials: true }) as any ) app.use(compress()) app.use(json()) app.use(urlencoded({ extended: true })) app.configure(rest()) // app.use(function (req, res, next) { // ;(req as any).feathers.req = req // ;(req as any).feathers.res = res // next() // }) // Configure other middleware (see `middleware/index.js`) app.configure(authentication) // Set up our services (see `services/index.js`) app.configure(services) app.use('/healthcheck', (req, res) => { res.sendStatus(200) }) // Receive client-side log events (only active when APP_ENV != 'development') app.post('/api/log', (req, res) => { const { msg, ...mergeObject } = req.body if (!isDev) elasticOnlyLogger.info({ user: req.params?.user, ...mergeObject }, msg) return res.status(204).send() }) app.use(errorHandler({ logger })) return app }