morozovka-portal/app/gitea_api.py

139 lines
4.6 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""Gitea API client с простым in-memory кэшем (5 минут)."""
import re
import time
import requests
from . import config
_cache = {}
CACHE_TTL = 300
def _headers():
if config.GITEA_API_TOKEN:
return {"Authorization": f"token {config.GITEA_API_TOKEN}"}
return {}
def _cache_get(key):
entry = _cache.get(key)
if not entry: return None
ts, data = entry
if time.time() - ts > CACHE_TTL: return None
return data
def _cache_set(key, data):
_cache[key] = (time.time(), data)
def invalidate_cache(repo_full_name):
"""Сбросить весь кэш для указанного репо (owner/name)."""
keys_to_drop = [k for k in _cache if f":{repo_full_name}:" in k or k.endswith(f":{repo_full_name}")]
for k in keys_to_drop:
_cache.pop(k, None)
return len(keys_to_drop)
def _api_get(path, params=None):
url = f"{config.GITEA_URL}/api/v1{path}"
try:
r = requests.get(url, headers=_headers(), params=params, timeout=5)
if r.status_code == 200:
total_header = r.headers.get("X-Total-Count")
total = int(total_header) if total_header and total_header.isdigit() else None
try: data = r.json()
except Exception: data = None
return data, total
return None, None
except Exception as e:
print(f"[gitea_api] {path}: {e}", flush=True)
return None, None
def extract_repo_from_url(url):
if not url: return None
m = re.search(r"[:/]([^/\s]+)/([^/\s]+?)(?:\.git)?/?$", url.strip())
if m: return f"{m.group(1)}/{m.group(2)}"
return None
def get_repo_info(owner, repo):
key = f"repo:{owner}/{repo}:info"
cached = _cache_get(key)
if cached is not None: return cached
data, _ = _api_get(f"/repos/{owner}/{repo}")
_cache_set(key, data)
return data
def get_latest_commits(owner, repo, limit=5):
key = f"repo:{owner}/{repo}:commits:{limit}"
cached = _cache_get(key)
if cached is not None: return cached
data, _ = _api_get(f"/repos/{owner}/{repo}/commits",
params={"limit": limit, "page": 1, "stat": "false"})
result = []
if isinstance(data, list):
for c in data[:limit]:
co = c.get("commit") or {}
ao = co.get("author") or {}
result.append({
"sha_short": (c.get("sha") or "")[:7],
"message": (co.get("message") or "").split("\n")[0][:120],
"author": ao.get("name") or "",
"date": ao.get("date") or "",
"html_url": c.get("html_url") or "",
})
_cache_set(key, result)
return result
def get_open_issues_count(owner, repo):
key = f"repo:{owner}/{repo}:issues"
cached = _cache_get(key)
if cached is not None: return cached
_, total = _api_get(f"/repos/{owner}/{repo}/issues",
params={"state": "open", "type": "issues", "limit": 1})
_cache_set(key, total or 0)
return total or 0
def get_open_prs_count(owner, repo):
key = f"repo:{owner}/{repo}:prs"
cached = _cache_get(key)
if cached is not None: return cached
_, total = _api_get(f"/repos/{owner}/{repo}/pulls",
params={"state": "open", "limit": 1})
_cache_set(key, total or 0)
return total or 0
def get_project_stats(gitea_repo):
if not gitea_repo or "/" not in gitea_repo:
return {"ok": False, "error": "no_repo",
"message": "Не указан репозиторий проекта."}
owner, repo = gitea_repo.split("/", 1)
info = get_repo_info(owner, repo)
if info is None:
if not config.GITEA_API_TOKEN:
return {"ok": False, "error": "auth_required",
"message": "Репозиторий приватный или не найден. "
"Настройте GITEA_API_TOKEN в config.py."}
return {"ok": False, "error": "api_unreachable",
"message": "Не удалось получить данные из Gitea."}
return {
"ok": True,
"repo": {
"full_name": info.get("full_name", gitea_repo),
"description": info.get("description") or "",
"html_url": info.get("html_url", ""),
"default_branch": info.get("default_branch") or "main",
"stars": info.get("stars_count", 0),
"forks": info.get("forks_count", 0),
"is_private": bool(info.get("private")),
},
"commits": get_latest_commits(owner, repo, limit=5),
"open_issues": get_open_issues_count(owner, repo),
"open_prs": get_open_prs_count(owner, repo),
}