""" Repo Service. Business logic for repository management including: - Creating repository records - Listing repositories by server - Retrieving repository details - Updating repository status - Deleting repository records """ import time from typing import List, Optional from pathlib import Path from sqlalchemy.orm import Session from app.models.repo import Repo from app.models.server import Server from app.config import get_settings class RepoService: """ Service for managing repository records. Handles CRUD operations for repositories that are being mirrored from Gitea servers. """ def __init__(self, db: Session): """ Initialize the service with a database session. Args: db: SQLAlchemy database session """ self.db = db self.settings = get_settings() def create_repo( self, server_id: int, name: str, full_name: str, clone_url: str, local_path: Optional[str] = None ) -> Repo: """ Create a new repository record. Args: server_id: ID of the server this repo belongs to name: Repository name (e.g., "my-repo") full_name: Full repository name (e.g., "owner/my-repo") clone_url: Git clone URL (typically SSH format) local_path: Optional local path for the mirrored repo. If not provided, will be generated based on server and repo name. Returns: Created Repo model instance Raises: ValueError: If server_id is invalid """ # Verify server exists server = self.db.query(Server).filter_by(id=server_id).first() if not server: raise ValueError(f"Server not found with ID {server_id}") # Generate local_path if not provided if local_path is None: local_path = str(Path(server.local_path) / name) # Create the repo record current_time = int(time.time()) repo = Repo( server_id=server_id, name=name, full_name=full_name, clone_url=clone_url, local_path=local_path, status="pending", last_sync_at=None, created_at=current_time ) self.db.add(repo) self.db.commit() self.db.refresh(repo) return repo def list_repos(self, server_id: int) -> List[Repo]: """ List all repositories for a specific server. Args: server_id: ID of the server Returns: List of Repo model instances for the server, ordered by creation time """ return self.db.query(Repo).filter_by( server_id=server_id ).order_by(Repo.created_at).all() def get_repo(self, repo_id: int) -> Optional[Repo]: """ Get a repository by ID. Args: repo_id: ID of the repository Returns: Repo model instance or None if not found """ return self.db.query(Repo).filter_by(id=repo_id).first() def update_repo_status(self, repo_id: int, status: str) -> Repo: """ Update the status of a repository. Common status values: - "pending": Initial state, not yet synced - "syncing": Currently being synced - "success": Last sync was successful - "failed": Last sync failed Args: repo_id: ID of the repository status: New status value Returns: Updated Repo model instance Raises: ValueError: If repo not found """ repo = self.get_repo(repo_id) if not repo: raise ValueError(f"Repo not found with ID {repo_id}") repo.status = status # If status is success, update last_sync_at if status == "success": repo.last_sync_at = int(time.time()) self.db.commit() self.db.refresh(repo) return repo def delete_repo(self, repo_id: int) -> bool: """ Delete a repository. Args: repo_id: ID of the repository to delete Returns: True if deleted, False if not found """ repo = self.get_repo(repo_id) if not repo: return False self.db.delete(repo) self.db.commit() return True def get_repo_by_name(self, server_id: int, name: str) -> Optional[Repo]: """ Get a repository by server and name. Args: server_id: ID of the server name: Repository name Returns: Repo model instance or None if not found """ return self.db.query(Repo).filter_by( server_id=server_id, name=name ).first() def list_all_repos(self) -> List[Repo]: """ List all repositories across all servers. Returns: List of all Repo model instances, ordered by creation time """ return self.db.query(Repo).order_by(Repo.created_at).all() def update_repo( self, repo_id: int, **kwargs ) -> Repo: """ Update a repository's configuration. Args: repo_id: ID of the repository to update **kwargs: Fields to update (name, full_name, clone_url, local_path, status) Returns: Updated Repo model instance Raises: ValueError: If repo not found """ repo = self.get_repo(repo_id) if not repo: raise ValueError(f"Repo not found with ID {repo_id}") # Update fields for key, value in kwargs.items(): if hasattr(repo, key): setattr(repo, key, value) self.db.commit() self.db.refresh(repo) return repo