@@ -51,7 +51,9 @@ class HyperStorage {
5151 this . limit = 100000000000 ; //100 gb per session
5252 this . saved = 0 ;
5353 this . beams = [ ] ;
54- this . downloading = new Set ( ) ; // prevents concurrent downloads of same hash
54+ this . downloading = new Set ( ) ;
55+ this . savedFiles = new Set ( ) ;
56+ this . _purgeStarted = false ;
5557 }
5658
5759 async load_drive ( topic ) {
@@ -65,6 +67,10 @@ class HyperStorage {
6567 const drive = new Hyperdrive ( fileStore ) ;
6668 this . add ( drive , topic , fileStore ) ;
6769 await drive . ready ( ) ;
70+ if ( ! this . _purgeStarted ) {
71+ this . _purgeStarted = true ;
72+ this . startPurgeInterval ( ) ;
73+ }
6874 }
6975
7076 loaded ( topic ) {
@@ -86,6 +92,57 @@ class HyperStorage {
8692 }
8793 }
8894
95+ async purgeOldFiles ( ) {
96+ const ONE_WEEK = 7 * 24 * 60 * 60 * 1000 ;
97+ const MAX_STORAGE = 500 * 1024 * 1024 ; // 500 MB
98+ const now = Date . now ( ) ;
99+ let purged = 0 ;
100+ const allEntries = [ ] ;
101+
102+ for ( const room of this . drives ) {
103+ try {
104+ for await ( const entry of room . drive . entries ( ) ) {
105+ const meta = entry . value ?. metadata ;
106+ if ( ! meta ?. hash ) continue ;
107+ const size = parseInt ( meta . size ) || 0 ;
108+ const syncedAt = meta . syncedAt || meta . time || 0 ;
109+ allEntries . push ( { hash : meta . hash , size, syncedAt, drive : room . drive } ) ;
110+ }
111+ } catch ( e ) {
112+ console . log ( '[storage.js] Purge scan error:' , e ) ;
113+ }
114+ }
115+
116+ // Pass 1: purge unsaved files older than 1 week
117+ for ( const entry of allEntries ) {
118+ if ( this . savedFiles . has ( entry . hash ) ) continue ;
119+ if ( now - entry . syncedAt > ONE_WEEK ) {
120+ try { await entry . drive . del ( entry . hash ) ; purged ++ ; entry . deleted = true ; } catch ( e ) { }
121+ }
122+ }
123+
124+ // Pass 2: if still over storage cap, remove oldest unsaved files first
125+ const remaining = allEntries . filter ( e => ! e . deleted ) ;
126+ const totalSize = remaining . reduce ( ( sum , e ) => sum + e . size , 0 ) ;
127+ if ( totalSize > MAX_STORAGE ) {
128+ const purgeable = remaining . filter ( e => ! this . savedFiles . has ( e . hash ) ) ;
129+ purgeable . sort ( ( a , b ) => a . syncedAt - b . syncedAt ) ;
130+ let freed = 0 ;
131+ const excess = totalSize - MAX_STORAGE ;
132+ for ( const entry of purgeable ) {
133+ if ( freed >= excess ) break ;
134+ try { await entry . drive . del ( entry . hash ) ; purged ++ ; freed += entry . size ; } catch ( e ) { }
135+ }
136+ }
137+
138+ if ( purged > 0 ) console . log ( `[storage.js] Purged ${ purged } files` ) ;
139+ }
140+
141+ startPurgeInterval ( ) {
142+ this . purgeOldFiles ( ) ;
143+ setInterval ( ( ) => this . purgeOldFiles ( ) , 60 * 60 * 1000 ) ;
144+ }
145+
89146 async load_files ( topic ) {
90147 const drive = this . get_drive ( topic ) ;
91148 if ( ! drive ) return [ ] ;
@@ -166,8 +223,7 @@ class HyperStorage {
166223 if ( Hugin . files . includes ( meta . hash ) ) return ;
167224 if ( this . downloading . has ( meta . hash ) ) return ;
168225
169- const [ isMedia , fileType ] = check_if_media ( meta . fileName , meta . size ) ;
170- const autoSync = isMedia && ( dm || Hugin . syncImages ) ;
226+ const autoSync = dm || Hugin . syncImages ;
171227
172228 if ( autoSync ) {
173229 console . log ( '[storage.js] Auto-syncing:' , meta . fileName ) ;
@@ -228,18 +284,9 @@ class HyperStorage {
228284 signature : file . signature ,
229285 info : 'file-shared' ,
230286 type : 'file' ,
287+ syncedAt : Date . now ( ) ,
231288 } ,
232289 } ) ;
233- const filePath = uniqueFilePath ( Hugin . downloadDir , file . fileName ) ;
234- let writeOk = false ;
235- try {
236- fs . writeFileSync ( filePath , buf ) ;
237- writeOk = true ;
238- console . log ( '[storage.js] File written to:' , filePath ) ;
239- } catch ( e ) {
240- console . log ( '[storage.js] ERROR writing file to downloadDir:' , filePath , e ) ;
241- }
242- if ( ! writeOk ) { this . downloading . delete ( file . hash ) ; return ; }
243290 Hugin . files . push ( file . hash ) ;
244291 this . downloading . delete ( file . hash ) ;
245292 Hugin . send ( 'download-file-progress' , {
@@ -257,18 +304,34 @@ class HyperStorage {
257304 time : file . time ,
258305 size : file . size ,
259306 topic,
260- filePath,
307+ filePath : 'storage' ,
261308 roomKey,
262309 dm,
263310 } ) ;
264- if ( dm ) this . done ( file , topic , roomKey , dm , filePath ) ;
311+ if ( dm ) this . done ( file , topic , roomKey , dm , 'storage' ) ;
265312 console . log ( 'File saved from peer:' , file . fileName ) ;
266313 } catch ( e ) {
267314 this . downloading . delete ( file . hash ) ;
268315 console . log ( 'Error saving file from peer:' , e ) ;
269316 }
270317 }
271318
319+ async save_to_downloads ( hash , fileName , topic ) {
320+ const room = this . get_room ( topic ) ;
321+ if ( ! room ) return { success : false , error : 'Room not found' } ;
322+ try {
323+ const buf = await room . drive . get ( hash ) ;
324+ if ( ! buf ) return { success : false , error : 'File not found in storage' } ;
325+ const filePath = uniqueFilePath ( Hugin . downloadDir , fileName ) ;
326+ fs . writeFileSync ( filePath , buf ) ;
327+ this . savedFiles . add ( hash ) ;
328+ return { success : true , filePath } ;
329+ } catch ( e ) {
330+ console . log ( '[storage.js] Error saving to downloads:' , e ) ;
331+ return { success : false , error : e . message } ;
332+ }
333+ }
334+
272335 done ( file , topic , room , dm , filePath ) {
273336 if ( ! dm ) return ;
274337 const [ media , fileType ] = check_if_media ( file . fileName , file . size ) ;
0 commit comments