KrishnaCosmic's picture
bug fix 44
229d06d
/**
* GitHub Events/Activity Route
*
* GET /api/github/events/[username]
* Fetches user's recent GitHub events/activity
*/
import { NextRequest, NextResponse } from "next/server";
import { db } from "@/db";
import { users } from "@/db/schema";
import { eq, sql } from "drizzle-orm";
interface GitHubEvent {
id: string;
type: string;
repo: { name: string };
created_at: string;
payload: any;
}
interface ActivityItem {
type: 'commits' | 'pr' | 'issue' | 'repo' | 'review';
month: string;
description: string;
repos?: { name: string; count: number; maxCount?: number }[];
date?: string;
link?: string;
}
export async function GET(
request: NextRequest,
context: { params: Promise<{ username: string }> }
) {
try {
const { username } = await context.params;
const { searchParams } = new URL(request.url);
const year = parseInt(searchParams.get('year') || String(new Date().getFullYear()));
// Get user's GitHub token (case-insensitive lookup)
const user = await db.select()
.from(users)
.where(sql`LOWER(${users.username}) = LOWER(${username})`)
.limit(1);
const githubToken = user[0]?.githubAccessToken || process.env.GITHUB_TOKEN;
if (!githubToken) {
return NextResponse.json({ error: "No GitHub token available" }, { status: 401 });
}
// Fetch GitHub events (last 100)
const response = await fetch(`https://api.github.com/users/${username}/events?per_page=100`, {
headers: {
'Authorization': `Bearer ${githubToken}`,
'Accept': 'application/vnd.github.v3+json'
}
});
if (!response.ok) {
console.error(`GitHub events API error: ${response.status}`);
return NextResponse.json({ activities: [], source: 'error' });
}
const events: GitHubEvent[] = await response.json();
// Group events by month and type
const monthlyActivity: Record<string, {
commits: Record<string, number>;
prs: { repo: string; title: string; date: string }[];
issues: { repo: string; title: string; date: string }[];
repos: string[];
}> = {};
events.forEach(event => {
const date = new Date(event.created_at);
if (date.getFullYear() !== year) return;
const monthKey = date.toLocaleDateString('en-US', { month: 'long', year: 'numeric' });
if (!monthlyActivity[monthKey]) {
monthlyActivity[monthKey] = {
commits: {},
prs: [],
issues: [],
repos: []
};
}
const month = monthlyActivity[monthKey];
switch (event.type) {
case 'PushEvent':
const commits = event.payload?.commits?.length || 0;
month.commits[event.repo.name] = (month.commits[event.repo.name] || 0) + commits;
break;
case 'PullRequestEvent':
if (event.payload?.action === 'opened') {
month.prs.push({
repo: event.repo.name,
title: event.payload.pull_request?.title || 'Pull Request',
date: date.toLocaleDateString('en-US', { month: 'short', day: 'numeric' })
});
}
break;
case 'IssuesEvent':
if (event.payload?.action === 'opened') {
month.issues.push({
repo: event.repo.name,
title: event.payload.issue?.title || 'Issue',
date: date.toLocaleDateString('en-US', { month: 'short', day: 'numeric' })
});
}
break;
case 'CreateEvent':
if (event.payload?.ref_type === 'repository') {
month.repos.push(event.repo.name);
}
break;
}
});
// Convert to activity items
const activities: ActivityItem[] = [];
Object.entries(monthlyActivity).forEach(([month, data]) => {
// Commits summary - only include repos with actual commits
const repoCommits = Object.entries(data.commits).filter(([_, count]) => count > 0);
if (repoCommits.length > 0) {
const totalCommits = repoCommits.reduce((sum, [_, count]) => sum + count, 0);
const maxCount = Math.max(...repoCommits.map(([_, c]) => c));
activities.push({
type: 'commits',
month,
description: `Created ${totalCommits} commit${totalCommits !== 1 ? 's' : ''} in ${repoCommits.length} repositor${repoCommits.length !== 1 ? 'ies' : 'y'}`,
repos: repoCommits.map(([name, count]) => ({ name, count, maxCount }))
});
}
// Repos created
if (data.repos.length > 0) {
activities.push({
type: 'repo',
month,
description: `Created ${data.repos.length} repositor${data.repos.length !== 1 ? 'ies' : 'y'}`
});
}
// PRs opened
data.prs.forEach(pr => {
activities.push({
type: 'pr',
month,
description: `Opened a pull request in ${pr.repo}`,
date: pr.date,
link: pr.repo
});
});
});
return NextResponse.json({
activities,
months: Object.keys(monthlyActivity),
year,
source: 'github'
});
} catch (error) {
console.error("GET /api/github/events/:username error:", error);
return NextResponse.json({ activities: [], error: "Failed to fetch events" }, { status: 500 });
}
}