2026-07-29 17:36:22 +02:00
|
|
|
|
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
|
|
|
|
|
|
|
2026-08-11 22:45:58 +02:00
|
|
|
|
def _bootstrap(self) -> None:
|
|
|
|
|
|
"""Ensure venv and dependencies are ready (re-execs into venv if needed)."""
|
2026-07-29 17:36:22 +02:00
|
|
|
|
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.")
|
|
|
|
|
|
|
2026-08-11 22:45:58 +02:00
|
|
|
|
def setup(self) -> None:
|
2026-08-11 23:33:40 +02:00
|
|
|
|
ConsoleUI.headline("BackItUp Setup")
|
2026-08-11 22:45:58 +02:00
|
|
|
|
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
|
|
|
|
|
|
|
2026-08-11 23:33:40 +02:00
|
|
|
|
service_name = "BackItUp.service"
|
2026-08-11 22:45:58 +02:00
|
|
|
|
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]
|
2026-08-11 23:33:40 +02:00
|
|
|
|
Description=BackItUp Flask Service
|
2026-08-11 22:45:58 +02:00
|
|
|
|
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:
|
2026-08-11 23:33:40 +02:00
|
|
|
|
ConsoleUI.headline("BackItUp Bootstrap")
|
2026-08-11 22:45:58 +02:00
|
|
|
|
self._bootstrap()
|
|
|
|
|
|
|
2026-07-29 17:36:22 +02:00
|
|
|
|
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()
|