├── _config.yml
├── tsconfig.json
├── src
├── withexpress.ts
├── index.html
├── helper
│ ├── getRandomToken.ts
│ ├── repositoryFetch.ts
│ ├── getData.ts
│ ├── basicFetch.ts
│ └── template.ts
└── api
│ └── index.ts
├── scripts
└── createDynamicIndexHtml.js
├── package.json
├── README.md
├── .gitignore
└── LICENSE
/_config.yml:
--------------------------------------------------------------------------------
1 | theme: jekyll-theme-minimal
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es6",
4 | "module": "commonjs",
5 | "outDir": "./dist",
6 | "rootDir": "./src",
7 | "strict": true,
8 | "moduleResolution": "node",
9 | "esModuleInterop": true
10 | },
11 | "exclude": ["./node_modules"]
12 | }
13 |
--------------------------------------------------------------------------------
/src/withexpress.ts:
--------------------------------------------------------------------------------
1 | import express from "express";
2 | const app = express();
3 | import readmeStats from "./api/index";
4 |
5 | app.use("/api", readmeStats);
6 |
7 | const port: number = Number(process.env.PORT) || 3000;
8 |
9 | app.listen(port, () => {
10 | console.log(`Running on http://localhost:${port}`);
11 | });
12 |
--------------------------------------------------------------------------------
/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
Readme Stats for Github
3 |
4 |
5 |
6 |
7 |
11 |
12 |
13 |
14 |
15 | Redirecting to main website...
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/src/helper/getRandomToken.ts:
--------------------------------------------------------------------------------
1 | require("dotenv").config();
2 |
3 | function getRandomToken(bearerHeader: boolean): string {
4 | let getEnvs: any = process.env;
5 | let getGhEnv: any = Object.keys(getEnvs).filter((key) =>
6 | key.startsWith("GH_")
7 | );
8 | getGhEnv = getGhEnv.map((key: string) => getEnvs[key]);
9 | let getRandomEnv: string =
10 | getGhEnv[Math.floor(Math.random() * getGhEnv.length)];
11 |
12 | if (bearerHeader) {
13 | return `Bearer ${getRandomEnv}`;
14 | }
15 | return getRandomEnv;
16 | }
17 |
18 | export default getRandomToken;
19 |
--------------------------------------------------------------------------------
/scripts/createDynamicIndexHtml.js:
--------------------------------------------------------------------------------
1 | // const fs = require("fs");
2 | // const Markdoc = require("@markdoc/markdoc");
3 | // const readmeMd = fs.readFileSync("./README.md", "utf8");
4 |
5 | // const ast = Markdoc.parse(readmeMd);
6 | // const content = Markdoc.transform(ast);
7 | // const markdownhtml = Markdoc.renderers.html(content);
8 |
9 | // const html = `
10 | //
11 | //
12 | //
13 | //
14 | //
15 | // Readme Stats Github
16 | //
22 | //
23 | //
24 | // ${markdownhtml}
25 | //
26 | //
27 | // `;
28 |
29 | // fs.writeFileSync("./src/index.html", html);
30 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "readme-stats-github",
3 | "version": "1.1.0",
4 | "description": "Generate your GitHub's Stats in SVG",
5 | "main": "src/api/withexpress.ts",
6 | "dependencies": {
7 | "axios": "^0.21.0",
8 | "dotenv": "^16.0.1",
9 | "express": "^4.18.1",
10 | "millify": "^4.0.0",
11 | "node-base64-image": "^2.0.1"
12 | },
13 | "scripts": {
14 | "dev": "nodemon ./src/withexpress.ts",
15 | "build": "tsc",
16 | "start": "node ./dist/withexpress.js"
17 | },
18 | "author": "Tuhin Kanti Pal",
19 | "license": "Apache-2.0",
20 | "repository": {
21 | "type": "git",
22 | "url": "git+https://github.com/tuhinpal/readme-stats-github.git"
23 | },
24 | "keywords": [
25 | "github-readme-stats",
26 | "github-stats"
27 | ],
28 | "bugs": {
29 | "url": "https://github.com/tuhinpal/readme-stats-github/issues"
30 | },
31 | "homepage": "https://github.com/tuhinpal/readme-stats-github#readme",
32 | "devDependencies": {
33 | "@markdoc/markdoc": "^0.1.3",
34 | "@types/express": "^4.17.13",
35 | "@types/node": "^18.0.3",
36 | "nodemon": "^2.0.19",
37 | "ts-node": "^10.8.2",
38 | "typescript": "^4.7.4"
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/src/api/index.ts:
--------------------------------------------------------------------------------
1 | import getData from "../helper/getData";
2 | import template from "../helper/template";
3 |
4 | export type UiConfig = {
5 | text_color: string;
6 | icon_color: string;
7 | border_color: string;
8 | card_color: string;
9 | };
10 |
11 | export default async function readmeStats(req: any, res: any): Promise {
12 | try {
13 | let username = req.query.username;
14 |
15 | let uiConfig: UiConfig = {
16 | text_color: req.query.tc || "000",
17 | icon_color: req.query.ic || "FF0000",
18 | border_color: req.query.bc || "7e7979",
19 | card_color: req.query.cc || "fff",
20 | };
21 |
22 | if (!username) throw new Error("Username is required");
23 |
24 | var fetchStats = await getData(username);
25 | res.setHeader("Cache-Control", "s-maxage=1800, stale-while-revalidate");
26 |
27 | if (req.query.format === "json") {
28 | res.json(fetchStats);
29 | } else {
30 | res.setHeader("Content-Type", "image/svg+xml");
31 | let svg = template(fetchStats, uiConfig);
32 | res.send(svg);
33 | }
34 | } catch (error: any) {
35 | res.setHeader("Cache-Control", "s-maxage=3600, stale-while-revalidate");
36 | res.status(500).send(error.message);
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/src/helper/repositoryFetch.ts:
--------------------------------------------------------------------------------
1 | import axios from "axios";
2 | import getRandomToken from "./getRandomToken";
3 |
4 | type RepositoryData = {
5 | stars: number;
6 | forks: number;
7 | openedIssues: number;
8 | };
9 |
10 | export default async function repositoryFetch(
11 | username: string,
12 | totalpage: number
13 | ): Promise {
14 | let stars = 0;
15 | let forks = 0;
16 | let openedIssues = 0;
17 |
18 | await Promise.all(
19 | Array.from(
20 | { length: totalpage },
21 | async (_, i) => await getPerPageReposData(username, i + 1)
22 | )
23 | ).then((data: any) => {
24 | data.forEach((repo: any) => {
25 | stars += repo.stars;
26 | forks += repo.forks;
27 | openedIssues += repo.openedIssues;
28 | });
29 | });
30 |
31 | return {
32 | stars,
33 | forks,
34 | openedIssues,
35 | };
36 | }
37 |
38 | async function getPerPageReposData(
39 | username: string,
40 | pageno: number
41 | ): Promise