36 changed files with 1477 additions and 942 deletions
@ -0,0 +1,132 @@ |
|||||||
|
/** |
||||||
|
* RSS feed caching with IndexedDB |
||||||
|
*/ |
||||||
|
|
||||||
|
import { getDB } from './indexeddb-store.js'; |
||||||
|
|
||||||
|
export interface RSSItem { |
||||||
|
title: string; |
||||||
|
link: string; |
||||||
|
description: string; |
||||||
|
pubDate: Date; |
||||||
|
feedUrl: string; |
||||||
|
feedTitle?: string; |
||||||
|
} |
||||||
|
|
||||||
|
export interface CachedRSSFeed { |
||||||
|
feedUrl: string; |
||||||
|
items: RSSItem[]; |
||||||
|
cached_at: number; |
||||||
|
} |
||||||
|
|
||||||
|
const CACHE_DURATION = 15 * 60 * 1000; // 15 minutes
|
||||||
|
|
||||||
|
/** |
||||||
|
* Store RSS feed items in cache |
||||||
|
*/ |
||||||
|
export async function cacheRSSFeed(feedUrl: string, items: RSSItem[]): Promise<void> { |
||||||
|
try { |
||||||
|
const db = await getDB(); |
||||||
|
const cached: CachedRSSFeed = { |
||||||
|
feedUrl, |
||||||
|
items: items.map(item => ({ |
||||||
|
...item, |
||||||
|
pubDate: item.pubDate // Store as Date, will be serialized
|
||||||
|
})), |
||||||
|
cached_at: Date.now() |
||||||
|
}; |
||||||
|
await db.put('rss', cached); |
||||||
|
} catch (error) { |
||||||
|
console.debug('Error caching RSS feed:', error); |
||||||
|
// Don't throw - caching failures shouldn't break the app
|
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Get RSS feed items from cache |
||||||
|
*/ |
||||||
|
export async function getCachedRSSFeed(feedUrl: string): Promise<RSSItem[] | null> { |
||||||
|
try { |
||||||
|
const db = await getDB(); |
||||||
|
const cached = await db.get('rss', feedUrl) as CachedRSSFeed | undefined; |
||||||
|
|
||||||
|
if (!cached) { |
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
// Check if cache is still valid
|
||||||
|
const age = Date.now() - cached.cached_at; |
||||||
|
if (age > CACHE_DURATION) { |
||||||
|
// Cache expired, delete it
|
||||||
|
await db.delete('rss', feedUrl); |
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
// Restore Date objects from serialized format
|
||||||
|
const items: RSSItem[] = cached.items.map((item: RSSItem) => ({ |
||||||
|
...item, |
||||||
|
pubDate: new Date(item.pubDate) |
||||||
|
})); |
||||||
|
|
||||||
|
return items; |
||||||
|
} catch (error) { |
||||||
|
console.debug('Error getting cached RSS feed:', error); |
||||||
|
return null; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Get all cached RSS feeds |
||||||
|
*/ |
||||||
|
export async function getAllCachedRSSFeeds(): Promise<Map<string, RSSItem[]>> { |
||||||
|
try { |
||||||
|
const db = await getDB(); |
||||||
|
const allCached = await db.getAll('rss') as CachedRSSFeed[]; |
||||||
|
const result = new Map<string, RSSItem[]>(); |
||||||
|
|
||||||
|
const now = Date.now(); |
||||||
|
for (const cached of allCached) { |
||||||
|
// Check if cache is still valid
|
||||||
|
const age = now - cached.cached_at; |
||||||
|
if (age <= CACHE_DURATION) { |
||||||
|
// Restore Date objects
|
||||||
|
const items: RSSItem[] = cached.items.map((item: RSSItem) => ({ |
||||||
|
...item, |
||||||
|
pubDate: new Date(item.pubDate) |
||||||
|
})); |
||||||
|
result.set(cached.feedUrl, items); |
||||||
|
} else { |
||||||
|
// Cache expired, delete it
|
||||||
|
await db.delete('rss', cached.feedUrl); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return result; |
||||||
|
} catch (error) { |
||||||
|
console.debug('Error getting all cached RSS feeds:', error); |
||||||
|
return new Map(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Clear expired RSS feed caches |
||||||
|
*/ |
||||||
|
export async function clearExpiredRSSCaches(): Promise<void> { |
||||||
|
try { |
||||||
|
const db = await getDB(); |
||||||
|
const index = db.transaction('rss', 'readwrite').store.index('cached_at'); |
||||||
|
const now = Date.now(); |
||||||
|
const maxAge = now - CACHE_DURATION; |
||||||
|
|
||||||
|
// Get all entries older than cache duration
|
||||||
|
const range = IDBKeyRange.upperBound(maxAge); |
||||||
|
const expired = await index.getAll(range) as CachedRSSFeed[]; |
||||||
|
|
||||||
|
for (const cached of expired) { |
||||||
|
await db.delete('rss', cached.feedUrl); |
||||||
|
} |
||||||
|
} catch (error) { |
||||||
|
console.debug('Error clearing expired RSS caches:', error); |
||||||
|
// Don't throw - cleanup failures shouldn't break the app
|
||||||
|
} |
||||||
|
} |
||||||
Loading…
Reference in new issue