+44
-1
hosting-service/src/lib/firehose.ts
+44
-1
hosting-service/src/lib/firehose.ts
···
333
// Invalidate in-memory caches (includes metadata which stores settings)
334
invalidateSiteCache(did, rkey)
335
336
-
// Update on-disk metadata with new settings
337
try {
338
const { fetchSiteSettings, updateCacheMetadataSettings } = await import('./utils')
339
const settings = await fetchSiteSettings(did, rkey)
···
333
// Invalidate in-memory caches (includes metadata which stores settings)
334
invalidateSiteCache(did, rkey)
335
336
+
// Check if site is already cached
337
+
const cacheDir = `${CACHE_DIR}/${did}/${rkey}`
338
+
const isCached = existsSync(cacheDir)
339
+
340
+
if (!isCached) {
341
+
this.log('Site not cached yet, checking if fs record exists', { did, rkey })
342
+
343
+
// If site exists on PDS, cache it (which will include the new settings)
344
+
try {
345
+
const siteRecord = await fetchSiteRecord(did, rkey)
346
+
347
+
if (siteRecord) {
348
+
this.log('Site record found, triggering full cache with settings', { did, rkey })
349
+
const pdsEndpoint = await getPdsForDid(did)
350
+
351
+
if (pdsEndpoint) {
352
+
// Mark as being cached
353
+
markSiteAsBeingCached(did, rkey)
354
+
355
+
try {
356
+
await downloadAndCacheSite(did, rkey, siteRecord.record, pdsEndpoint, siteRecord.cid)
357
+
this.log('Successfully cached site with new settings', { did, rkey })
358
+
} finally {
359
+
unmarkSiteAsBeingCached(did, rkey)
360
+
}
361
+
} else {
362
+
this.log('Could not resolve PDS for DID', { did })
363
+
}
364
+
} else {
365
+
this.log('No fs record found for site, skipping cache', { did, rkey })
366
+
}
367
+
} catch (err) {
368
+
this.log('Failed to cache site after settings change', {
369
+
did,
370
+
rkey,
371
+
error: err instanceof Error ? err.message : String(err)
372
+
})
373
+
}
374
+
375
+
this.log('Successfully processed settings change (new cache)', { did, rkey })
376
+
return
377
+
}
378
+
379
+
// Site is already cached, just update the settings in metadata
380
try {
381
const { fetchSiteSettings, updateCacheMetadataSettings } = await import('./utils')
382
const settings = await fetchSiteSettings(did, rkey)
+23
-1
hosting-service/src/lib/utils.ts
+23
-1
hosting-service/src/lib/utils.ts
···
728
729
export async function getCachedSettings(did: string, rkey: string): Promise<WispSettings | null> {
730
const metadata = await getCacheMetadata(did, rkey);
731
-
return metadata?.settings || null;
732
}
733
734
export async function updateCacheMetadataSettings(did: string, rkey: string, settings: WispSettings | null): Promise<void> {
···
728
729
export async function getCachedSettings(did: string, rkey: string): Promise<WispSettings | null> {
730
const metadata = await getCacheMetadata(did, rkey);
731
+
732
+
// If metadata has settings, return them
733
+
if (metadata?.settings) {
734
+
return metadata.settings;
735
+
}
736
+
737
+
// If metadata exists but has no settings, try to fetch from PDS and update cache
738
+
if (metadata) {
739
+
console.log('[Cache] Metadata missing settings, fetching from PDS', { did, rkey });
740
+
try {
741
+
const settings = await fetchSiteSettings(did, rkey);
742
+
if (settings) {
743
+
// Update the cached metadata with the fetched settings
744
+
await updateCacheMetadataSettings(did, rkey, settings);
745
+
console.log('[Cache] Updated metadata with fetched settings', { did, rkey });
746
+
return settings;
747
+
}
748
+
} catch (err) {
749
+
console.error('[Cache] Failed to fetch/update settings', { did, rkey, err });
750
+
}
751
+
}
752
+
753
+
return null;
754
}
755
756
export async function updateCacheMetadataSettings(did: string, rkey: string, settings: WispSettings | null): Promise<void> {
+84
hosting-service/src/server.ts
+84
hosting-service/src/server.ts
···
765
}
766
}
767
768
+
// Directory listing fallback: if enabled, show root directory listing on 404
769
+
if (settings?.directoryListing) {
770
+
const rootPath = getCachedFilePath(did, rkey, '');
771
+
if (await fileExists(rootPath)) {
772
+
const { stat, readdir } = await import('fs/promises');
773
+
try {
774
+
const stats = await stat(rootPath);
775
+
if (stats.isDirectory()) {
776
+
const entries = await readdir(rootPath);
777
+
// Filter out .meta files and metadata
778
+
const visibleEntries = entries.filter(entry =>
779
+
!entry.endsWith('.meta') && entry !== '.metadata.json'
780
+
);
781
+
782
+
// Check which entries are directories
783
+
const entriesWithType = await Promise.all(
784
+
visibleEntries.map(async (name) => {
785
+
try {
786
+
const entryPath = `${rootPath}/${name}`;
787
+
const entryStats = await stat(entryPath);
788
+
return { name, isDirectory: entryStats.isDirectory() };
789
+
} catch {
790
+
return { name, isDirectory: false };
791
+
}
792
+
})
793
+
);
794
+
795
+
const html = generateDirectoryListing('', entriesWithType);
796
+
return new Response(html, {
797
+
status: 404,
798
+
headers: {
799
+
'Content-Type': 'text/html; charset=utf-8',
800
+
'Cache-Control': 'public, max-age=300',
801
+
},
802
+
});
803
+
}
804
+
} catch (err) {
805
+
// If directory listing fails, fall through to 404
806
+
}
807
+
}
808
+
}
809
+
810
// Default styled 404 page
811
const html = generate404Page();
812
return new Response(html, {
···
1202
status: 404,
1203
headers: response.headers,
1204
});
1205
+
}
1206
+
}
1207
+
1208
+
// Directory listing fallback: if enabled, show root directory listing on 404
1209
+
if (settings?.directoryListing) {
1210
+
const rootPath = getCachedFilePath(did, rkey, '');
1211
+
if (await fileExists(rootPath)) {
1212
+
const { stat, readdir } = await import('fs/promises');
1213
+
try {
1214
+
const stats = await stat(rootPath);
1215
+
if (stats.isDirectory()) {
1216
+
const entries = await readdir(rootPath);
1217
+
// Filter out .meta files and metadata
1218
+
const visibleEntries = entries.filter(entry =>
1219
+
!entry.endsWith('.meta') && entry !== '.metadata.json'
1220
+
);
1221
+
1222
+
// Check which entries are directories
1223
+
const entriesWithType = await Promise.all(
1224
+
visibleEntries.map(async (name) => {
1225
+
try {
1226
+
const entryPath = `${rootPath}/${name}`;
1227
+
const entryStats = await stat(entryPath);
1228
+
return { name, isDirectory: entryStats.isDirectory() };
1229
+
} catch {
1230
+
return { name, isDirectory: false };
1231
+
}
1232
+
})
1233
+
);
1234
+
1235
+
const html = generateDirectoryListing('', entriesWithType);
1236
+
return new Response(html, {
1237
+
status: 404,
1238
+
headers: {
1239
+
'Content-Type': 'text/html; charset=utf-8',
1240
+
'Cache-Control': 'public, max-age=300',
1241
+
},
1242
+
});
1243
+
}
1244
+
} catch (err) {
1245
+
// If directory listing fails, fall through to 404
1246
+
}
1247
}
1248
}
1249