From f2cce0a4f65b22627b1dcd3624a9a4607ca5ace0 Mon Sep 17 00:00:00 2001 From: ZGaetano Date: Wed, 6 May 2026 19:01:08 -0400 Subject: [PATCH] =?UTF-8?q?vpn:=20add=20RelayClient=20=E2=80=94=20HTTP=20A?= =?UTF-8?q?PI=20client=20for=20DragonRelay?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/vpn/relayclient.h | 110 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 110 insertions(+) create mode 100644 app/vpn/relayclient.h diff --git a/app/vpn/relayclient.h b/app/vpn/relayclient.h new file mode 100644 index 0000000..64c3da1 --- /dev/null +++ b/app/vpn/relayclient.h @@ -0,0 +1,110 @@ +// relayclient.h — Qt HTTP client for the DragonRelay backend API. +// +// Workflow: +// 1. login() → authenticates, stores JWT in QSettings +// 2. provisionVPN()→ POST /api/vpn/peer → returns WireGuardConfig +// 3. revokeVPN() → DELETE /api/vpn/peer/:id → cleans up peer on server +// 4. fetchHosts() → GET /api/hosts → returns list of streaming hosts +// +// All calls are asynchronous; results come back via signals. + +#pragma once + +#include +#include +#include +#include +#include + +#include "wireguardconfig.h" + +class QNetworkAccessManager; +class QNetworkReply; +class QSettings; + +// Represents a streaming host as returned by GET /api/hosts +struct RelayHost { + QString name; + QString ip; + int port = 47984; +}; + +class RelayClient : public QObject +{ + Q_OBJECT + +public: + explicit RelayClient(QObject *parent = nullptr); + ~RelayClient() override; + + // ── Persistence helpers ──────────────────────────────────────────────── + + /// Save relay URL + credentials to QSettings for auto-reconnect. + void saveServer(const QString &label, const QUrl &url, + const QString &username, const QString &password); + + struct SavedServer { QString label; QUrl url; QString username; QString password; }; + QList savedServers() const; + void removeServer(const QString &label); + + // ── Async API calls ──────────────────────────────────────────────────── + + /// Authenticate against the relay. Emits loginSucceeded or loginFailed. + void login(const QUrl &relayUrl, const QString &username, const QString &password); + + /// Provision a WireGuard peer for this device. + /// Must be called after a successful login(). + /// Emits vpnProvisioned or vpnProvisionFailed. + void provisionVPN(const QString &deviceName = QString()); + + /// Revoke the current peer from the server. + /// Emits vpnRevoked on completion (best-effort; errors are logged but not fatal). + void revokeVPN(); + + /// Fetch the list of streaming hosts visible to this user. + /// Emits hostsReady or hostsFetchFailed. + void fetchHosts(); + + // ── State ────────────────────────────────────────────────────────────── + + bool isLoggedIn() const { return !m_jwt.isEmpty(); } + QUrl relayUrl() const { return m_relayUrl; } + QString username() const { return m_username; } + int vpnPeerId() const { return m_vpnPeerId; } + +signals: + void loginSucceeded(); + void loginFailed(const QString &reason); + + void vpnProvisioned(const WireGuardConfig &cfg); + void vpnProvisionFailed(const QString &reason); + + void vpnRevoked(); + + void hostsReady(const QList &hosts); + void hostsFetchFailed(const QString &reason); + +private: + QNetworkAccessManager *m_nam; + QSettings *m_settings; + + QUrl m_relayUrl; + QString m_username; + QString m_jwt; + int m_vpnPeerId = -1; + + QUrl apiUrl(const QString &path) const; + void setAuthHeader(QNetworkReply *reply); + QNetworkReply *authedGet (const QString &path); + QNetworkReply *authedPost(const QString &path, const QByteArray &body); + QNetworkReply *authedDelete(const QString &path); + + void onLoginReply (QNetworkReply *reply, const QString &username, + const QString &password); + void onProvisionReply(QNetworkReply *reply); + void onRevokeReply (QNetworkReply *reply); + void onHostsReply (QNetworkReply *reply); + + void persistJwt(); + void loadJwt(); +};