Skip to content

Commit 6f45ead

Browse files
committed
revert: restore code to f30f5a6
1 parent 1f981a6 commit 6f45ead

5 files changed

Lines changed: 82 additions & 136 deletions

File tree

.eslintrc.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,5 @@
77
"@typescript-eslint/no-explicit-any": "warn"
88
}
99
}
10+
11+

components/leaderboard/LeaderBoard.tsx

Lines changed: 2 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -14,41 +14,25 @@ export const LeaderBoard: React.FC = () => {
1414
const [isLoading, setIsLoading] = useState(false);
1515
const [leaderBoardData, setLeaderBoardData] = useState<UserStat[] | null>(null);
1616
const [isError, setIsError] = useState(false);
17-
const [retryCount, setRetryCount] = useState(0);
1817

1918
useEffect(() => {
2019
const fetchData = async () => {
2120
setIsLoading(true);
2221
setIsError(false);
2322

2423
try {
25-
const response = await axios.get("/api/topUsers", {
26-
timeout: 60000, // Increased timeout to 60 seconds
27-
headers: {
28-
'Cache-Control': 'no-cache',
29-
'Pragma': 'no-cache',
30-
'Expires': '0',
31-
}
32-
});
24+
const response = await axios.get("/api/topUsers");
3325
setLeaderBoardData(response.data);
3426
} catch (error) {
3527
console.error("Failed to fetch leaderboard data:", error);
3628
setIsError(true);
37-
38-
// Retry logic with exponential backoff
39-
if (retryCount < 3) {
40-
const backoffTime = Math.pow(2, retryCount) * 2000; // 2s, 4s, 8s
41-
setTimeout(() => {
42-
setRetryCount(prev => prev + 1);
43-
}, backoffTime);
44-
}
4529
} finally {
4630
setIsLoading(false);
4731
}
4832
};
4933

5034
fetchData();
51-
}, [retryCount]); // Add retryCount as dependency
35+
}, []);
5236

5337
return (
5438
<div className="text-center">
@@ -103,6 +87,3 @@ export const LeaderBoard: React.FC = () => {
10387
};
10488

10589

106-
107-
108-

pages/api/topUsers.js

Lines changed: 36 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,64 +1,56 @@
1+
/* eslint-disable import/no-anonymous-default-export */
2+
import {
3+
getTopUsersFromDb,
4+
storeTopUsersInDb,
5+
} from "../../utils/fetchTopUsersFromDb";
16
import { fetchTopUsersByPullRequests } from "../../utils/fetchTopUsersByPullRequests";
2-
import { getTopUsersFromDb, storeTopUsersInDb } from "../../utils/fetchTopUsersFromDb";
37

4-
export default async function handler(req, res) {
5-
// Add CORS headers
6-
res.setHeader('Cache-Control', 'public, s-maxage=300, stale-while-revalidate=599');
8+
export default async (req, res) => {
9+
res.setHeader('Access-Control-Allow-Origin', '*');
10+
res.setHeader('Access-Control-Allow-Methods', 'GET');
11+
res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
712

813
try {
9-
console.log('API: Checking DB for cached top users...');
10-
let data = await getTopUsersFromDb();
11-
12-
// If we have cached data, return it immediately
13-
if (data && data.length > 0) {
14-
console.log('API: Using cached data from DB');
15-
res.status(200).json(data);
14+
const message = 'API: Checking DB for cached top users...';
15+
console.log(message);
16+
res.setHeader('X-Debug-Message', message);
1617

17-
// Optionally refresh cache in background if data is old
18-
const cacheAge = Date.now() - new Date(data[0].timestamp).getTime();
19-
if (cacheAge > 3600000) { // 1 hour
20-
refreshCache().catch(console.error);
21-
}
22-
return;
23-
}
18+
let data = await getTopUsersFromDb();
2419

25-
// If no cached data, fetch new data with timeout
26-
console.log('API: No cached data found, fetching from GitHub...');
27-
const controller = new AbortController();
28-
const timeout = setTimeout(() => controller.abort(), 50000); // 50 second timeout
20+
if (!data) {
21+
const message2 = 'API: No cached data found, fetching from GitHub...';
22+
console.log(message2);
23+
res.setHeader('X-Debug-Message-2', message2);
2924

30-
try {
3125
data = await fetchTopUsersByPullRequests("fork-commit-merge/fork-commit-merge");
32-
clearTimeout(timeout);
3326

3427
if (data && data.length > 0) {
35-
console.log('API: Storing new data in DB...');
28+
const message3 = 'API: Storing new data in DB...';
29+
console.log(message3);
30+
res.setHeader('X-Debug-Message-3', message3);
31+
3632
await storeTopUsersInDb(data);
37-
res.status(200).json(data);
38-
} else {
39-
res.status(404).json({ error: 'No data available' });
4033
}
41-
} catch (error) {
42-
clearTimeout(timeout);
43-
throw error;
34+
} else {
35+
const message4 = 'API: Using cached data from DB';
36+
console.log(message4);
37+
res.setHeader('X-Debug-Message-4', message4);
4438
}
45-
} catch (error) {
46-
console.error('API route error:', error);
47-
res.status(500).json({ error: 'Internal Server Error' });
48-
}
49-
}
5039

51-
// Background cache refresh
52-
async function refreshCache() {
53-
try {
54-
const data = await fetchTopUsersByPullRequests("fork-commit-merge/fork-commit-merge");
55-
if (data && data.length > 0) {
56-
await storeTopUsersInDb(data);
40+
if (!data || data.length === 0) {
41+
const message5 = 'API: No data available';
42+
console.log(message5);
43+
res.setHeader('X-Debug-Message-5', message5);
44+
return res.status(404).json({ error: 'No data available' });
5745
}
46+
47+
return res.status(200).json(data);
5848
} catch (error) {
59-
console.error('Failed to refresh cache:', error);
49+
console.error('API route error:', error);
50+
console.error('Error details:', error.response?.data || error.message);
51+
return res.status(500).json({ error: 'Internal Server Error' });
6052
}
61-
}
53+
};
6254

6355

6456

utils/fetchTopUsersByPullRequests.ts

Lines changed: 39 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -9,72 +9,49 @@ type UserStat = {
99
export const fetchTopUsersByPullRequests = async (
1010
repo: string
1111
): Promise<UserStat[]> => {
12-
const userStats: { [key: string]: { prCount: number; avatarUrl: string } } = {};
13-
const PER_PAGE = 100;
14-
15-
try {
16-
// Use cursor-based pagination instead of page numbers
17-
let hasNextPage = true;
18-
let cursor = null;
19-
20-
while (hasNextPage) {
21-
const response: { data: any[]; headers: { link?: string } } = await axios.get(`https://api.github.com/repos/${repo}/pulls`, {
22-
params: {
23-
state: 'closed',
24-
per_page: PER_PAGE,
25-
...(cursor ? { after: cursor } : {}),
26-
},
27-
headers: {
28-
Authorization: `token ${process.env.GITHUB_TOKEN}`,
29-
Accept: 'application/vnd.github.v3+json',
30-
},
31-
timeout: 30000, // 30 seconds timeout
32-
});
33-
34-
const prs = response.data;
35-
36-
// Process PRs
37-
prs.forEach((pr: any) => {
38-
if (pr.merged_at) {
39-
const username = pr.user.login;
40-
if (!shouldExcludeUser(username)) {
41-
userStats[username] = {
42-
prCount: (userStats[username]?.prCount || 0) + 1,
43-
avatarUrl: pr.user.avatar_url,
44-
};
45-
}
12+
let url = `https://api.github.com/repos/${repo}/pulls?state=closed&per_page=100`;
13+
const userStats: { [key: string]: { prCount: number; avatarUrl: string } } =
14+
{};
15+
16+
while (url) {
17+
const response = await axios.get(url);
18+
const prData = response.data;
19+
prData.forEach((pr: any) => {
20+
if (pr.merged_at) {
21+
const username = pr.user.login;
22+
23+
//* Ignore dependabot and nikohoffren PRs
24+
if (
25+
username === "dependabot" ||
26+
username === "dependabot[bot]" ||
27+
username === "nikohoffren"
28+
) {
29+
return;
4630
}
47-
});
4831

49-
// Check if there are more pages
50-
const linkHeader = response.headers.link;
51-
hasNextPage = linkHeader?.includes('rel="next"') ?? false;
52-
const nextLink = linkHeader?.match(/<([^>]+)>;\s*rel="next"/)?.[1];
53-
cursor = nextLink ? new URL(nextLink).searchParams.get('after') : null;
54-
55-
// Add delay between requests to avoid rate limiting
56-
if (hasNextPage) {
57-
await new Promise(resolve => setTimeout(resolve, 1000));
32+
const avatarUrl = pr.user.avatar_url;
33+
userStats[username] = {
34+
prCount: (userStats[username]?.prCount || 0) + 1,
35+
avatarUrl,
36+
};
5837
}
59-
}
60-
61-
return Object.entries(userStats)
62-
.sort(([, a], [, b]) => b.prCount - a.prCount)
63-
.slice(0, 20)
64-
.map(([username, { prCount, avatarUrl }]) => ({
65-
username,
66-
prCount,
67-
avatarUrl,
68-
}));
38+
});
6939

70-
} catch (error) {
71-
console.error('Error fetching PRs:', error);
72-
throw error;
40+
const linkHeader = response.headers.link;
41+
const nextLink = linkHeader
42+
? linkHeader.split(",").find((s: string) => s.includes('rel="next"'))
43+
: null;
44+
url = nextLink ? nextLink.match(/<(.*)>/)?.[1] : null;
7345
}
74-
};
7546

76-
function shouldExcludeUser(username: string): boolean {
77-
const excludedUsers = ['dependabot', 'dependabot[bot]', 'nikohoffren'];
78-
return excludedUsers.includes(username);
79-
}
47+
const sortedUsers: UserStat[] = Object.entries(userStats)
48+
.sort(([, a], [, b]) => b.prCount - a.prCount)
49+
.slice(0, 20)
50+
.map(([username, { prCount, avatarUrl }]) => ({
51+
username,
52+
prCount,
53+
avatarUrl,
54+
}));
8055

56+
return sortedUsers;
57+
};

utils/fetchTopUsersFromDb.ts

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,7 @@ import { connectToDB } from './db';
33
export async function getTopUsersFromDb() {
44
try {
55
const { db } = await connectToDB();
6-
const data = await db.collection('topUsers')
7-
.find()
8-
.maxTimeMS(5000) // 5 second timeout for DB query
9-
.toArray();
6+
const data = await db.collection('topUsers').find().toArray();
107
return data.length > 0 ? data : null;
118
} catch (error) {
129
console.error('DB Error:', error);
@@ -21,12 +18,11 @@ export async function storeTopUsersInDb(data: any[]) {
2118
await db.collection('topUsers').insertMany(
2219
data.map(user => ({
2320
...user,
24-
timestamp: new Date(),
25-
lastUpdated: new Date().toISOString()
21+
timestamp: new Date()
2622
}))
2723
);
2824
} catch (error) {
29-
console.error('Error storing top users:', error);
25+
console.error('DB Storage Error:', error);
3026
throw error;
3127
}
3228
}
@@ -60,5 +56,3 @@ export async function storeTopThreeUsersInDb(data: any[]) {
6056

6157

6258

63-
64-

0 commit comments

Comments
 (0)