first commit

This commit is contained in:
2026-02-08 14:42:58 +08:00
commit 20e1deae21
8197 changed files with 2264639 additions and 0 deletions

217
backend/app/gitea/client.py Normal file
View File

@@ -0,0 +1,217 @@
"""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)