Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions api/commits.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,15 +42,15 @@ module.exports = async (req, res) => {
if (!contributionData) {
try {
contributionData = await fetchContributionData(username);
if (!contributionData) throw new Error("No data");
setCache(cacheKey, contributionData, CACHE_TTL);
} catch (fetchErr) {
console.error("Commits fetch error:", fetchErr.message);
res.status(200).send(errorSVG(`Could not fetch data for ${username}`));
return;
contributionData = { totalContributions: 0, days: [] };
}
}

const { totalContributions, days } = contributionData;
const { totalContributions = 0, days = [] } = contributionData;

let colors = getTheme(theme);
colors = applyColorOverrides(colors, { bg_color, title_color, text_color, border_color });
Expand Down
6 changes: 3 additions & 3 deletions api/contribution.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,15 +42,15 @@ module.exports = async (req, res) => {
if (!contributionData) {
try {
contributionData = await fetchContributionData(username);
if (!contributionData) throw new Error("No data");
setCache(cacheKey, contributionData, CACHE_TTL);
} catch (fetchErr) {
console.error("Contribution fetch error:", fetchErr.message);
res.status(200).send(errorSVG(`Could not fetch data for ${username}`));
return;
contributionData = { totalContributions: 0, days: [] };
}
}

const { totalContributions, days } = contributionData;
const { totalContributions = 0, days = [] } = contributionData;

// Calculate streaks
let currentStreak = 0;
Expand Down
12 changes: 5 additions & 7 deletions api/issues.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,27 +42,25 @@ module.exports = async (req, res) => {
fetchUserIssues(username),
fetchOpenIssues(username),
fetchClosedIssues(username),
fetchUserProfile(username).catch(() => ({ name: username, login: username })),
fetchUserProfile(username)
]);

data = {
totalIssues: totalIssuesCount,
openIssues: openIssuesCount,
closedIssues: closedIssuesCount,
profileName: profile.name || profile.login || username,
totalIssues: totalIssuesCount || 0,
openIssues: openIssuesCount || 0,
closedIssues: closedIssuesCount || 0,
profileName: profile?.name || profile?.login || username,
};

setCache(cacheKey, data, CACHE_TTL);
} catch (fetchErr) {
console.error("Issues fetch error:", fetchErr.message);
// Fallback to zeros so card still renders
data = {
totalIssues: 0,
openIssues: 0,
closedIssues: 0,
profileName: username,
};
setCache(cacheKey, data, CACHE_TTL);
}
}

Expand Down
15 changes: 8 additions & 7 deletions api/languages.js
Original file line number Diff line number Diff line change
Expand Up @@ -67,11 +67,12 @@ module.exports = async (req, res) => {
} else {
langData = await fetchUserLanguages(username);
}

if (!langData) throw new Error("No data returned");
setCache(cacheKey, langData, CACHE_TTL);
} catch (fetchErr) {
console.error("Language fetch error:", fetchErr.message);
res.status(200).send(errorSVG(`Could not fetch languages for ${username}`));
return;
langData = { languages: [], totalRepos: 0, totalBytes: 0, totalActivity: 0 };
}
}

Expand All @@ -81,27 +82,27 @@ module.exports = async (req, res) => {
let svg;
if (cacheMode === "repos") {
svg = generateLanguageDonutByReposSVG({
languages: langData.languages,
languages: langData.languages || [],
hideBorder: hide_border === "true",
});
} else if (cacheMode === "commits") {
svg = generateLanguageDonutByCommitsSVG({
languages: langData.languages,
languages: langData.languages || [],
hideBorder: hide_border === "true",
});
} else {
svg = layout === "compact"
? generateLanguageCompactSVG({
username, languages: langData.languages, colors,
username, languages: langData.languages || [], colors,
hideBorder: hide_border === "true",
})
: layout === "donut"
? generateLanguageDonutSVG({
username, languages: langData.languages, colors,
username, languages: langData.languages || [], colors,
hideBorder: hide_border === "true",
})
: generateLanguageSVG({
username, languages: langData.languages, totalBytes: langData.totalBytes, colors,
username, languages: langData.languages || [], totalBytes: langData.totalBytes || 0, colors,
hideBorder: hide_border === "true",
maxLangs: parseInt(max_langs) || 12,
});
Expand Down
66 changes: 48 additions & 18 deletions api/master.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,11 @@ const {
fetchContributionData,
fetchUserProfile,
fetchUserLanguages,
fetchTotalCommitCount
fetchTotalCommitCount,
fetchUserIssues,
fetchOpenIssues,
fetchClosedIssues,
fetchMergedPullRequests
} = require("../src/github");
const { getTheme, applyColorOverrides } = require("../src/themes");
const { generateMasterCardSVG } = require("../src/svg-master");
Expand Down Expand Up @@ -43,16 +47,20 @@ module.exports = async (req, res) => {

if (!data) {
try {
const [prs, profile, contributionData, langData, totalCommits] = await Promise.all([
const [prs, profile, contributionData, langData, totalCommits, totalIssues, openIssues, closedIssues, mergedPRCount] = await Promise.all([
fetchUserPullRequests(username),
fetchUserProfile(username).catch(() => ({ public_repos: 0 })),
fetchUserProfile(username),
fetchContributionData(username),
fetchUserLanguages(username),
fetchTotalCommitCount(username)
fetchTotalCommitCount(username),
fetchUserIssues(username),
fetchOpenIssues(username),
fetchClosedIssues(username),
fetchMergedPullRequests(username)
]);

// Calculate streaks
const sortedDays = [...contributionData.days].sort((a, b) => a.date.localeCompare(b.date));
const days = contributionData?.days || [];
const sortedDays = [...days].sort((a, b) => a.date.localeCompare(b.date));
let currentStreak = 0;
let longestStreak = 0;
let tempStreak = 0;
Expand All @@ -66,9 +74,8 @@ module.exports = async (req, res) => {
else tempStreak = 0;
}

// Top repos by PR count
const repoMap = {};
prs.forEach(pr => {
(prs || []).forEach(pr => {
if (pr.repository_url) {
const name = pr.repository_url.split("/repos/")[1];
repoMap[name] = (repoMap[name] || 0) + 1;
Expand All @@ -79,24 +86,43 @@ module.exports = async (req, res) => {
.sort((a, b) => b.count - a.count);

data = {
username: profile.login || username,
totalPRs: prs.length,
openPRs: prs.filter(pr => pr.state === "open").length,
repoCount: profile.public_repos || 0,
languages: langData.languages,
contributions: contributionData.totalContributions,
username: profile?.login || username,
totalPRs: prs?.length || 0,
openPRs: (prs || []).filter(pr => pr.state === "open").length,
mergedPRs: mergedPRCount || 0,
repoCount: profile?.public_repos || 0,
Comment on lines 88 to +93
languages: langData?.languages || [],
contributions: contributionData?.totalContributions || 0,
repoList,
contributionDays: contributionData.days,
contributionDays: days,
currentStreak,
longestStreak,
totalCommits
totalCommits,
totalIssues: totalIssues || 0,
openIssues: openIssues || 0,
closedIssues: closedIssues || 0
};

setCache(cacheKey, data, CACHE_TTL);
} catch (fetchErr) {
console.error("Mastercard fetch error:", fetchErr.message);
res.status(200).send(errorSVG(`Could not fetch data for ${username}`));
return;
data = {
username: username,
totalPRs: 0,
openPRs: 0,
mergedPRs: 0,
repoCount: 0,
languages: [],
contributions: 0,
repoList: [],
contributionDays: [],
currentStreak: 0,
longestStreak: 0,
totalCommits: 0,
totalIssues: 0,
openIssues: 0,
closedIssues: 0
};
}
}

Expand All @@ -107,6 +133,7 @@ module.exports = async (req, res) => {
username: data.username,
totalPRs: data.totalPRs,
openPRs: data.openPRs,
mergedPRs: data.mergedPRs,
repoCount: data.repoCount,
languages: data.languages,
contributions: data.contributions,
Expand All @@ -115,6 +142,9 @@ module.exports = async (req, res) => {
contributionDays: data.contributionDays,
currentStreak: data.currentStreak,
longestStreak: data.longestStreak,
totalIssues: data.totalIssues,
openIssues: data.openIssues,
closedIssues: data.closedIssues,
colors,
hideBorder: hide_border === "true",
});
Expand Down
31 changes: 18 additions & 13 deletions api/overview.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ function normalizeLinesScope(value) {
return value === "all" ? "all" : "recent";
}

function parseMaxPRs(value, defaultValue = 30, hardLimit = 200) {
function parseMaxPRs(value, defaultValue = 20, hardLimit = 50) {
const parsed = parseInt(value, 10);
if (!Number.isFinite(parsed) || parsed <= 0) {
return defaultValue;
Expand All @@ -47,7 +47,7 @@ module.exports = async (req, res) => {

try {
const scope = normalizeLinesScope(lines_scope);
const maxPRs = scope === "all" ? 0 : parseMaxPRs(max_prs, 30, 200);
const maxPRs = scope === "all" ? 50 : parseMaxPRs(max_prs, 20, 50);
const cacheKey = scope === "all"
? `overview:${username.toLowerCase()}:lines_scope=all`
: `overview:${username.toLowerCase()}:lines_scope=recent:max_prs=${maxPRs}`;
Comment on lines 49 to 53
Expand All @@ -62,36 +62,41 @@ module.exports = async (req, res) => {
try {
const [prs, profile, contributionData, totalIssues, totalStars] = await Promise.all([
fetchUserPullRequests(username),
fetchUserProfile(username).catch(() => ({ public_repos: 0, public_gists: 0 })),
fetchUserProfile(username),
fetchContributionData(username),
fetchUserIssues(username),
fetchUserTotalStars(username),
]);

const linesChanged = await fetchRecentPRLinesChanged(prs, scope === "all" ? prs.length : maxPRs);
const linesChanged = await fetchRecentPRLinesChanged(prs, maxPRs);

// Count repos contributed to from PR data
const reposContributed = new Set();
prs.forEach(pr => {
(prs || []).forEach(pr => {
if (pr.repository_url) {
reposContributed.add(pr.repository_url.split("/repos/")[1]);
}
});

data = {
totalStars,
totalCommits: contributionData.totalContributions || 0,
totalPRs: prs.length,
totalIssues,
contributedTo: reposContributed.size || profile.public_repos || 0,
totalStars: totalStars || 0,
totalCommits: contributionData?.totalContributions || 0,
totalPRs: prs?.length || 0,
totalIssues: totalIssues || 0,
contributedTo: reposContributed.size || profile?.public_repos || 0,
linesChanged,
Comment on lines 80 to 86
};

setCache(cacheKey, data, CACHE_TTL);
} catch (fetchErr) {
console.error("Overview fetch error:", fetchErr.message);
res.status(200).send(errorSVG(`Could not fetch data for ${username}`));
return;
data = {
totalStars: 0,
totalCommits: 0,
totalPRs: 0,
totalIssues: 0,
contributedTo: 0,
linesChanged: 0,
};
}
}

Expand Down
14 changes: 6 additions & 8 deletions api/pr-stats.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ module.exports = async (req, res) => {
try {
const [prs, profile, openPRCount, closedPRCount, mergedPRCount] = await Promise.all([
fetchUserPullRequests(username),
fetchUserProfile(username).catch(() => ({ name: username, login: username })),
fetchUserProfile(username),
fetchOpenPullRequests(username),
fetchClosedPullRequests(username),
fetchMergedPullRequests(username),
Expand All @@ -50,18 +50,17 @@ module.exports = async (req, res) => {

data = {
repoMap,
totalPRs: prs.length,
openPRs: openPRCount,
closedPRs: closedPRCount,
mergedPRs: mergedPRCount,
totalPRs: prs?.length || 0,
openPRs: openPRCount || 0,
closedPRs: closedPRCount || 0,
mergedPRs: mergedPRCount || 0,
repoCount: Object.keys(repoMap).length,
profileName: profile.name || profile.login || username,
profileName: profile?.name || profile?.login || username,
Comment on lines 51 to +58
};

setCache(cacheKey, data, CACHE_TTL);
} catch (fetchErr) {
console.error("PR fetch error:", fetchErr.message);
// Fallback to zeros so card still renders
data = {
repoMap: {},
totalPRs: 0,
Expand All @@ -71,7 +70,6 @@ module.exports = async (req, res) => {
repoCount: 0,
profileName: username,
};
setCache(cacheKey, data, CACHE_TTL);
}
}

Expand Down
13 changes: 11 additions & 2 deletions api/profile.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ module.exports = async (req, res) => {
if (!data) {
try {
const profile = await fetchUserProfile(username);
if (!profile) throw new Error("No profile");
data = {
username: profile.login || username,
name: profile.name || profile.login || username,
Expand All @@ -52,8 +53,16 @@ module.exports = async (req, res) => {
setCache(cacheKey, data, CACHE_TTL);
} catch (fetchErr) {
console.error("Profile fetch error:", fetchErr.message);
res.status(200).send(errorSVG(`Could not fetch profile for ${username}`));
return;
data = {
username: username,
name: username,
bio: "",
avatarUrl: "",
createdAt: null,
publicRepos: 0,
followers: 0,
following: 0,
};
}
}

Expand Down
6 changes: 3 additions & 3 deletions api/streak.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,8 @@ module.exports = async (req, res) => {
fetchContributionData(username),
fetchTotalCommitCount(username),
]);
const { days, totalContributions } = contributionData;

const { days = [], totalContributions = 0 } = contributionData || {};

const sortedDays = [...days].sort((a, b) => a.date.localeCompare(b.date));
let currentStreak = 0;
Expand All @@ -61,8 +62,7 @@ module.exports = async (req, res) => {
setCache(cacheKey, data, CACHE_TTL);
} catch (fetchErr) {
console.error("Streak fetch error:", fetchErr.message);
res.status(200).send(errorSVG(`Could not fetch data for ${username}`));
return;
data = { currentStreak: 0, longestStreak: 0, totalContributions: 0, totalCommits: 0 };
}
}

Expand Down
Loading
Loading