from pathlib import Path from app.dependencies import DependencyManager from app.paths import AppPaths from app.ui import ConsoleUI from app.venv_manager import VirtualEnvManager class AppRunner: def __init__(self, base_dir: Path, entry_script: Path) -> None: self.paths = AppPaths(base_dir=base_dir) self.entry_script = entry_script def _bootstrap(self) -> None: """Ensure venv and dependencies are ready (re-execs into venv if needed).""" venv = VirtualEnvManager(self.paths.venv_dir, self.entry_script) if not venv.in_virtualenv(): ConsoleUI.info("Virtuelle Umgebung wird vorbereitet...") venv.ensure_and_reexec_if_needed() deps = DependencyManager(self.paths.requirements_path) missing = deps.install_missing() if missing: ConsoleUI.success(f"Abhaengigkeiten installiert: {', '.join(missing)}") else: ConsoleUI.info("Alle Abhaengigkeiten sind bereits installiert.") def setup(self) -> None: ConsoleUI.headline("BackItUp Setup") self._bootstrap() ConsoleUI.success("System ist bereit.") self._install_systemd_service() def _install_systemd_service(self) -> None: import shutil import subprocess if not shutil.which("systemctl"): ConsoleUI.warn("systemctl nicht gefunden – kein systemd-System erkannt.") ConsoleUI.info("Manuelle Installation: bash scripts/service.sh install") return service_name = "BackItUp.service" user_systemd_dir = Path.home() / ".config" / "systemd" / "user" user_systemd_dir.mkdir(parents=True, exist_ok=True) if self.paths.venv_dir.name == "nt": venv_python = self.paths.venv_dir / "Scripts" / "python.exe" else: venv_python = self.paths.venv_dir / "bin" / "python" service_content = f"""[Unit] Description=BackItUp Flask Service After=network-online.target Wants=network-online.target [Service] Type=simple WorkingDirectory={self.paths.base_dir} ExecStart={venv_python} {self.entry_script.resolve()} Restart=on-failure RestartSec=3 Environment=PYTHONUNBUFFERED=1 [Install] WantedBy=default.target """ target = user_systemd_dir / service_name target.write_text(service_content) ConsoleUI.success(f"Service-Datei geschrieben: {target}") try: subprocess.run(["systemctl", "--user", "daemon-reload"], check=True) ConsoleUI.success("systemd daemon neu geladen.") except subprocess.CalledProcessError as exc: ConsoleUI.warn(f"daemon-reload fehlgeschlagen: {exc}") ConsoleUI.info(f"Service aktivieren: systemctl --user enable {service_name}") ConsoleUI.info(f"Service starten: systemctl --user start {service_name}") ConsoleUI.info(f"Status pruefen: systemctl --user status {service_name}") ConsoleUI.info(f"Logs verfolgen: journalctl --user -u {service_name} -f") def run(self) -> None: ConsoleUI.headline("BackItUp Bootstrap") self._bootstrap() from app.config_store import ConfigStore from app.setup_wizard import SetupWizard from app.web_server import FlaskServer store = ConfigStore(self.paths.db_path) store.ensure_schema() config = store.load_config() if config is None: ConsoleUI.warn("Keine vollstaendige Konfiguration gefunden.") setup_data = SetupWizard().collect_initial() store.save_config(setup_data.config) store.create_user( setup_data.admin_username, setup_data.admin_password_hash, is_admin=True, ) config = setup_data.config ConsoleUI.success("Konfiguration und Admin-Benutzer wurden gespeichert.") elif not store.has_any_user(): ConsoleUI.warn("Keine Benutzer gefunden.") admin_username, admin_hash = SetupWizard().collect_admin_user() store.create_user(admin_username, admin_hash, is_admin=True) ConsoleUI.success("Admin-Benutzer wurde gespeichert.") ConsoleUI.success("System ist bereit.") ConsoleUI.info( f"Flask startet auf http://{config.ip}:{config.port} (debug={config.debug})" ) FlaskServer(config=config, store=store).run()