import * as socketIo from 'socket.io' ; import { SocketIOEvent } from './models/SocketIOEvent' ; import { IMessageModel , Message } from './models/Message' ; import * as jwt from 'jsonwebtoken' ; import * as cookie from 'cookie' ; import config from './config/main' ; import { errorCodes } from './config/errorCodes' ; import { User , IUserModel } from './models/User' ; import { ChatRoom } from './models/ChatRoom' ; import { SocketIOMessageType } from './models/SocketIOMessage' ; import { IMessage } from '../../shared/models/messaging/IMessage' ; import { ISocketIOMessagePost , ISocketIOMessage } from '../../shared/models/messaging/ISocketIOMessage' ; import { BadRequestError , UnauthorizedError , ForbiddenError } from 'routing-controllers' ; import { extractMongoId } from './utilities/ExtractMongoId' ; import * as Raven from 'raven' ; export default class ChatServer { private io : socketIo . Server ; constructor ( server ) { this . io = socketIo ( server , { path : '/chat' } ) ; this . io . use ( ( socket , next ) => { const token = cookie . parse ( socket . handshake . headers . cookie ) . token ; const roomId = socket . handshake . query . room ; jwt . verify ( token , config . secret , async ( err , decoded ) => { const [ user , room ] = await Promise . all ( [ User . findById ( decoded . _id ) . exec ( ) , ChatRoom . findById ( roomId ) . exec ( ) ] ) ; if ( err || ! user || ! room ) { next ( new UnauthorizedError ( ) ) ; return ; } const privileges = await room . checkPrivileges ( user ) ; if ( ! ( privileges . userCanViewMessages && privileges . userCanPostMessages ) ) { next ( new ForbiddenError ( ) ) ; return ; } socket . chatName = await this . obtainChatName ( user , roomId ) ; socket . userId = decoded . _id ; next ( ) ; } ) ; } ) ; } async obtainChatName ( user , roomId ) { const lastMessage = await Message . findOne ( { room : roomId , author : user } ) . sort ( { createdAt : - 1 } ) ; if ( lastMessage ) { return lastMessage . chatName ; } else { return user . role + Date . now ( ) ; } } init ( ) { this . io . on ( SocketIOEvent . CONNECT , ( socket ) => { const userId = socket . userId ; const roomId = socket . handshake . query . room ; const chatName = socket . chatName ; socket . join ( roomId ) ; socket . on ( SocketIOEvent . MESSAGE , ( message ) => this . onMessage ( message , userId , roomId , chatName ) ) ; } ) ; } async onMessage ( socketIOMessagePost , userId , roomId , chatName ) { const message = { _id : undefined , content : socketIOMessagePost . content , author : userId , room : roomId , chatName , comments : [ ] } ; const socketIOMessage = { meta : socketIOMessagePost . meta , message } ; if ( socketIOMessagePost . meta . type === SocketIOMessageType . COMMENT ) { let foundMessage = await Message . findById ( socketIOMessagePost . meta . parent ) ; if ( ! foundMessage ) { process . stdout . write ( errorCodes . chat . parentNotFound . text ) ; Raven . captureException ( new BadRequestError ( errorCodes . chat . parentNotFound . code ) ) ; return ; } if ( extractMongoId ( foundMessage . room ) !== roomId ) { process . stdout . write ( errorCodes . chat . badParent . text ) ; Raven . captureException ( new BadRequestError ( errorCodes . chat . badParent . code ) ) ; return ; } foundMessage . comments . push ( message ) ; foundMessage = await foundMessage . save ( ) ; socketIOMessage . message = ( < IMessageModel > foundMessage . comments . pop ( ) ) . forDisplay ( ) ; this . io . in ( roomId ) . emit ( SocketIOEvent . MESSAGE , socketIOMessage ) ; } else { let newMessage = new Message ( message ) ; newMessage = await newMessage . save ( ) ; socketIOMessage . message = newMessage . forDisplay ( ) ; this . io . in ( roomId ) . emit ( SocketIOEvent . MESSAGE , socketIOMessage ) ; } } }