2021-11-14 13:42:06 -05:00
// Generated by CoffeeScript 2.6.1
2011-11-06 12:21:27 -08:00
( function ( ) {
2021-03-16 19:34:11 -04:00
var fs , log , redis , unread _counts ;
2011-11-06 12:21:27 -08:00
fs = require ( 'fs' ) ;
redis = require ( 'redis' ) ;
2013-05-01 12:51:59 -07:00
log = require ( './log.js' ) ;
2021-03-16 19:34:11 -04:00
unread _counts = ( server ) => {
2025-02-27 23:57:28 -08:00
var ENV _DEV , ENV _DOCKER , ENV _PROD , REDIS _PORT , REDIS _SERVER , SECURE , active _connections , io , pub _client , redis _opts , setup _redis _client , sub _client ;
2021-03-19 11:16:29 -04:00
ENV _DEV = process . env . NODE _ENV === 'development' || process . env . NODE _ENV === 'debug' ;
2021-03-16 19:34:11 -04:00
ENV _PROD = process . env . NODE _ENV === 'production' ;
ENV _DOCKER = process . env . NODE _ENV === 'docker' ;
2021-03-16 20:00:55 -04:00
REDIS _SERVER = "db_redis" ;
2021-03-16 19:34:11 -04:00
if ( ENV _DEV ) {
REDIS _SERVER = 'localhost' ;
} else if ( ENV _PROD ) {
2021-04-02 18:43:11 -04:00
REDIS _SERVER = 'db-redis-pubsub.service.nyc1.consul' ;
2021-03-16 19:34:11 -04:00
}
SECURE = ! ! process . env . NODE _SSL ;
2024-02-25 14:24:58 -05:00
REDIS _PORT = ENV _DOCKER ? 6579 : 6383 ;
2021-03-16 19:34:11 -04:00
// client = redis.createClient 6379, REDIS_SERVER
// RedisStore = require 'socket.io/lib/stores/redis'
// rpub = redis.createClient 6379, REDIS_SERVER
// rsub = redis.createClient 6379, REDIS_SERVER
// rclient = redis.createClient 6379, REDIS_SERVER
log . debug ( "Starting NewsBlur unread count server..." ) ;
if ( ! ENV _DEV && ! process . env . NODE _ENV ) {
log . debug ( "Specify NODE_ENV=<development,production>" ) ;
return ;
} else if ( ENV _DEV ) {
log . debug ( "Running as development server" ) ;
2021-03-16 20:07:46 -04:00
} else if ( ENV _DOCKER ) {
log . debug ( "Running as docker server" ) ;
2021-03-16 19:34:11 -04:00
} else {
log . debug ( "Running as production server" ) ;
}
2025-02-02 22:56:57 -08:00
2025-02-27 22:54:41 -08:00
// Create Redis clients for Socket.IO adapter with improved configuration
redis _opts = {
host : REDIS _SERVER ,
port : REDIS _PORT ,
retry _strategy : function ( options ) {
// Exponential backoff with a cap
return Math . min ( options . attempt * 100 , 3000 ) ;
} ,
connect _timeout : 10000
} ;
pub _client = redis . createClient ( redis _opts ) ;
sub _client = redis . createClient ( redis _opts ) ;
// Handle Redis adapter client errors
pub _client . on ( "error" , function ( err ) {
return log . debug ( ` Redis Pub Error: ${ err } ` ) ;
} ) ;
sub _client . on ( "error" , function ( err ) {
return log . debug ( ` Redis Sub Error: ${ err } ` ) ;
} ) ;
pub _client . on ( "reconnecting" , function ( attempt ) {
return log . debug ( ` Redis Pub reconnecting... Attempt ${ attempt } ` ) ;
} ) ;
sub _client . on ( "reconnecting" , function ( attempt ) {
return log . debug ( ` Redis Sub reconnecting... Attempt ${ attempt } ` ) ;
} ) ;
2021-03-16 19:34:11 -04:00
io = require ( 'socket.io' ) ( server , {
2025-02-02 22:56:57 -08:00
path : "/v3/socket.io" ,
pingTimeout : 60000 , // Increase ping timeout to 60 seconds
pingInterval : 25000 , // Send ping every 25 seconds
connectTimeout : 45000 , // Connection timeout
transports : [ 'websocket' ] , // Prefer websocket transport
adapter : require ( '@socket.io/redis-adapter' ) . createAdapter ( pub _client , sub _client )
2016-11-30 15:26:42 -08:00
} ) ;
2025-02-02 22:56:57 -08:00
// Setup Redis error handling and reconnection
setup _redis _client = function ( socket , username ) {
var client ;
client = redis . createClient ( {
host : REDIS _SERVER ,
port : REDIS _PORT ,
retry _strategy : function ( options ) {
return Math . min ( options . attempt * 100 , 3000 ) ;
2025-02-27 22:54:41 -08:00
} ,
connect _timeout : 10000
2025-02-02 22:56:57 -08:00
} ) ;
client . on ( "error" , ( err ) => {
return log . info ( username , ` Redis Error: ${ err } ` ) ;
} ) ;
// Don't quit on error, let retry strategy handle it
client . on ( "reconnecting" , ( attempt ) => {
return log . info ( username , ` Redis reconnecting... Attempt ${ attempt } ` ) ;
} ) ;
return client ;
} ;
2025-02-27 23:57:28 -08:00
// Track active connections by username for debugging
active _connections = { } ;
// Log engine events for debugging
io . engine . on ( 'connection' , function ( socket ) {
return log . debug ( ` Engine connection established: ${ socket . id } ` ) ;
} ) ;
io . engine . on ( 'close' , function ( socket ) {
return log . debug ( ` Engine connection closed: ${ socket . id } ` ) ;
} ) ;
2021-03-16 19:34:11 -04:00
io . on ( 'connection' , function ( socket ) {
2025-02-27 23:57:28 -08:00
var ip , socket _id ;
2021-03-16 19:34:11 -04:00
ip = socket . handshake . headers [ 'X-Forwarded-For' ] || socket . handshake . address ;
2025-02-27 23:57:28 -08:00
socket _id = socket . id ;
log . debug ( ` Socket connected: ${ socket _id } from ${ ip } ` ) ;
socket . conn . on ( 'error' , function ( err ) {
return log . debug ( ` Socket ${ socket _id } - connection error: ${ err } ` ) ;
} ) ;
socket . conn . on ( 'close' , function ( reason ) {
return log . debug ( ` Socket ${ socket _id } - connection closed: ${ reason } ` ) ;
} ) ;
2025-02-02 22:56:57 -08:00
socket . on ( 'subscribe:feeds' , ( feeds , username1 ) => {
2021-03-16 19:34:11 -04:00
var ref ;
this . feeds = feeds ;
2025-02-02 22:56:57 -08:00
this . username = username1 ;
2025-02-27 23:57:28 -08:00
log . info ( this . username , ` Connecting ( ${ this . feeds . length } feeds, ${ ip } ), ( ${ io . engine . clientsCount } connected) ${ SECURE ? "(SSL)" : "" } ` ) ;
// Track connections by username for debugging
active _connections [ this . username ] = active _connections [ this . username ] || [ ] ;
active _connections [ this . username ] . push ( socket _id ) ;
2025-02-28 00:13:34 -08:00
log . debug ( ` ${ this . username } now has ${ active _connections [ this . username ] . length } active connections, adding ${ socket _id } ` ) ;
2021-03-16 19:34:11 -04:00
if ( ! this . username ) {
return ;
}
socket . on ( "error" , function ( err ) {
return log . debug ( ` Error (socket): ${ err } ` ) ;
} ) ;
2021-04-02 18:47:36 -04:00
if ( ( ref = socket . subscribe ) != null ) {
2021-03-16 19:34:11 -04:00
ref . quit ( ) ;
}
2025-02-02 22:56:57 -08:00
socket . subscribe = setup _redis _client ( socket , this . username ) ;
2021-04-02 18:47:36 -04:00
socket . subscribe . on ( "connect" , ( ) => {
2021-03-16 19:34:11 -04:00
var feeds _story ;
2025-02-27 23:57:28 -08:00
log . info ( this . username , ` Connected ( ${ this . feeds . length } feeds, ${ ip } ), ( ${ io . engine . clientsCount } connected) ${ SECURE ? "(SSL)" : "(non-SSL)" } ` ) ;
2021-04-02 18:47:36 -04:00
socket . subscribe . subscribe ( this . feeds ) ;
2021-03-16 19:34:11 -04:00
feeds _story = this . feeds . map ( function ( f ) {
return ` ${ f } :story ` ;
} ) ;
2021-04-02 18:47:36 -04:00
socket . subscribe . subscribe ( feeds _story ) ;
return socket . subscribe . subscribe ( this . username ) ;
2021-03-16 19:34:11 -04:00
} ) ;
2021-04-02 18:47:36 -04:00
return socket . subscribe . on ( 'message' , ( channel , message ) => {
2021-03-16 19:34:11 -04:00
var event _name ;
event _name = 'feed:update' ;
if ( channel === this . username ) {
event _name = 'user:update' ;
} else if ( channel . indexOf ( ':story' ) >= 0 ) {
event _name = 'feed:story:new' ;
}
log . info ( this . username , ` Update on ${ channel } : ${ event _name } - ${ message } ` ) ;
return socket . emit ( event _name , channel , message ) ;
2020-12-17 14:01:19 -05:00
} ) ;
} ) ;
2025-02-27 23:57:28 -08:00
return socket . on ( 'disconnect' , ( reason ) => {
var idx , ref , ref1 ;
2025-02-28 00:13:34 -08:00
log . debug ( ` Socket ${ socket _id } disconnected ${ this . username } : ${ reason } ` ) ;
2025-02-27 23:57:28 -08:00
// Update connection tracking
if ( this . username && active _connections [ this . username ] ) {
idx = active _connections [ this . username ] . indexOf ( socket _id ) ;
if ( idx > - 1 ) {
active _connections [ this . username ] . splice ( idx , 1 ) ;
log . debug ( ` ${ this . username } now has ${ active _connections [ this . username ] . length } active connections ` ) ;
2025-02-28 00:13:34 -08:00
} else {
log . debug ( ` Socket ${ socket _id } not found in active connections for ${ this . username } ` ) ;
2025-02-27 23:57:28 -08:00
}
if ( active _connections [ this . username ] . length === 0 ) {
delete active _connections [ this . username ] ;
}
}
2021-04-02 18:47:36 -04:00
if ( ( ref = socket . subscribe ) != null ) {
2021-03-16 19:34:11 -04:00
ref . quit ( ) ;
2020-12-17 14:01:19 -05:00
}
2025-02-27 23:57:28 -08:00
return log . info ( this . username , ` Disconnect ( ${ ( ref1 = this . feeds ) != null ? ref1 . length : void 0 } feeds, ${ ip } ), there are now ${ io . engine . clientsCount } users. ${ SECURE ? "(SSL)" : "(non-SSL)" } ` ) ;
2020-12-17 14:01:19 -05:00
} ) ;
2011-11-06 12:21:27 -08:00
} ) ;
2025-02-27 22:54:41 -08:00
io . engine . on ( 'connection_error' , function ( err ) {
return log . debug ( ` Connection Error: ${ err . code } - ${ err . message } ` ) ;
} ) ;
2025-02-02 23:07:00 -08:00
io . sockets . on ( 'error' , function ( err ) {
2021-03-16 19:34:11 -04:00
return log . debug ( ` Error (sockets): ${ err } ` ) ;
2011-11-06 12:21:27 -08:00
} ) ;
2025-02-27 23:57:28 -08:00
// Periodically log connection stats
setInterval ( function ( ) {
var total _connections , total _users ;
total _users = Object . keys ( active _connections ) . length ;
total _connections = io . engine . clientsCount ;
return log . debug ( ` Connection stats: ${ total _users } users with ${ total _connections } total connections ` ) ;
} , 60000 ) ;
2025-02-02 23:07:00 -08:00
return io ;
2021-03-16 19:34:11 -04:00
} ;
2011-11-06 12:21:27 -08:00
2021-03-16 19:34:11 -04:00
exports . unread _counts = unread _counts ;
2013-08-12 11:52:29 -07:00
2011-11-06 12:21:27 -08:00
} ) . call ( this ) ;