File size: 6,600 Bytes
97ab550
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
/**
 * Sync User GitHub Data Route
 * 
 * POST /api/sync/user-data
 * Triggers an immediate fetch of the user's GitHub contribution history
 * for new contributors to populate the heatmap and stats.
 */

import { NextRequest, NextResponse } from "next/server";
import { getCurrentUser } from "@/lib/auth";
import { Octokit } from "@octokit/rest";
import { db } from "@/db";
import { users, profiles } from "@/db/schema";
import { eq } from "drizzle-orm";
import { fetchGitHubContributions } from "@/lib/github-contributions";

export async function POST(request: NextRequest) {
    try {
        const user = await getCurrentUser(request);
        if (!user) {
            return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
        }

        if (!user.githubAccessToken) {
            return NextResponse.json({ error: "GitHub access token not found" }, { status: 400 });
        }

        const octokit = new Octokit({ auth: user.githubAccessToken });
        
        console.log(`[SyncUserData] Starting sync for ${user.username}`);
        
        let totalPRs = 0;
        let totalIssues = 0;
        const errors: string[] = [];

        // 1. Fetch contribution calendar data first (for heatmap)
        const contributionData = await fetchGitHubContributions(user.username, user.githubAccessToken);
        
        // 2. Search for all PRs authored by this user to get count
        try {
            const { data: prSearchResult } = await octokit.search.issuesAndPullRequests({
                q: `type:pr author:${user.username}`,
                sort: 'updated',
                order: 'desc',
                per_page: 1, // We just need the total count
            });
            totalPRs = prSearchResult.total_count;
        } catch (searchError: any) {
            console.error('PR search error:', searchError);
            errors.push(`PR search: ${searchError.message}`);
        }

        // 3. Search for all issues authored by this user to get count
        try {
            const { data: issueSearchResult } = await octokit.search.issuesAndPullRequests({
                q: `type:issue author:${user.username}`,
                sort: 'updated',
                order: 'desc',
                per_page: 1, // We just need the total count
            });
            totalIssues = issueSearchResult.total_count;
        } catch (searchError: any) {
            console.error('Issue search error:', searchError);
            errors.push(`Issue search: ${searchError.message}`);
        }

        // 4. Update user's profile with GitHub stats
        const existingProfile = await db.select()
            .from(profiles)
            .where(eq(profiles.userId, user.id))
            .limit(1);

        if (existingProfile.length > 0) {
            const currentStats = existingProfile[0].githubStats 
                ? (typeof existingProfile[0].githubStats === 'string' 
                    ? JSON.parse(existingProfile[0].githubStats) 
                    : existingProfile[0].githubStats)
                : {};

            // Calculate streak from contribution data
            let currentStreak = 0;
            if (contributionData?.weeks) {
                const allDays = contributionData.weeks.flatMap(w => w.contributionDays).reverse();
                for (const day of allDays) {
                    if (day.contributionCount > 0) {
                        currentStreak++;
                    } else {
                        break;
                    }
                }
            }

            await db.update(profiles)
                .set({
                    githubStats: JSON.stringify({
                        ...currentStats,
                        total_prs: totalPRs,
                        total_issues: totalIssues,
                        total_contributions: contributionData?.totalContributions || currentStats.total_contributions || 0,
                        current_streak: currentStreak || currentStats.current_streak || 0,
                        last_sync: new Date().toISOString(),
                    }),
                    updatedAt: new Date().toISOString(),
                })
                .where(eq(profiles.userId, user.id));
        }

        // 5. Update user's last sync timestamp
        await db.update(users)
            .set({ updatedAt: new Date().toISOString() })
            .where(eq(users.id, user.id));

        console.log(`[SyncUserData] Completed for ${user.username}: ${totalPRs} PRs, ${totalIssues} issues`);

        return NextResponse.json({
            success: true,
            username: user.username,
            totalPRs,
            totalIssues,
            contributionData: contributionData ? {
                totalContributions: contributionData.totalContributions,
                weeksCount: contributionData.weeks?.length || 0
            } : null,
            errors: errors.length > 0 ? errors : undefined,
            message: `Synced contribution data for ${user.username}`
        });

    } catch (error: any) {
        console.error("POST /api/sync/user-data error:", error);
        return NextResponse.json({ error: error.message || "Internal server error" }, { status: 500 });
    }
}

// GET endpoint to check sync status
export async function GET(request: NextRequest) {
    try {
        const user = await getCurrentUser(request);
        if (!user) {
            return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
        }

        // Get profile stats
        const profile = await db.select()
            .from(profiles)
            .where(eq(profiles.userId, user.id))
            .limit(1);

        const githubStats = profile[0]?.githubStats 
            ? (typeof profile[0].githubStats === 'string' 
                ? JSON.parse(profile[0].githubStats) 
                : profile[0].githubStats)
            : null;

        const totalPRs = githubStats?.total_prs || 0;
        const totalIssues = githubStats?.total_issues || 0;
        const lastSync = githubStats?.last_sync || null;

        return NextResponse.json({
            username: user.username,
            totalPRs,
            totalIssues,
            totalContributions: githubStats?.total_contributions || 0,
            currentStreak: githubStats?.current_streak || 0,
            lastSync,
            needsSync: !lastSync || totalPRs + totalIssues === 0
        });

    } catch (error) {
        console.error("GET /api/sync/user-data error:", error);
        return NextResponse.json({ error: "Internal server error" }, { status: 500 });
    }
}