Skip to content

Add repository link with cached star count to footer#48

Merged
DevSecNinja merged 9 commits intomainfrom
copilot/add-footer-repo-link
Dec 31, 2025
Merged

Add repository link with cached star count to footer#48
DevSecNinja merged 9 commits intomainfrom
copilot/add-footer-repo-link

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Dec 31, 2025

Adds a link to the hadiscover GitHub repository in the footer, displaying the current star count and encouraging users to star the project.

Changes

Backend

  • backend/app/services/indexer.py: Added _store_repo_star_count() method to fetch and cache repository star count during indexing runs
  • backend/app/services/search_service.py: Updated get_statistics() to return cached repo_star_count from database
  • backend/app/api/routes.py: Updated StatisticsResponse to include repo_star_count field

Frontend (frontend/app/page.tsx)

  • Added repository link section in footer below "Last indexed" timestamp
  • Displays cached star count from statistics endpoint
  • Call-to-action: "⭐ Star us on GitHub to show your support!"
  • Theme-aware styling with hover effects

Tests (backend/tests/test_api.py)

  • Added test_statistics_includes_star_count() to validate star count in statistics response

Implementation Details

The repository star count is fetched and cached during the indexing cron job (not on every page load) to prevent GitHub API rate limit exhaustion. The star count is stored in the indexing_metadata table and returned via the existing /api/v1/statistics endpoint.

Benefits:

  • ✅ Prevents API rate limit issues (fetched once per indexing run instead of on every browser refresh)
  • ✅ Better performance (no additional API call when loading the page)
  • ✅ Maintains same UI functionality

Screenshot

Original prompt

This section details on the original issue you should resolve

<issue_title>Include a link to this repo in the footer incl. the total stars and suggestion to star it</issue_title>
<issue_description></issue_description>

Comments on the Issue (you are @copilot in this section)


✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.

Copilot AI and others added 2 commits December 31, 2025 09:45
Co-authored-by: DevSecNinja <14926452+DevSecNinja@users.noreply.github.com>
Co-authored-by: DevSecNinja <14926452+DevSecNinja@users.noreply.github.com>
Copilot AI changed the title [WIP] Include repo link in footer with stars suggestion Add repository link with star count to footer Dec 31, 2025
Copilot AI requested a review from DevSecNinja December 31, 2025 09:52
@github-actions
Copy link
Copy Markdown

github-actions Bot commented Dec 31, 2025

Dependency Review

✅ No vulnerabilities or license issues or OpenSSF Scorecard issues found.

Scanned Files

None

Comment thread backend/tests/test_api.py Fixed
Comment thread backend/tests/test_api.py Fixed
Comment thread backend/tests/test_api.py Fixed
Comment thread backend/tests/test_api.py Fixed
Comment thread backend/tests/test_api.py Fixed
Comment thread backend/tests/test_api.py Fixed
Comment thread backend/tests/test_api.py Fixed
Comment thread backend/tests/test_api.py Fixed
Comment thread backend/tests/test_api.py Fixed
@github-actions
Copy link
Copy Markdown

🐳 Docker Images Published

The following Docker images have been built and published for this PR:

Backend

docker pull ghcr.io/devsecninja/hadiscover/backend:0.0.0-pr.48.86a3324

Frontend

docker pull ghcr.io/devsecninja/hadiscover/frontend:0.0.0-pr.48.86a3324

Test the PR build

# Test backend
docker run -d -p 8000:8000 -e ENVIRONMENT=development ghcr.io/devsecninja/hadiscover/backend:0.0.0-pr.48.86a3324
curl http://localhost:8000/api/v1/health

# Test frontend
docker run -d -p 8080:80 ghcr.io/devsecninja/hadiscover/frontend:0.0.0-pr.48.86a3324
curl http://localhost:8080/hadiscover/

Using docker-compose

services:
  backend:
    image: ghcr.io/devsecninja/hadiscover/backend:0.0.0-pr.48.86a3324
    ports:
      - "8000:8000"
    environment:
      - ENVIRONMENT=development

  frontend:
    image: ghcr.io/devsecninja/hadiscover/frontend:0.0.0-pr.48.86a3324
    ports:
      - "8080:80"

Images are tagged with 0.0.0-pr.48.86a3324 and will be retained for testing purposes.

@DevSecNinja
Copy link
Copy Markdown
Owner

@copilot Can you make sure that we don't run this on every browser refresh but make it part of the indexing cron? Otherwise we'll burn through the GitHub API limit quite fast.

Co-authored-by: DevSecNinja <14926452+DevSecNinja@users.noreply.github.com>
Copy link
Copy Markdown
Contributor Author

Copilot AI commented Dec 31, 2025

@copilot Can you make sure that we don't run this on every browser refresh but make it part of the indexing cron? Otherwise we'll burn through the GitHub API limit quite fast.

Done! The repository star count is now fetched and cached during the indexing cron job instead of on every browser refresh.

Changes in commit 03bb483:

  • Star count is fetched once during indexing and stored in the indexing_metadata table
  • Statistics endpoint now returns the cached repo_star_count
  • Removed the /repo-info endpoint that was making API calls on every request
  • Frontend updated to use the statistics endpoint

This prevents burning through the GitHub API rate limit while maintaining the same UI functionality.

Co-authored-by: DevSecNinja <14926452+DevSecNinja@users.noreply.github.com>
Copilot AI changed the title Add repository link with star count to footer Add repository link with cached star count to footer Dec 31, 2025
@github-actions
Copy link
Copy Markdown

🐳 Docker Images Published

The following Docker images have been built and published for this PR:

Backend

docker pull ghcr.io/devsecninja/hadiscover/backend:0.0.0-pr.48.ed5e517

Frontend

docker pull ghcr.io/devsecninja/hadiscover/frontend:0.0.0-pr.48.ed5e517

Test the PR build

# Test backend
docker run -d -p 8000:8000 -e ENVIRONMENT=development ghcr.io/devsecninja/hadiscover/backend:0.0.0-pr.48.ed5e517
curl http://localhost:8000/api/v1/health

# Test frontend
docker run -d -p 8080:80 ghcr.io/devsecninja/hadiscover/frontend:0.0.0-pr.48.ed5e517
curl http://localhost:8080/hadiscover/

Using docker-compose

services:
  backend:
    image: ghcr.io/devsecninja/hadiscover/backend:0.0.0-pr.48.ed5e517
    ports:
      - "8000:8000"
    environment:
      - ENVIRONMENT=development

  frontend:
    image: ghcr.io/devsecninja/hadiscover/frontend:0.0.0-pr.48.ed5e517
    ports:
      - "8080:80"

Images are tagged with 0.0.0-pr.48.ed5e517 and will be retained for testing purposes.

@github-actions
Copy link
Copy Markdown

🐳 Docker Images Published

The following Docker images have been built and published for this PR:

Backend

docker pull ghcr.io/devsecninja/hadiscover/backend:0.0.0-pr.48.7dede89

Frontend

docker pull ghcr.io/devsecninja/hadiscover/frontend:0.0.0-pr.48.7dede89

Test the PR build

# Test backend
docker run -d -p 8000:8000 -e ENVIRONMENT=development ghcr.io/devsecninja/hadiscover/backend:0.0.0-pr.48.7dede89
curl http://localhost:8000/api/v1/health

# Test frontend
docker run -d -p 8080:80 ghcr.io/devsecninja/hadiscover/frontend:0.0.0-pr.48.7dede89
curl http://localhost:8080/hadiscover/

Using docker-compose

services:
  backend:
    image: ghcr.io/devsecninja/hadiscover/backend:0.0.0-pr.48.7dede89
    ports:
      - "8000:8000"
    environment:
      - ENVIRONMENT=development

  frontend:
    image: ghcr.io/devsecninja/hadiscover/frontend:0.0.0-pr.48.7dede89
    ports:
      - "8080:80"

Images are tagged with 0.0.0-pr.48.7dede89 and will be retained for testing purposes.

@DevSecNinja DevSecNinja marked this pull request as ready for review December 31, 2025 12:23
Copilot AI review requested due to automatic review settings December 31, 2025 12:23
@DevSecNinja DevSecNinja merged commit 96158be into main Dec 31, 2025
8 of 9 checks passed
@DevSecNinja DevSecNinja deleted the copilot/add-footer-repo-link branch December 31, 2025 12:25
@github-actions
Copy link
Copy Markdown

🐳 Docker Images Published

The following Docker images have been built and published for this PR:

Backend

docker pull ghcr.io/devsecninja/hadiscover/backend:0.0.0-pr.48.c747387

Frontend

docker pull ghcr.io/devsecninja/hadiscover/frontend:0.0.0-pr.48.c747387

Test the PR build

# Test backend
docker run -d -p 8000:8000 -e ENVIRONMENT=development ghcr.io/devsecninja/hadiscover/backend:0.0.0-pr.48.c747387
curl http://localhost:8000/api/v1/health

# Test frontend
docker run -d -p 8080:80 ghcr.io/devsecninja/hadiscover/frontend:0.0.0-pr.48.c747387
curl http://localhost:8080/hadiscover/

Using docker-compose

services:
  backend:
    image: ghcr.io/devsecninja/hadiscover/backend:0.0.0-pr.48.c747387
    ports:
      - "8000:8000"
    environment:
      - ENVIRONMENT=development

  frontend:
    image: ghcr.io/devsecninja/hadiscover/frontend:0.0.0-pr.48.c747387
    ports:
      - "8080:80"

Images are tagged with 0.0.0-pr.48.c747387 and will be retained for testing purposes.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR adds a GitHub repository link with a cached star count to the footer of the hadiscover application. The star count is fetched during the indexing cron job and stored in the database to avoid GitHub API rate limit exhaustion on page loads.

Key Changes:

  • Backend caches repository star count during indexing runs via new _store_repo_star_count() method
  • API returns star count through existing /api/v1/statistics endpoint with updated StatisticsResponse schema
  • Frontend displays GitHub repository link with star count and call-to-action in footer

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 10 comments.

Show a summary per file
File Description
backend/app/services/indexer.py Added _store_repo_star_count() method to fetch and cache hadiscover repository star count from GitHub API during successful indexing runs
backend/app/services/search_service.py Updated get_statistics() to retrieve cached repo_star_count from database metadata table
backend/app/api/routes.py Added repo_star_count field to StatisticsResponse model to expose star count via API
backend/tests/test_api.py Added test case to verify statistics endpoint includes the new repo_star_count field
frontend/app/page.tsx Added repository link component with GitHub logo, star count, and hover effects; also changed time format to 24-hour

.first()
)

current_time = datetime.utcnow()
Copy link

Copilot AI Dec 31, 2025

Choose a reason for hiding this comment

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

Using deprecated datetime.utcnow() which will be removed in future Python versions. Consider using datetime.now(timezone.utc) instead for timezone-aware datetime objects.

Copilot uses AI. Check for mistakes.
Comment on lines +129 to +177
async def _store_repo_star_count(self, db: Session) -> None:
"""
Fetch and store the hadiscover repository star count.

Args:
db: Database session
"""
try:
async with httpx.AsyncClient() as client:
url = f"{self.github_service.BASE_URL}/repos/DevSecNinja/hadiscover"
response = await client.get(
url, headers=self.github_service.headers, timeout=10.0
)

if response.status_code == 200:
data = response.json()
star_count = data.get("stargazers_count", 0)

# Check if metadata record exists
metadata = (
db.query(IndexingMetadata)
.filter_by(key="repo_star_count")
.first()
)

current_time = datetime.utcnow()

if metadata:
# Update existing record
metadata.value = str(star_count)
metadata.updated_at = current_time
else:
# Create new record
metadata = IndexingMetadata(
key="repo_star_count",
value=str(star_count),
updated_at=current_time,
)
db.add(metadata)

db.commit()
logger.info(f"Stored repository star count: {star_count}")
else:
logger.warning(
f"Failed to fetch repo star count. Status: {response.status_code}"
)
except Exception as e:
logger.error(f"Error storing repo star count: {e}")
db.rollback()
Copy link

Copilot AI Dec 31, 2025

Choose a reason for hiding this comment

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

The _store_repo_star_count method lacks test coverage. While there is a test for the API endpoint that returns the star count, there are no unit tests for the actual fetching and storing logic. Consider adding tests similar to those in test_indexing_completion.py that verify:

  • Successful star count fetch and storage
  • Behavior when GitHub API returns non-200 status codes
  • Behavior when GitHub API times out or returns invalid data
  • Updates to existing star count records on subsequent runs

Copilot uses AI. Check for mistakes.
Comment thread frontend/app/page.tsx
Comment on lines +1705 to +1731
<a
href="https://github.com/DevSecNinja/hadiscover"
target="_blank"
rel="noopener noreferrer"
className="inline-flex items-center gap-2 px-5 py-3 rounded-xl transition-all duration-200"
style={{
color: isDark ? "rgba(255, 255, 255, 0.7)" : "#1f2937",
background: isDark
? "rgba(255, 255, 255, 0.05)"
: "rgba(0, 0, 0, 0.04)",
border: isDark
? "1px solid rgba(255, 255, 255, 0.08)"
: "1px solid rgba(0, 0, 0, 0.08)",
}}
onMouseEnter={(e) => {
e.currentTarget.style.background = isDark
? "rgba(255, 255, 255, 0.08)"
: "rgba(0, 0, 0, 0.06)";
e.currentTarget.style.transform = "translateY(-1px)";
}}
onMouseLeave={(e) => {
e.currentTarget.style.background = isDark
? "rgba(255, 255, 255, 0.05)"
: "rgba(0, 0, 0, 0.04)";
e.currentTarget.style.transform = "translateY(0)";
}}
>
Copy link

Copilot AI Dec 31, 2025

Choose a reason for hiding this comment

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

This repository link is hardcoded. If someone forks this project, they would need to manually update this URL. Consider making the repository owner/name configurable via environment variables (e.g., NEXT_PUBLIC_REPO_OWNER and NEXT_PUBLIC_REPO_NAME) or fetching it from the statistics endpoint, which already knows the repository name from the backend configuration.

Suggested change
<a
href="https://github.com/DevSecNinja/hadiscover"
target="_blank"
rel="noopener noreferrer"
className="inline-flex items-center gap-2 px-5 py-3 rounded-xl transition-all duration-200"
style={{
color: isDark ? "rgba(255, 255, 255, 0.7)" : "#1f2937",
background: isDark
? "rgba(255, 255, 255, 0.05)"
: "rgba(0, 0, 0, 0.04)",
border: isDark
? "1px solid rgba(255, 255, 255, 0.08)"
: "1px solid rgba(0, 0, 0, 0.08)",
}}
onMouseEnter={(e) => {
e.currentTarget.style.background = isDark
? "rgba(255, 255, 255, 0.08)"
: "rgba(0, 0, 0, 0.06)";
e.currentTarget.style.transform = "translateY(-1px)";
}}
onMouseLeave={(e) => {
e.currentTarget.style.background = isDark
? "rgba(255, 255, 255, 0.05)"
: "rgba(0, 0, 0, 0.04)";
e.currentTarget.style.transform = "translateY(0)";
}}
>
{(() => {
const repoOwner =
process.env.NEXT_PUBLIC_REPO_OWNER ?? "DevSecNinja";
const repoName =
process.env.NEXT_PUBLIC_REPO_NAME ?? "hadiscover";
const repositoryUrl = `https://github.com/${repoOwner}/${repoName}`;
return (
<a
href={repositoryUrl}
target="_blank"
rel="noopener noreferrer"
className="inline-flex items-center gap-2 px-5 py-3 rounded-xl transition-all duration-200"
style={{
color: isDark ? "rgba(255, 255, 255, 0.7)" : "#1f2937",
background: isDark
? "rgba(255, 255, 255, 0.05)"
: "rgba(0, 0, 0, 0.04)",
border: isDark
? "1px solid rgba(255, 255, 255, 0.08)"
: "1px solid rgba(0, 0, 0, 0.08)",
}}
onMouseEnter={(e) => {
e.currentTarget.style.background = isDark
? "rgba(255, 255, 255, 0.08)"
: "rgba(0, 0, 0, 0.06)";
e.currentTarget.style.transform = "translateY(-1px)";
}}
onMouseLeave={(e) => {
e.currentTarget.style.background = isDark
? "rgba(255, 255, 255, 0.05)"
: "rgba(0, 0, 0, 0.04)";
e.currentTarget.style.transform = "translateY(0)";
}}
>

Copilot uses AI. Check for mistakes.
Comment thread frontend/app/page.tsx
clipRule="evenodd"
/>
</svg>
<span className="font-medium">DevSecNinja/hadiscover</span>
Copy link

Copilot AI Dec 31, 2025

Choose a reason for hiding this comment

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

The repository name "DevSecNinja/hadiscover" is hardcoded in the display text. Similar to the URL, this should be made configurable to support forks of the project.

Copilot uses AI. Check for mistakes.
star_count_metadata = (
db.query(IndexingMetadata).filter_by(key="repo_star_count").first()
)
star_count = int(star_count_metadata.value) if star_count_metadata else 0
Copy link

Copilot AI Dec 31, 2025

Choose a reason for hiding this comment

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

The int() conversion could raise a ValueError if the stored value is not a valid integer (e.g., corrupted data). While the outer try-except will catch this, it would be better to handle this specific case more gracefully with a try-except around the conversion or use a default value for invalid data, logging a warning about the corrupted metadata.

Suggested change
star_count = int(star_count_metadata.value) if star_count_metadata else 0
star_count = 0
if star_count_metadata:
try:
star_count = int(star_count_metadata.value)
except (TypeError, ValueError):
logger.warning(
"Invalid repo_star_count metadata value %r; defaulting to 0",
star_count_metadata.value,
)

Copilot uses AI. Check for mistakes.
"""
try:
async with httpx.AsyncClient() as client:
url = f"{self.github_service.BASE_URL}/repos/DevSecNinja/hadiscover"
Copy link

Copilot AI Dec 31, 2025

Choose a reason for hiding this comment

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

The repository owner/name is hardcoded as "DevSecNinja/hadiscover". This makes it difficult for forks to use this feature. Consider making this configurable via an environment variable (e.g., HADISCOVER_REPO_OWNER and HADISCOVER_REPO_NAME) so that forks can easily update the configuration without modifying code.

Copilot uses AI. Check for mistakes.
Comment thread frontend/app/page.tsx
Comment on lines +1719 to +1730
onMouseEnter={(e) => {
e.currentTarget.style.background = isDark
? "rgba(255, 255, 255, 0.08)"
: "rgba(0, 0, 0, 0.06)";
e.currentTarget.style.transform = "translateY(-1px)";
}}
onMouseLeave={(e) => {
e.currentTarget.style.background = isDark
? "rgba(255, 255, 255, 0.05)"
: "rgba(0, 0, 0, 0.04)";
e.currentTarget.style.transform = "translateY(0)";
}}
Copy link

Copilot AI Dec 31, 2025

Choose a reason for hiding this comment

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

Inline event handlers with style mutations can cause performance issues in React and make the component harder to test. Consider using CSS hover states via className instead, or extract this into a reusable styled component. This would also improve consistency with the rest of the application's styling approach.

Copilot uses AI. Check for mistakes.
Comment thread frontend/app/page.tsx
day: "numeric",
hour: "2-digit",
minute: "2-digit",
hour12: false,
Copy link

Copilot AI Dec 31, 2025

Choose a reason for hiding this comment

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

This change to use 24-hour time format appears unrelated to the main PR purpose of adding repository link and star count. While not problematic, it should ideally be in a separate commit or PR for better traceability. Additionally, this changes the user experience for users in locales that typically use 12-hour format.

Copilot uses AI. Check for mistakes.
Comment thread backend/app/api/routes.py
total_repositories: int
total_automations: int
last_indexed_at: Optional[str] = None
repo_star_count: int
Copy link

Copilot AI Dec 31, 2025

Choose a reason for hiding this comment

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

The repo_star_count field lacks a docstring or description comment. Consider adding a brief comment explaining what this field represents (e.g., "Star count of the hadiscover GitHub repository") to improve API documentation clarity, especially since this is exposed through the OpenAPI/Swagger docs.

Copilot uses AI. Check for mistakes.
Comment on lines +137 to +174
async with httpx.AsyncClient() as client:
url = f"{self.github_service.BASE_URL}/repos/DevSecNinja/hadiscover"
response = await client.get(
url, headers=self.github_service.headers, timeout=10.0
)

if response.status_code == 200:
data = response.json()
star_count = data.get("stargazers_count", 0)

# Check if metadata record exists
metadata = (
db.query(IndexingMetadata)
.filter_by(key="repo_star_count")
.first()
)

current_time = datetime.utcnow()

if metadata:
# Update existing record
metadata.value = str(star_count)
metadata.updated_at = current_time
else:
# Create new record
metadata = IndexingMetadata(
key="repo_star_count",
value=str(star_count),
updated_at=current_time,
)
db.add(metadata)

db.commit()
logger.info(f"Stored repository star count: {star_count}")
else:
logger.warning(
f"Failed to fetch repo star count. Status: {response.status_code}"
)
Copy link

Copilot AI Dec 31, 2025

Choose a reason for hiding this comment

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

This code doesn't use the existing GitHubService._check_rate_limit() method to check for rate limit responses. While the outer error handling will catch rate limit errors, it would be more consistent to use the existing rate limit checking infrastructure. Consider either:

  1. Creating a method in GitHubService for fetching repository metadata (recommended for consistency)
  2. Extracting and calling _check_rate_limit() after the API call

This would provide better error handling and consistency with other GitHub API calls in the codebase.

Copilot uses AI. Check for mistakes.
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.

Include a link to this repo in the footer incl. the total stars and suggestion to star it

4 participants