Files
hdg/core/interfaces.py
MohammadAmin Baranzehi 8a9269bb79 feat(core): implement modular OOP engine following SOLID and DRY principles
- models: DomainConfig, ServiceConfig, DiagnosticReport (single source of truth)

- interfaces: DIP & ISP contracts for detector, generator, executor, doctor, and migrator

- detector: Smart auto-detection for modular (config/wsgi.py), traditional, and flat layouts

- service_generator: Systemd service generation with --umask 007 and security sandboxing

- executor: Linux command execution with dry-run support

- nginx_manager: HestiaCP template installation and validation

- migrator: Automated migration and repair of existing deployments

- doctor: Comprehensive post-deployment health check engine
2026-09-14 13:54:05 +03:30

90 lines
2.9 KiB
Python

# -*- coding: utf-8 -*-
"""
Abstract interfaces (contracts) for the deployment engine.
Applying the **Interface Segregation Principle** (ISP) and
**Dependency Inversion Principle** (DIP): every high-level orchestrator
depends on these thin abstractions — never on concrete implementations.
"""
from __future__ import annotations
from abc import ABC, abstractmethod
from pathlib import Path
from typing import Optional, Tuple
from core.models import (
DiagnosticReport,
DjangoProjectInfo,
DomainConfig,
ServiceConfig,
)
class IDjangoDetector(ABC):
"""Inspects a Django project directory and returns structured metadata."""
@abstractmethod
def detect(self, web_dir: Path, project_hint: Optional[str] = None) -> DjangoProjectInfo:
"""Analyse *web_dir* and return a populated ``DjangoProjectInfo``."""
class IServiceGenerator(ABC):
"""Renders systemd service unit content from a ``ServiceConfig``."""
@abstractmethod
def generate(self, config: ServiceConfig) -> str:
"""Return the full text of a systemd ``.service`` unit file."""
class ISystemExecutor(ABC):
"""Abstraction over OS-level command execution."""
@abstractmethod
def run(self, command: str, check: bool = True) -> Tuple[int, str, str]:
"""Execute *command* and return ``(return_code, stdout, stderr)``."""
@abstractmethod
def write_file(self, path: Path, content: str, mode: int = 0o644) -> None:
"""Write *content* to *path* with the given POSIX *mode*."""
@abstractmethod
def file_exists(self, path: Path) -> bool:
"""Return ``True`` if *path* exists on the filesystem."""
@abstractmethod
def backup_file(self, path: Path) -> Optional[Path]:
"""Create a timestamped backup of *path* and return the backup path."""
class INginxManager(ABC):
"""Manages HestiaCP NGINX proxy template installation."""
@abstractmethod
def install_templates(self, tpl_source: Path, stpl_source: Path) -> None:
"""Copy templates to the HestiaCP template directory and set permissions."""
@abstractmethod
def rebuild_domain(self, domain_config: DomainConfig) -> None:
"""Force-rebuild the NGINX configuration for a domain."""
class IProjectMigrator(ABC):
"""Upgrades / repairs a previously deployed project in-place."""
@abstractmethod
def needs_migration(self, domain_config: DomainConfig) -> bool:
"""Return ``True`` if the existing deployment requires migration."""
@abstractmethod
def migrate(self, domain_config: DomainConfig) -> DiagnosticReport:
"""Perform the migration / repair and return a diagnostic report."""
class IDeploymentDoctor(ABC):
"""Runs post-deployment health checks."""
@abstractmethod
def diagnose(self, domain_config: DomainConfig) -> DiagnosticReport:
"""Run all health checks and return a ``DiagnosticReport``."""