dragonmoonlight/app/vpn/relayclient.h

113 lines
3.9 KiB
C
Raw Normal View History

// 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 <QObject>
#include <QString>
#include <QUrl>
#include <QList>
#include <QJsonObject>
2026-05-06 20:16:06 -04:00
#include <QVariantList>
#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;
2026-05-06 20:16:06 -04:00
QVariantList displays; // Each entry: QVariantMap with name, friendlyName, width, height, isPrimary
};
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<SavedServer> 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<RelayHost> &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();
};