@@ -111,13 +111,12 @@ class SyncService {
111111 agentVersion : data . agent_version || '' ,
112112 agentStatus : 'online' ,
113113 agentLastSeen : new Date ( ) ,
114- onlineUsers : data . users_count || 0 ,
115114 status : 'online' ,
116115 healthFailures : 0 ,
117116 } ,
118117 } ) ;
119118
120- return { online : true , xrayVersion : data . xray_version , usersCount : data . users_count } ;
119+ return { online : true , xrayVersion : data . xray_version } ;
121120 } catch ( error ) {
122121 logger . warn ( `[Agent] ${ node . name } health check failed: ${ error . message } ` ) ;
123122
@@ -397,8 +396,6 @@ class SyncService {
397396 const nodeRx = nodeTraffic . rx || 0 ;
398397
399398 const userEntries = Object . entries ( users ) ;
400- if ( userEntries . length === 0 && nodeTx === 0 && nodeRx === 0 ) return ;
401-
402399 const bulkOps = [ ] ;
403400 const now = new Date ( ) ;
404401
@@ -425,24 +422,29 @@ class SyncService {
425422 this . _checkUserLimits ( userEntries . map ( ( [ email ] ) => email ) ) . catch ( ( ) => { } ) ;
426423 }
427424
425+ // Online = users with non-zero traffic in the last poll interval.
426+ // Always update (even to 0) so the counter falls back after idle intervals.
427+ const activeUsers = bulkOps . length ;
428+ const nodeUpdate = { $set : { onlineUsers : activeUsers } } ;
428429 if ( nodeTx > 0 || nodeRx > 0 ) {
429- await HyNode . updateOne (
430- { _id : node . _id } ,
431- {
432- $inc : { 'traffic.tx' : nodeTx , 'traffic.rx' : nodeRx } ,
433- $set : { 'traffic.lastUpdate' : now } ,
434- }
435- ) ;
436- logger . info ( `[Agent Stats] ${ node . name } : ${ bulkOps . length } users, node ↑${ ( nodeTx / 1024 / 1024 ) . toFixed ( 1 ) } MB ↓${ ( nodeRx / 1024 / 1024 ) . toFixed ( 1 ) } MB` ) ;
430+ nodeUpdate . $inc = { 'traffic.tx' : nodeTx , 'traffic.rx' : nodeRx } ;
431+ nodeUpdate . $set [ 'traffic.lastUpdate' ] = now ;
432+ }
433+ await HyNode . updateOne ( { _id : node . _id } , nodeUpdate ) ;
434+
435+ if ( nodeTx > 0 || nodeRx > 0 ) {
436+ logger . info ( `[Agent Stats] ${ node . name } : ${ activeUsers } online, node ↑${ ( nodeTx / 1024 / 1024 ) . toFixed ( 1 ) } MB ↓${ ( nodeRx / 1024 / 1024 ) . toFixed ( 1 ) } MB` ) ;
437437 }
438438 } catch ( error ) {
439439 logger . error ( `[Agent Stats] ${ node . name } error: ${ error . message } ` ) ;
440440 }
441441 }
442442
443443 /**
444- * Get online users and health info from Xray node via Agent GET /info.
445- * Also updates xrayVersion and agentStatus in DB.
444+ * Health-check an Xray node via Agent GET /info and refresh metadata
445+ * (xrayVersion, agentVersion, agentStatus, status). Does NOT touch
446+ * `onlineUsers` — that counter is owned by collectXrayTrafficStats,
447+ * which derives it from per-user traffic deltas over the poll interval.
446448 */
447449 async getXrayOnlineUsers ( node ) {
448450 if ( ! ( node . xray ?. agentToken ) ) {
@@ -454,13 +456,10 @@ class SyncService {
454456 const response = await this . _agentRequest ( node , 'GET' , '/info' ) ;
455457 const data = response . data || { } ;
456458
457- const usersCount = data . users_count || 0 ;
458-
459459 const prevNode = await HyNode . findOneAndUpdate (
460460 { _id : node . _id } ,
461461 {
462462 $set : {
463- onlineUsers : usersCount ,
464463 status : 'online' ,
465464 healthFailures : 0 ,
466465 xrayVersion : data . xray_version || '' ,
@@ -475,7 +474,7 @@ class SyncService {
475474 webhook . emit ( webhook . EVENTS . NODE_ONLINE , { nodeId : node . _id , name : node . name } ) ;
476475 }
477476
478- return usersCount ;
477+ return 0 ;
479478 } catch ( error ) {
480479 logger . warn ( `[Agent] ${ node . name } : unavailable - ${ error . message } ` ) ;
481480
0 commit comments