Skip to content

feat: add GitHub OAuth support for private repositories#130

Open
anshk1234 wants to merge 5 commits intoBeyteFlow:mainfrom
anshk1234:feat/github-oauth-private-repos-clean
Open

feat: add GitHub OAuth support for private repositories#130
anshk1234 wants to merge 5 commits intoBeyteFlow:mainfrom
anshk1234:feat/github-oauth-private-repos-clean

Conversation

@anshk1234
Copy link
Copy Markdown

@anshk1234 anshk1234 commented Apr 23, 2026

🚀 BΞYTΞFLʘW | Pull Request Protocol

PR Type: feat
Issue 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

  • GitHub auth is integrated and working.
  • Private repo access works only after login.
  • The GitHub access token is no longer exposed in client session data.
  • The token is used server-side in /api/generate.
  • The UI gives a clear auth-required state.

🛠️ Technical Changes

  • Logic change in src/app/api/generate/route.ts
  • Logic change in src/lib/octokit.ts
  • Logic change in src/lib/auth.ts
  • New UI component added: src/components/GitHubLoginButton.tsx
  • Logic/UI updates in src/components/Generator/SearchInput.tsx
  • Logic/UI updates in src/components/layout/Navbar.tsx
  • Database schema updated: N/A

🧪 Quality Assurance (QA)

  • Linting: Code style matches the BeyteFlow grid.
  • Build: npm run build executed without errors.
  • Testing: Public and private repository auth flow verified manually.
  • Dark Mode: UI is high-contrast and neon-optimized.

🖼️ Visual Evidence

Added GitHub login/logout UI in the navbar and inline auth-required prompt for private repository access.

image --- image

📡 Developer Authorization

  • I have performed a self-review of my code.
  • My changes generate no new warnings in the console.
  • I have updated the documentation (if applicable).

Authorized by: @anshk1234
Timestamp: 2026-04-23

@vercel
Copy link
Copy Markdown
Contributor

vercel Bot commented Apr 23, 2026

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

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 23, 2026

📝 Walkthrough

Summary by CodeRabbit

  • New Features

    • GitHub sign-in/sign-out and session support site-wide
    • Consent flow for accessing private repositories during generation
    • Login button and authenticated user display added to the navbar
    • More informative auth/error prompts during generation
  • Bug Fixes

    • Improved handling and reporting of repository access and auth errors
  • Chores

    • Updated dependencies to enable authentication features

Walkthrough

Implements 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 /api/generate endpoint to use session tokens and ack flow for private repos, and updates UI (login button, consent checkbox, error state) across generator and navbar components.

Changes

Cohort / File(s) Summary
Dependencies
package.json
Bumped next from 16.2.2^16.2.4 and added next-auth (^4.24.14).
NextAuth Config & Types
src/lib/auth.ts, src/types/next-auth.d.ts
Added authOptions NextAuth config using GitHub provider with repo scope and JWT strategy; augmented JWT type with accessToken field.
NextAuth Route & Provider
src/app/api/auth/[...nextauth]/route.ts, src/app/providers.tsx
Added NextAuth App Router API route and a client Providers component that wraps children in SessionProvider.
Layout Integration
src/app/layout.tsx
Wrapped app children with new Providers component to enable session context.
GitHub API Helpers
src/lib/octokit.ts
Replaced global/cached Octokit with createOctokit(accessToken?), added getRepoSnapshot(owner, repo, accessToken?), introduced RepoAccessError and toRepoAccessError for structured error handling; removed prior getOctokit, getRepoData, and getRepoContents.
Generate API
src/app/api/generate/route.ts
Switched to NextRequest, obtain token via getToken, call getRepoSnapshot with optional access token, accept ackPrivateRepo flag, return structured JSON errors including private_repo_consent_required and authRequired.
Generate Page Client
src/app/generate/GeneratePageClient.tsx
Added UI state for errorMessage, authRequired, privateRepoConsentRequired; send ackPrivateRepo to /api/generate; parse JSON error responses and forward auth/error state to SearchInput.
Search Input
src/components/Generator/SearchInput.tsx
Extended props to serverError?, authRequired?, privateRepoConsentRequired?; updated onGenerate signature to accept ackPrivateRepo; added client-side consent checkbox and conditional rendering of GitHubLoginButton when auth required.
GitHub Login Button
src/components/GitHubLoginButton.tsx
New client component using useSession() to render login/logout UI and optional scope note; supports onBeforeSignIn callback.
Navbar
src/components/layout/Navbar.tsx
Integrated GitHubLoginButton in desktop and mobile; adjusted flex layout and moved star link into an external anchor; mobile login closes drawer before sign-in.

Sequence Diagram

sequenceDiagram
    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
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested labels

area: backend, area: frontend

Suggested reviewers

  • adithyanmkd

Poem

🐇 A rabbit hops to open gates,
Tokens tucked to fetch locked crates.
Private repos no longer shy —
Sign in, consent, and give it a try! ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 11.11% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title 'feat: add GitHub OAuth support for private repositories' clearly and specifically summarizes the main change in the changeset.
Description check ✅ Passed The description provides meaningful context about implementing GitHub OAuth with NextAuth for private repository access, which is directly related to the changeset.
Linked Issues check ✅ Passed The PR addresses all coding requirements from issue #39: GitHub OAuth integration via NextAuth, private repo access gating, secure token handling server-side, proper error responses, and clear authentication UI.
Out of Scope Changes check ✅ Passed All changes directly support GitHub OAuth implementation: auth configuration, API handler updates, token management, UI components, and provider setup with no unrelated modifications.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 | 🟠 Major

Add 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.local is 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-1 to the brand <Link> (line 35) alongside flex-1 on 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

📥 Commits

Reviewing files that changed from the base of the PR and between bee12e5 and 011b07f.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (13)
  • .gitignore
  • package.json
  • src/app/api/auth/[...nextauth]/route.ts
  • src/app/api/generate/route.ts
  • src/app/generate/GeneratePageClient.tsx
  • src/app/layout.tsx
  • src/app/providers.tsx
  • src/components/Generator/SearchInput.tsx
  • src/components/GitHubLoginButton.tsx
  • src/components/layout/Navbar.tsx
  • src/lib/auth.ts
  • src/lib/octokit.ts
  • src/types/next-auth.d.ts

Comment thread src/components/Generator/SearchInput.tsx Outdated
Comment thread src/components/layout/Navbar.tsx Outdated
Comment thread src/lib/auth.ts
Comment on lines +13 to +15
authorization: {
params: { scope: "repo read:user" },
},
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

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).

Comment thread src/lib/auth.ts
Comment on lines +18 to +29
callbacks: {
async jwt({ token, account }) {
if (account?.access_token) {
token.accessToken = account.access_token;
}

return token;
},
async session({ session }) {
return session;
},
},
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

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.

Comment thread src/lib/octokit.ts Outdated
Comment thread src/lib/octokit.ts
Comment thread src/lib/octokit.ts
@naheel0
Copy link
Copy Markdown
Member

naheel0 commented Apr 23, 2026

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.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 | 🟡 Minor

Desktop link group won't be visually centered.

With justify-between removed from the row and only the actions container getting flex-1 justify-end, the brand has intrinsic width while the links group (justify-center on 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 container flex-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 previous justify-between layout.

🤖 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

📥 Commits

Reviewing files that changed from the base of the PR and between 011b07f and 6c84b9b.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (7)
  • src/app/api/generate/route.ts
  • src/app/generate/GeneratePageClient.tsx
  • src/components/Generator/SearchInput.tsx
  • src/components/GitHubLoginButton.tsx
  • src/components/layout/Navbar.tsx
  • src/lib/auth.ts
  • src/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

Comment thread src/app/api/generate/route.ts
Comment on lines +101 to +110
<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>
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

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.

Suggested change
<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.

Comment thread src/lib/octokit.ts
Comment on lines +67 to +73
if (status === 403) {
return new RepoAccessError(
"GitHub denied access to this repository.",
403,
"FORBIDDEN",
);
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 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:


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.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

📥 Commits

Reviewing files that changed from the base of the PR and between 6c84b9b and 2dcc56e.

📒 Files selected for processing (5)
  • src/app/api/generate/route.ts
  • src/app/generate/GeneratePageClient.tsx
  • src/components/Generator/SearchInput.tsx
  • src/components/GitHubLoginButton.tsx
  • src/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

Comment on lines +61 to +68
if (privateRepoConsentRequired && !ackPrivateRepo) {
setError(
"Please confirm the private repository consent checkbox, then click Generate again.",
);
return;
}

onGenerate(url.trim(), language, ackPrivateRepo);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

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.

@anshk1234
Copy link
Copy Markdown
Author

anshk1234 commented Apr 23, 2026

Pushed a follow-up refinement pass in commit 2dcc56e.

Updates included:

  • Fixed the public repository error path so unexpected server failures no longer render raw HTML in the UI
  • Preserved the private repository consent flow, but reworked it into a cleaner and more user-friendly confirmation step
  • Improved the messaging around private repo confirmation so the action is clear and intentional
  • Cleaned up the logged-out navbar state by removing the extra scope note from the header button
  • Adjusted navbar layout/alignment so the center navigation is properly positioned
  • Re-tested the public/private repository generation flow locally

I also reviewed the updated flow locally and tested it again after these changes. Everything is now working smoothly from my side.

Ran npm install and npm run build locally to verify.
The production build completed successfully, including the postbuild sitemap generation.

some screenshots :-

  1. readme generation working good
Screenshot (836)
  1. consent flow to take user permission before acessing private repo
Screenshot (838)
  1. when accesed private repo without login , shows this message
Screenshot (837)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

🔐 Add GitHub Login (Next.js Only) to Support Private Repositories

2 participants