@xrengine/server-core
Version:
Shared components for XREngine server
388 lines (358 loc) • 11.7 kB
text/typescript
import appRootPath from 'app-root-path'
import * as chargebeeInst from 'chargebee'
import dotenv from 'dotenv-flow'
import path from 'path'
import url from 'url'
import multiLogger from './ServerLogger'
const { register } = require('trace-unhandled')
register()
const logger = multiLogger.child({ component: 'server-core:config' })
const kubernetesEnabled = process.env.KUBERNETES === 'true'
const testEnabled = process.env.TEST === 'true'
// ensure process fails properly
process.on('exit', async (code) => {
const message = `Server EXIT(${code}).`
if (code === 0) {
logger.info(message)
} else {
logger.fatal(message)
}
})
process.on('SIGTERM', async (err) => {
logger.warn(err, 'Server SIGTERM.')
process.exit(1)
})
process.on('SIGINT', () => {
logger.warn('RECEIVED SIGINT.')
process.exit(1)
})
// emitted when an uncaught JavaScript exception bubbles
process.on('uncaughtException', (err) => {
logger.fatal(err, 'UNCAUGHT EXCEPTION.')
process.exit(1)
})
//emitted whenever a Promise is rejected and no error handler is attached to it
process.on('unhandledRejection', (reason, p) => {
logger.fatal({ reason, promise: p }, 'UNHANDLED PROMISE REJECTION.')
process.exit(1)
})
if (process.env.APP_ENV === 'development' || process.env.LOCAL === 'true') {
// Avoids DEPTH_ZERO_SELF_SIGNED_CERT error for self-signed certs - needed for local storage provider
process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0'
var fs = require('fs')
if (!fs.existsSync(appRootPath.path + '/.env') && !fs.existsSync(appRootPath.path + '/.env.local')) {
var fromEnvPath = appRootPath.path + '/.env.local.default'
var toEnvPath = appRootPath.path + '/.env.local'
fs.copyFileSync(fromEnvPath, toEnvPath, fs.constants.COPYFILE_EXCL)
}
}
if (!kubernetesEnabled) {
dotenv.config({
path: appRootPath.path,
silent: true
})
}
/**
* Database
*/
export const db = {
username: testEnabled ? process.env.MYSQL_TEST_USER! : process.env.MYSQL_USER!,
password: testEnabled ? process.env.MYSQL_TEST_PASSWORD! : process.env.MYSQL_PASSWORD!,
database: testEnabled ? process.env.MYSQL_TEST_DATABASE! : process.env.MYSQL_DATABASE!,
host: testEnabled ? process.env.MYSQL_TEST_HOST! : process.env.MYSQL_HOST!,
port: testEnabled ? process.env.MYSQL_TEST_PORT! : process.env.MYSQL_PORT!,
dialect: 'mysql',
forceRefresh: process.env.FORCE_DB_REFRESH === 'true',
url: '',
charset: 'utf8mb4',
collate: 'utf8mb4_general_ci',
pool: {
max: parseInt(process.env.SEQUELIZE_POOL_MAX || '5')
}
}
db.url =
(testEnabled ? process.env.MYSQL_TEST_URL : process.env.MYSQL_URL) ||
`mysql://${db.username}:${db.password}@${db.host}:${db.port}/${db.database}`
/**
* Server / backend
*/
const server = {
mode: process.env.SERVER_MODE!,
hostname: process.env.SERVER_HOST!,
port: process.env.SERVER_PORT!,
clientHost: process.env.APP_HOST!,
// Public directory (used for favicon.ico, logo, etc)
rootDir:
process.env.BUILD_MODE! === 'individual'
? path.resolve(appRootPath.path)
: path.resolve(appRootPath.path, 'packages', 'server'),
publicDir:
process.env.SERVER_PUBLIC_DIR! ||
(process.env.BUILD_MODE === 'individual'
? path.resolve(appRootPath.path, 'public')
: path.resolve(appRootPath.path, 'packages', 'server', 'public')),
nodeModulesDir: path.resolve(__dirname, '../..', 'node_modules'),
localStorageProvider: process.env.LOCAL_STORAGE_PROVIDER!,
localStorageProviderPort: process.env.LOCAL_STORAGE_PROVIDER_PORT!,
corsServerPort: process.env.CORS_SERVER_PORT!,
storageProvider: process.env.STORAGE_PROVIDER!,
gaTrackingId: process.env.GOOGLE_ANALYTICS_TRACKING_ID!,
hub: {
endpoint: process.env.HUB_ENDPOINT!
},
paginate: {
default: 10,
max: 100
},
url: '',
certPath: appRootPath.path.toString() + '/' + process.env.CERT,
keyPath: appRootPath.path.toString() + '/' + process.env.KEY,
gitPem: '',
local: process.env.LOCAL === 'true',
releaseName: process.env.RELEASE_NAME || 'local',
matchmakerEmulationMode: process.env.MATCHMAKER_EMULATION_MODE === 'true',
instanceserverUnreachableTimeoutSeconds: process.env.INSTANCESERVER_UNREACHABLE_TIMEOUT_SECONDS
? parseInt(process.env.INSTANCESERVER_UNREACHABLE_TIMEOUT_SECONDS)
: 10
}
const obj = kubernetesEnabled ? { protocol: 'https', hostname: server.hostname } : { protocol: 'https', ...server }
server.url = process.env.SERVER_URL || url.format(obj)
/**
* Client / frontend
*/
const client = {
logo: process.env.APP_LOGO!,
title: process.env.APP_TITLE!,
get dist() {
if (process.env.SERVE_CLIENT_FROM_STORAGE_PROVIDER === 'true') {
if (process.env.STORAGE_PROVIDER === 'aws' && process.env.STORAGE_CLOUDFRONT_DOMAIN) {
return `https://${process.env.STORAGE_CLOUDFRONT_DOMAIN}/client/`
} else if (process.env.STORAGE_PROVIDER === 'local') {
return `https://${process.env.LOCAL_STORAGE_PROVIDER}/client/`
}
}
return client.url
},
url:
process.env.APP_URL ||
(process.env.VITE_LOCAL_BUILD
? 'http://' + process.env.APP_HOST + ':' + process.env.APP_PORT
: 'https://' + process.env.APP_HOST + ':' + process.env.APP_PORT),
releaseName: process.env.RELEASE_NAME || 'local'
}
// TODO: rename to 'instanceserver'
const instanceserver = {
clientHost: process.env.APP_HOST!,
hostname: process.env.INSTANCESERVER_HOST,
rtc_start_port: parseInt(process.env.RTC_START_PORT!),
rtc_end_port: parseInt(process.env.RTC_END_PORT!),
rtc_port_block_size: parseInt(process.env.RTC_PORT_BLOCK_SIZE!),
identifierDigits: 5,
local: process.env.LOCAL === 'true',
domain: process.env.INSTANCESERVER_DOMAIN || 'instanceserver.etherealengine.com',
releaseName: process.env.RELEASE_NAME || 'local',
port: process.env.INSTANCESERVER_PORT!,
locationName: process.env.PRELOAD_LOCATION_NAME!,
shutdownDelayMs: parseInt(process.env.INSTANCESERVER_SHUTDOWN_DELAY_MS!) || 0
}
/**
* Task server generator
*/
const taskserver = {
port: process.env.TASKSERVER_PORT!,
processInterval: process.env.TASKSERVER_PROCESS_INTERVAL_SECONDS!
}
/**
* Email / SMTP
*/
const email = {
smtp: {
host: process.env.SMTP_HOST!,
port: parseInt(process.env.SMTP_PORT!),
secure: process.env.SMTP_SECURE === 'true',
auth: {
user: process.env.SMTP_USER!,
pass: process.env.SMTP_PASS!
}
},
// Name and email of default sender (for login emails, etc)
from: `${process.env.SMTP_FROM_NAME}` + ` <${process.env.SMTP_FROM_EMAIL}>`,
subject: {
// Subject of the Login Link email
'new-user': 'Signup',
location: 'Location invitation',
instance: 'Location invitation',
login: 'Login link',
friend: 'Friend request',
group: 'Group invitation',
party: 'Party invitation'
},
smsNameCharacterLimit: 20
}
/**
* Authentication
*/
const authentication = {
service: 'identity-provider',
entity: 'identity-provider',
secret: process.env.AUTH_SECRET!,
authStrategies: ['jwt', 'local', 'discord', 'facebook', 'github', 'google', 'linkedin', 'twitter', 'didWallet'],
local: {
usernameField: 'email',
passwordField: 'password'
},
jwtOptions: {
expiresIn: '30 days'
},
bearerToken: {
numBytes: 16
},
callback: {
discord: process.env.DISCORD_CALLBACK_URL || `${client.url}/auth/oauth/discord`,
facebook: process.env.FACEBOOK_CALLBACK_URL || `${client.url}/auth/oauth/facebook`,
github: process.env.GITHUB_CALLBACK_URL || `${client.url}/auth/oauth/github`,
google: process.env.GOOGLE_CALLBACK_URL || `${client.url}/auth/oauth/google`,
linkedin: process.env.LINKEDIN_CALLBACK_URL || `${client.url}/auth/oauth/linkedin`,
twitter: process.env.TWITTER_CALLBACK_URL || `${client.url}/auth/oauth/twitter`
// didWallet does not have a callback endpoint
},
oauth: {
defaults: {
host:
server.hostname !== '127.0.0.1' && server.hostname !== 'localhost'
? server.hostname
: server.hostname + ':' + server.port,
protocol: 'https'
},
discord: {
key: process.env.DISCORD_CLIENT_ID!,
secret: process.env.DISCORD_CLIENT_SECRET!,
scope: ['identify', 'email'],
custom_params: {
prompt: 'none'
}
},
facebook: {
key: process.env.FACEBOOK_CLIENT_ID!,
secret: process.env.FACEBOOK_CLIENT_SECRET!
},
github: {
appid: process.env.GITHUB_APP_ID!,
key: process.env.GITHUB_CLIENT_ID!,
secret: process.env.GITHUB_CLIENT_SECRET!,
scope: ['repo', 'user']
},
google: {
key: process.env.GOOGLE_CLIENT_ID!,
secret: process.env.GOOGLE_CLIENT_SECRET!,
scope: ['profile', 'email']
},
linkedin: {
key: process.env.LINKEDIN_CLIENT_ID!,
secret: process.env.LINKEDIN_CLIENT_SECRET!,
scope: ['r_liteprofile', 'r_emailaddress']
},
twitter: {
key: process.env.TWITTER_CLIENT_ID!,
secret: process.env.TWITTER_CLIENT_SECRET!
}
}
}
/**
* AWS
*/
const aws = {
keys: {
accessKeyId: process.env.STORAGE_AWS_ACCESS_KEY_ID!,
secretAccessKey: process.env.STORAGE_AWS_ACCESS_KEY_SECRET!
},
route53: {
hostedZoneId: process.env.ROUTE53_HOSTED_ZONE_ID!,
keys: {
accessKeyId: process.env.ROUTE53_ACCESS_KEY_ID!,
secretAccessKey: process.env.ROUTE53_ACCESS_KEY_SECRET!
}
},
s3: {
baseUrl: 'https://s3.amazonaws.com',
endpoint: process.env.STORAGE_S3_ENDPOINT!,
staticResourceBucket: process.env.STORAGE_S3_STATIC_RESOURCE_BUCKET!,
region: process.env.STORAGE_S3_REGION!,
avatarDir: process.env.STORAGE_S3_AVATAR_DIRECTORY!,
s3DevMode: process.env.STORAGE_S3_DEV_MODE!
},
cloudfront: {
domain: process.env.STORAGE_CLOUDFRONT_DOMAIN!,
distributionId: process.env.STORAGE_CLOUDFRONT_DISTRIBUTION_ID!,
region: process.env.STORAGE_CLOUDFRONT_REGION || process.env.STORAGE_S3_REGION
},
sms: {
accessKeyId: process.env.AWS_SMS_ACCESS_KEY_ID!,
applicationId: process.env.AWS_SMS_APPLICATION_ID!,
region: process.env.AWS_SMS_REGION!,
senderId: process.env.AWS_SMS_SENDER_ID!,
secretAccessKey: process.env.AWS_SMS_SECRET_ACCESS_KEY!
}
}
const chargebee = {
url: process.env.CHARGEBEE_SITE + '.chargebee.com' || 'dummy.not-chargebee.com',
apiKey: process.env.CHARGEBEE_API_KEY!
}
const coil = {
paymentPointer: process.env.COIL_PAYMENT_POINTER,
clientId: process.env.COIL_API_CLIENT_ID,
clientSecret: process.env.COIL_API_CLIENT_SECRET
}
const redis = {
enabled: process.env.REDIS_ENABLED === 'true',
address: process.env.REDIS_ADDRESS!,
port: process.env.REDIS_PORT!,
password: process.env.REDIS_PASSWORD == '' || process.env.REDIS_PASSWORD == null ? null! : process.env.REDIS_PASSWORD!
}
/**
* Scope
*/
const scopes = {
guest: process.env.DEFAULT_GUEST_SCOPES?.split(',') || [],
user: process.env.DEFAULT_USER_SCOPES?.split(',') || []
}
const blockchain = {
blockchainUrl: process.env.BLOCKCHAIN_URL,
blockchainUrlSecret: process.env.BLOCKCHAIN_URL_SECRET
}
const ipfs = {
enabled: process.env.USE_IPFS
}
/**
* Full config
*/
const config = {
deployStage: process.env.DEPLOY_STAGE!,
authentication,
aws,
chargebee,
client,
coil,
db,
email,
instanceserver,
ipfs,
server,
taskserver,
redis,
scopes,
blockchain,
kubernetes: {
enabled: kubernetesEnabled,
serviceHost: process.env.KUBERNETES_SERVICE_HOST!,
tcpPort: process.env.KUBERNETES_PORT_443_TCP_PORT!
},
noSSL: process.env.NOSSL === 'true',
localBuild: process.env.VITE_LOCAL_BUILD === 'true',
testEnabled
}
chargebeeInst.configure({
site: process.env.CHARGEBEE_SITE!,
api_key: config.chargebee.apiKey
})
export default config