Skip to content

feat: implement initial repository aggregator with TypeScript and modular architecture#45

Open
lidiaAsamnew wants to merge 3 commits intoAOSSIE-Org:mainfrom
lidiaAsamnew:main
Open

feat: implement initial repository aggregator with TypeScript and modular architecture#45
lidiaAsamnew wants to merge 3 commits intoAOSSIE-Org:mainfrom
lidiaAsamnew:main

Conversation

@lidiaAsamnew
Copy link

@lidiaAsamnew lidiaAsamnew commented Mar 19, 2026

Description

I am submitting this initial PR to demonstrate my technical approach and architectural vision for the Org Explorer as a GSoC 2026 candidate. This PR implements the foundational data-fetching architecture and repository aggregator for the 3 AOSSIE organizations.

Technical Highlights:

  • Parallel Data Fetching: Optimized aggregator using Promise.all for AOSSIE-Org, StabilityNexus, and DjedAlliance.
  • Strict Type Safety: Comprehensive TypeScript interfaces for Repository and Organization entities.
  • Modular Component Architecture: Reusable RepositoryCard component following React best practices.
  • Environment Management: Support for .env and provided .env.example template.

Addressed Issues:

Fixes #30

Screenshots/Recordings:

Screenshot from 2026-03-19 15-17-00

Additional Notes:

Verified locally with a Personal Access Token, successfully aggregating 169+ repositories. This serves as the visualization foundation for the Mentor Dashboard.

Checklist

  • My code follows the project's code style and conventions
  • I have made corresponding changes to the documentation
  • My changes generate no new warnings or errors
  • I have joined the Discord server and I will share a link to this PR with the project maintainers there
  • I have read the Contributing Guidelines

⚠️ AI Notice - Important!

I have utilized AI tools to assist in structural design and documentation. I have personally reviewed and verified all code changes.

Summary by CodeRabbit

  • New Features

    • Dashboard now dynamically fetches and displays repositories from configured organizations in a responsive grid.
    • Repository cards show owner, name (opens in new tab), description, star count, fork count, and language.
    • Header displays total repository count; results are sorted by star count.
    • Fetching handles multi-page org results to surface more repositories.
  • Bug Fixes / Resilience

    • Improved loading and error states: full-screen loading, clear error view, and partial failures no longer block other results.

@coderabbitai
Copy link

coderabbitai bot commented Mar 19, 2026

Walkthrough

Replaces static UI with a data-driven dashboard: App fetches GitHub org repositories on mount, manages loading/error state, aggregates and sorts results, and renders a grid of RepositoryCard components; adds Repository type and UI string constants.

Changes

Cohort / File(s) Summary
Core App Logic
src/App.tsx
Replaced static UI with fetch-driven dashboard. Added state (repos, loading, error), mount-time useEffect fetching per-org repos (paginated, optional VITE_GITHUB_TOKEN auth), concurrent aggregation via Promise.allSettled, flattening and sorting by stargazers_count, and conditional loading/error/empty rendering.
Repository Card Component
src/components/RepositoryCard.tsx
New RepositoryCard component exporting typed Props { repo: Repository }, renders owner, external repo link (new tab, rel="noreferrer"), description fallback (UI_STRINGS.NO_DESCRIPTION), star/fork counts, and language fallback (UI_STRINGS.FALLBACK_LANGUAGE).
Types & Constants
src/types/github.ts, src/constants/strings.ts
Added exported Repository TypeScript interface matching GitHub repo shape and UI_STRINGS constant with dashboard strings, loading/error text, and fallback labels.

Sequence Diagram

sequenceDiagram
    participant App as App Component
    participant GH as GitHub API
    participant State as App State
    participant Card as RepositoryCard

    App->>App: useEffect on mount
    App->>GH: Fetch /orgs/{org}/repos (paginated, parallel per org)
    GH-->>App: Per-org repo pages (fulfilled/rejected)
    App->>State: Aggregate fulfilled results, flatten, sort by stargazers_count
    State-->>App: Update repos, set loading=false
    alt No successful repos
        App->>App: Set error state / render error
    else Some repos fetched
        App->>Card: Render RepositoryCard grid for each repo
        Card-->>App: Display repo details (link, desc, stars, forks, language)
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • AOSSIE-Org/OrgExplorer#2: Replaces and extends the initial App.tsx scaffold with a data-driven dashboard and related components/types.

Suggested labels

Typescript Lang

Suggested reviewers

  • Zahnentferner

Poem

🐰 I hopped through code to fetch and sort,
Cards of repos in tidy court,
Stars and forks in neat array,
Org-by-org I fetched away,
Hooray — a rabbit-coded port! 🥕

🚥 Pre-merge checks | ✅ 3 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Linked Issues check ⚠️ Warning The PR implements foundational data-fetching architecture but does not implement the GitHub GraphQL API migration or Contributor Activity Matrix features required by issue #30. It uses REST API pagination instead of the proposed GraphQL optimization. Implement GitHub GraphQL API integration with organization-wide search queries to fetch PRs and issues across all orgs efficiently, as specified in issue #30's technical execution plan.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately reflects the main change: implementing a repository aggregator with TypeScript and modular architecture, as evidenced by the new data-fetching logic in App.tsx, the new RepositoryCard component, and supporting type/string files.
Out of Scope Changes check ✅ Passed All changes (App.tsx refactor, RepositoryCard component, Repository type definition, UI_STRINGS constant) are directly related to building the repository aggregator foundational layer and are within scope as preparatory work for issue #30.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
📝 Coding Plan
  • Generate coding plan for human review comments

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.

@lidiaAsamnew lidiaAsamnew changed the title feat: implement initial repository aggregator with TypeScript and Ind… feat: implement initial repository aggregator with TypeScript and modular architecture Mar 19, 2026
@github-actions github-actions bot added size/XL and removed size/XL labels Mar 19, 2026
Copy link

@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: 5

🤖 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.tsx`:
- Around line 47-57: The component App currently hardcodes user-facing strings
("Loading AOSSIE Ecosystem...", the error message rendered when error is truthy,
"AOSSIE Org Explorer" title and the subtitle using repos.length) — extract these
literals into your i18n resources and replace inline strings with calls to the
i18n lookup (e.g., t('loading'), t('error', { message: error }), t('title'),
t('subtitle', { count: repos.length })) inside the App component; update imports
to include the i18n hook/function you use (e.g., useTranslation or t) and ensure
pluralization/placeholder usage for repos.length is defined in the resource
files.
- Around line 17-27: fetchOrgRepos lost the GITHUB_TOKEN auth header causing
unauthenticated requests; update fetchOrgRepos to include the Authorization
header (using the module-scope GITHUB_TOKEN or accept it as a parameter) so each
fetch uses headers: GITHUB_TOKEN ? { Authorization: `token ${GITHUB_TOKEN}` } :
{}, and keep the pagination + Promise.allSettled approach (references:
fetchOrgRepos, GITHUB_TOKEN, ORGS, Promise.allSettled).
- Around line 6-20: The client currently exposes GITHUB_TOKEN and performs org
repo fetches inside fetchAllRepos using per_page=100 and Promise.all; move
authentication and all GitHub API calls to a server-side endpoint (e.g.,
/api/github/orgs) so the PAT is only used on the server (remove
import.meta.env.VITE_GITHUB_TOKEN usage from App), implement pagination on the
server (follow Link header or iterate pages rather than relying on per_page=100)
to return complete repo lists for ORGS, change the client-side fetchAllRepos to
call that server endpoint and handle partial failures by using
Promise.allSettled or by returning per-org status from the server and updating
setError/setRepos accordingly, and extract all user-facing strings currently
hardcoded in App (e.g., loading/error messages) into i18n resource files.

In `@src/components/RepositoryCard.tsx`:
- Around line 28-37: The RepositoryCard component currently uses inline
user-visible strings for fallbacks ("No description provided." and "Docs");
replace these literals with i18n resource lookups instead (e.g., use your
translation function like t('repo.noDescription') and t('repo.language.docs') or
pull from the i18n resource object) so repo.description and repo.language
fallbacks are externalized; update RepositoryCard to import/use the project's
i18n helper and reference keys for the two messages rather than hard-coded
strings.

In `@src/types/github.ts`:
- Around line 4-8: The type declaration currently marks the properties
description and language as string but GitHub can return null; update the
interface/type that declares description and language so both are typed as
string | null (e.g., change description: string; → description: string | null;
and language: string; → language: string | null;) to match the API and avoid
unsafe assumptions in consuming code like RepositoryCard.tsx.
🪄 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: ASSERTIVE

Plan: Pro

Run ID: 032ac4e8-8fd4-47f7-91e8-9141cc795576

📥 Commits

Reviewing files that changed from the base of the PR and between 1cfc3e7 and 8044abf.

⛔ Files ignored due to path filters (1)
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (3)
  • src/App.tsx
  • src/components/RepositoryCard.tsx
  • src/types/github.ts

src/App.tsx Outdated
Comment on lines +6 to +20
const GITHUB_TOKEN = import.meta.env.VITE_GITHUB_TOKEN;

function App() {
const [repos, setRepos] = useState<Repository[]>([]);
const [loading, setLoading] = useState<boolean>(true);
const [error, setError] = useState<string | null>(null);

useEffect(() => {
const fetchAllRepos = async () => {
try {
setLoading(true);
const requests = ORGS.map(org =>
fetch(`https://api.github.com/orgs/${org}/repos?per_page=100`, {
headers: GITHUB_TOKEN ? { Authorization: `token ${GITHUB_TOKEN}` } : {}
}).then(res => {
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
rg -n -C2 --type ts --type tsx 'VITE_GITHUB_TOKEN|Authorization:\s*`token\s+\$\{GITHUB_TOKEN\}`|import\.meta\.env\.VITE_'

Repository: AOSSIE-Org/OrgExplorer

Length of output: 93


🏁 Script executed:

#!/bin/bash
# Find and read the App.tsx file
echo "=== Checking if App.tsx exists ==="
ls -la src/App.tsx 2>&1 || echo "File not found"

echo ""
echo "=== Reading src/App.tsx ==="
cat -n src/App.tsx | head -50

Repository: AOSSIE-Org/OrgExplorer

Length of output: 2176


🏁 Script executed:

#!/bin/bash
# Search for VITE_GITHUB_TOKEN and Authorization header patterns
echo "=== Searching for VITE_GITHUB_TOKEN ==="
rg -n 'VITE_GITHUB_TOKEN' . 2>&1

echo ""
echo "=== Searching for Authorization header with token ==="
rg -n 'Authorization.*token.*GITHUB_TOKEN' . 2>&1

echo ""
echo "=== Searching for import.meta.env.VITE usage ==="
rg -n 'import\.meta\.env\.VITE_' . 2>&1

Repository: AOSSIE-Org/OrgExplorer

Length of output: 455


🏁 Script executed:

#!/bin/bash
# Check for i18n setup and usage
echo "=== Looking for i18n configuration ==="
find src -name '*i18n*' -o -name '*translation*' -o -name '*locale*' 2>&1 | head -20

echo ""
echo "=== Searching for potential user-visible strings in App.tsx ==="
rg -n '(loading|error|Loading|Error|fetching|Fetching)' src/App.tsx 2>&1

Repository: AOSSIE-Org/OrgExplorer

Length of output: 753


Move GitHub authentication to a server-side endpoint—do not send tokens from client code.

Lines 6 and 19 expose a GitHub PAT to the browser. Vite's VITE_ prefixed environment variables are automatically embedded in the client-side bundle, making the token visible to users. Authentication must happen server-side with credentials kept secure.

Additionally:

  • Pagination: Line 18 uses per_page=100 which truncates results for organizations with more than 100 repositories.
  • Reliability: Line 26 uses Promise.all(), which fails all requests if any single organization request fails. Consider Promise.allSettled() to handle individual org failures gracefully.
  • Internationalization: User-visible strings at lines 35, 47, and 51 must be externalized to resource files per i18n guidelines (currently hardcoded English strings).
Example server-side endpoint pattern

Move fetch logic to a backend route (e.g., /api/github/orgs) that holds the token server-side, and call that from the client instead.

-const GITHUB_TOKEN = import.meta.env.VITE_GITHUB_TOKEN;
...
-          fetch(`https://api.github.com/orgs/${org}/repos?per_page=100`, {
-            headers: GITHUB_TOKEN ? { Authorization: `token ${GITHUB_TOKEN}` } : {}
-          }).then(res => {
+          fetch(`/api/github/orgs/${org}/repos`).then(res => {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/App.tsx` around lines 6 - 20, The client currently exposes GITHUB_TOKEN
and performs org repo fetches inside fetchAllRepos using per_page=100 and
Promise.all; move authentication and all GitHub API calls to a server-side
endpoint (e.g., /api/github/orgs) so the PAT is only used on the server (remove
import.meta.env.VITE_GITHUB_TOKEN usage from App), implement pagination on the
server (follow Link header or iterate pages rather than relying on per_page=100)
to return complete repo lists for ORGS, change the client-side fetchAllRepos to
call that server endpoint and handle partial failures by using
Promise.allSettled or by returning per-org status from the server and updating
setError/setRepos accordingly, and extract all user-facing strings currently
hardcoded in App (e.g., loading/error messages) into i18n resource files.

@github-actions github-actions bot added size/XL and removed size/XL labels Mar 19, 2026
Copy link

@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

♻️ Duplicate comments (3)
src/App.tsx (2)

8-15: ⚠️ Potential issue | 🔴 Critical

The PAT is still public in a Vite build.

The note at Lines 9-12 is not accurate for VITE_ variables: they are compiled into browser-accessible code. If this must stay backend-less, collect the PAT at runtime and keep it in memory instead of import.meta.env.

In Vite, are environment variables prefixed with `VITE_` embedded into client-side bundles and accessible from browser code?

As per coding guidelines, "No exposed API keys or sensitive data".

Also applies to: 26-27

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/App.tsx` around lines 8 - 15, The GITHUB_TOKEN constant uses
import.meta.env.VITE_GITHUB_TOKEN which Vite embeds into the client bundle
making the PAT public; change to collecting the PAT at runtime (e.g., prompt or
input component) and store it only in-memory (React state or a context) instead
of using import.meta.env, replacing references to GITHUB_TOKEN with the runtime
state variable and removing import.meta.env.VITE_GITHUB_TOKEN usage in App.tsx.

22-30: ⚠️ Potential issue | 🟠 Major

fetchOrgRepos() still stops at page 1.

page is interpolated into the URL, but the function never advances it, so any org above 100 repos is truncated. Loop until a page returns fewer than 100 items, or follow the Link header.

📄 Suggested pagination fix
-    const fetchOrgRepos = async (org: string, page = 1): Promise<Repository[]> => {
-      const response = await fetch(
-        `https://api.github.com/orgs/${org}/repos?per_page=100&page=${page}`,
-        {
-          headers: GITHUB_TOKEN ? { Authorization: `token ${GITHUB_TOKEN}` } : {},
-        }
-      );
-      if (!response.ok) throw new Error(`${response.status} while fetching ${org}`);
-      return response.json();
-    };
+    const fetchOrgRepos = async (org: string): Promise<Repository[]> => {
+      const allRepos: Repository[] = [];
+      for (let page = 1; ; page += 1) {
+        const response = await fetch(
+          `https://api.github.com/orgs/${org}/repos?per_page=100&page=${page}`,
+          {
+            headers: GITHUB_TOKEN ? { Authorization: `token ${GITHUB_TOKEN}` } : {},
+          }
+        );
+        if (!response.ok) throw new Error(`${response.status} while fetching ${org}`);
+
+        const pageRepos: Repository[] = await response.json();
+        allRepos.push(...pageRepos);
+        if (pageRepos.length < 100) break;
+      }
+      return allRepos;
+    };
For GitHub REST `GET /orgs/{org}/repos`, is `per_page` capped at 100 and do clients need to paginate with `page` or the `Link` header to retrieve all repositories?
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/App.tsx` around lines 22 - 30, fetchOrgRepos only fetches a single page
because it never advances or loops over the page parameter; update fetchOrgRepos
to paginate (either by following the Link header or by looping pages
incrementing page) and accumulate results across requests until no more repos
are returned (response length < 100 or Link header has no "next"). Keep the
existing signature (fetchOrgRepos) but perform repeated fetches with the same
Authorization header, push each page's JSON into a growing array, and return the
combined Repository[]; ensure errors still throw on non-ok responses and handle
Link parsing if you choose that approach.
src/constants/strings.ts (1)

1-10: ⚠️ Potential issue | 🟠 Major

This is still not i18n-ready.

The new constants file centralizes copy, but it still hardcodes English, and ERROR_PREFIX/SUBTITLE_* split sentences in a way translators cannot reorder or pluralize. Use full locale keys with parameters instead of prefix/suffix fragments.

As per coding guidelines, "Internationalization: User-visible strings should be externalized to resource files (i18n)".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/constants/strings.ts` around lines 1 - 10, UI_STRINGS currently hardcodes
English and splits sentences into fragments (ERROR_PREFIX, SUBTITLE_PREFIX,
SUBTITLE_SUFFIX) which prevents proper i18n and pluralization; replace these
fragmented keys with full, parameterized locale keys (e.g., ERROR_MESSAGE with a
{message} param, SUBTITLE with a {count} param and possible plural forms) and
move all user-facing strings out of src/constants/strings.ts into the app's i18n
resource files (locale JSONs) so callers use the i18n lookup (e.g.,
t('ERROR_MESSAGE', { message }) or t('SUBTITLE', { count })) instead of
concatenating prefixes/suffixes or using hardcoded English in UI_STRINGS.
🤖 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.tsx`:
- Around line 37-49: The current logic drops rejected org fetches and only sets
error when all orgs fail; modify the fetch flow that uses ORGS and fetchOrgRepos
so each promise is paired with its org name (e.g., map ORGS to {org, promise})
and after Promise.allSettled inspect results to build two things: (1)
successfulRepos (as you already do) and call setRepos(successfulRepos), and (2)
a list of failedOrgs derived from rejected results; introduce or reuse a state
like setWarning/setPartialFetchWarning and set a non-blocking warning message
describing failedOrgs (instead of only calling setError when
successfulRepos.length === 0), so partial results are shown while failed orgs
are surfaced to the UI.

---

Duplicate comments:
In `@src/App.tsx`:
- Around line 8-15: The GITHUB_TOKEN constant uses
import.meta.env.VITE_GITHUB_TOKEN which Vite embeds into the client bundle
making the PAT public; change to collecting the PAT at runtime (e.g., prompt or
input component) and store it only in-memory (React state or a context) instead
of using import.meta.env, replacing references to GITHUB_TOKEN with the runtime
state variable and removing import.meta.env.VITE_GITHUB_TOKEN usage in App.tsx.
- Around line 22-30: fetchOrgRepos only fetches a single page because it never
advances or loops over the page parameter; update fetchOrgRepos to paginate
(either by following the Link header or by looping pages incrementing page) and
accumulate results across requests until no more repos are returned (response
length < 100 or Link header has no "next"). Keep the existing signature
(fetchOrgRepos) but perform repeated fetches with the same Authorization header,
push each page's JSON into a growing array, and return the combined
Repository[]; ensure errors still throw on non-ok responses and handle Link
parsing if you choose that approach.

In `@src/constants/strings.ts`:
- Around line 1-10: UI_STRINGS currently hardcodes English and splits sentences
into fragments (ERROR_PREFIX, SUBTITLE_PREFIX, SUBTITLE_SUFFIX) which prevents
proper i18n and pluralization; replace these fragmented keys with full,
parameterized locale keys (e.g., ERROR_MESSAGE with a {message} param, SUBTITLE
with a {count} param and possible plural forms) and move all user-facing strings
out of src/constants/strings.ts into the app's i18n resource files (locale
JSONs) so callers use the i18n lookup (e.g., t('ERROR_MESSAGE', { message }) or
t('SUBTITLE', { count })) instead of concatenating prefixes/suffixes or using
hardcoded English in UI_STRINGS.
🪄 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: ASSERTIVE

Plan: Pro

Run ID: 0d387a10-40b5-4ade-8ce9-f4a612a98667

📥 Commits

Reviewing files that changed from the base of the PR and between 8044abf and f24e441.

📒 Files selected for processing (4)
  • src/App.tsx
  • src/components/RepositoryCard.tsx
  • src/constants/strings.ts
  • src/types/github.ts

Comment on lines +37 to +49
const results = await Promise.allSettled(ORGS.map(org => fetchOrgRepos(org)));

const successfulRepos = results
.filter((result): result is PromiseFulfilledResult<Repository[]> => result.status === 'fulfilled')
.map(result => result.value)
.flat()
.sort((a, b) => b.stargazers_count - a.stargazers_count);

setRepos(successfulRepos);

if (successfulRepos.length === 0) {
setError("No data could be retrieved from GitHub.");
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Surface partial-org failures instead of silently dropping them.

Rejected org fetches are filtered out, but error is only set when every org fails. That lets the dashboard look complete while one or more organizations are missing. Wrap each promise with its org name, track the rejected orgs, and show a non-blocking warning alongside the partial results.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/App.tsx` around lines 37 - 49, The current logic drops rejected org
fetches and only sets error when all orgs fail; modify the fetch flow that uses
ORGS and fetchOrgRepos so each promise is paired with its org name (e.g., map
ORGS to {org, promise}) and after Promise.allSettled inspect results to build
two things: (1) successfulRepos (as you already do) and call
setRepos(successfulRepos), and (2) a list of failedOrgs derived from rejected
results; introduce or reuse a state like setWarning/setPartialFetchWarning and
set a non-blocking warning message describing failedOrgs (instead of only
calling setError when successfulRepos.length === 0), so partial results are
shown while failed orgs are surfaced to the UI.

@github-actions github-actions bot added size/XL and removed size/XL labels Mar 19, 2026
Copy link

@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

♻️ Duplicate comments (2)
src/App.tsx (2)

61-66: ⚠️ Potential issue | 🟠 Major

Partial org failures are silently dropped.

When some orgs succeed and others fail, the error state is only set if successfulRepos.length === 0. Users see incomplete data without any indication that one or more organizations failed to load. Consider tracking rejected orgs separately and displaying a non-blocking warning alongside the partial results.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/App.tsx` around lines 61 - 66, The code only sets an error when all org
requests fail, which silently hides partial failures; update the handling around
results/successfulRepos to collect rejected items (e.g., filter results for
status === 'rejected' and extract their reasons and associated org identifiers),
then call a non-blocking warning state setter (create/use a state like
setWarning or setPartialFailure) with a concise message listing failed
orgs/reasons while still calling setRepos/setSuccessfulRepos for the successful
responses; ensure the UI reads that warning state to display a warning alongside
the partial results.

9-9: ⚠️ Potential issue | 🔴 Critical

Security: GitHub PAT is exposed to the browser.

VITE_ prefixed environment variables are embedded in the client-side bundle, making the token visible to users via browser dev tools or source inspection. This authentication should be moved to a server-side endpoint.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/App.tsx` at line 9, The code exposes a GitHub PAT by using
import.meta.env.VITE_GITHUB_TOKEN (GITHUB_TOKEN constant) in the client; remove
any direct usage of GITHUB_TOKEN/import.meta.env.VITE_GITHUB_TOKEN from the
frontend and instead create a server-side endpoint that reads the secret from a
non-VITE_ env var and performs authenticated GitHub requests (or proxies
requests) on behalf of the client; update the frontend to call that endpoint
(e.g., fetch /api/github/...) and handle responses, and ensure the secret is
stored only in server env vars and not bundled into the client.
🤖 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.tsx`:
- Around line 16-75: The effect should use an AbortController to cancel
in-flight fetches on unmount: create const controller = new AbortController() in
the useEffect, pass controller.signal into fetchOrgRepos (add a signal param to
fetchOrgRepos and include it in the fetch options), and return a cleanup
function that calls controller.abort(). Inside fetchOrgRepos and fetchAllData,
handle aborts by checking controller.signal.aborted (or catching an error with
name === 'AbortError') and bail out early so you don't call setLoading,
setRepos, or setError after unmount; also, before applying results after
Promise.allSettled in fetchAllData, check controller.signal.aborted and return
if true. Ensure references: fetchOrgRepos, fetchAllData, ORGS, setLoading,
setRepos, setError, and useEffect are updated accordingly.

---

Duplicate comments:
In `@src/App.tsx`:
- Around line 61-66: The code only sets an error when all org requests fail,
which silently hides partial failures; update the handling around
results/successfulRepos to collect rejected items (e.g., filter results for
status === 'rejected' and extract their reasons and associated org identifiers),
then call a non-blocking warning state setter (create/use a state like
setWarning or setPartialFailure) with a concise message listing failed
orgs/reasons while still calling setRepos/setSuccessfulRepos for the successful
responses; ensure the UI reads that warning state to display a warning alongside
the partial results.
- Line 9: The code exposes a GitHub PAT by using
import.meta.env.VITE_GITHUB_TOKEN (GITHUB_TOKEN constant) in the client; remove
any direct usage of GITHUB_TOKEN/import.meta.env.VITE_GITHUB_TOKEN from the
frontend and instead create a server-side endpoint that reads the secret from a
non-VITE_ env var and performs authenticated GitHub requests (or proxies
requests) on behalf of the client; update the frontend to call that endpoint
(e.g., fetch /api/github/...) and handle responses, and ensure the secret is
stored only in server env vars and not bundled into the client.
🪄 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: ASSERTIVE

Plan: Pro

Run ID: b02287cf-f2d8-4c8e-b737-d6b15358d8cc

📥 Commits

Reviewing files that changed from the base of the PR and between f24e441 and 776f727.

📒 Files selected for processing (2)
  • src/App.tsx
  • src/types/github.ts

Comment on lines +16 to +75
useEffect(() => {

const fetchOrgRepos = async (org: string): Promise<Repository[]> => {
let allOrgRepos: Repository[] = [];
let page = 1;
let hasNextPage = true;

while (hasNextPage) {
const response = await fetch(
`https://api.github.com/orgs/${org}/repos?per_page=100&page=${page}`,
{
headers: GITHUB_TOKEN ? { Authorization: `token ${GITHUB_TOKEN}` } : {},
}
);

if (!response.ok) {
throw new Error(`${response.status} while fetching ${org}`);
}

const data: Repository[] = await response.json();
allOrgRepos = [...allOrgRepos, ...data];

if (data.length === 100) {
page++;
} else {
hasNextPage = false;
}
}
return allOrgRepos;
};

const fetchAllData = async () => {
try {
setLoading(true);

const results = await Promise.allSettled(ORGS.map(org => fetchOrgRepos(org)));

const successfulRepos = results
.filter((result): result is PromiseFulfilledResult<Repository[]> => result.status === 'fulfilled')
.map(result => result.value)
.flat()
.sort((a, b) => b.stargazers_count - a.stargazers_count);

setRepos(successfulRepos);

if (successfulRepos.length === 0) {
const failureReasons = results
.filter((r): r is PromiseRejectedResult => r.status === 'rejected')
.map(r => r.reason.message).join(', ');
setError(`Failed to fetch data: ${failureReasons}`);
}
} catch (err) {
setError(err instanceof Error ? err.message : UI_STRINGS.UNKNOWN_ERROR);
} finally {
setLoading(false);
}
};

fetchAllData();
}, []);
Copy link

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Consider adding AbortController for fetch cleanup.

The useEffect lacks cleanup logic to abort pending fetch requests if the component unmounts mid-fetch. While less critical for the root App component, this is a React best practice that prevents potential memory leaks and "setState on unmounted component" warnings.

Suggested pattern
  useEffect(() => {
+   const controller = new AbortController();
+
    const fetchOrgRepos = async (org: string): Promise<Repository[]> => {
      // ...
      const response = await fetch(
        `https://api.github.com/orgs/${org}/repos?per_page=100&page=${page}`,
        {
          headers: GITHUB_TOKEN ? { Authorization: `token ${GITHUB_TOKEN}` } : {},
+         signal: controller.signal,
        }
      );
      // ...
    };

    // ... fetchAllData ...

    fetchAllData();
+
+   return () => controller.abort();
  }, []);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/App.tsx` around lines 16 - 75, The effect should use an AbortController
to cancel in-flight fetches on unmount: create const controller = new
AbortController() in the useEffect, pass controller.signal into fetchOrgRepos
(add a signal param to fetchOrgRepos and include it in the fetch options), and
return a cleanup function that calls controller.abort(). Inside fetchOrgRepos
and fetchAllData, handle aborts by checking controller.signal.aborted (or
catching an error with name === 'AbortError') and bail out early so you don't
call setLoading, setRepos, or setError after unmount; also, before applying
results after Promise.allSettled in fetchAllData, check
controller.signal.aborted and return if true. Ensure references: fetchOrgRepos,
fetchAllData, ORGS, setLoading, setRepos, setError, and useEffect are updated
accordingly.

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[FEATURE]: "GSoC Mentor Dashboard" & Cross-Repository Contributor Activity Matrix

1 participant