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