-
Notifications
You must be signed in to change notification settings - Fork 1
prove github creds via graphql (faster, but token always required) #4
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,5 +1,6 @@ | ||
| import { NextRequest, NextResponse } from 'next/server'; | ||
| import { sql } from '@/lib/db'; | ||
| import { transformGraphQLPRResponse, extractRepoInfo } from '@/lib/graphql-transformer'; | ||
|
|
||
| // Configure max duration for Vercel (up to 90 seconds) | ||
| export const maxDuration = 90; | ||
|
|
@@ -55,33 +56,11 @@ export async function POST(request: NextRequest) { | |
| } | ||
|
|
||
| const verifyData = await verifyResponse.json(); | ||
|
|
||
| console.log('Verify data:', verifyData); | ||
|
|
||
|
Comment on lines
58
to
61
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Avoid logging full verification payloads (GitHub PAT leakage risk)
- console.log('Verify data:', verifyData);
+ // Avoid logging full verifyData because it may contain sensitive headers (e.g., GitHub PATs).
+ // console.debug('Verify summary:', {
+ // hasRequest: !!verifyData.request,
+ // hasResponse: !!verifyData.response,
+ // });Also applies to: 79-83 🤖 Prompt for AI Agents |
||
| // Extract repository info from the verified response | ||
| let repoOwner: string | null = null; | ||
| let repoName: string | null = null; | ||
|
|
||
| try { | ||
| // Try multiple locations where the URL might be | ||
| let urlToMatch = null; | ||
|
|
||
| if (verifyData?.response?.url) { | ||
| urlToMatch = verifyData.response.url; | ||
| } else if (verifyData?.request?.url) { | ||
| urlToMatch = verifyData.request.url; | ||
| } else if (verifyData?.response?.request) { | ||
| urlToMatch = verifyData.response.request; | ||
| } | ||
|
|
||
| if (urlToMatch) { | ||
| const urlMatch = urlToMatch.match(/\/repos\/([^\/]+)\/([^\/]+)\/contributors/); | ||
| if (urlMatch) { | ||
| repoOwner = urlMatch[1]; | ||
| repoName = urlMatch[2]; | ||
| } | ||
| } | ||
| } catch (e) { | ||
| console.error('Could not extract repo info from response:', e); | ||
| } | ||
| const { owner: repoOwner, name: repoName } = extractRepoInfo(verifyData); | ||
|
|
||
| if (!repoOwner || !repoName) { | ||
| return NextResponse.json( | ||
|
|
@@ -91,38 +70,28 @@ export async function POST(request: NextRequest) { | |
| } | ||
|
|
||
| // Parse the response body to extract contributor data | ||
| // Handle GraphQL PR response, contributors API, and commits API (for backward compatibility) | ||
| let githubUsername: string | null = null; | ||
| let contributionCount: number | null = null; | ||
| let avatarUrl: string | null = null; | ||
| let githubUrl: string | null = null; | ||
|
|
||
| if (verifyData.response && verifyData.response.body) { | ||
| try { | ||
| const contributorsData = JSON.parse(verifyData.response.body); | ||
| const responseBody = verifyData.response.body; | ||
| const contributorData = transformGraphQLPRResponse(responseBody, username.trim()); | ||
|
|
||
| if (Array.isArray(contributorsData)) { | ||
| // Find the contributor matching the provided username | ||
| const targetContributor = contributorsData.find((c: any) => | ||
| c.login && c.login.toLowerCase() === username.trim().toLowerCase() | ||
| ); | ||
|
|
||
| if (!targetContributor) { | ||
| return NextResponse.json( | ||
| { error: `No contributions found for username: ${username.trim()}` }, | ||
| { status: 404 } | ||
| ); | ||
| } | ||
|
|
||
| githubUsername = targetContributor.login; | ||
| contributionCount = targetContributor.contributions; | ||
| avatarUrl = targetContributor.avatar_url || null; | ||
| githubUrl = targetContributor.html_url || null; | ||
| } else { | ||
| if (!contributorData) { | ||
| return NextResponse.json( | ||
| { error: 'Invalid contributors data format' }, | ||
| { status: 400 } | ||
| { error: `No contributions found for username: ${username.trim()}` }, | ||
| { status: 404 } | ||
| ); | ||
| } | ||
|
|
||
| githubUsername = contributorData.username; | ||
| contributionCount = contributorData.contributions; | ||
| avatarUrl = contributorData.avatar; | ||
| githubUrl = contributorData.githubUrl; | ||
| } catch (parseError) { | ||
| console.error('Failed to parse contributor data:', parseError); | ||
| return NextResponse.json( | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,6 +1,7 @@ | ||
| import { NextResponse } from 'next/server'; | ||
| import { readdir, readFile } from 'fs/promises'; | ||
| import { join } from 'path'; | ||
| import { transformGraphQLPRResponse, extractRepoInfo } from '@/lib/graphql-transformer'; | ||
|
|
||
| // Configure max duration for Vercel | ||
| export const maxDuration = 300; | ||
|
|
@@ -78,75 +79,31 @@ export async function GET() { | |
| console.log('Verify response structure:', JSON.stringify(verifyData, null, 2)); | ||
|
|
||
| // Extract repository info from the verified response | ||
| let repoOwner: string | null = null; | ||
| let repoName: string | null = null; | ||
| try { | ||
| // Try multiple locations where the URL might be | ||
| let urlToMatch = null; | ||
|
|
||
| // Check different possible locations | ||
| if (verifyData?.response?.url) { | ||
| urlToMatch = verifyData.response.url; | ||
| } else if (verifyData?.request?.url) { | ||
| urlToMatch = verifyData.request.url; | ||
| } else if (verifyData?.response?.request) { | ||
| urlToMatch = verifyData.response.request; | ||
| } | ||
|
|
||
| console.log('URL to match:', urlToMatch); | ||
|
|
||
| if (urlToMatch) { | ||
| const urlMatch = urlToMatch.match(/\/repos\/([^\/]+)\/([^\/]+)\/contributors/); | ||
| if (urlMatch) { | ||
| repoOwner = urlMatch[1]; | ||
| repoName = urlMatch[2]; | ||
| console.log(`Extracted repo: ${repoOwner}/${repoName}`); | ||
| } | ||
| } | ||
| } catch (e) { | ||
| console.log('Could not extract repo info from response:', e); | ||
| } | ||
| const { owner: repoOwner, name: repoName } = extractRepoInfo(verifyData); | ||
| console.log(`Extracted repo: ${repoOwner}/${repoName}`); | ||
|
|
||
|
Comment on lines
79
to
84
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sanitize or remove logs that include full verification payloads Between - console.log('Verify response structure:', JSON.stringify(verifyData, null, 2));
+ // Avoid logging full verifyData; it may contain sensitive headers (e.g., GitHub PATs).
+ console.debug('Verify response summary:', {
+ file,
+ hasRequest: !!verifyData.request,
+ hasResponse: !!verifyData.response,
+ });
@@
- const responseBody = verifyData.response.body;
- const contributorData = transformGraphQLPRResponse(responseBody, targetUsername || undefined);
+ const responseBody = verifyData.response.body;
+ const contributorData = transformGraphQLPRResponse(responseBody, targetUsername || undefined);Also applies to: 87-91 🤖 Prompt for AI Agents |
||
| // Parse the response body to extract contributor data | ||
| // Handle GraphQL PR response, contributors API, and commits API (for backward compatibility) | ||
| if (verifyData.response && verifyData.response.body) { | ||
| try { | ||
| const contributorsData = JSON.parse(verifyData.response.body); | ||
| const responseBody = verifyData.response.body; | ||
| const contributorData = transformGraphQLPRResponse(responseBody, targetUsername || undefined); | ||
|
|
||
| // Extract only the specific contributor that was verified | ||
| if (Array.isArray(contributorsData)) { | ||
| let targetContributor = null; | ||
|
|
||
| console.log(`Found ${contributorsData.length} contributors in data`); | ||
|
|
||
| if (targetUsername) { | ||
| // Find the contributor matching the username from filename | ||
| console.log(`Looking for username: ${targetUsername}`); | ||
| targetContributor = contributorsData.find((c: any) => | ||
| c.login && c.login.toLowerCase() === targetUsername.toLowerCase() | ||
| ); | ||
| console.log(`Found contributor:`, targetContributor ? targetContributor.login : 'NOT FOUND'); | ||
| } else { | ||
| // Fallback: use the first contributor if no username in filename | ||
| console.log('No username in filename, using first contributor'); | ||
| targetContributor = contributorsData[0]; | ||
| } | ||
|
|
||
| if (targetContributor && targetContributor.login) { | ||
| console.log(`Adding contributor: ${targetContributor.login} with ${targetContributor.contributions} contributions`); | ||
| const repoUrl = repoOwner && repoName ? `https://github.com/${repoOwner}/${repoName}` : undefined; | ||
| contributors.push({ | ||
| username: targetContributor.login, | ||
| contributions: targetContributor.contributions, | ||
| avatar: targetContributor.avatar_url, | ||
| githubUrl: targetContributor.html_url, | ||
| verified: true, | ||
| repoOwner: repoOwner || undefined, | ||
| repoName: repoName || undefined, | ||
| repoUrl | ||
| }); | ||
| } else { | ||
| console.log(`No valid contributor found for ${file}`); | ||
| } | ||
| if (contributorData) { | ||
| console.log(`Adding contributor: ${contributorData.username} with ${contributorData.contributions} contributions`); | ||
| const repoUrl = repoOwner && repoName ? `https://github.com/${repoOwner}/${repoName}` : undefined; | ||
| contributors.push({ | ||
| username: contributorData.username, | ||
| contributions: contributorData.contributions, | ||
| avatar: contributorData.avatar, | ||
| githubUrl: contributorData.githubUrl, | ||
| verified: true, | ||
| repoOwner: repoOwner || undefined, | ||
| repoName: repoName || undefined, | ||
| repoUrl | ||
| }); | ||
| } else { | ||
| console.log(`No valid contributor data found for ${file}`); | ||
| } | ||
| } catch (parseError) { | ||
| console.error(`Failed to parse contributor data from ${file}:`, parseError); | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do not log GitHub PAT in
requestBodyrequestBody.headersincludesAuthorization: Bearer ${body.githubToken}, andconsole.log('Request body:', JSON.stringify(requestBody, null, 2));will emit that PAT into server logs. This is high‑risk credential leakage; please avoid logging the full request body or explicitly strip/redact the Authorization header before logging.Also applies to: 61-62
🤖 Prompt for AI Agents