RelayReg: heartbeat/unregisterHost now send {wg_ip} body required by relay; pass body on DELETE

This commit is contained in:
Zac Gaetano 2026-05-07 00:18:50 -04:00
parent 66b4721b34
commit 9d51a96aa3

View file

@ -1,4 +1,4 @@
// src/wg/relayreg.cpp DragonRelay HTTP registration client for Artemis. // src/wg/relayreg.cpp - DragonRelay HTTP registration client for Artemis.
// //
// Depends on: libcurl, nlohmann/json (header-only, shipped with Sunshine) // Depends on: libcurl, nlohmann/json (header-only, shipped with Sunshine)
// //
@ -17,7 +17,7 @@ namespace wg {
using json = nlohmann::json; using json = nlohmann::json;
// ── libcurl write callback ──────────────────────────────────────────────────── // libcurl write callback
static size_t curlWrite(void *data, size_t size, size_t nmemb, void *userp) { static size_t curlWrite(void *data, size_t size, size_t nmemb, void *userp) {
size_t total = size * nmemb; size_t total = size * nmemb;
@ -25,7 +25,7 @@ static size_t curlWrite(void *data, size_t size, size_t nmemb, void *userp) {
return total; return total;
} }
// ── RelayReg ────────────────────────────────────────────────────────────────── // RelayReg
RelayReg::RelayReg() { RelayReg::RelayReg() {
curl_global_init(CURL_GLOBAL_DEFAULT); curl_global_init(CURL_GLOBAL_DEFAULT);
@ -35,7 +35,7 @@ RelayReg::~RelayReg() {
curl_global_cleanup(); curl_global_cleanup();
} }
// ── HTTP helper ─────────────────────────────────────────────────────────────── // HTTP helper
int RelayReg::request(const std::string &method, int RelayReg::request(const std::string &method,
const std::string &path, const std::string &path,
@ -53,7 +53,6 @@ int RelayReg::request(const std::string &method,
curl_easy_setopt(curl, CURLOPT_TIMEOUT, 15L); curl_easy_setopt(curl, CURLOPT_TIMEOUT, 15L);
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1L); curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1L);
// Headers
struct curl_slist *hdrs = nullptr; struct curl_slist *hdrs = nullptr;
hdrs = curl_slist_append(hdrs, "Content-Type: application/json"); hdrs = curl_slist_append(hdrs, "Content-Type: application/json");
if (!m_jwt.empty()) { if (!m_jwt.empty()) {
@ -62,15 +61,17 @@ int RelayReg::request(const std::string &method,
} }
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, hdrs); curl_easy_setopt(curl, CURLOPT_HTTPHEADER, hdrs);
// Method + body
if (method == "POST" || method == "PUT") { if (method == "POST" || method == "PUT") {
curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, method.c_str()); curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, method.c_str());
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, body.c_str()); curl_easy_setopt(curl, CURLOPT_POSTFIELDS, body.c_str());
curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, (long)body.size()); curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, (long)body.size());
} else if (method == "DELETE") { } else if (method == "DELETE") {
curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "DELETE"); curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "DELETE");
if (!body.empty()) {
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, body.c_str());
curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, (long)body.size());
}
} }
// GET is default
CURLcode res = curl_easy_perform(curl); CURLcode res = curl_easy_perform(curl);
long httpCode = 0; long httpCode = 0;
@ -87,7 +88,7 @@ int RelayReg::request(const std::string &method,
return (int)httpCode; return (int)httpCode;
} }
// ── login ───────────────────────────────────────────────────────────────────── // login
bool RelayReg::login(const std::string &username, const std::string &password, bool RelayReg::login(const std::string &username, const std::string &password,
std::string &errOut) { std::string &errOut) {
@ -113,7 +114,7 @@ void RelayReg::logout() {
m_jwt.clear(); m_jwt.clear();
} }
// ── provisionVPN ────────────────────────────────────────────────────────────── // provisionVPN
bool RelayReg::provisionVPN(const std::string &deviceName, bool RelayReg::provisionVPN(const std::string &deviceName,
VPNConf &out, std::string &errOut) { VPNConf &out, std::string &errOut) {
@ -126,7 +127,12 @@ bool RelayReg::provisionVPN(const std::string &deviceName,
} }
try { try {
auto j = json::parse(resp); auto j = json::parse(resp);
out.id = j.at("id").get<std::string>(); // The relay returns id as a number; render it as a string for our struct.
if (j.contains("id")) {
const auto &v = j["id"];
if (v.is_number_integer()) out.id = std::to_string(v.get<int64_t>());
else if (v.is_string()) out.id = v.get<std::string>();
}
out.name = j.at("name").get<std::string>(); out.name = j.at("name").get<std::string>();
out.publicKey = j.at("public_key").get<std::string>(); out.publicKey = j.at("public_key").get<std::string>();
out.allowedIP = j.at("allowed_ip").get<std::string>(); out.allowedIP = j.at("allowed_ip").get<std::string>();
@ -149,7 +155,7 @@ bool RelayReg::deleteVPNPeer(const std::string &peerId, std::string &errOut) {
return true; return true;
} }
// ── registerHost ────────────────────────────────────────────────────────────── // registerHost
bool RelayReg::registerHost(const std::string &name, const std::string &wgIP, bool RelayReg::registerHost(const std::string &name, const std::string &wgIP,
int port, const std::vector<DisplayInfo> &displays, int port, const std::vector<DisplayInfo> &displays,
@ -183,19 +189,21 @@ bool RelayReg::registerHost(const std::string &name, const std::string &wgIP,
return true; return true;
} }
bool RelayReg::heartbeat(std::string &errOut) { bool RelayReg::heartbeat(const std::string &wgIP, std::string &errOut) {
json body = { {"wg_ip", wgIP} };
std::string resp; std::string resp;
int code = request("PUT", "/api/host/heartbeat", "{}", resp); int code = request("PUT", "/api/host/heartbeat", body.dump(), resp);
if (code != 200 && code != 204) { if (code != 200 && code != 204) {
errOut = "heartbeat failed (HTTP " + std::to_string(code) + ")"; errOut = "heartbeat failed (HTTP " + std::to_string(code) + "): " + resp;
return false; return false;
} }
return true; return true;
} }
bool RelayReg::unregisterHost(std::string &errOut) { bool RelayReg::unregisterHost(const std::string &wgIP, std::string &errOut) {
json body = { {"wg_ip", wgIP} };
std::string resp; std::string resp;
int code = request("DELETE", "/api/host/unregister", "", resp); int code = request("DELETE", "/api/host/unregister", body.dump(), resp);
if (code != 200 && code != 204) { if (code != 200 && code != 204) {
errOut = "unregisterHost failed (HTTP " + std::to_string(code) + ")"; errOut = "unregisterHost failed (HTTP " + std::to_string(code) + ")";
return false; return false;