declare var require ; import { DriverInterface } from '../types/driver' ; import { OptionsInterface } from '../types/options' ; import { RealTimeLog } from '../logger' ; import * as server from 'socket.io' ; import * as client from 'socket.io-client' ; import * as _ from 'underscore' ; export class IODriver implements DriverInterface { client : any ; server : any ; internal : any ; options : OptionsInterface ; connections : Array < any > = new Array ( ) ; private customAuthListener : Function ; method param { OptionsInterface } options description connect ( options ? ) { this . options = options ; this . server = server ( options . server , { transports : options . driver . options . transports } ) ; this . onConnection ( ( socket ) => this . newConnection ( socket ) ) ; this . setupClustering ( ) ; this . setupAuthResolver ( ) ; this . setupAuthentication ( ) ; this . setupClient ( ) ; this . setupInternal ( ) ; this . options . app . emit ( 'fire-connection-started' ) ; } setupAuthResolver ( ) { if ( this . options . auth ) { RealTimeLog . log ( 'RTC requesting custom resolvers' ) ; this . options . app . on ( 'fire-auth-resolver' , ( authResolver ) => { if ( ! authResolver || ! authResolver . name || ! authResolver . handler ) { throw new Error ( 'FireLoop: Custom auth resolver does not provide either name or handler' ) ; } this . server . on ( 'connection' , ( socket ) => { socket . on ( authResolver . name , ( payload ) => authResolver . handler ( socket , payload , ( token ) => { if ( token ) { this . restoreNameSpaces ( socket ) ; socket . token = token ; socket . emit ( 'authenticated' ) ; } } ) ) ; } ) ; } ) ; } } method description setupClustering ( ) { if ( this . options . driver . options . adapter && this . options . driver . options . adapter . name && this . options . driver . options . adapter . datasource && this . options . app . datasources [ this . options . driver . options . adapter . datasource ] && this . options . app . datasources [ this . options . driver . options . adapter . datasource ] . settings ) { let adapter = require ( this . options . driver . options . adapter . name ) ; let ds = this . options . app . datasources [ this . options . driver . options . adapter . datasource ] ; if ( ds . settings . url ) { RealTimeLog . log ( 'Running in clustering environment' ) ; this . server . adapter ( adapter ( ds . settings . url ) ) ; } else if ( ds . settings . host && ds . settings . port && ds . settings . db ) { let adapterOptions = { host : ds . settings . host , port : ds . settings . port , db : ds . settings . db } ; if ( ds . settings . user ) adapterOptions . user = ds . settings . user ; if ( ds . settings . password ) adapterOptions . password = ds . settings . password ; RealTimeLog . log ( 'Running in clustering environment' ) ; this . server . adapter ( adapter ( adapterOptions ) ) ; } else { throw new Error ( 'loopback-realtime-component: Unexpected datasource options for clustering mode.' ) ; } } else { RealTimeLog . log ( 'Running in a not clustered environment' ) ; } } method description setupAuthentication ( ) { if ( this . options . auth ) { RealTimeLog . log ( 'RTC authentication mechanism enabled' ) ; _ . each ( this . server . nsps , ( nsp ) => { nsp . on ( 'connect' , ( socket ) => { if ( ! socket . token ) { delete nsp . connected [ socket . id ] ; } } ) ; } ) ; this . server . on ( 'connection' , ( socket ) => { /**
                 * Register Built in Auth Resolver
                 */ socket . on ( 'authentication' , ( token ) => { if ( ! token ) { return ; } if ( token . is === '-*!#fl1nter#!*-' ) { RealTimeLog . log ( 'Internal connection has been established' ) ; this . restoreNameSpaces ( socket ) ; socket . token = token ; return socket . emit ( 'authenticated' ) ; } var AccessToken = this . options . custom && this . options . custom . AccessToken ? this . options . app . models [ this . options . custom . AccessToken ] : this . options . app . models . AccessToken ; var token = AccessToken . findOne ( { where : { id : token . id || 0 } } , ( err , tokenInstance ) => { if ( tokenInstance ) { this . restoreNameSpaces ( socket ) ; socket . token = tokenInstance ; socket . emit ( 'authenticated' ) ; } } ) ; } ) ; /**
                 * Wait 1 second for token to be available
                 * Or disconnect
                 **/ const to = setTimeout ( ( ) => { if ( ! socket . token ) { socket . emit ( 'unauthorized' ) ; socket . disconnect ( 1 ) ; } clearTimeout ( to ) ; } , 3000 ) ; } ) ; } } method description setupClient ( ) { this . client = client ( `http${ this . options . secure ? 's' : '' }://127.0.0.1:${ this . options . app . get ( 'port' ) }` , { transports : [ 'websocket' ] , secure : this . options . secure } ) ; this . client . on ( 'connect' , ( ) => { if ( this . options . auth ) { RealTimeLog . log ( 'Server side client is connected, trying to authenticate' ) ; this . client . emit ( 'authentication' , { is : '-*!#fl1nter#!*-' } ) ; this . client . on ( 'authenticated' , ( ) => RealTimeLog . log ( 'Internal server client is authenticated' ) ) ; } } ) ; } method description setupInternal ( ) { this . internal = client ( `http${ this . options . secure ? 's' : '' }://127.0.0.1:${ this . options . app . get ( 'port' ) }` , { transports : [ 'websocket' ] , secure : this . options . secure } ) ; this . internal . on ( 'connect' , ( ) => { if ( this . options . auth ) { RealTimeLog . log ( 'Internal client is connected, trying to authenticate' ) ; this . internal . emit ( 'authentication' , { is : '-*!#fl1nter#!*-' } ) ; this . internal . on ( 'authenticated' , ( ) => { RealTimeLog . log ( 'Internal client is authenticated' ) ; this . internal . emit ( 'fl-reg' ) ; } ) ; } else { this . internal . emit ( 'fl-reg' ) ; } } ) ; } emit ( event , message ) { this . server . emit ( event , message ) ; } on ( event , callback ) { this . client . on ( event , callback ) ; } once ( event , callback ) { this . client . once ( event , callback ) ; } of ( event ) { return this . server . of ( event ) ; } getUserConnection ( userId ) { if ( ! userId || userId === '' ) return null ; let connection ; this . forEachConnection ( ( _connection ) => { if ( _connection . token && _connection . token . userId === userId ) { connection = _connection ; } } ) ; return connection ; } forEachConnection ( handler ) { this . connections . forEach ( ( connection ) => handler ( connection ) ) ; } onConnection ( handler ) { this . server . on ( 'connect' , ( socket ) => handler ( socket , this . server ) ) ; } removeListener ( name , listener ) { this . server . sockets . removeListener ( name , listener ) ; } newConnection ( socket ) { this . connections . push ( socket ) ; socket . setMaxListeners ( 0 ) ; socket . on ( 'ME:RT:1://event' , ( input ) => { this . server . emit ( input . event , input . data ) ; } ) ; socket . on ( 'disconnect' , ( ) => { this . options . app . emit ( 'socket-disconnect' , socket ) ; socket . removeAllListeners ( ) ; } ) ; socket . on ( 'lb-ping' , ( ) => socket . emit ( 'lb-pong' , new Date ( ) . getTime ( ) / 1000 ) ) ; socket . on ( 'fl-reg' , ( ) => socket . join ( 'flint' ) ) ; } restoreNameSpaces ( socket ) { _ . each ( this . server . nsps , ( nsp ) => { if ( _ . findWhere ( nsp . sockets , { id : socket . id } ) ) { nsp . connected [ socket . id ] = socket ; } } ) ; } }