Skip to content
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
Expand Down
4 changes: 3 additions & 1 deletion backend/.env.example
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
SUPABASE_URL=
SUPABASE_SERVICE_ROLE_KEY=
SUPABASE_SERVICE_ROLE_KEY=
CORS_ORIGINS=http://localhost:3000
GITHUB_TOKEN=
59 changes: 59 additions & 0 deletions backend/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# Setting Up the .env File for FastAPI

This guide provides step-by-step instructions to configure your `.env` file and securely use a GitHub Personal Access Token in your FastAPI project.

## 1. Create the `.env` File

1. Navigate to the `backend` directory where your project is set up.
2. Create a new file named `.env` (if it doesn’t already exist).

## 2. Add the GitHub Access Token

1. Open the `.env` file and add the following line:

```text
GITHUB_TOKEN=your_personal_access_token_here
```

2. Replace `your_personal_access_token_here` with your actual GitHub Personal Access Token.

## 3. Generate a GitHub Personal Access Token

1. Go to [GitHub Developer Settings](https://github.com/settings/tokens).
2. Click on **Tokens (classic)** and then **Generate new token** and then generate the classic token.
3. Select the required scopes for your application:
- **For public repositories**: Check `repo`.
- **For additional access**: Check `read:org`, `read:user`, etc., as needed.
4. Copy the generated token (it will only be displayed once).


## 5. Load Environment Variables in FastAPI

Ensure you have these things installed in your project:

```bash
pip install python-dotenv requests fastapi uvicorn

```

### Test API Requests
Run your FastAPI server:

```bash
uvicorn backend.main:app --reload
```

Use `curl` or **Postman** to test the `/api/repo-stats` endpoint:

```bash
curl -X POST http://localhost:8000/api/repo-stats \
-H "Content-Type: application/json" \
-d '{"repo_url": "https://github.com/AOSSIE-Org/Devr.AI/"}'
```

If all the things correctly got setup then you will see the JSON repsponse

---


Now, your FastAPI application is securely set up to use GitHub API credentials from a `.env` file!
7 changes: 6 additions & 1 deletion backend/app/core/events/enums.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,15 @@ class PlatformType(str, Enum):

class EventType(str, Enum):
ISSUE_CREATED = "issue.created"
ISSUE_CLOSED = "issue.closed"

ISSUE_UPDATED = "issue.updated"
ISSUE_COMMENTED = "issue.commented"
PR_CREATED = "pr.created"
PR_UPDATED = "pr.updated"
PR_COMMENTED = "pr.commented"
PR_MERGED = "pr.merged"

PR_REVIEWED = "pr.reviewed"

MESSAGE_CREATED = "message.created"
Expand All @@ -25,4 +29,5 @@ class EventType(str, Enum):
ONBOARDING_COMPLETED = "onboarding.completed"
FAQ_REQUESTED = "faq.requested"
KNOWLEDGE_UPDATED = "knowledge.updated"
ANALYTICS_COLLECTED = "analytics.collected"
ANALYTICS_COLLECTED = "analytics.collected"

4 changes: 4 additions & 0 deletions backend/app/models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
from pydantic import BaseModel

class RepoRequest(BaseModel):
repo_url: str
12 changes: 12 additions & 0 deletions backend/app/routes.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
from fastapi import APIRouter, HTTPException
from app.models import RepoRequest
from app.utils.github_api import get_repo_stats

router = APIRouter()

@router.post("/repo-stats")
async def repo_stats_endpoint(repo: RepoRequest):
try:
return await get_repo_stats(repo.repo_url)
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
4 changes: 3 additions & 1 deletion backend/app/services/vector_db/service.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import logging

import os
from typing import List, Dict, Any, Optional
from uuid import UUID
Expand Down Expand Up @@ -197,4 +199,4 @@ async def check_connection(self) -> bool:
except Exception as e:
logger.error(f"Connection check failed: {str(e)}")
return False

Empty file added backend/app/utils/__init__.py
Empty file.
135 changes: 135 additions & 0 deletions backend/app/utils/github_api.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
import requests
import os
from urllib.parse import urlparse
from dotenv import load_dotenv

load_dotenv() # Load environment variables

app = FastAPI()

class RepoRequest(BaseModel):
repo_url: str

def parse_github_url(url: str) -> tuple:
"""Extract owner/repo from GitHub URL"""
parsed = urlparse(url)
path = parsed.path.strip('/').split('/')
if len(path) < 2:
raise ValueError("Invalid GitHub URL")
return path[0], path[1]

def github_api_request(endpoint: str) -> dict:
"""Make authenticated GitHub API request"""
headers = {
"Authorization": f"token {os.getenv('GITHUB_TOKEN')}",
"Accept": "application/vnd.github.v3+json"
}
response = requests.get(f"https://api.github.com{endpoint}", headers=headers)
response.raise_for_status()
return response.json()


@app.post("/repo-stats")
async def get_repo_stats(repo_url: str):
try:
owner, repo_name = parse_github_url(repo_url)
# Rest of your function...
# Get basic repo info
repo_info = github_api_request(f"/repos/{owner}/{repo_name}")

# Get contributors
contributors = github_api_request(f"/repos/{owner}/{repo_name}/contributors")

# Get pull requests
prs = github_api_request(f"/repos/{owner}/{repo_name}/pulls?state=all")

# Get issues
issues = github_api_request(f"/repos/{owner}/{repo_name}/issues?state=all")

community_profile = github_api_request(f"/repos/{owner}/{repo_name}/community/profile")

# Recent commits (last 5)
commits = github_api_request(f"/repos/{owner}/{repo_name}/commits?per_page=5")

code_frequency = github_api_request(f"/repos/{owner}/{repo_name}/stats/code_frequency")

pull_requests_by_state = {
"open": sum(1 for pr in prs if pr["state"] == "open"),
"closed": sum(1 for pr in prs if pr["state"] == "closed"),
"draft": sum(1 for pr in prs if pr.get("draft", False)),
"merged": sum(1 for pr in prs if pr.get("merged_at"))
}
pr_details = [{
"title": pr["title"],
"number": pr["number"],
"state": pr["state"],
"url": pr["html_url"],
"author": {
"login": pr["user"]["login"],
"avatar_url": pr["user"]["avatar_url"],
"profile_url": pr["user"]["html_url"]
},
} for pr in prs]

return {
"name": repo_info["full_name"],
"stars": repo_info["stargazers_count"],
"forks": repo_info["forks_count"],
"watchers": repo_info["subscribers_count"],
"created_at": repo_info["created_at"],
"updated_at": repo_info["updated_at"],
# Licensing and topics
# "license": repo_info.get("license", {}).get("spdx_id", "No License"),

"topics": repo_info.get("topics", []),

"contributors": [{
"login": c["login"],
"contributions": c["contributions"],
"avatar_url": c["avatar_url"]
} for c in contributors],
"recent_commits": [{
"sha": commit["sha"][:7],
"author": commit["commit"]["author"]["name"],
"message": commit["commit"]["message"],
"date": commit["commit"]["author"]["date"]
} for commit in commits],


"community": {
"health_percentage": community_profile["health_percentage"],
"code_of_conduct": community_profile.get("files", {}).get("code_of_conduct") is not None,
"license": community_profile.get("files", {}).get("license") is not None,
"readme": community_profile.get("files", {}).get("readme") is not None
},
# Issues
"issues": {
"total": len(issues),
"open": sum(1 for issue in issues if issue["state"] == "open"),
"closed": sum(1 for issue in issues if issue["state"] == "closed"),
"labels": list({label["name"] for issue in issues for label in issue["labels"]})
},

# Code statistics
"code_activity": {
"weekly_commits": len(code_frequency) if isinstance(code_frequency, list) else 0,
"total_additions": sum(week[1] for week in code_frequency) if isinstance(code_frequency, list) else 0,
"total_deletions": sum(abs(week[2]) for week in code_frequency) if isinstance(code_frequency, list) else 0
},

# Pull Requests
"pull_requests": {
**pull_requests_by_state,
"total": len(prs),
"details": pr_details
},
}

except requests.HTTPError as e:
raise HTTPException(status_code=e.response.status_code,
detail="GitHub API error")
except ValueError:
raise HTTPException(status_code=400,
detail="Invalid GitHub URL format")
23 changes: 22 additions & 1 deletion backend/main.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,32 @@

import asyncio
import uuid
import logging
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from app.routes import router
import uvicorn
from .app.core.events.event_bus import EventBus
from .app.core.events.enums import EventType, PlatformType
from .app.core.events.base import BaseEvent
from .app.core.handler.handler_registry import HandlerRegistry

logging.basicConfig(level=logging.INFO)

app = FastAPI()

# CORS Configuration
app.add_middleware(
CORSMiddleware,
allow_origins=["http://localhost:5173", "http://127.0.0.1:5173"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
expose_headers=["*"],
)

app.include_router(router, prefix="/api")

# Initialize Handler Registry and Event Bus
handler_registry = HandlerRegistry()
event_bus = EventBus(handler_registry)
Expand Down Expand Up @@ -54,4 +73,6 @@ async def main():
logging.info("All EventBus Tests Completed.")

if __name__ == "__main__":
asyncio.run(main())
uvicorn.run(app, host="127.0.0.1", port=8000)
asyncio.run(main())

Loading