mirror of
https://github.com/Sea-Haven-Industries/open-swe.git
synced 2026-09-30 20:53:15 +00:00
* feat: add scheduled web agents Co-authored-by: open-swe[bot] <215916821+open-swe[bot]@users.noreply.github.com> * fix: secure scheduled agent repositories Co-authored-by: open-swe[bot] <215916821+open-swe[bot]@users.noreply.github.com> * feat: rebuild scheduled agents as Automations tab Scrap the inline ScheduledAgentsPanel and replace it with a dedicated Automations tab: sidebar nav entry, list view with stat cards + empty state, and a full editor (name, Active toggle, repo, scheduled trigger picker, agent instructions + model). * fix: clear collapsed-sidebar button on mobile in Automations * fix: allow clearing automation repo on update --------- Co-authored-by: open-swe[bot] <215916821+open-swe[bot]@users.noreply.github.com>
65 lines
2.3 KiB
Python
65 lines
2.3 KiB
Python
"""GitHub repository access checks for dashboard actions."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import httpx
|
|
from fastapi import HTTPException
|
|
|
|
from .profiles import get_valid_access_token
|
|
from .review_styles import normalize_repo_full_name
|
|
|
|
|
|
def _raise_for_github_repo_status(status_code: int) -> None:
|
|
if status_code == 401:
|
|
raise HTTPException(401, "github token expired, re-login required")
|
|
if status_code == 404:
|
|
raise HTTPException(404, "repository not found")
|
|
if status_code == 403:
|
|
raise HTTPException(403, "no access to this private repository")
|
|
if status_code != 200:
|
|
raise HTTPException(502, f"github API error ({status_code})")
|
|
|
|
|
|
async def assert_repo_access(full_name: str, token: str) -> str:
|
|
full_name = normalize_repo_full_name(full_name)
|
|
headers = {
|
|
"Authorization": f"Bearer {token}",
|
|
"Accept": "application/vnd.github+json",
|
|
"X-GitHub-Api-Version": "2022-11-28",
|
|
}
|
|
owner, name = full_name.split("/", 1)
|
|
async with httpx.AsyncClient() as client:
|
|
response = await client.get(
|
|
f"https://api.github.com/repos/{owner}/{name}",
|
|
headers=headers,
|
|
)
|
|
_raise_for_github_repo_status(response.status_code)
|
|
return full_name
|
|
|
|
|
|
async def require_repo_access_for_user(login: str, full_name: str) -> str:
|
|
token = await get_valid_access_token(login)
|
|
if not token:
|
|
raise HTTPException(401, "github token unavailable, re-login required")
|
|
try:
|
|
await assert_repo_access(full_name, token)
|
|
except HTTPException as exc:
|
|
if exc.status_code != 401:
|
|
raise
|
|
token = await get_valid_access_token(login, force_refresh=True)
|
|
if not token:
|
|
raise HTTPException(401, "github token expired, re-login required") from exc
|
|
await assert_repo_access(full_name, token)
|
|
return token
|
|
|
|
|
|
async def repo_config_for_user(login: str, full_name: str | None) -> dict[str, str] | None:
|
|
if not isinstance(full_name, str) or not full_name.strip():
|
|
return None
|
|
try:
|
|
normalized = normalize_repo_full_name(full_name)
|
|
except ValueError as exc:
|
|
raise HTTPException(422, str(exc)) from exc
|
|
await require_repo_access_for_user(login, normalized)
|
|
owner, name = normalized.split("/", 1)
|
|
return {"owner": owner, "name": name}
|