Skip to content

Commit fa140ee

Browse files
committed
fix: Fix caching issues on API
1 parent 9129cc7 commit fa140ee

15 files changed

Lines changed: 148 additions & 47 deletions

File tree

apps/www/src/app/api/bump/plugin/[slug]/route.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import { FieldValue } from 'firebase-admin/firestore';
2-
3-
export const revalidate = 1800;
2+
import { Database } from '../../../Firebase';
43

54
function withCORS(response: Response): Response {
65
response.headers.set('Access-Control-Allow-Origin', 'https://steamloopback.host');

apps/www/src/app/api/bump/theme/[slug]/route.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { FieldValue } from 'firebase-admin/firestore';
2+
import { Database } from '../../../Firebase';
23

34
function withCORS(response: Response): Response {
45
response.headers.set('Access-Control-Allow-Origin', 'https://steamloopback.host');

apps/www/src/app/api/checkupdates/route.ts

Lines changed: 81 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,88 @@
11
import { NextResponse } from 'next/server';
22
import { FetchPlugins } from '../v1/plugins/GetPlugins';
33
import { FetchThemes } from '../v2/FetchThemes';
4+
import { Database, firebaseAdmin } from '../Firebase';
45

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+
};
686

787
interface PluginUpdateCheck {
888
id: string;

apps/www/src/app/api/v1/plugin/[slug]/route.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { StorageBucket } from '../../../Firebase';
22
import { FetchPlugins } from '../../plugins/GetPlugins';
33

4-
export const revalidate = 1800;
4+
export const revalidate = 60;
55

66
const FindPlugin = async (id: string) => {
77
const plugin = (await FetchPlugins()).pluginData.find((plugin) => plugin.id === id);

apps/www/src/app/api/v1/plugins/GetDownloadInfo.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ const GetDownloadInfo = async () => {
55
Authorization: process.env.BEARER!,
66
'Content-Type': 'application/json',
77
},
8-
next: { revalidate: 1800 },
8+
next: { revalidate: 300 },
99
})
1010
.then((text) => text.json())
1111
.then((data) => {

apps/www/src/app/api/v1/plugins/GetPluginList.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ const RetrievePluginList = async () => {
77
Authorization: process.env.BEARER!,
88
'Content-Type': 'application/json',
99
},
10-
next: { revalidate: 1800 },
10+
next: { revalidate: 300 },
1111
})
1212
.then((text) => text.json())
1313
.then((data) => {

apps/www/src/app/api/v1/plugins/GetPluginMetadata.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ const GetPluginMetadata = async () => {
1010
Authorization: process.env.BEARER!,
1111
'Content-Type': 'application/json',
1212
},
13-
next: { revalidate: 1800 },
13+
next: { revalidate: 300 },
1414
})
1515
.then((text) => text.json())
1616
.then((data) => {

apps/www/src/app/api/v1/plugins/GetPlugins.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@ const FormatBytes = (bytes: number, decimals = 2) => {
1010
return `${(bytes / Math.pow(1024, i)).toFixed(decimals)} ${sizes[i]}`;
1111
};
1212

13-
const CACHE_DURATION_MS = 30 * 60 * 1000;
13+
// Cache duration: 5 minutes (in milliseconds)
14+
const CACHE_DURATION_MS = 5 * 60 * 1000;
1415

1516
// In-memory cache — single source of truth, no Firestore round-trips
1617
let cachedResult: PluginDataTable | null = null;

apps/www/src/app/api/v1/plugins/route.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { FetchPlugins } from './GetPlugins';
22

3-
export const revalidate = 1800;
3+
export const revalidate = 60;
44

55
export async function GET(request: Request) {
66
return Response.json((await FetchPlugins()).pluginData);

apps/www/src/app/api/v2/FetchThemes.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,11 @@ async function fetchFreshThemes(): Promise<ThemeData[]> {
8888

8989
const result = await GithubGraphQL.Post(query);
9090

91+
if (!result.data) {
92+
console.error('GraphQL response missing data:', JSON.stringify(result).slice(0, 500));
93+
return [];
94+
}
95+
9196
const themes: ThemeData[] = [];
9297
for (const [key, repo] of Object.entries(result.data) as [string, any][]) {
9398
const index = parseInt(key.slice(1), 10);

0 commit comments

Comments
 (0)