RelayClient: fix syntax error in savedServers(); strip trailing slash on relay URLs
This commit is contained in:
parent
9bcef4c0a9
commit
f270350ae6
1 changed files with 26 additions and 41 deletions
|
|
@ -14,23 +14,22 @@
|
||||||
RelayClient::RelayClient(QObject *parent)
|
RelayClient::RelayClient(QObject *parent)
|
||||||
: QObject(parent)
|
: QObject(parent)
|
||||||
, m_nam(new QNetworkAccessManager(this))
|
, m_nam(new QNetworkAccessManager(this))
|
||||||
, m_settings(new QSettings(QStringLiteral("WildDragon"), QStringLiteral("DragonMoonlight"), this))
|
, m_settings(new QSettings(QStringLiteral("WildDragon"),
|
||||||
|
QStringLiteral("DragonMoonlight"), this))
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
RelayClient::~RelayClient() = default;
|
RelayClient::~RelayClient() = default;
|
||||||
|
|
||||||
// ─── URL helpers ──────────────────────────────────────────────────────────────
|
|
||||||
|
|
||||||
QUrl RelayClient::apiUrl(const QString &path) const
|
QUrl RelayClient::apiUrl(const QString &path) const
|
||||||
{
|
{
|
||||||
QUrl u = m_relayUrl;
|
QUrl u = m_relayUrl;
|
||||||
u.setPath(u.path() + path);
|
QString base = u.path();
|
||||||
|
while (base.endsWith(QLatin1Char('/'))) base.chop(1);
|
||||||
|
u.setPath(base + path);
|
||||||
return u;
|
return u;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ─── Auth-header injection ────────────────────────────────────────────────
|
|
||||||
|
|
||||||
QNetworkReply *RelayClient::authedGet(const QString &path)
|
QNetworkReply *RelayClient::authedGet(const QString &path)
|
||||||
{
|
{
|
||||||
QNetworkRequest req(apiUrl(path));
|
QNetworkRequest req(apiUrl(path));
|
||||||
|
|
@ -53,8 +52,6 @@ QNetworkReply *RelayClient::authedDelete(const QString &path)
|
||||||
return m_nam->deleteResource(req);
|
return m_nam->deleteResource(req);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ─── login() ─────────────────────────────────────────────────────────────────
|
|
||||||
|
|
||||||
void RelayClient::login(const QUrl &relayUrl, const QString &username, const QString &password)
|
void RelayClient::login(const QUrl &relayUrl, const QString &username, const QString &password)
|
||||||
{
|
{
|
||||||
m_relayUrl = relayUrl;
|
m_relayUrl = relayUrl;
|
||||||
|
|
@ -62,7 +59,8 @@ void RelayClient::login(const QUrl &relayUrl, const QString &username, const QSt
|
||||||
m_jwt.clear();
|
m_jwt.clear();
|
||||||
|
|
||||||
QNetworkRequest req(apiUrl(QStringLiteral("/api/auth/login")));
|
QNetworkRequest req(apiUrl(QStringLiteral("/api/auth/login")));
|
||||||
req.setHeader(QNetworkRequest::ContentTypeHeader, QByteArrayLiteral("application/json"));
|
req.setHeader(QNetworkRequest::ContentTypeHeader,
|
||||||
|
QByteArrayLiteral("application/json"));
|
||||||
|
|
||||||
QJsonObject body;
|
QJsonObject body;
|
||||||
body[QStringLiteral("username")] = username;
|
body[QStringLiteral("username")] = username;
|
||||||
|
|
@ -87,7 +85,8 @@ void RelayClient::onLoginReply(QNetworkReply *reply, const QString &username,
|
||||||
const QJsonObject json = QJsonDocument::fromJson(reply->readAll()).object();
|
const QJsonObject json = QJsonDocument::fromJson(reply->readAll()).object();
|
||||||
const QString token = json[QStringLiteral("token")].toString();
|
const QString token = json[QStringLiteral("token")].toString();
|
||||||
if (token.isEmpty()) {
|
if (token.isEmpty()) {
|
||||||
const QString err = json[QStringLiteral("error")].toString(QStringLiteral("Unknown error"));
|
const QString err = json[QStringLiteral("error")]
|
||||||
|
.toString(QStringLiteral("Unknown error"));
|
||||||
emit loginFailed(err);
|
emit loginFailed(err);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -98,11 +97,8 @@ void RelayClient::onLoginReply(QNetworkReply *reply, const QString &username,
|
||||||
emit loginSucceeded();
|
emit loginSucceeded();
|
||||||
}
|
}
|
||||||
|
|
||||||
// ─── provisionVPN() ───────────────────────────────────────────────────────────
|
|
||||||
|
|
||||||
void RelayClient::provisionVPN(const QString &deviceName)
|
void RelayClient::provisionVPN(const QString &deviceName)
|
||||||
{
|
{
|
||||||
// Use the machine's host name if the caller didn't provide one.
|
|
||||||
const QString device = deviceName.isEmpty()
|
const QString device = deviceName.isEmpty()
|
||||||
? QSysInfo::machineHostName()
|
? QSysInfo::machineHostName()
|
||||||
: deviceName;
|
: deviceName;
|
||||||
|
|
@ -128,14 +124,14 @@ void RelayClient::onProvisionReply(QNetworkReply *reply)
|
||||||
const QJsonObject json = QJsonDocument::fromJson(reply->readAll()).object();
|
const QJsonObject json = QJsonDocument::fromJson(reply->readAll()).object();
|
||||||
const QString conf = json[QStringLiteral("conf")].toString();
|
const QString conf = json[QStringLiteral("conf")].toString();
|
||||||
if (conf.isEmpty()) {
|
if (conf.isEmpty()) {
|
||||||
const QString err = json[QStringLiteral("error")].toString(QStringLiteral("No conf in response"));
|
const QString err = json[QStringLiteral("error")]
|
||||||
|
.toString(QStringLiteral("No conf in response"));
|
||||||
emit vpnProvisionFailed(err);
|
emit vpnProvisionFailed(err);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_vpnPeerId = json[QStringLiteral("id")].toInt(-1);
|
m_vpnPeerId = json[QStringLiteral("id")].toInt(-1);
|
||||||
|
|
||||||
// Persist peer ID so we can revoke it even after a restart.
|
|
||||||
m_settings->setValue(QStringLiteral("vpn/peer_id"), m_vpnPeerId);
|
m_settings->setValue(QStringLiteral("vpn/peer_id"), m_vpnPeerId);
|
||||||
m_settings->setValue(QStringLiteral("vpn/relay_url"), m_relayUrl.toString());
|
m_settings->setValue(QStringLiteral("vpn/relay_url"), m_relayUrl.toString());
|
||||||
|
|
||||||
|
|
@ -144,20 +140,18 @@ void RelayClient::onProvisionReply(QNetworkReply *reply)
|
||||||
emit vpnProvisioned(vpnConf);
|
emit vpnProvisioned(vpnConf);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ─── revokeVPN() ─────────────────────────────────────────────────────────────
|
|
||||||
|
|
||||||
void RelayClient::revokeVPN()
|
void RelayClient::revokeVPN()
|
||||||
{
|
{
|
||||||
if (m_vpnPeerId < 0) {
|
if (m_vpnPeerId < 0) {
|
||||||
// Try to load from settings (post-restart cleanup).
|
|
||||||
m_vpnPeerId = m_settings->value(QStringLiteral("vpn/peer_id"), -1).toInt();
|
m_vpnPeerId = m_settings->value(QStringLiteral("vpn/peer_id"), -1).toInt();
|
||||||
}
|
}
|
||||||
if (m_vpnPeerId < 0) {
|
if (m_vpnPeerId < 0) {
|
||||||
emit vpnRevoked(); // nothing to do
|
emit vpnRevoked();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const QString path = QStringLiteral("/api/vpn/peer/") + QString::number(m_vpnPeerId);
|
const QString path = QStringLiteral("/api/vpn/peer/")
|
||||||
|
+ QString::number(m_vpnPeerId);
|
||||||
QNetworkReply *reply = authedDelete(path);
|
QNetworkReply *reply = authedDelete(path);
|
||||||
connect(reply, &QNetworkReply::finished, this, [this, reply]() {
|
connect(reply, &QNetworkReply::finished, this, [this, reply]() {
|
||||||
reply->deleteLater();
|
reply->deleteLater();
|
||||||
|
|
@ -175,8 +169,6 @@ void RelayClient::onRevokeReply(QNetworkReply *reply)
|
||||||
emit vpnRevoked();
|
emit vpnRevoked();
|
||||||
}
|
}
|
||||||
|
|
||||||
// ─── fetchHosts() ─────────────────────────────────────────────────────────────
|
|
||||||
|
|
||||||
void RelayClient::fetchHosts()
|
void RelayClient::fetchHosts()
|
||||||
{
|
{
|
||||||
QNetworkReply *reply = authedGet(QStringLiteral("/api/hosts"));
|
QNetworkReply *reply = authedGet(QStringLiteral("/api/hosts"));
|
||||||
|
|
@ -202,7 +194,6 @@ void RelayClient::onHostsReply(QNetworkReply *reply)
|
||||||
h.ip = obj[QStringLiteral("ip")].toString();
|
h.ip = obj[QStringLiteral("ip")].toString();
|
||||||
h.port = obj[QStringLiteral("port")].toInt(47984);
|
h.port = obj[QStringLiteral("port")].toInt(47984);
|
||||||
|
|
||||||
// Parse displays array if present
|
|
||||||
const QJsonArray displaysArr = obj[QStringLiteral("displays")].toArray();
|
const QJsonArray displaysArr = obj[QStringLiteral("displays")].toArray();
|
||||||
for (const QJsonValue &dv : displaysArr) {
|
for (const QJsonValue &dv : displaysArr) {
|
||||||
const QJsonObject displayObj = dv.toObject();
|
const QJsonObject displayObj = dv.toObject();
|
||||||
|
|
@ -216,7 +207,8 @@ void RelayClient::onHostsReply(QNetworkReply *reply)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (h.displays.isEmpty()) {
|
if (h.displays.isEmpty()) {
|
||||||
qWarning() << "Host" << h.name << "has no display data in relay response";
|
qDebug() << "Host" << h.name
|
||||||
|
<< "has no display data in relay response (older Artemis?)";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!h.ip.isEmpty())
|
if (!h.ip.isEmpty())
|
||||||
|
|
@ -225,21 +217,15 @@ void RelayClient::onHostsReply(QNetworkReply *reply)
|
||||||
emit hostsReady(hosts);
|
emit hostsReady(hosts);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ─── Persistence ──────────────────────────────────────────────────────────────
|
|
||||||
|
|
||||||
void RelayClient::persistJwt()
|
void RelayClient::persistJwt()
|
||||||
{
|
{
|
||||||
// NOTE: QSettings on macOS stores in ~/Library/Preferences.
|
|
||||||
// The JWT has a limited lifetime — don't rely on it across sessions;
|
|
||||||
// the app will re-authenticate on next launch using saved credentials.
|
|
||||||
m_settings->setValue(QStringLiteral("auth/relay_url"), m_relayUrl.toString());
|
m_settings->setValue(QStringLiteral("auth/relay_url"), m_relayUrl.toString());
|
||||||
m_settings->setValue(QStringLiteral("auth/username"), m_username);
|
m_settings->setValue(QStringLiteral("auth/username"), m_username);
|
||||||
// We do NOT persist the JWT itself or the password.
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void RelayClient::loadJwt()
|
void RelayClient::loadJwt()
|
||||||
{
|
{
|
||||||
// Intentionally empty — re-auth on each launch for security.
|
// Intentionally empty - we re-auth on each launch for security.
|
||||||
}
|
}
|
||||||
|
|
||||||
void RelayClient::saveServer(const QString &label, const QUrl &url,
|
void RelayClient::saveServer(const QString &label, const QUrl &url,
|
||||||
|
|
@ -248,8 +234,7 @@ void RelayClient::saveServer(const QString &label, const QUrl &url,
|
||||||
m_settings->beginGroup(QStringLiteral("servers/") + label);
|
m_settings->beginGroup(QStringLiteral("servers/") + label);
|
||||||
m_settings->setValue(QStringLiteral("url"), url.toString());
|
m_settings->setValue(QStringLiteral("url"), url.toString());
|
||||||
m_settings->setValue(QStringLiteral("username"), username);
|
m_settings->setValue(QStringLiteral("username"), username);
|
||||||
// password stored in the system keychain would be better; plain QSettings
|
// TODO: replace with QKeychain so the password isn't stored in plaintext.
|
||||||
// as a baseline — replace with QKeychain in production.
|
|
||||||
m_settings->setValue(QStringLiteral("password"), password);
|
m_settings->setValue(QStringLiteral("password"), password);
|
||||||
m_settings->endGroup();
|
m_settings->endGroup();
|
||||||
}
|
}
|
||||||
|
|
@ -260,13 +245,13 @@ QList<RelayClient::SavedServer> RelayClient::savedServers() const
|
||||||
const QStringList groups = m_settings->childGroups();
|
const QStringList groups = m_settings->childGroups();
|
||||||
for (const QString &g : groups) {
|
for (const QString &g : groups) {
|
||||||
if (!g.startsWith(QStringLiteral("servers/"))) continue;
|
if (!g.startsWith(QStringLiteral("servers/"))) continue;
|
||||||
const QString label = g.mid(8);
|
const QString label = g.mid(8); // length of "servers/"
|
||||||
m_settings->beginGroup(g);
|
m_settings->beginGroup(g);
|
||||||
SavedServer s;
|
SavedServer s;
|
||||||
s.label = label;
|
s.label = label;
|
||||||
s.url = QUrl(m_settings->value(QStringLiteral("url")).toString());
|
s.url = QUrl(m_settings->value(QStringLiteral("url")).toString());
|
||||||
s.username = m_settings->value(QStringLiteral("username")).toString();
|
s.username = m_settings->value(QStringLiteral("username")).toString();
|
||||||
s.password = m_settings->value(QStringLiteral("password")).toString());
|
s.password = m_settings->value(QStringLiteral("password")).toString();
|
||||||
m_settings->endGroup();
|
m_settings->endGroup();
|
||||||
list << s;
|
list << s;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue