From d5a05da1f709a416bf8eed15eedde4dd9c4da5ac Mon Sep 17 00:00:00 2001 From: austinkelsay Date: Mon, 9 Dec 2024 20:40:22 -0600 Subject: [PATCH] linking github and nostr account (with no privkey) works, changed github integration to use account object from session instead of username --- .../charts/CombinedContributionChart.js | 4 +- src/components/profile/UserProfile.js | 2 +- .../profile/progress/UserProgress.js | 6 +- .../githubQueries/useFetchGithubCommits.js | 12 ++-- src/lib/github.js | 29 +++++++-- src/pages/api/auth/[...nextauth].js | 61 ++++++++++++++++--- 6 files changed, 90 insertions(+), 24 deletions(-) diff --git a/src/components/charts/CombinedContributionChart.js b/src/components/charts/CombinedContributionChart.js index 52d71e5..c4708cb 100644 --- a/src/components/charts/CombinedContributionChart.js +++ b/src/components/charts/CombinedContributionChart.js @@ -3,7 +3,7 @@ import { useFetchGithubCommits } from '@/hooks/githubQueries/useFetchGithubCommi import { Tooltip } from 'primereact/tooltip'; import { formatDateTime } from "@/utils/time"; -const CombinedContributionChart = ({ username, session }) => { +const CombinedContributionChart = ({ session }) => { const [contributionData, setContributionData] = useState({}); const [totalContributions, setTotalContributions] = useState(0); @@ -88,7 +88,7 @@ const CombinedContributionChart = ({ username, session }) => { setTotalContributions(totalCommits + Object.values(activityData).reduce((a, b) => a + b, 0)); }, [prepareProgressData]); - const { data, isLoading, isFetching } = useFetchGithubCommits(username, handleNewCommit); + const { data, isLoading, isFetching } = useFetchGithubCommits(session, handleNewCommit); // Initialize from cached data if available useEffect(() => { diff --git a/src/components/profile/UserProfile.js b/src/components/profile/UserProfile.js index a8f562c..6963822 100644 --- a/src/components/profile/UserProfile.js +++ b/src/components/profile/UserProfile.js @@ -134,7 +134,7 @@ const UserProfile = () => { )} {account && account?.provider === "github" ? ( - + ) : ( )} diff --git a/src/components/profile/progress/UserProgress.js b/src/components/profile/progress/UserProgress.js index 1ed5c3f..6ea37e7 100644 --- a/src/components/profile/progress/UserProgress.js +++ b/src/components/profile/progress/UserProgress.js @@ -148,9 +148,9 @@ const UserProgress = () => { const result = await signIn("github", { redirect: false, // Pass existing user data for linking - userId: session.user.id, - pubkey: session.user.pubkey, - privkey: session.user.privkey + userId: session?.user?.id, + pubkey: session?.user?.pubkey, + privkey: session?.user?.privkey || null }); if (result?.ok) { diff --git a/src/hooks/githubQueries/useFetchGithubCommits.js b/src/hooks/githubQueries/useFetchGithubCommits.js index f5c5da8..7adca7d 100644 --- a/src/hooks/githubQueries/useFetchGithubCommits.js +++ b/src/hooks/githubQueries/useFetchGithubCommits.js @@ -1,19 +1,23 @@ import { useQuery } from '@tanstack/react-query'; import { getAllCommits } from '@/lib/github'; -export function useFetchGithubCommits(username, onCommitReceived) { +export function useFetchGithubCommits(session, onCommitReceived) { + const accessToken = session?.account?.access_token; + return useQuery({ - queryKey: ['githubCommits', username], + queryKey: ['githubCommits', accessToken], queryFn: async () => { + if (!accessToken) return { commits: [], contributionData: {}, totalCommits: 0 }; + const today = new Date(); const oneYearAgo = new Date(today); - oneYearAgo.setDate(today.getDate() - 364); // Exactly 52 weeks + oneYearAgo.setDate(today.getDate() - 364); const commits = []; const contributionData = {}; let totalCommits = 0; - for await (const commit of getAllCommits(username, oneYearAgo)) { + for await (const commit of getAllCommits(accessToken, oneYearAgo)) { commits.push(commit); const date = commit.commit.author.date.split('T')[0]; contributionData[date] = (contributionData[date] || 0) + 1; diff --git a/src/lib/github.js b/src/lib/github.js index 779260c..aba8482 100644 --- a/src/lib/github.js +++ b/src/lib/github.js @@ -20,8 +20,29 @@ const octokit = new ThrottledOctokit({ }, }); -export async function* getAllCommits(username, since) { - // Create time windows of 1 month each +export async function* getAllCommits(accessToken, since) { + const auth = accessToken || process.env.NEXT_PUBLIC_GITHUB_ACCESS_KEY; + + const octokit = new ThrottledOctokit({ + auth, + throttle: { + onRateLimit: (retryAfter, options, octokit, retryCount) => { + octokit.log.warn(`Request quota exhausted for request ${options.method} ${options.url}`); + if (retryCount < 2) { + octokit.log.info(`Retrying after ${retryAfter} seconds!`); + return true; + } + }, + onSecondaryRateLimit: (retryAfter, options, octokit) => { + octokit.log.warn(`Secondary rate limit hit for request ${options.method} ${options.url}`); + return true; + }, + }, + }); + + // First, get the authenticated user's information + const { data: user } = await octokit.users.getAuthenticated(); + const endDate = new Date(); let currentDate = new Date(since); @@ -29,7 +50,6 @@ export async function* getAllCommits(username, since) { let nextDate = new Date(currentDate); nextDate.setMonth(nextDate.getMonth() + 1); - // If next date would be in the future, use current date instead if (nextDate > endDate) { nextDate = endDate; } @@ -39,7 +59,7 @@ export async function* getAllCommits(username, since) { while (true) { try { const { data } = await octokit.search.commits({ - q: `author:${username} committer-date:${currentDate.toISOString().split('T')[0]}..${nextDate.toISOString().split('T')[0]}`, + q: `author:${user.login} committer-date:${currentDate.toISOString().split('T')[0]}..${nextDate.toISOString().split('T')[0]}`, per_page: 100, page, }); @@ -58,7 +78,6 @@ export async function* getAllCommits(username, since) { } } - // Move to next time window currentDate = nextDate; } } diff --git a/src/pages/api/auth/[...nextauth].js b/src/pages/api/auth/[...nextauth].js index 03d43dd..c3bd5ef 100644 --- a/src/pages/api/auth/[...nextauth].js +++ b/src/pages/api/auth/[...nextauth].js @@ -166,7 +166,7 @@ export const authOptions = { ], callbacks: { // Move email handling to the signIn callback - async signIn({ user, account, profile, email }) { + async signIn({ user, account }) { // Only handle email provider sign ins if (account?.provider === "email") { try { @@ -230,7 +230,8 @@ export const authOptions = { userLessons: fullUser.userLessons, purchased: fullUser.purchased, nip05: fullUser.nip05, - lightningAddress: fullUser.lightningAddress + lightningAddress: fullUser.lightningAddress, + githubUsername: token.githubUsername }; // Add GitHub account info to session if it exists @@ -247,10 +248,10 @@ export const authOptions = { } return session; }, - async jwt({ token, user, account, profile }) { + async jwt({ token, user, account, profile, session }) { + console.log("JWT", token, user, account, profile, session); // If we are linking a github account to an existing email or anon account (we have privkey) if (account?.provider === "github" && user?.id && user?.pubkey && user?.privkey) { - console.log("Linking GitHub account to existing user", account, profile); try { // First update the user's profile with GitHub info const updatedUser = await updateUser(user.id, { @@ -260,22 +261,64 @@ export const authOptions = { image: profile?.avatar_url, }); - console.log("Updated user", updatedUser); - // Get the updated user const existingUser = await getUserById(updatedUser?.id); if (existingUser) { token.user = existingUser; } + + // add github username to token + token.githubUsername = profile?.login || profile?.name; } catch (error) { console.error("Error linking GitHub account:", error); } } - // If we are linking a github account to a nostr account (we do not have privkey) - if (account?.provider === "github" && account?.userId && account?.pubkey) { + // nostr login (we have no privkey) + if (account?.provider === "github" && user?.id && user?.pubkey) { + console.log("GITHUB LOGIN"); try { - // I think we just need auth + account in session and thats it? + // First check if there's already a GitHub account linked + const existingGithubAccount = await prisma.account.findFirst({ + where: { + userId: user.id, + provider: 'github' + } + }); + + // add github username to token + token.githubUsername = profile?.login || profile?.name; + + if (!existingGithubAccount) { + console.log("No existing GitHub account found"); + // Update user profile with GitHub info + const updatedUser = await updateUser(user.id, { + name: profile?.login || profile?.name, + username: profile?.login || profile?.name, + avatar: profile?.avatar_url, + image: profile?.avatar_url, + email: profile?.email // Add email if user wants it + }); + + // Create the GitHub account link + await prisma.account.create({ + data: { + userId: user.id, + type: account.type, + provider: account.provider, + providerAccountId: account.providerAccountId, + access_token: account.access_token, + token_type: account.token_type, + scope: account.scope + } + }); + + // Get the updated user + const existingUser = await getUserById(updatedUser?.id); + if (existingUser) { + token.user = existingUser; + } + } } catch (error) { console.error("Error linking GitHub account:", error); }