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(); +};