tianruci commited on
Commit
6b25ec8
·
verified ·
1 Parent(s): 00ed286

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +35 -80
app.py CHANGED
@@ -1,22 +1,21 @@
1
  import os
2
  import json
3
- import asyncio
4
  import httpx
5
- from fastapi import FastAPI, HTTPException
6
- from fastapi.responses import StreamingResponse
7
  from datetime import datetime, timedelta
8
  from fastapi.middleware.cors import CORSMiddleware
9
  from fastmcp.server import FastMCP
10
  from contextlib import asynccontextmanager
11
  from typing import List, Dict, Optional
12
 
 
 
 
13
  @asynccontextmanager
14
  async def lifespan(app: FastAPI):
15
- # 初始化时获取数据
16
  await fetch_github_trending()
17
  yield
18
 
19
- # 创建主 FastAPI 应用
20
  app = FastAPI(lifespan=lifespan)
21
 
22
  # CORS 配置
@@ -26,107 +25,63 @@ app.add_middleware(
26
  allow_credentials=True,
27
  allow_methods=["*"],
28
  allow_headers=["*"],
29
- expose_headers=["*"]
30
  )
31
 
32
- # 缓存设置
33
- cached_trending: List[Dict] = []
34
- last_updated: Optional[datetime] = None
35
- GITHUB_API_URL = "https://api.github.com/search/repositories"
36
- CACHE_EXPIRY = timedelta(minutes=30)
37
 
38
- async def fetch_github_trending() -> List[Dict]:
39
- """获取 GitHub 热门项目"""
40
  global cached_trending, last_updated
41
-
42
- params = {
43
- "q": "stars:>1000",
44
- "sort": "stars",
45
- "order": "desc",
46
- "per_page": 10
47
- }
48
-
49
- headers = {
50
- "Accept": "application/vnd.github.v3+json",
51
- "User-Agent": "GitHub-Trending-API"
52
- }
53
-
54
- if github_token := os.getenv("GITHUB_TOKEN"):
55
- headers["Authorization"] = f"token {github_token}"
56
 
57
  try:
58
  async with httpx.AsyncClient() as client:
59
- response = await client.get(
60
- GITHUB_API_URL,
61
- params=params,
62
- headers=headers,
63
- timeout=10.0
64
  )
65
- response.raise_for_status()
66
-
67
  cached_trending = [
68
  {
69
- "name": item["full_name"],
70
- "url": item["html_url"],
71
- "stars": item["stargazers_count"],
72
- "description": item["description"],
73
- "language": item["language"]
74
  }
75
- for item in response.json().get("items", [])
76
  ]
77
  last_updated = datetime.now()
78
- return cached_trending
79
-
80
- except httpx.HTTPStatusError as e:
81
- print(f"GitHub API error: {e.response.status_code} - {e.response.text}")
82
- if cached_trending:
83
- return cached_trending
84
- raise HTTPException(
85
- status_code=429,
86
- detail="GitHub API rate limit exceeded. Please try again later."
87
- )
88
  except Exception as e:
89
- print(f"Error fetching GitHub trending: {e}")
90
- if cached_trending:
91
- return cached_trending
92
- raise HTTPException(
93
- status_code=500,
94
- detail="Failed to fetch trending repositories"
95
- )
96
-
97
- # 初始化 MCP 服务器
98
- mcp_server = FastMCP(name="GitHubTrendingAPI")
99
 
100
  @mcp_server.tool()
101
  async def get_trending_repos(num: int = 10) -> dict:
102
- """获取 GitHub 热门仓库"""
103
- if not last_updated or (datetime.now() - last_updated) > CACHE_EXPIRY:
104
  await fetch_github_trending()
105
  return {"trending": cached_trending[:num]}
106
 
107
- # 创建独立的 FastAPI 子应用并挂载 MCP 服务
108
- mcp_subapp = FastAPI()
109
- mcp_subapp.router.routes.extend(mcp_server.http_app().routes) # 手动合并路由
110
- app.mount("/mcp", mcp_subapp) # 挂载到 /mcp 路径
111
 
112
  @app.get("/")
113
  async def root():
114
  return {
115
- "message": "GitHub Trending API is running",
116
- "documentation": "/docs",
117
- "mcp_endpoint": "/mcp",
118
- "trending_endpoint": "/trending"
 
119
  }
120
 
121
  @app.get("/trending")
122
  async def get_trending(num: int = 10):
123
- """公开的 HTTP 端点"""
124
- if not cached_trending or (datetime.now() - last_updated) > CACHE_EXPIRY:
125
  await fetch_github_trending()
126
-
127
- return {
128
- "trending": cached_trending[:num],
129
- "last_updated": last_updated.isoformat() if last_updated else None,
130
- "cache_expires_in": CACHE_EXPIRY.total_seconds(),
131
- "rate_limit_info": "Consider adding a GitHub token for higher rate limits"
132
- }
 
1
  import os
2
  import json
 
3
  import httpx
4
+ from fastapi import FastAPI, HTTPException, APIRouter
 
5
  from datetime import datetime, timedelta
6
  from fastapi.middleware.cors import CORSMiddleware
7
  from fastmcp.server import FastMCP
8
  from contextlib import asynccontextmanager
9
  from typing import List, Dict, Optional
10
 
11
+ # 初始化 MCP 服务器(必须在 FastAPI 之前)
12
+ mcp_server = FastMCP(name="GitHubTrendingAPI")
13
+
14
  @asynccontextmanager
15
  async def lifespan(app: FastAPI):
 
16
  await fetch_github_trending()
17
  yield
18
 
 
19
  app = FastAPI(lifespan=lifespan)
20
 
21
  # CORS 配置
 
25
  allow_credentials=True,
26
  allow_methods=["*"],
27
  allow_headers=["*"],
 
28
  )
29
 
30
+ # 缓存变量
31
+ cached_trending = []
32
+ last_updated = None
 
 
33
 
34
+ async def fetch_github_trending():
 
35
  global cached_trending, last_updated
36
+ headers = {"Accept": "application/vnd.github.v3+json"}
37
+ if token := os.getenv("GITHUB_TOKEN"):
38
+ headers["Authorization"] = f"token {token}"
 
 
 
 
 
 
 
 
 
 
 
 
39
 
40
  try:
41
  async with httpx.AsyncClient() as client:
42
+ r = await client.get(
43
+ "https://api.github.com/search/repositories",
44
+ params={"q": "stars:>1000", "sort": "stars", "per_page": 10},
45
+ headers=headers
 
46
  )
47
+ r.raise_for_status()
 
48
  cached_trending = [
49
  {
50
+ "name": repo["full_name"],
51
+ "url": repo["html_url"],
52
+ "stars": repo["stargazers_count"]
 
 
53
  }
54
+ for repo in r.json()["items"]
55
  ]
56
  last_updated = datetime.now()
 
 
 
 
 
 
 
 
 
 
57
  except Exception as e:
58
+ print(f"Fetch error: {e}")
59
+ if not cached_trending:
60
+ raise HTTPException(500, "Failed to fetch data")
 
 
 
 
 
 
 
61
 
62
  @mcp_server.tool()
63
  async def get_trending_repos(num: int = 10) -> dict:
64
+ if not last_updated or (datetime.now() - last_updated) > timedelta(minutes=30):
 
65
  await fetch_github_trending()
66
  return {"trending": cached_trending[:num]}
67
 
68
+ # 关键修改:手动注册 MCP 路由到主应用
69
+ mcp_router = APIRouter()
70
+ mcp_router.routes.extend(mcp_server.http_app().routes)
71
+ app.include_router(mcp_router, prefix="/mcp")
72
 
73
  @app.get("/")
74
  async def root():
75
  return {
76
+ "message": "API is running",
77
+ "endpoints": {
78
+ "mcp": "/mcp/tool/get_trending_repos",
79
+ "trending": "/trending"
80
+ }
81
  }
82
 
83
  @app.get("/trending")
84
  async def get_trending(num: int = 10):
85
+ if not cached_trending:
 
86
  await fetch_github_trending()
87
+ return {"trending": cached_trending[:num]}