import httpx import os import logging from pathlib import Path logging.basicConfig(level=logging.WARNING) logger = logging.getLogger("YouTrackClient") class YouTrackClient: def __init__(self): self.base_url = os.getenv("YOUTRACK_URL") self.token = os.getenv("YOUTRACK_TOKEN") self.ca_bundle = os.getenv("YOUTRACK_CA_BUNDLE") if not self.base_url or not self.token: self.enabled = False logger.info("YouTrack: .env не настроен — синхронизация отключена") return # Определяем verify параметр для httpx if self.ca_bundle: ca_path = Path(self.ca_bundle) if ca_path.exists(): self.verify = str(ca_path.resolve()) logger.info(f"YouTrack: использую CA bundle: {self.verify}") else: logger.error(f"YouTrack: CA bundle не найден: {ca_path}") self.verify = True # fallback на системные else: self.verify = True # стандартное поведение self.enabled = True self.headers = { "Authorization": f"Bearer {self.token}", "Content-Type": "application/json", "Accept": "application/json" } self.base_url = self.base_url.rstrip('/') def _get_client(self) -> httpx.Client: """Создаёт HTTP-клиент с правильной настройкой SSL""" return httpx.Client(verify=self.verify, timeout=15) def issue_exists(self, issue_id: str) -> bool: if not self.enabled: return False try: with self._get_client() as client: url = f"{self.base_url}/api/issues/{issue_id}" resp = client.get(url, headers=self.headers) exists = resp.status_code == 200 if not exists: logger.warning(f"YouTrack: задача {issue_id} не найдена (HTTP {resp.status_code})") return exists except Exception as e: logger.error(f"YouTrack: ошибка при проверке задачи {issue_id}: {e}") return False def add_work_item(self, issue_id: str, minutes: float) -> bool: if not self.enabled: return False if minutes <= 0: return True payload = {"duration": {"minutes": round(minutes)}} try: with self._get_client() as client: url = f"{self.base_url}/api/issues/{issue_id}/timeTracking/workItems" resp = client.post(url, headers=self.headers, json=payload) if resp.status_code in (200, 201): logger.info(f"YouTrack: +{round(minutes)} мин к {issue_id}") return True else: logger.error(f"YouTrack: ошибка отправки времени ({issue_id}): {resp.status_code} – {resp.text}") return False except Exception as e: logger.error(f"YouTrack: сетевая ошибка при отправке времени ({issue_id}): {e}") return False