github-readme-stats/api/index.js

157 lines
4.2 KiB
JavaScript
Raw Normal View History

2020-07-09 18:33:41 +08:00
const axios = require("axios");
2020-07-10 20:42:31 +08:00
const { renderError, kFormatter } = require("../utils");
2020-07-09 18:33:41 +08:00
require("dotenv").config();
async function fetchStats(username) {
2020-07-10 20:42:31 +08:00
if (!username) throw Error("Invalid username");
2020-07-09 18:33:41 +08:00
const res = await axios({
url: "https://api.github.com/graphql",
method: "post",
headers: {
Authorization: `bearer ${process.env.GITHUB_TOKEN}`,
},
data: {
query: `
query userInfo($login: String!) {
user(login: $login) {
name
repositoriesContributedTo(first: 100, contributionTypes: [COMMIT, ISSUE, PULL_REQUEST, REPOSITORY]) {
totalCount
}
2020-07-09 21:16:41 +08:00
contributionsCollection {
totalCommitContributions
}
2020-07-09 18:33:41 +08:00
pullRequests(first: 100) {
totalCount
}
issues(first: 100) {
totalCount
}
2020-07-09 18:33:41 +08:00
repositories(first: 100) {
nodes {
stargazers {
totalCount
}
}
}
}
}
`,
variables: {
login: username,
},
},
});
const stats = {
name: "",
totalPRs: 0,
2020-07-09 21:16:41 +08:00
totalCommits: 0,
totalIssues: 0,
totalStars: 0,
contributedTo: 0,
};
2020-07-10 20:42:31 +08:00
if (res.data.errors) {
console.log(res.data.errors);
throw Error("Could not fetch user");
}
2020-07-09 18:33:41 +08:00
const user = res.data.data.user;
stats.name = user.name;
stats.totalIssues = user.issues.totalCount;
2020-07-09 21:16:41 +08:00
stats.totalCommits = user.contributionsCollection.totalCommitContributions;
stats.totalPRs = user.pullRequests.totalCount;
stats.contributedTo = user.repositoriesContributedTo.totalCount;
2020-07-09 18:33:41 +08:00
stats.totalStars = user.repositories.nodes.reduce((prev, curr) => {
return prev + curr.stargazers.totalCount;
}, 0);
return stats;
}
const createTextNode = (icon, label, value, lheight) => {
2020-07-10 20:42:31 +08:00
const classname = icon === "★" && "star-icon";
return `
<tspan x="25" dy="${lheight}" class="stat bold">
2020-07-10 20:42:31 +08:00
<tspan class="icon ${classname}" fill="#4C71F2">${icon}</tspan> ${label}:</tspan>
<tspan x="155" dy="0" class="stat">${kFormatter(value)}</tspan>
`;
};
const renderSVG = (stats, options) => {
2020-07-09 21:16:41 +08:00
const {
name,
totalStars,
totalCommits,
totalIssues,
totalPRs,
contributedTo,
} = stats;
const { hide, show_icons, hide_border, line_height } = options || {};
const lheight = line_height || 25;
const STAT_MAP = {
stars: createTextNode("★", "Total Stars", totalStars, lheight),
commits: createTextNode("🕗", "Total Commits", totalCommits, lheight),
prs: createTextNode("🔀", "Total PRs", totalPRs, lheight),
issues: createTextNode("ⓘ", "Total Issues", totalIssues, lheight),
contribs: createTextNode("📕", "Contributed to", contributedTo, lheight),
};
2020-07-09 21:16:41 +08:00
const statItems = Object.keys(STAT_MAP)
.filter((key) => !hide.includes(key))
.map((key) => STAT_MAP[key]);
const height = 45 + (statItems.length + 1) * lheight;
2020-07-09 21:16:41 +08:00
return `
<svg width="495" height="${height}" viewBox="0 0 495 ${height}" fill="none" xmlns="http://www.w3.org/2000/svg">
<style>
.header { font: 600 18px 'Segoe UI', Ubuntu, Sans-Serif; fill: #2F80ED }
.stat { font: 600 14px 'Segoe UI', Ubuntu, Sans-Serif; fill: #333 }
.star-icon { font: 600 18px 'Segoe UI', Ubuntu, Sans-Serif; }
.bold { font-weight: 700 }
2020-07-09 21:16:41 +08:00
.icon {
display: none;
${!!show_icons && "display: block"}
2020-07-09 21:16:41 +08:00
}
</style>
2020-07-09 23:02:34 +08:00
${
!hide_border &&
`<rect x="0.5" y="0.5" width="494" height="99%" rx="4.5" fill="#FFFEFE" stroke="#E4E2E2"/>`
}
2020-07-09 21:16:41 +08:00
2020-07-09 21:58:56 +08:00
<text x="25" y="35" class="header">${name}'s GitHub Stats</text>
<text y="45">
2020-07-09 21:16:41 +08:00
${statItems}
</text>
</svg>
`;
};
2020-07-09 18:33:41 +08:00
module.exports = async (req, res) => {
2020-07-11 01:05:14 +08:00
const { username, hide, hide_border, show_icons, line_height } = req.query;
2020-07-10 20:42:31 +08:00
let stats;
2020-07-09 18:33:41 +08:00
res.setHeader("Content-Type", "image/svg+xml");
2020-07-10 20:42:31 +08:00
try {
stats = await fetchStats(username);
} catch (err) {
return res.send(renderError(err.message));
}
res.send(
renderSVG(stats, {
hide: JSON.parse(hide || "[]"),
show_icons,
hide_border,
line_height,
})
);
2020-07-09 18:33:41 +08:00
};