diff --git a/dtp-chat.js b/dtp-chat.js index 5899c5d..b4342f3 100644 --- a/dtp-chat.js +++ b/dtp-chat.js @@ -51,6 +51,16 @@ class SiteWebApp extends SiteRuntime { await this.startExpressJS(); } + async shutdown ( ) { + await super.shutdown(); + + if (this.io) { + await this.io.shutdown(); + } + + return 0; // exitCode + } + async startExpressJS ( ) { const { session: sessionService } = this.services; diff --git a/lib/site-ioserver.js b/lib/site-ioserver.js index a4790e5..447deb4 100644 --- a/lib/site-ioserver.js +++ b/lib/site-ioserver.js @@ -33,6 +33,7 @@ export class SiteIoServer extends SiteCommon { super(dtp); this.dtp = dtp; this.log = new SiteLog(dtp, SiteIoServer); + this.sessions = { }; } async start (httpServer) { @@ -121,8 +122,23 @@ export class SiteIoServer extends SiteCommon { this.log.error('Redis error', { error }); } - async stop ( ) { - this.log.info('stopping Socket.io server'); + async shutdown ( ) { + const { chat: chatService } = this.dtp.services; + this.log.alert('Socket.io server shutting down'); + + for (const socketId in this.sessions) { + const session = this.sessions[socketId]; + + this.log.debug('leaving joined rooms', { size: session.joinedRooms.size }); + for (const roomId of session.joinedRooms) { + const room = await chatService.getRoomById(roomId); + await chatService.chatRoomCheckOut(room, session.user); + } + session.joinedRooms.clear(); + + this.log.debug('closing socket connection', { socketId }); + await session.socket.disconnect(true); + } } async onSocketConnect (socket) { @@ -197,6 +213,7 @@ export class SiteIoServer extends SiteCommon { joinedChannels: new Set(), joinedRooms: new Map(), }; + this.sessions[socket.id] = session; session.onSocketDisconnect = this.onSocketDisconnect.bind(this, session); socket.on('disconnect', session.onSocketDisconnect); @@ -291,6 +308,8 @@ export class SiteIoServer extends SiteCommon { session.socket.off('user-chat', session.onUserChat); delete session.onUserChat; } + + delete this.sessions[session.socket.id]; } async onJoinChannel (session, message) { diff --git a/lib/site-runtime.js b/lib/site-runtime.js index b599cfe..a1ee31d 100644 --- a/lib/site-runtime.js +++ b/lib/site-runtime.js @@ -50,6 +50,34 @@ export class SiteRuntime { await this.loadModels(); await this.loadServices(); + + process.on('unhandledRejection', (error, p) => { + this.log.error('Unhandled rejection', { + error: error, + promise: p, + stack: error.stack + }); + }); + + process.on('warning', (error) => { + this.log.alert('warning', { error }); + }); + + process.once('SIGINT', async ( ) => { + this.log.info('SIGINT received (requesting shutdown)'); + const exitCode = await this.shutdown(); + this.terminate(exitCode); + }); + } + + async shutdown ( ) { + this.log.alert('System runtime shutting down'); + } + + terminate (exitCode = 0) { + process.nextTick(( ) => { + process.exit(exitCode); + }); } async loadConfig ( ) { @@ -218,8 +246,7 @@ export class SiteRuntime { viewModel.DTP_SCRIPT_DEBUG = (process.env.NODE_ENV !== 'production'); viewModel.dtp = this; - const pkg = await import(path.join(this.config.root, 'package.json'), { assert: { type: 'json' } }); // jshint ignore:line - viewModel.pkg = pkg.default; // jshint ignore:line + viewModel.pkg = this.config.pkg; viewModel.dayjs = dayjs; viewModel.numeral = numeral; // viewModel.phoneNumberJS = require('libphonenumber-js');