Add git repository selection for panel updates

- Add repository URL setting to panel_settings table
- Support for multiple git hosting providers (GitHub, Gitea, GitLab, etc.)
- UI selector for predefined repositories (git.tzim.uz and github.com) or custom URL
- Update service now uses repository from database settings
- API endpoints: GET/PUT /api/updates/repository
- Migration for default repository setting
- Translations for repository selection UI
This commit is contained in:
2025-12-16 20:04:57 +05:00
parent 2541360f51
commit 8dced5479d
5 changed files with 301 additions and 30 deletions

View File

@@ -131,6 +131,53 @@ def configure_git_for_public_access():
return False
def get_repository_url_from_settings():
"""Get configured repository URL from database settings"""
try:
from ..db import SessionLocal
from ..models import PanelSettings
db = SessionLocal()
try:
setting = db.query(PanelSettings).filter(PanelSettings.key == "git_repository_url").first()
if setting and setting.value:
return setting.value
finally:
db.close()
except Exception as e:
logger.warning(f"Could not get repository URL from settings: {e}")
# Default fallback
return "https://git.tzim.uz/dilshod/pve_manager"
def parse_repo_url(url: str) -> tuple:
"""Parse repository URL to extract owner and repo name"""
# Remove .git suffix
url = url.replace(".git", "")
# GitHub: https://github.com/owner/repo
if "github.com" in url:
parts = url.split("github.com/")
if len(parts) == 2:
owner_repo = parts[1].split("/")
if len(owner_repo) >= 2:
return owner_repo[0], owner_repo[1], "github"
# GitLab or other Git hosting (Gitea, etc.)
# https://git.tzim.uz/dilshod/pve_manager
parts = url.split("://")
if len(parts) == 2:
path_parts = parts[1].split("/")
if len(path_parts) >= 3:
domain = path_parts[0]
owner = path_parts[1]
repo = path_parts[2]
return owner, repo, domain
return None, None, None
async def check_for_updates() -> Dict[str, Any]:
"""
Проверить наличие обновлений через GitHub API
@@ -155,37 +202,17 @@ async def check_for_updates() -> Dict[str, Any]:
result["latest_version"] = current_version
return result
# Получаем URL репозитория
repo_url = None
if result["project_mounted"]:
try:
ensure_safe_directory()
remote_result = subprocess.run(
["git", "remote", "get-url", "origin"],
cwd=PROJECT_ROOT,
capture_output=True,
text=True,
timeout=5
)
if remote_result.returncode == 0:
repo_url = remote_result.stdout.strip()
except Exception as e:
logger.warning(f"Could not get git remote URL: {e}")
# Получаем URL репозитория из настроек БД
repo_url = get_repository_url_from_settings()
result["repository_url"] = repo_url
# Парсим owner/repo из URL
owner, repo = None, None
if repo_url:
# https://github.com/owner/repo.git -> owner/repo
if "github.com" in repo_url:
parts = repo_url.replace(".git", "").split("github.com/")
if len(parts) == 2:
owner_repo = parts[1].split("/")
if len(owner_repo) >= 2:
owner, repo = owner_repo[0], owner_repo[1]
owner, repo, host = parse_repo_url(repo_url)
if not owner or not repo:
# Fallback на хардкод для этого проекта
owner, repo = "markmorado", "pvemanager"
result["error"] = "Could not parse repository URL"
result["latest_version"] = current_version
return result
try:
# Подготавливаем заголовки для запроса
@@ -193,14 +220,20 @@ async def check_for_updates() -> Dict[str, Any]:
if GITHUB_TOKEN:
headers["Authorization"] = f"token {GITHUB_TOKEN}"
# Используем GitHub API для получения файлов
# Используем raw URL для получения файлов
async with httpx.AsyncClient(timeout=30.0) as client:
# Пробуем оба варианта: main и master
version_url = None
version_response = None
for branch in ["main", "master"]:
test_url = f"https://raw.githubusercontent.com/{owner}/{repo}/{branch}/VERSION"
# Формируем URL в зависимости от хостинга
if host == "github":
test_url = f"https://raw.githubusercontent.com/{owner}/{repo}/{branch}/VERSION"
else:
# Для Gitea, GitLab и других (формат: https://domain/owner/repo/raw/branch/file)
test_url = f"https://{host}/{owner}/{repo}/raw/branch/{branch}/VERSION"
test_response = await client.get(test_url, headers=headers)
if test_response.status_code == 200:
@@ -225,7 +258,12 @@ async def check_for_updates() -> Dict[str, Any]:
# Получить CHANGELOG.md (используем ту же ветку, что и для VERSION)
branch = version_url.split("/")[-2] if version_url else "main"
changelog_url = f"https://raw.githubusercontent.com/{owner}/{repo}/{branch}/CHANGELOG.md"
if host == "github":
changelog_url = f"https://raw.githubusercontent.com/{owner}/{repo}/{branch}/CHANGELOG.md"
else:
changelog_url = f"https://{host}/{owner}/{repo}/raw/branch/{branch}/CHANGELOG.md"
changelog_response = await client.get(changelog_url, headers=headers)
if changelog_response.status_code == 200:
@@ -318,6 +356,19 @@ async def perform_update() -> Dict[str, Any]:
# Настраиваем git для публичного доступа
configure_git_for_public_access()
# Получаем URL репозитория из настроек и устанавливаем remote
try:
repo_url = get_repository_url_from_settings()
subprocess.run(
["git", "remote", "set-url", "origin", repo_url],
cwd=PROJECT_ROOT,
capture_output=True,
timeout=10
)
logger.info(f"Set git remote to: {repo_url}")
except Exception as e:
logger.warning(f"Could not set git remote: {e}")
# Сбросить статус
update_status = {
"is_updating": True,