Kiosk: System Name storage, validation, sync, and sent on license validation

Adds local System Name persistence (trimmed, 1-40) in system_kv, an
injectable sync module with conflict/invalid revert and a pending-sync
flag, a background SystemNameSync worker (retries on boot and on a timer),
and api_client support for PUT /licenses/{key}/system-name plus the
additive systemName field/systemNameStatus on license validation. The
license service sends the stored name and applies the status. Covers
validation, pending retry, and conflict revert with a faked HTTP layer.
This commit is contained in:
NeilClack
2026-09-29 09:57:52 -05:00
parent 003a90d810
commit e7df02344b
16 changed files with 812 additions and 13 deletions
+25 -2
View File
@@ -26,9 +26,10 @@ struct HttpResponse {
bool transportOk = false; // true when the request completed without a transport error
};
// Performs a GET/POST against `path` (relative to API_BASE_URL).
// Performs a GET/POST/PUT against `path` (relative to API_BASE_URL).
HttpResponse httpGet(const std::string& path);
HttpResponse httpPost(const std::string& path, const std::string& jsonBody);
HttpResponse httpPut(const std::string& path, const std::string& jsonBody);
// ─── GET /frequencies ──────────────────────────────────────────────────
@@ -79,6 +80,10 @@ bool fetchColorPalettes(std::vector<ColorPalette>& out);
struct LicenseInfo {
std::string key;
std::string email_address;
// Advisory System Name status returned by the portal: one of "ok",
// "unchanged", "conflict", "invalid", or empty when the portal did not
// report one (e.g. older portal).
std::string system_name_status;
};
// Parse the 200 JSON payload returned by the license endpoint.
@@ -95,7 +100,25 @@ struct LicenseResult {
// Validate a license key / email pair against the portal. Never throws; failures
// (network, HTTP error status) are reported through the returned LicenseResult.
// A 2xx response means the license is valid and the subscription is active.
LicenseResult validateLicense(const std::string& key, const std::string& email);
// When `systemName` is non-empty it is sent as the additive `systemName` field
// (the Kiosk is the only writer); the response's `systemNameStatus` is advisory.
LicenseResult validateLicense(const std::string& key, const std::string& email,
const std::string& systemName = "");
// ─── PUT /licenses/{key}/system-name ───────────────────────────────────
// Result of a System Name PUT. `status` is the HTTP status (0 on transport
// failure); `transport_ok` is false when the request never completed.
struct PutNameResult {
int status = 0;
bool transport_ok = false;
bool ok() const { return status >= 200 && status < 300; }
};
// Set the System Name for a License Key. 200 on success, 400 invalid_name,
// 404 unknown_license, 409 name_taken. Never throws.
PutNameResult putSystemName(const std::string& key, const std::string& name);
// ─── POST /automations/create/account ──────────────────────────────────