first commit
This commit is contained in:
@@ -0,0 +1,96 @@
|
||||
"""Einstellungen und Pfade (plattformabhaengig, ohne Qt nutzbar)."""
|
||||
import json
|
||||
import os
|
||||
import platform
|
||||
import secrets
|
||||
import socket
|
||||
import sys
|
||||
from dataclasses import asdict, dataclass, field, fields
|
||||
from pathlib import Path
|
||||
|
||||
APP = "NetClip"
|
||||
SYSTEM = platform.system() # Windows | Darwin | Linux
|
||||
|
||||
|
||||
def config_dir() -> Path:
|
||||
if SYSTEM == "Windows":
|
||||
return Path(os.environ.get("APPDATA", Path.home())) / APP
|
||||
if SYSTEM == "Darwin":
|
||||
return Path.home() / "Library" / "Application Support" / APP
|
||||
return Path(os.environ.get("XDG_CONFIG_HOME", Path.home() / ".config")) / "netclip"
|
||||
|
||||
|
||||
def cache_dir() -> Path:
|
||||
if SYSTEM == "Windows":
|
||||
return Path(os.environ.get("LOCALAPPDATA", Path.home())) / APP / "cache"
|
||||
if SYSTEM == "Darwin":
|
||||
return Path.home() / "Library" / "Caches" / APP
|
||||
return Path(os.environ.get("XDG_CACHE_HOME", Path.home() / ".cache")) / "netclip"
|
||||
|
||||
|
||||
def exe_dir() -> Path:
|
||||
if getattr(sys, "frozen", False):
|
||||
return Path(sys.executable).resolve().parent
|
||||
return Path(__file__).resolve().parent.parent
|
||||
|
||||
|
||||
@dataclass
|
||||
class Config:
|
||||
server: str = "" # z.B. https://clip.example.de
|
||||
token: str = "" # gemeinsamer Token aller eigenen Geraete
|
||||
server_key: str = "" # optional, siehe SERVER_KEY im Server
|
||||
verify_tls: bool = True # False = Zertifikat nicht pruefen (selbstsigniert, nur im LAN)
|
||||
ca_file: str = "" # alternativ: Pfad zum Server-Zertifikat (fullchain.pem)
|
||||
device_id: str = field(default_factory=lambda: secrets.token_hex(8))
|
||||
device_name: str = field(default_factory=socket.gethostname)
|
||||
send_enabled: bool = True # lokale Zwischenablage hochladen
|
||||
receive_enabled: bool = True # fremde Clips automatisch in die Zwischenablage legen
|
||||
send_text: bool = True
|
||||
send_images: bool = True
|
||||
send_files: bool = True
|
||||
max_send_mb: int = 500 # groessere Dateien/Ordner werden nicht automatisch gesendet
|
||||
max_auto_receive_mb: int = 200 # groessere Clips nur auf Klick im Verlauf herunterladen
|
||||
keep_received_days: int = 7 # empfangene Dateien im Cache so lange aufheben
|
||||
notifications: bool = True
|
||||
autostart: bool = False
|
||||
|
||||
@property
|
||||
def configured(self) -> bool:
|
||||
return bool(self.server and self.token)
|
||||
|
||||
|
||||
def config_file() -> Path:
|
||||
return config_dir() / "config.json"
|
||||
|
||||
|
||||
def load() -> Config:
|
||||
"""Laedt config.json. Fuer die Verteilung: eine config.default.json neben der
|
||||
Programmdatei liefert Vorgaben (Server, Token, ...), die beim ersten Start uebernommen werden."""
|
||||
data = {}
|
||||
default = exe_dir() / "config.default.json"
|
||||
for path in (default, config_file()):
|
||||
if path.exists():
|
||||
try:
|
||||
values = json.loads(path.read_text(encoding="utf-8"))
|
||||
except (OSError, ValueError) as e:
|
||||
print(f"Konfiguration {path} nicht lesbar: {e}", file=sys.stderr)
|
||||
continue
|
||||
if path == default:
|
||||
values.pop("device_id", None) # muss pro Geraet eindeutig sein
|
||||
values.pop("device_name", None)
|
||||
data.update(values)
|
||||
known = {f.name for f in fields(Config)}
|
||||
cfg = Config(**{k: v for k, v in data.items() if k in known})
|
||||
if not config_file().exists():
|
||||
save(cfg)
|
||||
return cfg
|
||||
|
||||
|
||||
def save(cfg: Config):
|
||||
path = config_file()
|
||||
path.parent.mkdir(parents=True, exist_ok=True)
|
||||
tmp = path.with_suffix(".tmp")
|
||||
tmp.write_text(json.dumps(asdict(cfg), indent=2), encoding="utf-8")
|
||||
os.replace(tmp, path)
|
||||
if SYSTEM != "Windows":
|
||||
os.chmod(path, 0o600)
|
||||
Reference in New Issue
Block a user