|
1 | | -import asyncio |
2 | 1 | import logging |
3 | 2 | import os |
4 | 3 | from urllib.parse import urlencode |
5 | 4 |
|
6 | 5 | import httpx |
7 | | -from database.pg.token_ops.provider_token_crud import ( |
8 | | - delete_provider_token, |
9 | | - store_github_token_for_user, |
10 | | -) |
| 6 | +from database.pg.token_ops.provider_token_crud import delete_provider_token, store_provider_token |
11 | 7 | from fastapi import APIRouter, Depends, HTTPException, Request, status |
12 | | -from models.github import GithubCommitState, GitHubStatusResponse, Repo |
| 8 | +from models.github import GitHubStatusResponse, Repo |
13 | 9 | from pydantic import BaseModel, ValidationError |
14 | 10 | from services.auth import get_current_user_id |
15 | 11 | from services.crypto import encrypt_api_key |
16 | | -from services.github import ( |
17 | | - CLONED_REPOS_BASE_DIR, |
18 | | - build_file_tree_for_branch, |
19 | | - clone_repo, |
20 | | - get_files_content_for_branch, |
21 | | - get_github_access_token, |
22 | | - get_latest_local_commit_info, |
23 | | - get_latest_online_commit_info, |
24 | | - list_branches, |
25 | | - pull_repo, |
26 | | -) |
| 12 | +from services.github import get_github_access_token |
27 | 13 | from slowapi import Limiter |
28 | 14 | from slowapi.util import get_remote_address |
29 | 15 | from starlette.responses import RedirectResponse |
@@ -85,7 +71,6 @@ async def github_callback( |
85 | 71 | detail="GitHub OAuth is not configured on the server.", |
86 | 72 | ) |
87 | 73 |
|
88 | | - # Exchange the code for an access token |
89 | 74 | async with httpx.AsyncClient() as client: |
90 | 75 | token_response = await client.post( |
91 | 76 | "https://github.com/login/oauth/access_token", |
@@ -128,11 +113,13 @@ async def github_callback( |
128 | 113 | github_user = user_response.json() |
129 | 114 | github_username = github_user.get("login") |
130 | 115 |
|
131 | | - # Encrypt + store the token |
132 | 116 | encrypted_token = await encrypt_api_key(access_token) |
133 | | - await delete_provider_token(request.app.state.pg_engine, user_id, "github") |
| 117 | + if not encrypted_token: |
| 118 | + raise HTTPException( |
| 119 | + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="Failed to secure token." |
| 120 | + ) |
134 | 121 |
|
135 | | - await store_github_token_for_user(request.app.state.pg_engine, user_id, encrypted_token or "") |
| 122 | + await store_provider_token(request.app.state.pg_engine, user_id, "github", encrypted_token) |
136 | 123 |
|
137 | 124 | return { |
138 | 125 | "message": "GitHub account connected successfully.", |
@@ -180,7 +167,7 @@ async def get_github_connection_status( |
180 | 167 | @router.get("/github/repos", response_model=list[Repo]) |
181 | 168 | async def get_github_repos(request: Request, user_id: str = Depends(get_current_user_id)): |
182 | 169 | """ |
183 | | - Fetches repositories using GitHub App permissions. |
| 170 | + Fetches repositories using GitHub App permissions via the API. |
184 | 171 | """ |
185 | 172 | access_token = await get_github_access_token(request, user_id) |
186 | 173 |
|
@@ -217,158 +204,3 @@ async def get_github_repos(request: Request, user_id: str = Depends(get_current_ |
217 | 204 | status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, |
218 | 205 | detail=f"Failed to parse repository data: {e}", |
219 | 206 | ) |
220 | | - |
221 | | - |
222 | | -@router.get("/github/repos/{owner}/{repo}/branches") |
223 | | -async def get_github_repo_branches( |
224 | | - owner: str, |
225 | | - repo: str, |
226 | | - request: Request, |
227 | | - user_id: str = Depends(get_current_user_id), |
228 | | -): |
229 | | - """ |
230 | | - Get a list of branches for a GitHub repository. |
231 | | - """ |
232 | | - access_token = await get_github_access_token(request, user_id) |
233 | | - repo_dir = CLONED_REPOS_BASE_DIR / owner / repo |
234 | | - |
235 | | - if not repo_dir.exists(): |
236 | | - try: |
237 | | - await clone_repo(owner, repo, access_token or "", repo_dir) |
238 | | - except Exception as e: |
239 | | - logger.error(f"Error cloning repo: {e}") |
240 | | - raise HTTPException( |
241 | | - status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, |
242 | | - detail=f"Failed to clone repository: {str(e)}", |
243 | | - ) |
244 | | - |
245 | | - try: |
246 | | - branches = await list_branches(repo_dir) |
247 | | - return branches |
248 | | - except Exception as e: |
249 | | - logger.error(f"Error listing branches: {e}") |
250 | | - raise HTTPException( |
251 | | - status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, |
252 | | - detail=f"Failed to list branches: {str(e)}", |
253 | | - ) |
254 | | - |
255 | | - |
256 | | -@router.get("/github/repos/{owner}/{repo}/tree") |
257 | | -async def get_github_repo_tree( |
258 | | - owner: str, |
259 | | - repo: str, |
260 | | - branch: str, |
261 | | - request: Request, |
262 | | - user_id: str = Depends(get_current_user_id), |
263 | | - force_pull: bool = False, |
264 | | -): |
265 | | - """ |
266 | | - Get file tree structure for a specific branch of a GitHub repository. |
267 | | - Clones the repo if not already cloned locally. |
268 | | - """ |
269 | | - access_token = await get_github_access_token(request, user_id) |
270 | | - repo_dir = CLONED_REPOS_BASE_DIR / owner / repo |
271 | | - |
272 | | - # Clone repo if it doesn't exist |
273 | | - if not repo_dir.exists(): |
274 | | - try: |
275 | | - await clone_repo(owner, repo, access_token or "", repo_dir) |
276 | | - except Exception as e: |
277 | | - logger.error(f"Error cloning repo: {e}") |
278 | | - raise HTTPException( |
279 | | - status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, |
280 | | - detail=f"Failed to clone repository: {str(e)}", |
281 | | - ) |
282 | | - |
283 | | - if force_pull: |
284 | | - try: |
285 | | - await pull_repo(repo_dir, branch) |
286 | | - except Exception as e: |
287 | | - logger.error(f"Error pulling repo: {e}") |
288 | | - raise HTTPException( |
289 | | - status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, |
290 | | - detail=f"Failed to pull repository: {str(e)}", |
291 | | - ) |
292 | | - |
293 | | - # Build file tree structure for the specific branch |
294 | | - try: |
295 | | - tree = await build_file_tree_for_branch(repo_dir, branch) |
296 | | - return tree |
297 | | - except FileNotFoundError as e: |
298 | | - raise HTTPException( |
299 | | - status_code=status.HTTP_404_NOT_FOUND, |
300 | | - detail=str(e), |
301 | | - ) |
302 | | - except Exception as e: |
303 | | - logger.error(f"Error building file tree for branch {branch}: {e}") |
304 | | - raise HTTPException( |
305 | | - status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, |
306 | | - detail=f"Failed to build file tree: {str(e)}", |
307 | | - ) |
308 | | - |
309 | | - |
310 | | -@router.get("/github/repos/{owner}/{repo}/commit/state") |
311 | | -async def get_github_repo_commit_state( |
312 | | - owner: str, |
313 | | - repo: str, |
314 | | - branch: str, |
315 | | - request: Request, |
316 | | - user_id: str = Depends(get_current_user_id), |
317 | | -) -> GithubCommitState: |
318 | | - """ |
319 | | - Get the state of a GitHub repository branch. |
320 | | - Return latest commit information and if the cloned version is latest |
321 | | - """ |
322 | | - access_token = await get_github_access_token(request, user_id) |
323 | | - |
324 | | - repo_dir = CLONED_REPOS_BASE_DIR / owner / repo |
325 | | - repo_id = f"{owner}/{repo}" |
326 | | - |
327 | | - latest_local_commit_info, latest_online_commit_info = await asyncio.gather( |
328 | | - get_latest_local_commit_info(repo_dir, branch), |
329 | | - get_latest_online_commit_info(repo_id, access_token, branch), |
330 | | - ) |
331 | | - |
332 | | - return GithubCommitState( |
333 | | - latest_local=latest_local_commit_info, |
334 | | - latest_online=latest_online_commit_info, |
335 | | - is_up_to_date=latest_local_commit_info.hash == latest_online_commit_info.hash, |
336 | | - ) |
337 | | - |
338 | | - |
339 | | -@router.get("/github/repos/{owner}/{repo}/contents/{file_path:path}") |
340 | | -async def get_github_repo_file( |
341 | | - owner: str, |
342 | | - repo: str, |
343 | | - file_path: str, |
344 | | - branch: str, |
345 | | - request: Request, |
346 | | - user_id: str = Depends(get_current_user_id), |
347 | | -): |
348 | | - """ |
349 | | - Get the content of a file in a specific branch of a GitHub repository. |
350 | | - """ |
351 | | - await get_github_access_token(request, user_id) |
352 | | - |
353 | | - repo_dir = CLONED_REPOS_BASE_DIR / owner / repo |
354 | | - |
355 | | - if not repo_dir.exists(): |
356 | | - raise HTTPException( |
357 | | - status_code=status.HTTP_404_NOT_FOUND, |
358 | | - detail="Repository not found locally. Please select it first to clone.", |
359 | | - ) |
360 | | - |
361 | | - try: |
362 | | - content = await get_files_content_for_branch(repo_dir, branch, [file_path]) |
363 | | - return {"content": content.get(file_path, "")} |
364 | | - except FileNotFoundError as e: |
365 | | - raise HTTPException( |
366 | | - status_code=status.HTTP_404_NOT_FOUND, |
367 | | - detail=str(e), |
368 | | - ) |
369 | | - except Exception as e: |
370 | | - logger.error(f"Error reading file content: {e}") |
371 | | - raise HTTPException( |
372 | | - status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, |
373 | | - detail=f"Failed to read file content: {str(e)}", |
374 | | - ) |
0 commit comments