feat: Add GitHub link with star count to navbar#2926
feat: Add GitHub link with star count to navbar#2926AnvitaPrasad wants to merge 4 commits intoJabRef:mainfrom
Conversation
Made-with: Cursor
Made-with: Cursor
There was a problem hiding this comment.
Pull request overview
Adds a GitHub repository link with a live star count to the landing page navigation bar, supporting issue #2518 by making the repo easier to reach from the site header.
Changes:
- Adds a
showGithubLinkprop toNavBarand renders a GitHub icon + star count on the right side (desktop). - Introduces
/api/githubStarsto fetch star counts from the GitHub REST API. - Adds
useGitHubStarscomposable and wires it intoNavBar; adds a GitHub link entry to the collapsed (mobile) menu on the homepage.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
| server/api/githubStars.ts | New API endpoint to fetch stargazer count from GitHub |
| composables/useGitHubStars.ts | New composable to fetch/format star count from the new API |
| components/NavBar.vue | Adds showGithubLink prop and renders the GitHub icon + star count on desktop |
| pages/index.vue | Enables the GitHub link on the landing page navbar and adds a mobile-menu GitHub link |
server/api/githubStars.ts
Outdated
| export default defineEventHandler(async (event) => { | ||
| const query = getQuery(event) | ||
| const repo = query.repo as string | ||
|
|
||
| if (!repo) { | ||
| throw createError({ | ||
| statusCode: 400, | ||
| message: 'Missing repo parameter', | ||
| }) | ||
| } | ||
|
|
||
| try { | ||
| const response = await fetch(`https://api.github.com/repos/${repo}`) | ||
|
|
||
| if (!response.ok) { | ||
| throw new Error(`GitHub API responded with ${response.status}`) | ||
| } | ||
|
|
||
| const data = (await response.json()) as { stargazers_count?: number } | ||
|
|
||
| return { | ||
| stars: data.stargazers_count ?? 0, | ||
| } | ||
| } catch (error) { | ||
| console.debug('Failed to fetch GitHub stars for repo', repo, error) | ||
| throw createError({ | ||
| statusCode: 500, | ||
| message: 'Failed to fetch GitHub stars', | ||
| }) | ||
| } | ||
| }) |
There was a problem hiding this comment.
The PR description mentions caching, but this endpoint currently fetches GitHub on every request. In production this is likely to hit GitHub’s unauthenticated rate limits and adds latency to every page view. Consider adding server-side caching (e.g., Nitro storage-based TTL cache / cached event handler) and/or setting appropriate cache headers so repeated requests reuse the cached star count for a period (e.g., 5–30 minutes).
| export default defineEventHandler(async (event) => { | |
| const query = getQuery(event) | |
| const repo = query.repo as string | |
| if (!repo) { | |
| throw createError({ | |
| statusCode: 400, | |
| message: 'Missing repo parameter', | |
| }) | |
| } | |
| try { | |
| const response = await fetch(`https://api.github.com/repos/${repo}`) | |
| if (!response.ok) { | |
| throw new Error(`GitHub API responded with ${response.status}`) | |
| } | |
| const data = (await response.json()) as { stargazers_count?: number } | |
| return { | |
| stars: data.stargazers_count ?? 0, | |
| } | |
| } catch (error) { | |
| console.debug('Failed to fetch GitHub stars for repo', repo, error) | |
| throw createError({ | |
| statusCode: 500, | |
| message: 'Failed to fetch GitHub stars', | |
| }) | |
| } | |
| }) | |
| export default defineCachedEventHandler( | |
| async (event) => { | |
| const query = getQuery(event) | |
| const repo = query.repo as string | |
| if (!repo) { | |
| throw createError({ | |
| statusCode: 400, | |
| message: 'Missing repo parameter', | |
| }) | |
| } | |
| // Allow shared caches (CDN/reverse proxy) to reuse this response. | |
| // This is aligned with the server-side TTL below. | |
| event.node.res.setHeader( | |
| 'Cache-Control', | |
| 'public, s-maxage=300, stale-while-revalidate=600', | |
| ) | |
| try { | |
| const response = await fetch(`https://api.github.com/repos/${repo}`) | |
| if (!response.ok) { | |
| throw new Error(`GitHub API responded with ${response.status}`) | |
| } | |
| const data = (await response.json()) as { stargazers_count?: number } | |
| return { | |
| stars: data.stargazers_count ?? 0, | |
| } | |
| } catch (error) { | |
| console.debug('Failed to fetch GitHub stars for repo', repo, error) | |
| throw createError({ | |
| statusCode: 500, | |
| message: 'Failed to fetch GitHub stars', | |
| }) | |
| } | |
| }, | |
| { | |
| // Cache the computed star count on the server for 5 minutes | |
| maxAge: 300, | |
| }, | |
| ) |
server/api/githubStars.ts
Outdated
| const query = getQuery(event) | ||
| const repo = query.repo as string | ||
|
|
||
| if (!repo) { | ||
| throw createError({ | ||
| statusCode: 400, | ||
| message: 'Missing repo parameter', | ||
| }) | ||
| } | ||
|
|
There was a problem hiding this comment.
repo is taken directly from the query string and interpolated into the GitHub API path without validation/normalization. Since the UI always queries a single repo, this effectively exposes a public proxy that can be abused to trigger arbitrary GitHub API calls (and burn rate limit). Prefer hardcoding the JabRef repo in the handler (no repo param), or strictly validate the parameter (e.g., owner/name with a tight regex) and reject anything else.
| const query = getQuery(event) | |
| const repo = query.repo as string | |
| if (!repo) { | |
| throw createError({ | |
| statusCode: 400, | |
| message: 'Missing repo parameter', | |
| }) | |
| } | |
| const repo = 'JabRef/jabref' |
server/api/githubStars.ts
Outdated
| try { | ||
| const response = await fetch(`https://api.github.com/repos/${repo}`) | ||
|
|
There was a problem hiding this comment.
This endpoint uses unauthenticated GitHub REST API requests, which are heavily rate-limited (and may fail intermittently under load). The codebase already has runtimeConfig.githubRepoToken used for GitHub API calls (see server/api/getLatestRelease.ts); consider reusing that token here (when set) to raise rate limits and improve reliability, while still keeping caching in place.
| try { | |
| const response = await fetch(`https://api.github.com/repos/${repo}`) | |
| const config = useRuntimeConfig() | |
| const githubToken = (config as { githubRepoToken?: string }).githubRepoToken | |
| try { | |
| const headers: Record<string, string> = { | |
| 'User-Agent': 'jabref-online', | |
| Accept: 'application/vnd.github.v3+json', | |
| } | |
| if (githubToken) { | |
| headers.Authorization = `token ${githubToken}` | |
| } | |
| const response = await fetch(`https://api.github.com/repos/${repo}`, { | |
| headers, | |
| }) |
🔗 Linked issue
Resolves #2518
📚 Description
Added a GitHub icon with star count to the top-right corner of the landing page navbar, similar to other open-source projects like Nuxt.com. This makes it easier for users to navigate to the JabRef GitHub repository directly from the website.
Changes:
showGithubLinkprop to NavBar component/api/githubStarsto fetch GitHub star count with cachinguseGitHubStarscomposable to fetch and format star count