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