Projektstruktur

Überblick über das Repository für Mitwirkende und Interessierte.

Verzeichnis-Übersicht

NeoSSHWinManager/
├── main.py                      # GUI entry point, single-instance, theme bootstrap
├── cli_main.py                  # CLI companion — talks to GUI via Named Pipe
├── build_dual.ps1               # PyInstaller build for GUI + CLI
├── NeoSSHWinManager.spec        # PyInstaller spec (GUI)
├── NeoSSHWinManager-cli.spec    # PyInstaller spec (CLI)
├── file_version_info.txt        # Windows VERSIONINFO block
├── requirements.txt
├── LICENSE
├── README.md
├── assets/
│   ├── app_icon.ico             # Multi-resolution Windows icon
│   ├── app_icon.png
│   └── screenshots/
├── src/
│   ├── config.py                # AppSettings + JSON load/save
│   ├── database.py              # SQLite schema + migrations
│   ├── auth_manager.py          # Login, key derivation, multi-user
│   ├── sshfs_controller.py      # Mount / unmount / status
│   ├── ssh_launcher.py          # SSH sessions (OpenSSH / PuTTY)
│   ├── single_instance.py       # Mutex + window activation
│   ├── ipc_server.py            # Named-pipe server for CLI companion
│   ├── i18n.py                  # Load / resolve translations
│   ├── translations/
│   │   ├── en.json
│   │   └── de.json
│   ├── models/
│   │   ├── connection.py        # Connection data model
│   │   └── settings.py          # AppSettings data model
│   └── ui/
│       ├── main_window.py
│       ├── login_window.py
│       ├── widgets/             # ConnectionCard, MountToggle, …
│       └── dialogs/             # add_edit, settings, ask_password, debug
└── tests/
    ├── test_config.py
    ├── test_auth_manager.py
    └── test_sshfs_controller.py

Hauptmodule

main.py

GUI entry point. In order, it:

  1. Loads the config (config.AppSettings.load()).
  2. Enforces single-instance mode (SingleInstance("SSHWinManager_Mutex_v1")).
  3. Initializes QApplication with the saved theme.
  4. Shows the login window (unless auto-login applies) → main window.
  5. Starts IPCServer in a worker thread.

cli_main.py

Argparse CLI. Connects to \\.\pipe\SSHWinManager_IPC_v1, sends a JSON request {op:"connect", key:"…", exec:"…"}, receives credentials, then calls ssh_launcher.spawn_session().

src/config.py

  • AppSettings — dataclass with defaults (theme, language, PuTTY path, auto-reconnect, check interval, tray, debug, …).
  • load()/save() — JSON in %APPDATA%\SSHWinManager\config.json.
  • Migrations: missing keys are filled with defaults.

src/database.py

  • SQLite, path: %APPDATA%\SSHWinManager\app.db.
  • Tables: users, connections, settings_per_user.
  • Schema version in PRAGMA user_version; migrate() adds new columns idempotently.

src/auth_manager.py

  • UserManager — create, sign in, change password, delete.
  • Password hash: PBKDF2-HMAC-SHA256, per-user salt, 200k iterations.
  • The session key is derived at sign-in and kept only in RAM.
  • UserConnectionManager — CRUD for connections, automatic encryption/decryption per user.

src/sshfs_controller.py

  • SSHFSController.mount(connection, password=None) — builds the argument list and starts sshfs.exe as a detached subprocess.
  • unmount(letter) — three strategies (see Connections).
  • list_mounts() — parses fsutil output.
  • status_for(connection) — returns "mounted", "unmounted", or "stale".

src/ssh_launcher.py

Starts an interactive SSH session. Chooses OpenSSH or PuTTY based on AppSettings.use_putty. Passes the key or password securely.

src/ipc_server.py

Worker thread with a named-pipe server. Accepts JSON requests, validates the access key against the database, and returns decrypted credentials.

UI-Module

Datei Aufgabe
login_window.pyLogin form and first-profile setup wizard.
main_window.pyMain window: sidebar + connection list + status bar.
tray.pySystem tray icon with quick-access menu.
widgets/connection_card.pyOne card per connection with a mount toggle.
widgets/mount_toggle.pyCustom switch with cyan/green states and spinner.
dialogs/add_edit_dialog.pyCreate / edit a connection.
dialogs/settings_dialog.pyApp-wide settings.
dialogs/ask_password_dialog.pyPassword prompt for “Ask each time”.
dialogs/debug_dialog.pyLive log viewer with filter.

Datenmodelle

Connection

@dataclass
class Connection:
    id: int | None
    user_id: int
    name: str
    host: str
    port: int = 22
    user: str = ""
    remote_path: str = "/"
    drive_letter: str = "Z:"
    auth_method: str = "password"   # password | key | ask
    password: str | None = None     # plaintext only in RAM
    key_path: str | None = None
    cli_access_enabled: bool = False
    access_key: str | None = None
    auto_mount: bool = False
    created_at: datetime
    updated_at: datetime

AppSettings

@dataclass
class AppSettings:
    theme: str = "dark"             # dark | light
    language: str = "en"            # en | de
    start_with_windows: bool = False
    minimize_to_tray: bool = True
    auto_login_with_windows: bool = False
    auto_reconnect: bool = False
    auto_reconnect_mounts_on_start: bool = True
    check_interval_seconds: int = 30
    debug_mode: bool = False
    use_putty: bool = False
    putty_path: str = r"C:\Program Files\PuTTY\putty.exe"

Build- & Release-Prozess

  1. Bump the version in file_version_info.txt, README, and changelog.html.
  2. Run .\build_dual.ps1 — builds GUI and CLI EXEs with PyInstaller into dist/.
  3. Test both EXEs locally (login, one mount, one CLI call).
  4. Create the tag: git tag v1.x.0 && git push --tags.
  5. Create a new GitHub release, attach both EXEs, and add the changelog.