linking github and nostr account (with no privkey) works, changed github integration to use account object from session instead of username

This commit is contained in:
austinkelsay 2024-12-09 20:40:22 -06:00
parent c87ccb8c2d
commit d5a05da1f7
No known key found for this signature in database
GPG Key ID: 44CB4EC6D9F2FA02
6 changed files with 90 additions and 24 deletions

View File

@ -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(() => {

View File

@ -134,7 +134,7 @@ const UserProfile = () => {
</h3>
)}
{account && account?.provider === "github" ? (
<CombinedContributionChart username={user.username || user.name} session={session} />
<CombinedContributionChart session={session} />
) : (
<ActivityContributionChart session={session} />
)}

View File

@ -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) {

View File

@ -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;

View File

@ -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;
}
}

View File

@ -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);
}