···11+MIT License Copyright (c) 2024 flo-bit
22+33+Permission is hereby granted, free of
44+charge, to any person obtaining a copy of this software and associated
55+documentation files (the "Software"), to deal in the Software without
66+restriction, including without limitation the rights to use, copy, modify, merge,
77+publish, distribute, sublicense, and/or sell copies of the Software, and to
88+permit persons to whom the Software is furnished to do so, subject to the
99+following conditions:
1010+1111+The above copyright notice and this permission notice
1212+(including the next paragraph) shall be included in all copies or substantial
1313+portions of the Software.
1414+1515+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
1616+ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
1717+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO
1818+EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
1919+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
2020+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
2121+THE SOFTWARE.
···11+# edge-function-github-contributions
22+33+vercel edge function that returns some infos of your github account as a json (contributions, repositories, followers, following, issues, prs, status).
44+55+can be called from any frontend application, including static sites.
66+77+detailed infos:
88+99+- contributions of the last year total, and split by day
1010+(return the contribution count of each day and a color for each day, that is used to color the heatmap on github)
1111+1212+- contributions of the last year split by type (commits, pull requests, issues)
1313+1414+- up to 100 user owned repositories sorted by most stars
1515+(return the name, description, stars, forks, watchers, top 10 languages each with a name and size in bytes)
1616+1717+- count of: starred repositories, followers, following, repositories, pr requests, issues
1818+1919+- current status of user
2020+2121+## Usage
2222+2323+1. create a personal access token on github, go to `settings` -> `developer settings` -> `personal access tokens (classic)` -> `generate new token (classic)`, set expiration date to "No expiration" and add the `public_repo`, `read:user` and `repo:status` scopes.
2424+2525+2. fork repository
2626+2727+3. deploy to vercel
2828+2929+[](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2Fflo-bit%2Fedge-function-github-contribution&env=GITHUB_TOKEN)
3030+3131+4. set environment variable `GITHUB_TOKEN` to the personal access token you created in step 1
3232+3333+5. get the url of the deployed function and use it in your application, by sending a GET request to the url
3434+3535+```
3636+https://<your-deployment-url>/api/github-data
3737+```
3838+3939+6. simple example using fetch:
4040+4141+```js
4242+const res = await fetch('https://edge-function-github-contribution.vercel.app/api/github-data');
4343+const json = await res.json(); // as GitHubResponse, see below
4444+4545+console.log(json);
4646+```
4747+4848+7. it's recommended to remove data points you don't need, to reduce the size of the response and speed up the request
4949+5050+## development
5151+5252+to run the function locally, you need to have the vercel cli installed:
5353+5454+```
5555+npm i -g vercel@latest
5656+```
5757+5858+then clone the repository:
5959+6060+```
6161+git clone https://github.com/flo-bit/edge-function-github-contribution
6262+cd edge-function-github-contribution
6363+```
6464+6565+add a `.env` file with the `GITHUB_TOKEN` environment variable and run:
6666+6767+```
6868+vercel dev
6969+```
7070+7171+## response type
7272+7373+if you're using it in a typescript project, here the type definition of the (successful) response:
7474+7575+```ts
7676+const githubGraphColors = ['#ebedf0', '#9be9a8', '#40c463', '#30a14e', '#216e39'] as const;
7777+7878+// Define a type based on the color values
7979+export type GithubGraphColor = (typeof githubGraphColors)[number];
8080+8181+export type GitHubResponse = {
8282+ data: {
8383+ viewer: {
8484+ login: string;
8585+ contributionsCollection: {
8686+ totalCommitContributions: number;
8787+ totalIssueContributions: number;
8888+ totalPullRequestContributions: number;
8989+ totalPullRequestReviewContributions: number;
9090+ contributionCalendar: {
9191+ totalContributions: number;
9292+ weeks: {
9393+ contributionDays: {
9494+ date: string;
9595+ contributionCount: number;
9696+ color: GithubGraphColor;
9797+ }[];
9898+ }[];
9999+ };
100100+ };
101101+ repositories: {
102102+ totalCount: number;
103103+ nodes: {
104104+ name: string;
105105+ stargazerCount: number;
106106+ forkCount: number;
107107+ watchers: {
108108+ totalCount: number;
109109+ };
110110+ languages: {
111111+ totalSize: number;
112112+ edges: {
113113+ node: {
114114+ name: string;
115115+ };
116116+ size: number;
117117+ }[];
118118+ };
119119+ }[];
120120+ };
121121+ starredRepositories: {
122122+ totalCount: number;
123123+ };
124124+ followers: {
125125+ totalCount: number;
126126+ };
127127+ following: {
128128+ totalCount: number;
129129+ };
130130+ issues_sum: {
131131+ totalCount: number;
132132+ };
133133+ issues_open: {
134134+ totalCount: number;
135135+ };
136136+ issues_closed: {
137137+ totalCount: number;
138138+ };
139139+ pr_sum: {
140140+ totalCount: number;
141141+ };
142142+ pr_open: {
143143+ totalCount: number;
144144+ };
145145+ pr_closed: {
146146+ totalCount: number;
147147+ };
148148+ pr_merged: {
149149+ totalCount: number;
150150+ };
151151+ status: {
152152+ emoji?: string;
153153+ message?: string;
154154+ expiresAt?: string;
155155+ updatedAt?: string;
156156+ } | null;
157157+ };
158158+ };
159159+};
160160+```
161161+162162+## calculate more infos
163163+164164+here is a typescript example to combine some of the infos to get:
165165+166166+- total stars of user owned repositories
167167+- total forks of user owned repositories
168168+- total watchers of user owned repositories
169169+- total size of user owned repositories
170170+- total size of each language in user owned repositories
171171+172172+```ts
173173+let totalStars = 0;
174174+let totalSize = 0;
175175+let totalForks = 0;
176176+let totalWatchers = 0;
177177+178178+const languages = new Map<string, number>();
179179+180180+for (const repo of json.data.viewer.repositories.nodes) {
181181+ totalStars += repo.stargazerCount;
182182+ totalSize += repo.languages.totalSize;
183183+ totalForks += repo.forkCount;
184184+ totalWatchers += repo.watchers.totalCount;
185185+186186+ for (const edge of repo.languages.edges) {
187187+ const language = edge.node.name;
188188+ const size = edge.size;
189189+ languages.set(language, (languages.get(language) ?? 0) + size);
190190+ }
191191+}
192192+193193+json.data.viewer.totalStars = totalStars;
194194+json.data.viewer.totalSize = totalSize;
195195+json.data.viewer.totalForks = totalForks;
196196+json.data.viewer.totalWatchers = totalWatchers;
197197+json.data.viewer.languages = Array.from(languages, ([name, size]) => ({ name, size })).sort(
198198+ (a, b) => b.size - a.size
199199+);
200200+```
201201+202202+## license
203203+204204+MIT