@@ -218,6 +218,7 @@ export class SubdomainStore extends PuterStore {
218218 } ;
219219 await this . #refreshCache( row ) ;
220220 await this . #invalidatePrefixListsForUser( userId ) ;
221+ await this . #invalidateRootDirEntry( row . root_dir_id ) ;
221222
222223 return row ;
223224 }
@@ -268,6 +269,15 @@ export class SubdomainStore extends PuterStore {
268269 for ( const uid of affectedUsers ) {
269270 await this . #invalidatePrefixListsForUser( uid ) ;
270271 }
272+ // FSEntry rows embed a `subdomains_agg` JSON of associated subdomains,
273+ // so any rename / root_dir reassignment must drop the stale entry
274+ // caches on both the old and new root_dir_id.
275+ const affectedRootDirIds = new Set (
276+ [ before ?. root_dir_id , after ?. root_dir_id ] . filter ( ( v ) => v != null ) ,
277+ ) ;
278+ for ( const id of affectedRootDirIds ) {
279+ await this . #invalidateRootDirEntry( id ) ;
280+ }
271281 return after ;
272282 }
273283
@@ -293,6 +303,7 @@ export class SubdomainStore extends PuterStore {
293303 if ( row . user_id != null ) {
294304 await this . #invalidatePrefixListsForUser( row . user_id ) ;
295305 }
306+ await this . #invalidateRootDirEntry( row . root_dir_id ) ;
296307 }
297308 return affected ;
298309 }
@@ -317,6 +328,25 @@ export class SubdomainStore extends PuterStore {
317328 } ) ;
318329 }
319330
331+ // FSEntryStore caches each row with an embedded `subdomains_agg` JSON
332+ // (uuid + subdomain) keyed on `root_dir_id`. Without this, a deleted or
333+ // renamed subdomain keeps showing up under its old folder in the GUI
334+ // (website badge, "associated websites" popover) until the entry's
335+ // independent TTL expires.
336+ async #invalidateRootDirEntry( rootDirId ) {
337+ if ( rootDirId == null ) return ;
338+ const id =
339+ typeof rootDirId === 'number' ? rootDirId : Number ( rootDirId ) ;
340+ if ( ! Number . isFinite ( id ) ) return ;
341+ const fsEntry = this . stores ?. fsEntry ;
342+ if ( ! fsEntry ?. invalidateEntryCacheById ) return ;
343+ try {
344+ await fsEntry . invalidateEntryCacheById ( id ) ;
345+ } catch {
346+ /* best-effort */
347+ }
348+ }
349+
320350 async #invalidatePrefixListsForUser( userId ) {
321351 if ( userId == null ) return ;
322352 const trackerKey = this . #prefixListTrackerKey( userId ) ;
0 commit comments