feat: Sync/Clone-Modus, Rotation, --setup Deployment, Scheduler-Fix, Dashboard-Updates, README

This commit is contained in:
Björn Nehlsen
2026-08-11 22:45:58 +02:00
parent 2eee55384e
commit a495570b20
14 changed files with 3851 additions and 681 deletions
+94 -4
View File
@@ -251,9 +251,9 @@ def test_connection(target: TargetConnection) -> tuple[bool, str]:
if protocol == "LOCAL":
check_path = Path(target.subpath)
if check_path.exists() and check_path.is_dir():
if check_path.exists() and (check_path.is_dir() or check_path.is_file()):
return True, "Lokaler Pfad erreichbar."
return False, "Lokaler Pfad ist nicht erreichbar oder kein Verzeichnis."
return False, "Lokaler Pfad nicht gefunden oder nicht zugänglich."
if protocol == "SMB":
if not target.share:
@@ -271,7 +271,13 @@ def test_connection(target: TargetConnection) -> tuple[bool, str]:
try:
ftp.connect(target.host, target.port or 21, timeout=8)
ftp.login(target.username, target.password)
ftp.cwd(target.subpath or "/")
subpath = target.subpath or "/"
try:
ftp.cwd(subpath)
except Exception: # noqa: BLE001
# subpath may be a file — try parent directory
parent = "/".join(subpath.rstrip("/").split("/")[:-1]) or "/"
ftp.cwd(parent)
return True, "FTP-Verbindung und Pfad sind erreichbar."
except Exception as exc: # noqa: BLE001
return False, f"FTP-Test fehlgeschlagen: {exc}"
@@ -288,7 +294,13 @@ def test_connection(target: TargetConnection) -> tuple[bool, str]:
transport = paramiko.Transport((target.host, target.port or 22))
transport.connect(username=target.username, password=target.password)
sftp = paramiko.SFTPClient.from_transport(transport)
sftp.listdir(target.subpath or "/")
subpath = target.subpath or "/"
try:
sftp.listdir(subpath)
except Exception: # noqa: BLE001
# subpath may be a file — try parent directory
parent = "/".join(subpath.rstrip("/").split("/")[:-1]) or "/"
sftp.listdir(parent)
return True, "SFTP-Verbindung und Pfad sind erreichbar."
except Exception as exc: # noqa: BLE001
return False, f"SFTP-Test fehlgeschlagen: {exc}"
@@ -301,6 +313,84 @@ def test_connection(target: TargetConnection) -> tuple[bool, str]:
return False, "Unbekanntes Protokoll."
def browse_ftp_directories(target: TargetConnection) -> tuple[bool, str, list[dict[str, str]]]:
ftp = FTP()
try:
ftp.connect(target.host, target.port or 21, timeout=8)
ftp.login(target.username, target.password)
current_path = normalize_subpath(target.subpath or "/")
try:
ftp.cwd(current_path)
except Exception:
current_path = "/"
ftp.cwd("/")
entries: list[dict[str, str]] = []
if current_path != "/":
parent = "/".join(current_path.rstrip("/").split("/")[:-1]) or "/"
entries.append({"name": "..", "path": parent, "entry_type": "up"})
raw_lines: list[str] = []
try:
ftp.retrlines("LIST", raw_lines.append)
except Exception:
pass
items: list[dict[str, str]] = []
for line in raw_lines:
parts = line.split(None, 8)
if len(parts) < 9:
continue
name = parts[8].strip()
if name in (".", ".."):
continue
is_dir = parts[0].startswith("d")
sub_path = _build_child_path(current_path, name)
items.append({"name": name, "path": sub_path, "entry_type": "directory" if is_dir else "file"})
entries.extend(_sort_entries(items))
return True, current_path, entries
except Exception as exc: # noqa: BLE001
return False, f"FTP-Fehler: {exc}", []
finally:
try:
ftp.quit()
except Exception: # noqa: BLE001
pass
def browse_sftp_directories(target: TargetConnection) -> tuple[bool, str, list[dict[str, str]]]:
import stat as stat_module
transport = None
sftp_client = None
try:
transport = paramiko.Transport((target.host, target.port or 22))
transport.connect(username=target.username, password=target.password)
sftp_client = paramiko.SFTPClient.from_transport(transport)
current_path = normalize_subpath(target.subpath or "/")
entries: list[dict[str, str]] = []
if current_path != "/":
parent = "/".join(current_path.rstrip("/").split("/")[:-1]) or "/"
entries.append({"name": "..", "path": parent, "entry_type": "up"})
items: list[dict[str, str]] = []
for attr in sftp_client.listdir_attr(current_path):
is_dir = stat_module.S_ISDIR(attr.st_mode or 0)
sub_path = _build_child_path(current_path, attr.filename)
items.append({"name": attr.filename, "path": sub_path, "entry_type": "directory" if is_dir else "file"})
entries.extend(_sort_entries(items))
return True, current_path, entries
except Exception as exc: # noqa: BLE001
return False, f"SFTP-Fehler: {exc}", []
finally:
if sftp_client is not None:
sftp_client.close()
if transport is not None:
transport.close()
def browse_smb_directories(target: TargetConnection) -> tuple[bool, str, list[dict[str, str]]]:
if target.protocol.upper() != "SMB":
return False, "SMB-Browser ist nur fuer SMB verfuegbar.", []