feat: add GitHub OAuth support for private repositories#130
feat: add GitHub OAuth support for private repositories#130anshk1234 wants to merge 5 commits intoBeyteFlow:mainfrom
Conversation
|
@anshk1234 is attempting to deploy a commit to the naheel0's projects Team on Vercel. A member of the Team first needs to authorize it. |
📝 WalkthroughSummary by CodeRabbit
WalkthroughImplements GitHub authentication via NextAuth and integrates it end-to-end: adds NextAuth config and route, wraps the app with a SessionProvider, replaces Octokit helpers with an authenticated per-call builder and structured RepoAccessError, updates the Changes
Sequence DiagramsequenceDiagram
participant User
participant Client as Client App
participant NextAuth
participant GitHub as GitHub OAuth/API
participant API as /api/generate
participant Octokit
User->>Client: Clicks "Login with GitHub"
Client->>NextAuth: signIn("github")
NextAuth->>GitHub: OAuth authorization request
GitHub-->>User: Presents login/consent
User->>GitHub: Authorizes
GitHub-->>NextAuth: Authorization code / access token
NextAuth->>Client: Session established (JWT with accessToken)
User->>Client: Submits repo URL
Client->>API: POST /api/generate (with session cookie)
API->>NextAuth: getToken(request)
NextAuth-->>API: Returns accessToken (if present)
API->>Octokit: getRepoSnapshot(owner, repo, accessToken?)
Octokit->>GitHub: Fetch repo & tree (uses token if provided)
alt Private repo & no token
GitHub-->>Octokit: 403 Forbidden
Octokit-->>API: RepoAccessError(AUTH_REQUIRED)
API-->>Client: 403 { error: "private_repo_consent_required", authRequired: false }
else Repo accessible
GitHub-->>Octokit: Repo data & contents
Octokit-->>API: repoSnapshot
API-->>Client: 200 OK (generated README)
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Suggested labels
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 7
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/app/api/generate/route.ts (1)
111-160:⚠️ Potential issue | 🟠 MajorAdd an explicit privacy gate before sending private repo data to Gemini.
With OAuth enabled, this prompt can now include private repository name, description, license, and file paths before calling
model.generateContent. Please add user-facing consent/notice for private repositories, or block private repo generation until that processing is acknowledged.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/app/api/generate/route.ts` around lines 111 - 160, The prompt currently includes potentially sensitive fields (repo, repoInfo, licenseName, fileListString) and is passed directly to model.generateContent; before building/sending the prompt in route.ts you must add a privacy gate that checks the repository visibility (private) and enforces explicit user consent: detect private repos via the repo/repoInfo metadata, and if private either (1) require an explicit consent flag/acknowledgement in the request (e.g., req.body.ackPrivateRepo) before proceeding, or (2) block generation and return a clear client-facing notice explaining that private repo data will not be sent without consent; ensure this gate is applied before constructing the prompt and before calling model.generateContent so no private data is transmitted without consent.
🧹 Nitpick comments (3)
.gitignore (1)
34-35: Redundant ignore entry.
.env.localis already matched by the.env*glob on line 34, so the new line is a no-op. Safe to keep for explicitness, but you could drop it to reduce noise.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In @.gitignore around lines 34 - 35, The .gitignore contains a redundant entry: ".env.local" is already covered by the ".env*" glob; remove the explicit ".env.local" line (or alternatively remove/adjust the ".env*" entry) so the ignore file has no duplicate patterns—look for the entries ".env*" and ".env.local" and delete the redundant ".env.local" line to reduce noise.src/lib/auth.ts (1)
10-16: Fail fast if GitHub OAuth env vars are missing.Non-null assertions (
process.env.GITHUB_CLIENT_ID!) coerce missing values to the string"undefined", producing confusing OAuth errors from GitHub at runtime rather than a clear startup failure. Validate at module load and throw explicitly.🛡️ Proposed fix
+const githubClientId = process.env.GITHUB_CLIENT_ID; +const githubClientSecret = process.env.GITHUB_CLIENT_SECRET; +if (!githubClientId || !githubClientSecret) { + throw new Error( + "Missing GITHUB_CLIENT_ID or GITHUB_CLIENT_SECRET environment variables", + ); +} + export const authOptions: NextAuthOptions = { secret: process.env.NEXTAUTH_SECRET, session: { strategy: "jwt", }, providers: [ GitHubProvider({ - clientId: process.env.GITHUB_CLIENT_ID!, - clientSecret: process.env.GITHUB_CLIENT_SECRET!, + clientId: githubClientId, + clientSecret: githubClientSecret, authorization: { params: { scope: "repo read:user" }, }, }), ],🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/lib/auth.ts` around lines 10 - 16, Replace the non-null assertions used when constructing GitHubProvider so missing env vars fail fast: check process.env.GITHUB_CLIENT_ID and process.env.GITHUB_CLIENT_SECRET at module load (or via a small helper like getRequiredEnv) and throw a clear Error if either is missing, then pass the validated strings into GitHubProvider instead of using the `!` coercion; reference the GitHubProvider constructor and the env var names (GITHUB_CLIENT_ID, GITHUB_CLIENT_SECRET) when making the change.src/components/layout/Navbar.tsx (1)
31-60: Flex layout: brand logo will grow to fill available space.Applying
flex-1to the brand<Link>(line 35) alongsideflex-1on the actions container (line 60) makes the logo stretch across the left half of the nav — clickable area now extends far beyond the visible logo, which can trap accidental clicks on the left region. Typically you'd want only the two side containers to flex-1 and the brand to be its intrinsic width. Consider wrapping the brand in a non-flex-1 container on the left.💡 Suggested structure
- <div className="flex items-center"> - {/* Brand Logo */} - <Link - href="/" - className="flex flex-1 items-center gap-3 group cursor-pointer" + <div className="flex items-center"> + <div className="flex flex-1 items-center"> + {/* Brand Logo */} + <Link + href="/" + className="flex items-center gap-3 group cursor-pointer" aria-label="ReadmeGenAI Home" > ... </Link> + </div>🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/components/layout/Navbar.tsx` around lines 31 - 60, Remove the unintended flex-grow on the brand Link: delete "flex-1" from the Link's className (the Link element that renders the logo) so the logo keeps its intrinsic width, and instead ensure the side containers are the ones that expand — keep or add "flex-1" on the actions container (the div with className "flex flex-1 items-center justify-end gap-3") and, if needed for layout balance, place "flex-1" on the left or middle container that should take remaining space (e.g., the desktop menu div). This prevents the Link (logo) from stretching and expanding its clickable area while preserving the intended flexible spacing.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/components/Generator/SearchInput.tsx`:
- Around line 119-129: The login CTA currently renders when authRequired is true
even if a local client-side error is shown; update the conditional so the
GitHubLoginButton only renders for server-side auth errors by requiring
serverError as well. In the SearchInput component change the JSX condition from
{authRequired && ( ... )} to require serverError (e.g. {serverError &&
authRequired && ( <GitHubLoginButton /> )}) so the CTA appears only for server
auth failures; reference the authRequired, serverError, error variables and the
GitHubLoginButton component when making the change.
In `@src/components/layout/Navbar.tsx`:
- Around line 100-103: The mobile drawer has two UX issues: the "Star our Repo"
primary Button should be an actual link like the desktop "Star on GitHub"
anchor, and the GitHubLoginButton should close the drawer before starting auth.
Fix by replacing/wrapping the <Button variant="primary" className="w-full
...">Star our Repo</Button> with an anchor/Link pointing to the repo URL (use
the same href used by the desktop anchor) so it behaves as a link, and ensure
GitHubLoginButton triggers setIsMenuOpen(false) before redirect — either by
passing an onClick prop that calls setIsMenuOpen(false) and then invokes the
login flow, or by updating GitHubLoginButton to accept and call a
onBeforeLogin/onClick callback; also mirror the nav links' behavior of calling
setIsMenuOpen(false) when interacted.
In `@src/lib/auth.ts`:
- Around line 13-15: The OAuth scope currently set in authorization.params.scope
("repo read:user") is overly broad; update the code that sets
authorization.params.scope to use a narrower scope if possible (e.g.,
"public_repo read:user" when private repo access is not required) or switch to a
GitHub App installation flow to get per-repo, least-privilege access; if you
must keep "repo" for private README access, add clear UI/consent text that
surfaces why full repo scope is requested and document this rationale in the
authentication/login flow code paths (the code that builds the authorization
params and the login UI rendering).
In `@src/lib/octokit.ts`:
- Around line 45-51: The current block that returns a RepoAccessError when
((status === 403 || status === 404) && !hasUserAccessToken) claims the repo is
private; change the error text to a neutral message that doesn't assume privacy
(e.g., "Repository not accessible. You may need to log in to GitHub or the
repository may not exist.") while keeping the same error type (RepoAccessError),
status and code; update the message where RepoAccessError is constructed so
callers see a non-presumptive prompt to authenticate or verify the repo.
- Around line 20-24: The createOctokit function currently falls back to
process.env.GITHUB_TOKEN when accessToken is not provided, which can expose
private-repo access to unauthenticated user requests; change createOctokit to
NOT use process.env.GITHUB_TOKEN as a fallback for user-submitted flows—only use
the passed accessToken (accessToken parameter) to set auth, and if you need a
separate server-side/public-only token, inject it explicitly via a new parameter
or a different factory function instead of reading GITHUB_TOKEN implicitly;
update any callers that rely on the implicit env fallback to pass the
appropriate token or use the new server-side factory.
- Around line 94-96: The filter on repoTree.tree that produces repoContents
doesn't narrow item.path's type for TypeScript; change the predicate to a type
predicate so downstream code can assume path is string. Replace the current
filter callback on repoTree.tree.filter((item) => item.path &&
!item.path.includes("/")) with a type-guard signature like (item): item is {
path: string } => Boolean(item.path) && !item.path.includes("/") so repoContents
is typed as items with path: string and callers (e.g., the map in route.ts) no
longer need manual annotations.
---
Outside diff comments:
In `@src/app/api/generate/route.ts`:
- Around line 111-160: The prompt currently includes potentially sensitive
fields (repo, repoInfo, licenseName, fileListString) and is passed directly to
model.generateContent; before building/sending the prompt in route.ts you must
add a privacy gate that checks the repository visibility (private) and enforces
explicit user consent: detect private repos via the repo/repoInfo metadata, and
if private either (1) require an explicit consent flag/acknowledgement in the
request (e.g., req.body.ackPrivateRepo) before proceeding, or (2) block
generation and return a clear client-facing notice explaining that private repo
data will not be sent without consent; ensure this gate is applied before
constructing the prompt and before calling model.generateContent so no private
data is transmitted without consent.
---
Nitpick comments:
In @.gitignore:
- Around line 34-35: The .gitignore contains a redundant entry: ".env.local" is
already covered by the ".env*" glob; remove the explicit ".env.local" line (or
alternatively remove/adjust the ".env*" entry) so the ignore file has no
duplicate patterns—look for the entries ".env*" and ".env.local" and delete the
redundant ".env.local" line to reduce noise.
In `@src/components/layout/Navbar.tsx`:
- Around line 31-60: Remove the unintended flex-grow on the brand Link: delete
"flex-1" from the Link's className (the Link element that renders the logo) so
the logo keeps its intrinsic width, and instead ensure the side containers are
the ones that expand — keep or add "flex-1" on the actions container (the div
with className "flex flex-1 items-center justify-end gap-3") and, if needed for
layout balance, place "flex-1" on the left or middle container that should take
remaining space (e.g., the desktop menu div). This prevents the Link (logo) from
stretching and expanding its clickable area while preserving the intended
flexible spacing.
In `@src/lib/auth.ts`:
- Around line 10-16: Replace the non-null assertions used when constructing
GitHubProvider so missing env vars fail fast: check process.env.GITHUB_CLIENT_ID
and process.env.GITHUB_CLIENT_SECRET at module load (or via a small helper like
getRequiredEnv) and throw a clear Error if either is missing, then pass the
validated strings into GitHubProvider instead of using the `!` coercion;
reference the GitHubProvider constructor and the env var names
(GITHUB_CLIENT_ID, GITHUB_CLIENT_SECRET) when making the change.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: ec926579-198e-4c1d-9ce8-65ae1401d89f
⛔ Files ignored due to path filters (1)
package-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (13)
.gitignorepackage.jsonsrc/app/api/auth/[...nextauth]/route.tssrc/app/api/generate/route.tssrc/app/generate/GeneratePageClient.tsxsrc/app/layout.tsxsrc/app/providers.tsxsrc/components/Generator/SearchInput.tsxsrc/components/GitHubLoginButton.tsxsrc/components/layout/Navbar.tsxsrc/lib/auth.tssrc/lib/octokit.tssrc/types/next-auth.d.ts
| authorization: { | ||
| params: { scope: "repo read:user" }, | ||
| }, |
There was a problem hiding this comment.
Consider narrower OAuth scope for least-privilege.
The repo scope grants full read/write access to all private repositories, including code, issues, and admin actions. Since this app only needs to read repo contents/metadata for README generation, you'd ideally use a finer-grained scope — but classic OAuth only offers repo (full) for private access. If minimizing blast radius matters here, consider migrating to a GitHub App (with per-install repository selection) or documenting that full repo access is requested and why. At minimum, surface this clearly in the login UI so users know the consent scope.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/lib/auth.ts` around lines 13 - 15, The OAuth scope currently set in
authorization.params.scope ("repo read:user") is overly broad; update the code
that sets authorization.params.scope to use a narrower scope if possible (e.g.,
"public_repo read:user" when private repo access is not required) or switch to a
GitHub App installation flow to get per-repo, least-privilege access; if you
must keep "repo" for private README access, add clear UI/consent text that
surfaces why full repo scope is requested and document this rationale in the
authentication/login flow code paths (the code that builds the authorization
params and the login UI rendering).
| callbacks: { | ||
| async jwt({ token, account }) { | ||
| if (account?.access_token) { | ||
| token.accessToken = account.access_token; | ||
| } | ||
|
|
||
| return token; | ||
| }, | ||
| async session({ session }) { | ||
| return session; | ||
| }, | ||
| }, |
There was a problem hiding this comment.
No token refresh handling.
GitHub OAuth access tokens from the user-to-server flow can expire (especially for GitHub Apps and when expiration is enabled on the OAuth app). The jwt callback only writes accessToken on initial sign-in (when account is present) and never refreshes. Once the token expires, /api/generate will start returning 401/403 for private repos until the user signs out and back in. If you want to keep this simple for now, detect a 401 from Octokit server-side and surface a "please re-authenticate" error to the client; otherwise, store refresh_token and implement refresh in the jwt callback.
|
Thanks for the PR — great feature. I pushed fixes to the PR branch covering CodeRabbit items: removed implicit token fallback, added private-repo consent gate, fail-fast env checks, re-auth handling, neutralized 403/404 messaging, type predicate for repo tree, refined login CTA logic, and improved mobile navbar behavior. Please run npm install then npm run build to verify. |
There was a problem hiding this comment.
Actionable comments posted: 3
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/components/layout/Navbar.tsx (1)
31-60:⚠️ Potential issue | 🟡 MinorDesktop link group won't be visually centered.
With
justify-betweenremoved from the row and only the actions container gettingflex-1 justify-end, the brand has intrinsic width while the links group (justify-centeron line 47) sits immediately after the brand — so links are left-of-center, not centered in the nav. If centering within the viewport is the intent, give the links containerflex-1 justify-center(and mirror a flex-1 spacer on the brand side, or use a 3-column grid). Otherwise this is a visual regression from the previousjustify-betweenlayout.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/components/layout/Navbar.tsx` around lines 31 - 60, The desktop navigation links are no longer centered because the parent row lost justify-between and only the actions container uses flex-1; update the layout in the Navbar component so the desktop links group is centered in the viewport—either give the links container the classes "flex-1 justify-center" (replace "hidden md:flex items-center justify-center space-x-1" with a flex-1 variant) and add a matching flex-1 spacer for the brand side (or make the brand container use flex-1) to balance spacing, or convert the row into a 3-column grid; target the links rendering block (the links.map() anchor group) and the surrounding brand/action containers when applying the fix.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/app/api/generate/route.ts`:
- Around line 81-92: The response sets authRequired incorrectly for the
private-repo consent branch; when isPrivateRepo && !ackPrivateRepo the caller is
already authenticated, so update the NextResponse.json call in the route handler
(the block using isPrivateRepo, ackPrivateRepo, accessToken) to either omit the
authRequired property or set authRequired to !accessToken (i.e., false for
authenticated callers) so the client (GeneratePageClient.tsx) will only show the
consent UI and not the login prompt; ensure the change is made where
NextResponse.json is returned for the "private_repo_consent_required" error.
In `@src/components/layout/Navbar.tsx`:
- Around line 101-110: In Navbar, avoid nesting a native <button> inside an <a>
— update the block that renders the GitHub link so it mirrors the desktop
pattern: either render an anchor element styled as the button (move classes and
children — including <Github /> and "Star our Repo" — onto the <a>), or render
the Button component as an anchor (e.g., via an as="a" / href / target / rel
prop if Button supports it) instead of wrapping Button with <a>; change the JSX
in the Navbar component where the GitHub link is produced so there is no <a>
wrapping a <button>, keeping the URL, target, rel and visual styles identical.
In `@src/lib/octokit.ts`:
- Around line 67-73: The 403 branch currently assumes access denial; update the
handler that returns RepoAccessError to inspect the Octokit error response
headers (error.response.headers['x-ratelimit-remaining'] and
error.response.headers['retry-after']) and, when x-ratelimit-remaining === "0"
or retry-after is present, return/throw a rate-limit specific error (e.g., a
RateLimitError or similar) with the retry info; otherwise keep returning
RepoAccessError for true permission denial. Locate the status === 403 branch and
adjust it to read error.response.headers and branch to a rate-limit error when
those header conditions are met.
---
Outside diff comments:
In `@src/components/layout/Navbar.tsx`:
- Around line 31-60: The desktop navigation links are no longer centered because
the parent row lost justify-between and only the actions container uses flex-1;
update the layout in the Navbar component so the desktop links group is centered
in the viewport—either give the links container the classes "flex-1
justify-center" (replace "hidden md:flex items-center justify-center space-x-1"
with a flex-1 variant) and add a matching flex-1 spacer for the brand side (or
make the brand container use flex-1) to balance spacing, or convert the row into
a 3-column grid; target the links rendering block (the links.map() anchor group)
and the surrounding brand/action containers when applying the fix.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 2b937eef-e6de-45ab-ae2f-cfa348a817d6
⛔ Files ignored due to path filters (1)
package-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (7)
src/app/api/generate/route.tssrc/app/generate/GeneratePageClient.tsxsrc/components/Generator/SearchInput.tsxsrc/components/GitHubLoginButton.tsxsrc/components/layout/Navbar.tsxsrc/lib/auth.tssrc/lib/octokit.ts
🚧 Files skipped from review as they are similar to previous changes (4)
- src/lib/auth.ts
- src/components/GitHubLoginButton.tsx
- src/components/Generator/SearchInput.tsx
- src/app/generate/GeneratePageClient.tsx
| <a | ||
| href="https://github.com/BeyteFlow/ReadmeGenAI" | ||
| target="_blank" | ||
| rel="noopener noreferrer" | ||
| className="w-full" | ||
| > | ||
| <Button variant="primary" className="w-full justify-center mt-4"> | ||
| <Github size={18} /> Star our Repo | ||
| </Button> | ||
| </a> |
There was a problem hiding this comment.
Invalid nesting: <a> wrapping <button>.
HTML spec disallows interactive content inside <a>. The Button component renders a native <button>, so <a><button>…</button></a> is invalid and can cause inconsistent behavior across browsers/assistive tech (duplicate tab stops, unpredictable activation). The desktop counterpart at lines 64–72 uses an anchor styled as a button — mirror that pattern here.
♻️ Proposed fix
- <a
- href="https://github.com/BeyteFlow/ReadmeGenAI"
- target="_blank"
- rel="noopener noreferrer"
- className="w-full"
- >
- <Button variant="primary" className="w-full justify-center mt-4">
- <Github size={18} /> Star our Repo
- </Button>
- </a>
+ <a
+ href="https://github.com/BeyteFlow/ReadmeGenAI"
+ target="_blank"
+ rel="noopener noreferrer"
+ onClick={() => setIsMenuOpen(false)}
+ className="mt-4 w-full inline-flex items-center justify-center gap-2 px-4 py-2 rounded-full bg-white text-black font-medium hover:bg-white/90 transition-all"
+ >
+ <Github size={18} /> Star our Repo
+ </a>📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| <a | |
| href="https://github.com/BeyteFlow/ReadmeGenAI" | |
| target="_blank" | |
| rel="noopener noreferrer" | |
| className="w-full" | |
| > | |
| <Button variant="primary" className="w-full justify-center mt-4"> | |
| <Github size={18} /> Star our Repo | |
| </Button> | |
| </a> | |
| <a | |
| href="https://github.com/BeyteFlow/ReadmeGenAI" | |
| target="_blank" | |
| rel="noopener noreferrer" | |
| onClick={() => setIsMenuOpen(false)} | |
| className="mt-4 w-full inline-flex items-center justify-center gap-2 px-4 py-2 rounded-full bg-white text-black font-medium hover:bg-white/90 transition-all" | |
| > | |
| <Github size={18} /> Star our Repo | |
| </a> |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/components/layout/Navbar.tsx` around lines 101 - 110, In Navbar, avoid
nesting a native <button> inside an <a> — update the block that renders the
GitHub link so it mirrors the desktop pattern: either render an anchor element
styled as the button (move classes and children — including <Github /> and "Star
our Repo" — onto the <a>), or render the Button component as an anchor (e.g.,
via an as="a" / href / target / rel prop if Button supports it) instead of
wrapping Button with <a>; change the JSX in the Navbar component where the
GitHub link is produced so there is no <a> wrapping a <button>, keeping the URL,
target, rel and visual styles identical.
| if (status === 403) { | ||
| return new RepoAccessError( | ||
| "GitHub denied access to this repository.", | ||
| 403, | ||
| "FORBIDDEN", | ||
| ); | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
Octokit request error object shape for GitHub rate limit 403: which properties expose x-ratelimit-remaining and retry-after headers?
💡 Result:
The Octokit request error object for a GitHub rate limit 403 is an instance of @octokit/request-error.RequestError (also called HttpError). It exposes the rate limit headers via error.response.headers. - x-ratelimit-remaining: Access as error.response.headers['x-ratelimit-remaining'] (string value, typically "0" for rate limit exceeded). - retry-after: Access as error.response.headers['retry-after'] (string value in seconds, if present; used for secondary rate limits). Example usage: try { await octokit.request('GET /some/endpoint'); } catch (error) { if (error.status === 403) { const remaining = error.response.headers['x-ratelimit-remaining']; const retryAfter = error.response.headers['retry-after']; // Handle rate limit: wait retryAfter seconds or until x-ratelimit-reset } } This structure is standard across Octokit libraries using @octokit/request or @octokit/core (e.g., @octokit/rest, octokit). Headers are lowercase in the object. For primary rate limits, x-ratelimit-remaining is 0; for secondary, check error message and use retry-after if available.
Citations:
- 1: https://github.com/octokit/request-error.js/
- 2: https://github.com/octokit/request-error.js/blob/main/src/index.ts
- 3: https://docs.github.com/en/rest/overview/troubleshooting
- 4: https://docs.github.com/rest/using-the-rest-api/rate-limits-for-the-rest-api
403 responses from GitHub API can indicate either insufficient permissions or rate limiting, not just access denied.
GitHub returns 403 for both insufficient permissions and primary/secondary rate limits (core rate limit, secondary abuse limit, SAML enforcement, etc.). The current message "GitHub denied access to this repository." will mislead authenticated users experiencing rate limiting.
Inspect the response headers to differentiate these cases. Octokit error objects expose rate-limit headers via error.response.headers['x-ratelimit-remaining'] and error.response.headers['retry-after']. When x-ratelimit-remaining === "0" or retry-after is present, return a rate-limit error instead.
Example fix
if (status === 403) {
+ const headers =
+ typeof error === "object" && error !== null && "response" in error
+ ? (error as { response?: { headers?: Record<string, string> } }).response?.headers
+ : undefined;
+ const rateLimited =
+ headers?.["x-ratelimit-remaining"] === "0" || Boolean(headers?.["retry-after"]);
+ if (rateLimited) {
+ return new RepoAccessError(
+ "GitHub API rate limit exceeded. Please try again later.",
+ 429,
+ "UNKNOWN",
+ );
+ }
return new RepoAccessError(
"GitHub denied access to this repository.",
403,
"FORBIDDEN",
);
}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/lib/octokit.ts` around lines 67 - 73, The 403 branch currently assumes
access denial; update the handler that returns RepoAccessError to inspect the
Octokit error response headers (error.response.headers['x-ratelimit-remaining']
and error.response.headers['retry-after']) and, when x-ratelimit-remaining ===
"0" or retry-after is present, return/throw a rate-limit specific error (e.g., a
RateLimitError or similar) with the retry info; otherwise keep returning
RepoAccessError for true permission denial. Locate the status === 403 branch and
adjust it to read error.response.headers and branch to a rate-limit error when
those header conditions are met.
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/components/Generator/SearchInput.tsx`:
- Around line 61-68: The component currently only resets local ackPrivateRepo
when the URL changes, leaving the parent privateRepoConsentRequired flag set and
blocking future submits; add a prop callback (e.g.,
clearPrivateRepoConsentRequired or onClearPrivateRepoConsent) and call it from
the URL change handler (the function that updates URL state) to set the parent's
privateRepoConsentRequired to false; also invoke that callback wherever you
reset ackPrivateRepo or validate submission (the branches around the onGenerate
call and the other submit paths at the sections referenced, e.g., the blocks
that check privateRepoConsentRequired/ackPrivateRepo and call onGenerate) so the
parent flag is cleared whenever the URL is edited or the local consent is reset.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: bef3033a-0283-4d8c-858d-1996802effff
📒 Files selected for processing (5)
src/app/api/generate/route.tssrc/app/generate/GeneratePageClient.tsxsrc/components/Generator/SearchInput.tsxsrc/components/GitHubLoginButton.tsxsrc/components/layout/Navbar.tsx
🚧 Files skipped from review as they are similar to previous changes (3)
- src/components/GitHubLoginButton.tsx
- src/components/layout/Navbar.tsx
- src/app/api/generate/route.ts
| if (privateRepoConsentRequired && !ackPrivateRepo) { | ||
| setError( | ||
| "Please confirm the private repository consent checkbox, then click Generate again.", | ||
| ); | ||
| return; | ||
| } | ||
|
|
||
| onGenerate(url.trim(), language, ackPrivateRepo); |
There was a problem hiding this comment.
Clear stale private-repo consent state when the URL changes.
After one privateRepoConsentRequired response, editing the URL only resets local ackPrivateRepo; the parent flag stays true, so Line 61 blocks future submits before onGenerate can clear it. This can force the private-repo consent checkbox for unrelated or public repos.
Proposed fix
interface SearchInputProps {
onGenerate: (url: string, language: string, ackPrivateRepo: boolean) => void;
isLoading: boolean;
initialValue?: string; // optional initial value
ariaLabel?: string; // optional aria-label for accessibility
serverError?: string | null;
authRequired?: boolean;
privateRepoConsentRequired?: boolean;
+ onRepositoryChange?: () => void;
}
export const SearchInput = ({
onGenerate,
isLoading,
initialValue,
ariaLabel,
serverError,
authRequired = false,
privateRepoConsentRequired = false,
+ onRepositoryChange,
}: SearchInputProps) => { onChange={(e) => {
setUrl(e.target.value);
+ onRepositoryChange?.();
if (ackPrivateRepo) setAckPrivateRepo(false);
if (error) setError(null);
}}Then wire it from GeneratePageClient:
+ const resetServerState = () => {
+ setErrorMessage(null);
+ setAuthRequired(false);
+ setPrivateRepoConsentRequired(false);
+ };
+
<SearchInput
onGenerate={handleGenerate}
isLoading={isLoading}
initialValue={repoSlug ? `https://github.com/${repoSlug}` : ""}
ariaLabel="Enter GitHub repository URL to generate README"
serverError={errorMessage}
authRequired={authRequired}
privateRepoConsentRequired={privateRepoConsentRequired}
+ onRepositoryChange={resetServerState}
/>Also applies to: 87-90, 130-167
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/components/Generator/SearchInput.tsx` around lines 61 - 68, The component
currently only resets local ackPrivateRepo when the URL changes, leaving the
parent privateRepoConsentRequired flag set and blocking future submits; add a
prop callback (e.g., clearPrivateRepoConsentRequired or
onClearPrivateRepoConsent) and call it from the URL change handler (the function
that updates URL state) to set the parent's privateRepoConsentRequired to false;
also invoke that callback wherever you reset ackPrivateRepo or validate
submission (the branches around the onGenerate call and the other submit paths
at the sections referenced, e.g., the blocks that check
privateRepoConsentRequired/ackPrivateRepo and call onGenerate) so the parent
flag is cleared whenever the URL is edited or the local consent is reset.
|
Pushed a follow-up refinement pass in commit 2dcc56e. Updates included:
I also reviewed the updated flow locally and tested it again after these changes. Everything is now working smoothly from my side. Ran some screenshots :-
|



🚀 BΞYTΞFLʘW | Pull Request Protocol
PR Type:
featIssue Link: Fixes #39
📝 System Summary
Implements GitHub OAuth with NextAuth to support private repository access in ReadmeGenAI. Users can now sign in with GitHub, access private repositories after authentication, and see a clear login prompt when a protected repository requires auth.
✅ What’s Added
/api/generate.🛠️ Technical Changes
src/app/api/generate/route.tssrc/lib/octokit.tssrc/lib/auth.tssrc/components/GitHubLoginButton.tsxsrc/components/Generator/SearchInput.tsxsrc/components/layout/Navbar.tsxN/A🧪 Quality Assurance (QA)
npm run buildexecuted without errors.🖼️ Visual Evidence
Added GitHub login/logout UI in the navbar and inline auth-required prompt for private repository access.
📡 Developer Authorization
Authorized by: @anshk1234
Timestamp: 2026-04-23