'use strict' ; import 'module-alias/register' ; import { Config , initializeConfiguration } from '@Base/config' ; import http from 'http' ; import https from 'https' ; import { Socket } from 'net' ; import path from 'path' ; import express from 'express' ; import cors from 'cors' ; import fs from 'fs' ; import crypto from 'crypto' ; import glob from 'glob' ; import morgan from 'morgan' ; import { Router , RequestHandler , Request , Response , NextFunction } from 'express' ; import { initAccounts } from '@Entities/Accounts' ; import { initDomains } from '@Entities/Domains' ; import { initPlaces } from '@Entities/Places' ; import { initTokens } from '@Entities/Tokens' ; import { initSessions } from '@Entities/Sessions' ; import { initRequests } from '@Entities/Requests' ; import { initMonitoring } from '@Monitoring/Monitoring' ; import { setupDB } from '@Tools/Db' ; import { IsNotNullOrEmpty } from '@Tools/Misc' ; import { Logger , initLogging , morganOptions } from '@Tools/Logging' ; initializeConfiguration ( ) . catch ( err => { Logger . error ( 'main: failed configuration: ' + err ) ; return ; } ) . then ( ( ) => { initLogging ( ) ; initMonitoring ( ) ; initSessions ( ) ; initTokens ( ) ; initRequests ( ) ; initAccounts ( ) ; initDomains ( ) ; initPlaces ( ) ; return setupDB ( ) ; } ) . catch ( err => { Logger . error ( 'main: failure opening database: ' + err ) ; return ; } ) . then ( ( ) => { const expr = express ( ) ; expr . use ( morgan ( 'dev' , morganOptions ) ) ; expr . use ( cors ( { 'allowedHeaders' : [ 'authorization' , 'content-type' , 'x-vircadia-error-handle' ] , 'credentials' : true } ) ) ; expr . use ( express . json ( { 'strict' : false , 'limit' : Config . server [ 'max-body-size' ] } ) ) ; expr . use ( ( err , req , resp , next ) => { if ( err instanceof SyntaxError ) { if ( 'body' in err ) { Logger . error ( `JSON parseError: have body and trying reparse with terminator` ) ; try { req . body = JSON . parse ( err [ 'body' ] + '}' ) ; next ( ) ; } catch ( ex ) { Logger . error ( `parseError: ${ err [ 'body' ] }` ) ; resp . status ( 400 ) . send ( { 'status' : 'failure' , 'error' : 'JSON parse error' } ) ; } ; } else { Logger . error ( `JSON parseError: no body` ) ; resp . status ( 400 ) . send ( { 'status' : 'failure' , 'error' : 'JSON parse error' } ) ; } ; } else { next ( ) ; } ; } ) ; expr . use ( createAPIRouter ( 'routes-first' ) ) ; expr . use ( createAPIRouter ( 'routes' ) ) ; expr . use ( Config . server [ "static-base" ] ?? '/static' , express . static ( path . join ( __dirname , 'static' ) ) ) ; expr . use ( createAPIRouter ( 'routes-last' ) ) ; let server ; if ( IsNotNullOrEmpty ( Config . server [ "key-file" ] ) && IsNotNullOrEmpty ( Config . server [ "cert-file" ] ) ) { try { const httpsOptions = { key : fs . readFileSync ( Config . server [ "key-file" ] , 'utf8' ) , cert : fs . readFileSync ( Config . server [ "cert-file" ] , 'utf8' ) , secureProtocol : 'SSLv23_server_method' , secureOptions : crypto . constants . SSL_OP_NO_SSLv3 | crypto . constants . SSL_OP_NO_TLSv1 } ; server = https . createServer ( httpsOptions , expr ) ; } catch ( err ) { Logger . error ( `main: exception initializing https: ${ err }` ) ; } ; } else { server = http . createServer ( expr ) ; } ; ManageServer ( server ) ; server . on ( 'listening' , ( ) => { Logger . info ( `Started metaverse-server version ${ Config . server [ "server-version" ] [ 'version-tag' ] }` ) ; } ) . on ( 'error' , ( err ) => { Logger . error ( 'server exception: ' + err . message ) ; } ) . listen ( Config . server [ "listen-port" ] , Config . server [ "listen-host" ] ) ; process . on ( 'SIGTERM' , ( ) => { Logger . info ( 'SIGTERM' ) ; ShutdownServer ( server , 'SIGTERM' ) ; } ) ; process . on ( 'SIGINT' , ( ) => { Logger . info ( 'SIGINT' ) ; ShutdownServer ( server , 'SIGINT' ) ; } ) ; } ) . catch ( err => { Logger . error ( 'main: bad failure: ' + err ) ; } ) ; let nextSockerId = 444 ; const currentConnections = new Map < number , Socket > ( ) ; function ManageServer ( pServer ) { pServer . on ( 'connection' , sock => { const sockId = nextSockerId ++ ; currentConnections . set ( sockId , sock ) ; sock . on ( 'close' , ( ) => { currentConnections . delete ( sockId ) ; } ) ; } ) ; } ; function ShutdownServer ( pServer , pMsg ) { currentConnections . forEach ( ( val , id ) => { val . destroy ( ) ; } ) ; currentConnections . clear ( ) ; pServer . close ( ( ) => { Logger . info ( `${ pMsg }: Stopped metaverse-server version ${ Config . server [ "server-version" ] [ 'version-tag' ] }` ) ; process . exit ( ) ; } ) ; } ; function createAPIRouter ( pBaseDir ) { return glob . sync ( '**/*.js' , { cwd : `${ __dirname }/${ pBaseDir }/` } ) . map ( filename => require ( `./${ pBaseDir }/${ filename }` ) ) . filter ( router => router . hasOwnProperty ( 'router' ) ) . map ( rr => { Logger . debug ( 'createAPIRouter: adding ' + rr . name ?? 'UNKNOWN' ) ; return rr ; } ) . reduce ( ( rootRouter , router ) => rootRouter . use ( router . router ) , Router ( { mergeParams : true } ) ) ; } ;