|
1 | 1 | import { NextResponse } from 'next/server'; |
2 | 2 | import { FetchPlugins } from '../v1/plugins/GetPlugins'; |
3 | 3 | import { FetchThemes } from '../v2/FetchThemes'; |
| 4 | +import { Database, firebaseAdmin } from '../Firebase'; |
4 | 5 |
|
5 | | -export const revalidate = 1800; |
| 6 | +export const revalidate = 60; |
| 7 | +const CACHE_DURATION_MS = 5 * 60 * 1000; |
| 8 | + |
| 9 | +interface CachedRepositoryData { |
| 10 | + [key: string]: any; |
| 11 | +} |
| 12 | + |
| 13 | +interface UpdateCacheEntry { |
| 14 | + owner: string; |
| 15 | + repo: string; |
| 16 | + data: CachedRepositoryData; |
| 17 | + timestamp: FirebaseFirestore.Timestamp; |
| 18 | + expiresAt: FirebaseFirestore.Timestamp; |
| 19 | +} |
| 20 | + |
| 21 | +const createSafeCacheKey = (owner: string, repo: string): string => { |
| 22 | + const combined = `${owner}__${repo}`; |
| 23 | + const sanitized = combined.replace(/[^a-zA-Z0-9_-]/g, '_'); |
| 24 | + const withoutLeadingDot = sanitized.replace(/^\.+/, '_'); |
| 25 | + |
| 26 | + const maxLength = 1500; |
| 27 | + const truncated = withoutLeadingDot.length > maxLength ? withoutLeadingDot.substring(0, maxLength) : withoutLeadingDot; |
| 28 | + |
| 29 | + const cleaned = truncated || 'unknown_repo'; |
| 30 | + return cleaned.replace(/__+/g, '__'); |
| 31 | +}; |
| 32 | + |
| 33 | +const isCacheValid = (timestamp: FirebaseFirestore.Timestamp): boolean => { |
| 34 | + const now = new Date(); |
| 35 | + const cacheTime = timestamp.toDate(); |
| 36 | + return now.getTime() - cacheTime.getTime() < CACHE_DURATION_MS; |
| 37 | +}; |
| 38 | + |
| 39 | +const getCachedRepositoryData = async (owner: string, repo: string): Promise<CachedRepositoryData | null> => { |
| 40 | + try { |
| 41 | + const cacheKey = createSafeCacheKey(owner, repo); |
| 42 | + const docRef = Database.collection('UpdateCache').doc(cacheKey); |
| 43 | + const doc = await docRef.get(); |
| 44 | + |
| 45 | + if (!doc.exists) { |
| 46 | + return null; |
| 47 | + } |
| 48 | + |
| 49 | + const data = doc.data() as UpdateCacheEntry; |
| 50 | + if (data && isCacheValid(data.timestamp)) { |
| 51 | + console.log(`Found valid cached data for ${owner}/${repo} (key: ${cacheKey})`); |
| 52 | + return data.data; |
| 53 | + } else if (data) { |
| 54 | + // Remove expired entry |
| 55 | + await docRef.delete(); |
| 56 | + console.log(`Removed expired cache entry for ${owner}/${repo} (key: ${cacheKey})`); |
| 57 | + } |
| 58 | + |
| 59 | + return null; |
| 60 | + } catch (error) { |
| 61 | + console.error(`Error retrieving cached data for ${owner}/${repo}:`, error); |
| 62 | + return null; |
| 63 | + } |
| 64 | +}; |
| 65 | + |
| 66 | +const setCachedRepositoryData = async (owner: string, repo: string, data: CachedRepositoryData): Promise<void> => { |
| 67 | + try { |
| 68 | + const cacheKey = createSafeCacheKey(owner, repo); |
| 69 | + const docRef = Database.collection('UpdateCache').doc(cacheKey); |
| 70 | + const now = new Date(); |
| 71 | + |
| 72 | + const cacheEntry: UpdateCacheEntry = { |
| 73 | + owner, |
| 74 | + repo, |
| 75 | + data, |
| 76 | + timestamp: firebaseAdmin.firestore.Timestamp.fromDate(now), |
| 77 | + expiresAt: firebaseAdmin.firestore.Timestamp.fromDate(new Date(now.getTime() + CACHE_DURATION_MS)), |
| 78 | + }; |
| 79 | + |
| 80 | + await docRef.set(cacheEntry); |
| 81 | + console.log(`Cached repository data for ${owner}/${repo} (key: ${cacheKey}) for ${CACHE_DURATION_MS / (1000 * 60)} minutes`); |
| 82 | + } catch (error) { |
| 83 | + console.error(`Error caching data for ${owner}/${repo}:`, error); |
| 84 | + } |
| 85 | +}; |
6 | 86 |
|
7 | 87 | interface PluginUpdateCheck { |
8 | 88 | id: string; |
|
0 commit comments