@@ -33,7 +33,9 @@ export const [latestConnectionMsg, setLatestConnectionMsg] =
3333let lastUploadTotal = 0
3434let lastDownloadTotal = 0
3535
36- // Global effect to monitor data usage - runs always, independent of page
36+ // Global effect to monitor data usage - runs always in background, independent of page
37+ // This effect continuously tracks connection data and updates data usage statistics
38+ // It also detects service restarts by monitoring if total values decrease
3739createEffect ( ( ) => {
3840 const msg = latestConnectionMsg ( )
3941 const rawConns = msg ?. connections
@@ -42,13 +44,15 @@ createEffect(() => {
4244 const currentUploadTotal = msg ?. uploadTotal || 0
4345 const currentDownloadTotal = msg ?. downloadTotal || 0
4446
45- // If totals decreased, service was restarted - reset tracking
47+ // If totals decreased, service was restarted - reset all tracking
48+ // This ensures data usage stats are cleared on service restart, same as TrafficWidget behavior
4649 if (
4750 currentUploadTotal < lastUploadTotal ||
4851 currentDownloadTotal < lastDownloadTotal
4952 ) {
50- // Service restarted, clear connection tracking data
53+ // Service restarted, clear connection tracking data and data usage
5154 resetConnectionTracking ( )
55+ clearDataUsage ( )
5256 }
5357
5458 lastUploadTotal = currentUploadTotal
@@ -63,7 +67,7 @@ createEffect(() => {
6367 const prevConns = allConnections ( )
6468 const activeConns = restructRawMsgToConnection ( rawConns , prevConns )
6569
66- // Update data usage tracking
70+ // Update data usage tracking - accumulates data per source IP
6771 updateDataUsage ( activeConns )
6872
6973 // Cleanup inactive connection tracking data periodically
@@ -233,23 +237,69 @@ export const [dataUsageMap, setDataUsageMap] = makePersisted(
233237 } ,
234238)
235239
240+ // Store baseline totals from backend to calculate incremental changes across browser sessions
241+ export const [ baselineTotals , setBaselineTotals ] = makePersisted (
242+ createSignal < { upload : number ; download : number } > ( {
243+ upload : 0 ,
244+ download : 0 ,
245+ } ) ,
246+ {
247+ name : 'dataUsageBaseline' ,
248+ storage : localStorage ,
249+ } ,
250+ )
251+
236252// Track last known data for each connection to calculate incremental changes
237253const connectionLastData = new Map <
238254 string ,
239255 { upload : number ; download : number }
240256> ( )
241257
258+ // Track if session has been initialized
259+ let hasInitializedSession = false
260+
242261// Reset connection tracking data when service restarts
243262export const resetConnectionTracking = ( ) => {
244263 connectionLastData . clear ( )
245- console . log ( '[Data Usage] Connection tracking reset due to service restart' )
264+ setBaselineTotals ( { upload : 0 , download : 0 } )
265+ hasInitializedSession = false
266+ console . log (
267+ '[Data Usage] Connection tracking reset due to service restart. Data usage map will be cleared.' ,
268+ )
246269}
247270
271+ // Update data usage statistics by tracking incremental changes per connection
272+ // This function is called continuously in the background to accumulate data usage per IP
273+ // The accumulated totals are persisted in localStorage and synced with TrafficWidget behavior
248274export const updateDataUsage = ( connections : Connection [ ] ) => {
249- const updates : Record < string , DataUsageEntry > = { ...dataUsageMap ( ) }
275+ const msg = latestConnectionMsg ( )
276+ const currentGlobalUpload = msg ?. uploadTotal || 0
277+ const currentGlobalDownload = msg ?. downloadTotal || 0
278+
279+ // Initialize session baseline on first run
280+ if ( ! hasInitializedSession ) {
281+ const baseline = baselineTotals ( )
282+ hasInitializedSession = true
283+ console . log (
284+ '[Data Usage] Session initialized. Baseline:' ,
285+ baseline ,
286+ 'Current global:' ,
287+ { upload : currentGlobalUpload , download : currentGlobalDownload } ,
288+ )
289+ }
250290
251- // Group connections by source IP and sum their current data
252- const ipDataMap = new Map < string , { upload : number ; download : number } > ( )
291+ const updates : Record < string , DataUsageEntry > = { ...dataUsageMap ( ) }
292+ const now = Date . now ( )
293+
294+ // Group connections by source IP and use their current cumulative data
295+ const ipDataMap = new Map <
296+ string ,
297+ {
298+ upload : number
299+ download : number
300+ connectionIds : Set < string >
301+ }
302+ > ( )
253303
254304 connections . forEach ( ( conn ) => {
255305 const sourceIP = conn . metadata . sourceIP
@@ -259,78 +309,114 @@ export const updateDataUsage = (connections: Connection[]) => {
259309 const currentUpload = conn . upload || 0
260310 const currentDownload = conn . download || 0
261311
312+ // Track connection IDs per IP
313+ if ( ! ipDataMap . has ( sourceIP ) ) {
314+ ipDataMap . set ( sourceIP , {
315+ upload : 0 ,
316+ download : 0 ,
317+ connectionIds : new Set ( ) ,
318+ } )
319+ }
320+
321+ const ipData = ipDataMap . get ( sourceIP ) !
322+ ipData . connectionIds . add ( conn . id )
323+
262324 // Get last known data for this connection
263325 const lastData = connectionLastData . get ( conn . id )
264326
265- // Calculate incremental change
266- let uploadDelta = 0
267- let downloadDelta = 0
268-
269327 if ( lastData ) {
270- uploadDelta = currentUpload - lastData . upload
271- downloadDelta = currentDownload - lastData . download
328+ // Calculate incremental change from last known state
329+ // Only count positive deltas to avoid negative values on connection resets
330+ const uploadDelta = Math . max ( 0 , currentUpload - lastData . upload )
331+ const downloadDelta = Math . max ( 0 , currentDownload - lastData . download )
332+
333+ ipData . upload += uploadDelta
334+ ipData . download += downloadDelta
272335 } else {
273- // First time seeing this connection, use full amount
274- uploadDelta = currentUpload
275- downloadDelta = currentDownload
336+ // First time seeing this connection, add its current cumulative data
337+ ipData . upload + = currentUpload
338+ ipData . download + = currentDownload
276339 }
277340
278- // Update last known data
341+ // Update last known data for this connection
279342 connectionLastData . set ( conn . id , {
280343 upload : currentUpload ,
281344 download : currentDownload ,
282345 } )
346+ } )
283347
284- // Accumulate data per IP
285- if ( ! ipDataMap . has ( sourceIP ) ) {
286- ipDataMap . set ( sourceIP , {
287- upload : 0 ,
288- download : 0 ,
289- } )
290- }
348+ // Update baseline totals based on current global totals
349+ // This ensures data persists across browser sessions
350+ const totalTracked = Object . values ( updates ) . reduce (
351+ ( acc , entry ) => ( {
352+ upload : acc . upload + entry . upload ,
353+ download : acc . download + entry . download ,
354+ } ) ,
355+ { upload : 0 , download : 0 } ,
356+ )
291357
292- const ipData = ipDataMap . get ( sourceIP ) !
358+ // Add new data from this update cycle
359+ ipDataMap . forEach ( ( data ) => {
360+ totalTracked . upload += data . upload
361+ totalTracked . download += data . download
362+ } )
293363
294- ipData . upload += uploadDelta
295- ipData . download += downloadDelta
364+ // Update baseline to reflect all data we've tracked so far
365+ setBaselineTotals ( {
366+ upload : totalTracked . upload ,
367+ download : totalTracked . download ,
296368 } )
297369
298370 // Update data usage map with accumulated changes
299371 ipDataMap . forEach ( ( data , sourceIP ) => {
300372 const existing = updates [ sourceIP ]
301- const now = Date . now ( )
302373
303374 if ( existing ) {
304- updates [ sourceIP ] = {
305- ...existing ,
306- upload : existing . upload + data . upload ,
307- download : existing . download + data . download ,
308- total :
309- existing . upload + data . upload + existing . download + data . download ,
310- firstSeen : existing . firstSeen || now , // Ensure firstSeen exists
311- lastSeen : now ,
375+ // Only update if there's actual new data
376+ if ( data . upload > 0 || data . download > 0 ) {
377+ updates [ sourceIP ] = {
378+ ...existing ,
379+ upload : existing . upload + data . upload ,
380+ download : existing . download + data . download ,
381+ total :
382+ existing . upload + data . upload + ( existing . download + data . download ) ,
383+ firstSeen : existing . firstSeen || now ,
384+ lastSeen : now ,
385+ }
386+ } else {
387+ // Just update lastSeen to show it's still active
388+ updates [ sourceIP ] = {
389+ ...existing ,
390+ lastSeen : now ,
391+ }
312392 }
313393 } else {
314- updates [ sourceIP ] = {
315- sourceIP,
316- macAddress : '' ,
317- upload : data . upload ,
318- download : data . download ,
319- total : data . upload + data . download ,
320- firstSeen : now ,
321- lastSeen : now ,
394+ // New IP entry
395+ if ( data . upload > 0 || data . download > 0 ) {
396+ updates [ sourceIP ] = {
397+ sourceIP,
398+ macAddress : '' ,
399+ upload : data . upload ,
400+ download : data . download ,
401+ total : data . upload + data . download ,
402+ firstSeen : now ,
403+ lastSeen : now ,
404+ }
322405 }
323406 }
324407 } )
325408
326409 setDataUsageMap ( updates )
327410}
328411
412+ // Clear all data usage statistics and reset connection tracking
413+ // This is called on manual clear or automatic service restart detection
329414export const clearDataUsage = ( ) => {
330415 setDataUsageMap ( { } )
331416 connectionLastData . clear ( )
332417}
333418
419+ // Remove a specific IP entry from data usage tracking
334420export const removeDataUsageEntry = ( sourceIP : string ) => {
335421 setDataUsageMap ( ( prev ) => {
336422 const updates = { ...prev }
@@ -341,6 +427,8 @@ export const removeDataUsageEntry = (sourceIP: string) => {
341427 } )
342428}
343429
430+ // Cleanup tracking data for connections that no longer exist
431+ // This prevents memory leaks from accumulating stale connection data
344432export const cleanupInactiveConnections = ( activeConns ?: Connection [ ] ) => {
345433 const activeConnectionIds = activeConns
346434 ? new Set ( activeConns . map ( ( conn ) => conn . id ) )
0 commit comments