Param20h commited on
Commit
43c605f
Β·
unverified Β·
1 Parent(s): 8f2df0f

fix(ui): proxy github api requests through backend to avoid rate limit 403s

Browse files
backend/app/main.py CHANGED
@@ -77,10 +77,12 @@ logger.info(f"CORS origins: {settings.cors_origins}")
77
  from app.routes.auth import router as auth_router
78
  from app.routes.documents import router as documents_router
79
  from app.routes.chat import router as chat_router
 
80
 
81
  app.include_router(auth_router, prefix="/api/v1")
82
  app.include_router(documents_router, prefix="/api/v1")
83
  app.include_router(chat_router, prefix="/api/v1")
 
84
 
85
 
86
  # ── Health Check ─────────────────────────────────────
 
77
  from app.routes.auth import router as auth_router
78
  from app.routes.documents import router as documents_router
79
  from app.routes.chat import router as chat_router
80
+ from app.routes.github import router as github_router
81
 
82
  app.include_router(auth_router, prefix="/api/v1")
83
  app.include_router(documents_router, prefix="/api/v1")
84
  app.include_router(chat_router, prefix="/api/v1")
85
+ app.include_router(github_router, prefix="/api/v1")
86
 
87
 
88
  # ── Health Check ─────────────────────────────────────
backend/app/routes/github.py ADDED
@@ -0,0 +1,55 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import json
2
+ import time
3
+ import urllib.request
4
+ from urllib.error import URLError, HTTPError
5
+ from fastapi import APIRouter, HTTPException
6
+
7
+ router = APIRouter()
8
+
9
+ CACHE = {
10
+ "contribs": {"data": None, "timestamp": 0},
11
+ "repo": {"data": None, "timestamp": 0}
12
+ }
13
+ TTL = 3600 # 1 hour cache to avoid 403 Rate Limit
14
+
15
+ REPO = "param20h/PDF-Assistant-RAG"
16
+
17
+ def fetch_github(url: str, cache_key: str):
18
+ now = time.time()
19
+ if CACHE[cache_key]["data"] is not None and now - CACHE[cache_key]["timestamp"] < TTL:
20
+ return CACHE[cache_key]["data"]
21
+
22
+ req = urllib.request.Request(url, headers={
23
+ "Accept": "application/vnd.github.v3+json",
24
+ "User-Agent": "PDF-Assistant-RAG"
25
+ })
26
+
27
+ try:
28
+ with urllib.request.urlopen(req) as response:
29
+ data = json.loads(response.read().decode())
30
+ CACHE[cache_key]["data"] = data
31
+ CACHE[cache_key]["timestamp"] = now
32
+ return data
33
+ except HTTPError as e:
34
+ # Fallback to cache if rate limited
35
+ if CACHE[cache_key]["data"] is not None:
36
+ return CACHE[cache_key]["data"]
37
+ raise HTTPException(status_code=e.code, detail="GitHub API Error")
38
+ except URLError as e:
39
+ if CACHE[cache_key]["data"] is not None:
40
+ return CACHE[cache_key]["data"]
41
+ raise HTTPException(status_code=500, detail="Failed to connect to GitHub")
42
+
43
+ @router.get("/github/stats")
44
+ def get_github_stats():
45
+ contribs = fetch_github(f"https://api.github.com/repos/{REPO}/contributors?per_page=30", "contribs")
46
+ repo = fetch_github(f"https://api.github.com/repos/{REPO}", "repo")
47
+
48
+ return {
49
+ "contributors": contribs if isinstance(contribs, list) else [],
50
+ "stats": {
51
+ "stargazers_count": repo.get("stargazers_count", 0),
52
+ "forks_count": repo.get("forks_count", 0),
53
+ "open_issues_count": repo.get("open_issues_count", 0)
54
+ }
55
+ }
frontend/src/components/layout/ContributorsPanel.tsx CHANGED
@@ -3,6 +3,7 @@
3
  import { useState, useEffect } from "react";
4
  import { GitBranch, Star, GitPullRequest, Users, X, Trophy, ExternalLink } from "lucide-react";
5
  import { Button } from "@/components/ui/button";
 
6
 
7
  interface Contributor {
8
  login: string;
@@ -25,13 +26,10 @@ export default function ContributorsPanel({ onClose }: { onClose: () => void })
25
  const REPO = "param20h/PDF-Assistant-RAG";
26
 
27
  useEffect(() => {
28
- Promise.all([
29
- fetch(`https://api.github.com/repos/${REPO}/contributors?per_page=30`).then((r) => r.json()),
30
- fetch(`https://api.github.com/repos/${REPO}`).then((r) => r.json()),
31
- ])
32
- .then(([contribs, repo]) => {
33
- setContributors(Array.isArray(contribs) ? contribs : []);
34
- setStats(repo);
35
  })
36
  .catch(() => {})
37
  .finally(() => setLoading(false));
@@ -58,7 +56,7 @@ export default function ContributorsPanel({ onClose }: { onClose: () => void })
58
  </div>
59
  <div>
60
  <h2 className="font-bold text-base">Hall of Fame</h2>
61
- <p className="text-xs text-muted-foreground">GSSOC Contributors β€” thank you! πŸŽ‰</p>
62
  </div>
63
  </div>
64
  <Button variant="ghost" size="icon" className="h-8 w-8" onClick={onClose}>
 
3
  import { useState, useEffect } from "react";
4
  import { GitBranch, Star, GitPullRequest, Users, X, Trophy, ExternalLink } from "lucide-react";
5
  import { Button } from "@/components/ui/button";
6
+ import { api } from "@/lib/api";
7
 
8
  interface Contributor {
9
  login: string;
 
26
  const REPO = "param20h/PDF-Assistant-RAG";
27
 
28
  useEffect(() => {
29
+ api.get<{ contributors: Contributor[], stats: RepoStats }>("/api/v1/github/stats")
30
+ .then((data) => {
31
+ setContributors(Array.isArray(data.contributors) ? data.contributors : []);
32
+ setStats(data.stats);
 
 
 
33
  })
34
  .catch(() => {})
35
  .finally(() => setLoading(false));
 
56
  </div>
57
  <div>
58
  <h2 className="font-bold text-base">Hall of Fame</h2>
59
+ <p className="text-xs text-muted-foreground">Contributors β€” thank you! πŸŽ‰</p>
60
  </div>
61
  </div>
62
  <Button variant="ghost" size="icon" className="h-8 w-8" onClick={onClose}>