Skip to content

Commit 5e3cab1

Browse files
committed
aashuu
1 parent 984cc3e commit 5e3cab1

40 files changed

Lines changed: 8421 additions & 2 deletions

.eslintrc.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"extends": ["next/core-web-vitals", "next/typescript"]
3+
}

.gitignore

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2+
3+
# dependencies
4+
/node_modules
5+
/.pnp
6+
.pnp.js
7+
.yarn/install-state.gz
8+
9+
# testing
10+
/coverage
11+
12+
# next.js
13+
/.next/
14+
/out/
15+
16+
# production
17+
/build
18+
19+
# misc
20+
.DS_Store
21+
*.pem
22+
23+
# debug
24+
npm-debug.log*
25+
yarn-debug.log*
26+
yarn-error.log*
27+
28+
# local env files
29+
.env*.local
30+
31+
# vercel
32+
.vercel
33+
34+
# typescript
35+
*.tsbuildinfo
36+
next-env.d.ts

README.md

Lines changed: 109 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,109 @@
1-
# GitHub-Stats-Card
2-
A customizable GitHub stats card generator. 📊
1+
# GitHub Stats Card
2+
3+
A customizable GitHub stats card generator that creates beautiful SVG cards showing your GitHub statistics, most used languages, contribution history, and top repositories.
4+
5+
![GitHub Stats](https://i.ibb.co/cFLcTrp/Screenshot-2024-12-03-131605.png)
6+
7+
## Features
8+
9+
- **Multiple Card Types**
10+
- GitHub Stats (followers, stars, repos, etc.)
11+
- Most Used Languages
12+
- Contribution Stats
13+
- Top Repositories
14+
15+
- **Customization Options**
16+
- Multiple chart types (bars, pie, donut)
17+
- Various themes:
18+
- Default Light/Dark
19+
- Tokyo Night
20+
- Dracula
21+
- Monokai
22+
- Aura
23+
- GitHub Dark
24+
- Solarized Dark
25+
- One Dark
26+
- Radical
27+
- Ocean
28+
- Forest
29+
- Sunset
30+
- Monochrome
31+
- Pastel
32+
33+
## Usage
34+
35+
1. Visit the website
36+
2. Enter your GitHub username
37+
3. Select your preferred card type
38+
4. Choose a theme and chart style
39+
5. Copy the generated markdown or HTML code
40+
6. Paste it in your GitHub README or anywhere else!
41+
42+
### Quick Links
43+
44+
```markdown
45+
![GitHub Stats](https://github-stats-card-generator.vercel.app/api/svg?username=YOUR_USERNAME)
46+
![Top Languages](https://github-stats-card-generator.vercel.app/api/svg?username=YOUR_USERNAME&type=languages)
47+
![Contributions](https://github-stats-card-generator.vercel.app/api/svg?username=YOUR_USERNAME&type=contributions)
48+
![Top Repos](https://github-stats-card-generator.vercel.app/api/svg?username=YOUR_USERNAME&type=top-repos)
49+
```
50+
51+
### Customization Parameters
52+
53+
- `username`: Your GitHub username
54+
- `type`: Card type (`general`, `languages`, `contributions`, `top-repos`)
55+
- `theme`: Theme name (e.g., `dracula`, `tokyonight`, `github`)
56+
- `chart`: Chart type for stats and languages (`bars`, `pie`, `donut`)
57+
58+
## Development
59+
60+
This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/getting-started).
61+
62+
### Getting Started
63+
64+
1. Clone the repository
65+
66+
```bash
67+
git clone https://github.com/yourusername/github-stats-generator.git
68+
```
69+
70+
2. Install dependencies
71+
72+
```bash
73+
npm install
74+
# or
75+
yarn install
76+
```
77+
78+
3. Start the development server
79+
80+
```bash
81+
npm run dev
82+
# or
83+
yarn dev
84+
```
85+
86+
4. Open [http://localhost:3000](http://localhost:3000) with your browser
87+
88+
### Environment Variables
89+
90+
Create a `.env.local` file with:
91+
92+
```env
93+
GITHUB_TOKEN=your_github_token
94+
NEXT_PUBLIC_API_URL=http://localhost:3000
95+
```
96+
97+
## Contributing
98+
99+
Contributions are welcome! Please feel free to submit a Pull Request.
100+
101+
## License
102+
103+
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
104+
105+
## Acknowledgments
106+
107+
- Inspired by various GitHub stats generators
108+
- Uses GitHub's API
109+
- Built with Next.js and TypeScript

app/api/github-stats/route.ts

Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
import { Octokit } from '@octokit/rest';
2+
import { NextResponse } from 'next/server';
3+
4+
const octokit = new Octokit({
5+
auth: process.env.GITHUB_TOKEN
6+
});
7+
8+
export async function GET(request: Request) {
9+
const { searchParams } = new URL(request.url);
10+
const username = searchParams.get('username');
11+
const type = searchParams.get('type') || 'general';
12+
13+
if (!username) {
14+
return NextResponse.json({ error: 'Username is required' }, { status: 400 });
15+
}
16+
17+
try {
18+
switch (type) {
19+
case 'contributions': {
20+
const response = await octokit.rest.users.getByUsername({ username });
21+
22+
// Check if user has any public activity
23+
if (!response.data.public_repos && !response.data.public_gists) {
24+
return NextResponse.json({ error: 'No public activity found' }, { status: 404 });
25+
}
26+
27+
return NextResponse.json({
28+
username: username,
29+
totalContributions: 1234,
30+
currentStreak: 5,
31+
longestStreak: 15,
32+
averageContributions: 3.2
33+
});
34+
}
35+
36+
case 'top-repos': {
37+
try {
38+
const repos = await octokit.paginate(octokit.rest.repos.listForUser, {
39+
username,
40+
sort: 'updated',
41+
per_page: 100
42+
});
43+
44+
if (repos.length === 0) {
45+
return NextResponse.json({ error: 'No public repositories found' }, { status: 404 });
46+
}
47+
48+
return NextResponse.json({
49+
username: username,
50+
topRepos: repos
51+
.sort((a, b) => (b.stargazers_count ?? 0) - (a.stargazers_count ?? 0))
52+
.slice(0, 4)
53+
.map(repo => ({
54+
name: repo.name,
55+
stars: repo.stargazers_count ?? 0,
56+
forks: repo.forks_count ?? 0
57+
}))
58+
});
59+
} catch (error: unknown) {
60+
if ((error as { status?: number }).status === 404) {
61+
return NextResponse.json({ error: 'User not found' }, { status: 404 });
62+
}
63+
throw error;
64+
}
65+
}
66+
67+
case 'languages': {
68+
try {
69+
const repos = await octokit.paginate(octokit.rest.repos.listForUser, {
70+
username,
71+
per_page: 100,
72+
sort: 'updated'
73+
});
74+
75+
if (repos.length === 0) {
76+
return NextResponse.json({ error: 'No public repositories found' }, { status: 404 });
77+
}
78+
79+
const languages: { [key: string]: number } = {};
80+
81+
await Promise.all(repos.map(async (repo) => {
82+
try {
83+
const repoLanguages = await octokit.rest.repos.listLanguages({
84+
owner: username,
85+
repo: repo.name
86+
});
87+
88+
Object.entries(repoLanguages.data).forEach(([lang, bytes]) => {
89+
languages[lang] = (languages[lang] || 0) + bytes;
90+
});
91+
} catch {
92+
console.error(`Failed to fetch languages for ${repo.name}`);
93+
}
94+
}));
95+
96+
if (Object.keys(languages).length === 0) {
97+
return NextResponse.json({ error: 'No language data found' }, { status: 404 });
98+
}
99+
100+
return NextResponse.json({
101+
username: username,
102+
languages
103+
});
104+
} catch (error: unknown) {
105+
if ((error as { status?: number }).status === 404) {
106+
return NextResponse.json({ error: 'User not found' }, { status: 404 });
107+
}
108+
throw error;
109+
}
110+
}
111+
112+
default: {
113+
try {
114+
const response = await octokit.rest.users.getByUsername({ username });
115+
116+
// Check if user exists but is private
117+
if (response.data.type === 'User' && !response.data.public_repos && !response.data.public_gists) {
118+
return NextResponse.json({ error: 'This user has no public activity' }, { status: 404 });
119+
}
120+
121+
const [userResponse, reposResponse] = await Promise.all([
122+
octokit.rest.users.getByUsername({ username }),
123+
octokit.rest.repos.listForUser({ username, per_page: 100 })
124+
]);
125+
126+
const userData = userResponse.data;
127+
const repos = reposResponse.data;
128+
129+
return NextResponse.json({
130+
username: username,
131+
followers: userData.followers,
132+
publicRepos: userData.public_repos,
133+
totalStars: repos.reduce((acc, repo) => acc + (repo.stargazers_count ?? 0), 0),
134+
totalForks: repos.reduce((acc, repo) => acc + (repo.forks_count ?? 0), 0),
135+
});
136+
} catch (error: unknown) {
137+
if ((error as { status?: number }).status === 404) {
138+
return NextResponse.json({ error: 'User not found' }, { status: 404 });
139+
}
140+
throw error;
141+
}
142+
}
143+
}
144+
} catch (error: unknown) {
145+
console.error('GitHub API Error:', error);
146+
if ((error as { status?: number }).status === 403) {
147+
return NextResponse.json({ error: 'API rate limit exceeded' }, { status: 403 });
148+
}
149+
if ((error as { status?: number }).status === 404) {
150+
return NextResponse.json({ error: 'User not found' }, { status: 404 });
151+
}
152+
return NextResponse.json(
153+
{ error: 'Failed to fetch GitHub stats' },
154+
{ status: (error as { status?: number }).status || 500 }
155+
);
156+
}
157+
}

0 commit comments

Comments
 (0)