From 3d8f54a5e453466300e0bc51e6bd07837ee3d875 Mon Sep 17 00:00:00 2001 From: ZGaetano Date: Wed, 6 May 2026 19:22:58 -0400 Subject: [PATCH] =?UTF-8?q?vpn:=20DragonRelayBackend=20=E2=80=94=20QObject?= =?UTF-8?q?=20bridge=20between=20RelayClient/TunnelManager=20and=20QML?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/vpn/dragonrelaybackend.h | 129 +++++++++++++++++++++++++++++++++++ 1 file changed, 129 insertions(+) create mode 100644 app/vpn/dragonrelaybackend.h diff --git a/app/vpn/dragonrelaybackend.h b/app/vpn/dragonrelaybackend.h new file mode 100644 index 0000000..0b30aca --- /dev/null +++ b/app/vpn/dragonrelaybackend.h @@ -0,0 +1,129 @@ +#pragma once +// app/vpn/dragonrelaybackend.h +// +// DragonRelayBackend is the QObject that QML binds to via the context property +// "dragonRelay". It owns a RelayClient and a TunnelManager, coordinates the +// login → VPN provision → tunnel-up → host-discovery workflow, and exposes +// everything QML needs through Q_PROPERTY / Q_INVOKABLE. +// +// Registration in main.cpp (or wherever the QML engine is created): +// +// DragonRelayBackend *backend = new DragonRelayBackend(engine); +// engine->rootContext()->setContextProperty("dragonRelay", backend); +// +// QML usage: +// +// dragonRelay.connectRelay(url, user, pass) +// dragonRelay.disconnectRelay() +// dragonRelay.streamHost(ip, app) +// dragonRelay.status // int — DragonRelayBackend::Status enum +// dragonRelay.statusText // QString +// dragonRelay.hosts // QVariantList of {name, ip, port, online, source} +// dragonRelay.tunnelIP // QString — local WG address (e.g. "10.99.0.2") + +#ifndef DRAGONRELAYBACKEND_H +#define DRAGONRELAYBACKEND_H + +#include +#include +#include +#include +#include +#include + +#include "relayclient.h" +#include "tunnelmanager.h" +#include "wireguardconfig.h" + +class DragonRelayBackend : public QObject { + Q_OBJECT + + // ── Properties exposed to QML ───────────────────────────────────────────── + + Q_PROPERTY(int status READ status NOTIFY statusChanged) + Q_PROPERTY(QString statusText READ statusText NOTIFY statusChanged) + Q_PROPERTY(QVariantList hosts READ hosts NOTIFY hostsChanged) + Q_PROPERTY(QString tunnelIP READ tunnelIP NOTIFY tunnelIPChanged) + +public: + // Status codes (exposed to QML as dragonRelay.StatusXxx or raw int) + enum Status { + Disconnected = 0, + Connecting = 1, // logging in / provisioning VPN peer + TunnelUp = 2, // WireGuard tunnel active, hosts loading + Ready = 3, // tunnel up + at least one host visible + Error = 4, + }; + Q_ENUM(Status) + + explicit DragonRelayBackend(QObject *parent = nullptr); + ~DragonRelayBackend() override; + + // ── Property accessors ──────────────────────────────────────────────────── + + int status() const { return m_status; } + QString statusText() const { return m_statusText; } + QVariantList hosts() const { return m_hosts; } + QString tunnelIP() const { return m_tunnelIP; } + +public slots: + // ── Invokable from QML ──────────────────────────────────────────────────── + + // Begin the full connect workflow: login → provision VPN → start tunnel → poll hosts. + // Idempotent if already connected. + Q_INVOKABLE void connectRelay(const QString &url, + const QString &username, + const QString &password); + + // Tear down tunnel, unregister peer, return to Disconnected state. + Q_INVOKABLE void disconnectRelay(); + + // Ask Moonlight to stream to a host discovered via the relay. + Q_INVOKABLE void streamHost(const QString &ip, const QString &app = QStringLiteral("Desktop")); + + // Refresh the host list immediately (called by QML pull-to-refresh). + Q_INVOKABLE void refreshHosts(); + +signals: + void statusChanged(); + void hostsChanged(); + void tunnelIPChanged(); + +private slots: + // RelayClient signals + void onLoginDone(bool ok, const QString &err); + void onVPNPeerProvisioned(const RelayVPNConf &conf); + void onVPNError(const QString &err); + void onHostsFetched(const QList &hosts); + void onHostsError(const QString &err); + + // TunnelManager signals + void onTunnelUp(const QString &localIP); + void onTunnelDown(); + void onTunnelError(const QString &err); + + // Timers + void pollHosts(); + +private: + void setStatus(Status s, const QString &text = {}); + void startHostPoll(); + void stopHostPoll(); + + RelayClient m_relay; + TunnelManager m_tunnel; + QTimer m_hostPollTimer; + + Status m_status = Disconnected; + QString m_statusText = QStringLiteral("Not connected"); + QVariantList m_hosts; + QString m_tunnelIP; + + QString m_savedURL; + QString m_savedUser; + QString m_savedPass; + + QSettings m_settings; // persists last-used URL + username +}; + +#endif // DRAGONRELAYBACKEND_H