Files
algorithm/backend/app/gitea/client.py
2026-02-08 14:42:58 +08:00

218 lines
7.0 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客户端用于与Gitea服务器通信"""
import requests
import json
import logging
from typing import Optional, Dict, Any, List
logger = logging.getLogger(__name__)
class GiteaClient:
"""Gitea API客户端类"""
def __init__(self, server_url: str, access_token: str):
"""初始化Gitea客户端
Args:
server_url: Gitea服务器URL
access_token: Gitea访问令牌
"""
self.server_url = server_url.rstrip('/')
self.access_token = access_token
self.api_url = f"{self.server_url}/api/v1"
self.headers = {
"Authorization": f"token {self.access_token}",
"Content-Type": "application/json"
}
def _request(self, method: str, endpoint: str, data: Optional[Dict[str, Any]] = None) -> Optional[Dict[str, Any]]:
"""发送API请求
Args:
method: HTTP方法GET, POST, PUT, DELETE
endpoint: API端点
data: 请求数据
Returns:
API响应数据
"""
url = f"{self.api_url}/{endpoint}"
try:
if method == "GET":
response = requests.get(url, headers=self.headers, params=data)
elif method == "POST":
response = requests.post(url, headers=self.headers, json=data)
elif method == "PUT":
response = requests.put(url, headers=self.headers, json=data)
elif method == "PATCH":
response = requests.patch(url, headers=self.headers, json=data)
elif method == "DELETE":
response = requests.delete(url, headers=self.headers)
else:
raise ValueError(f"Unsupported HTTP method: {method}")
response.raise_for_status()
if response.content:
return response.json()
return None
except requests.RequestException as e:
logger.error(f"Gitea API request failed: {str(e)}")
return None
def create_repository(self, owner: str, name: str, description: str = "", private: bool = False) -> Optional[Dict[str, Any]]:
"""创建仓库
Args:
owner: 所有者(用户或组织)
name: 仓库名称
description: 仓库描述
private: 是否私有
Returns:
创建的仓库信息
"""
data = {
"name": name,
"description": description,
"private": private,
"auto_init": True
}
# 优先尝试在指定的 owner组织下创建仓库
logger.info(f"Attempting to create repository for owner: {owner}")
owner_response = self._request("POST", f"org/{owner}/repos", data)
if owner_response:
logger.info(f"Repository created successfully under owner: {owner}")
return owner_response
# 如果组织创建失败,尝试在用户下创建仓库
logger.info(f"Organization creation failed, trying user account")
user_response = self._request("POST", f"user/repos", data)
if user_response:
logger.info(f"Repository created successfully in user account")
return user_response
logger.error(f"Failed to create repository for owner {owner}")
return None
def get_repository(self, owner: str, repo: str) -> Optional[Dict[str, Any]]:
"""获取仓库信息
Args:
owner: 所有者(用户或组织)
repo: 仓库名称
Returns:
仓库信息
"""
return self._request("GET", f"repos/{owner}/{repo}")
def list_repositories(self, owner: str) -> Optional[List[Dict[str, Any]]]:
"""列出用户或组织的仓库
Args:
owner: 所有者(用户或组织)
Returns:
仓库列表
"""
return self._request("GET", f"users/{owner}/repos")
def delete_repository(self, owner: str, repo: str) -> bool:
"""删除仓库
Args:
owner: 所有者(用户或组织)
repo: 仓库名称
Returns:
是否删除成功
"""
response = self._request("DELETE", f"repos/{owner}/{repo}")
return response is not None
def create_file(self, owner: str, repo: str, path: str, content: str, message: str) -> Optional[Dict[str, Any]]:
"""创建或更新文件
Args:
owner: 所有者(用户或组织)
repo: 仓库名称
path: 文件路径
content: 文件内容base64编码
message: 提交消息
Returns:
操作结果
"""
data = {
"content": content,
"message": message
}
return self._request("POST", f"repos/{owner}/{repo}/contents/{path}", data)
def get_file(self, owner: str, repo: str, path: str) -> Optional[Dict[str, Any]]:
"""获取文件内容
Args:
owner: 所有者(用户或组织)
repo: 仓库名称
path: 文件路径
Returns:
文件信息和内容
"""
return self._request("GET", f"repos/{owner}/{repo}/contents/{path}")
def get_repository_files(self, owner: str, repo: str, path: str = "") -> Optional[List[Dict[str, Any]]]:
"""获取仓库文件列表
Args:
owner: 所有者(用户或组织)
repo: 仓库名称
path: 目录路径(默认为根目录)
Returns:
文件列表
"""
result = self._request("GET", f"repos/{owner}/{repo}/contents/{path}")
return result if isinstance(result, list) else None
def check_connection(self) -> bool:
"""检查与Gitea服务器的连接
Returns:
是否连接成功
"""
response = self._request("GET", "user")
return response is not None
def update_repository(self, owner: str, repo: str, name: Optional[str] = None, description: Optional[str] = None, private: Optional[bool] = None) -> Optional[Dict[str, Any]]:
"""更新仓库信息
Args:
owner: 所有者(用户或组织)
repo: 仓库名称
name: 新的仓库名称(可选)
description: 新的仓库描述(可选)
private: 是否私有(可选)
Returns:
更新后的仓库信息
"""
data = {}
if name is not None:
data["name"] = name
if description is not None:
data["description"] = description
if private is not None:
data["private"] = private
return self._request("PATCH", f"repos/{owner}/{repo}", data)