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
This commit is contained in:
89
core/interfaces.py
Normal file
89
core/interfaces.py
Normal file
@@ -0,0 +1,89 @@
|
||||
# -*- 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``."""
|
||||
Reference in New Issue
Block a user