Files
radiantwave/src/network/system_name.cpp
T
NeilClack e7df02344b 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.
2026-09-29 09:57:52 -05:00

124 lines
3.7 KiB
C++

#include "network/system_name.h"
#include "db/db_keys.h"
#include "validation.h"
namespace radiantwave::systemname {
namespace {
void revertToSynced(db::DatabaseManager& db) {
const std::string synced = db.getKV(db::KV_SYSTEM_NAME_SYNCED);
if (synced.empty()) {
db.deleteKV(db::KV_SYSTEM_NAME);
} else {
db.setKV(db::KV_SYSTEM_NAME, synced);
}
}
} // namespace
Status parseStatus(const std::string& status) {
if (status == "ok") return Status::Ok;
if (status == "conflict") return Status::Conflict;
if (status == "invalid") return Status::Invalid;
return Status::Unchanged;
}
std::string storedName(const db::DatabaseManager& db) {
return db.getKV(db::KV_SYSTEM_NAME);
}
bool pendingSync(const db::DatabaseManager& db) {
return db.getKV(db::KV_SYSTEM_NAME_PENDING) == "1";
}
bool storeLocalName(db::DatabaseManager& db, const std::string& raw,
std::string* error) {
const std::string trimmed = validation::trim(raw);
if (!validation::isValidSystemName(trimmed)) {
if (error) *error = INVALID_MESSAGE;
return false;
}
db.setKV(db::KV_SYSTEM_NAME, trimmed);
db.setKV(db::KV_SYSTEM_NAME_PENDING, "1");
db.setKV(db::KV_SYSTEM_NAME_MESSAGE, "");
return true;
}
Status applyServerStatus(db::DatabaseManager& db, const std::string& serverStatus) {
const Status status = parseStatus(serverStatus);
switch (status) {
case Status::Ok:
db.setKV(db::KV_SYSTEM_NAME_SYNCED, db.getKV(db::KV_SYSTEM_NAME));
db.setKV(db::KV_SYSTEM_NAME_PENDING, "0");
db.setKV(db::KV_SYSTEM_NAME_MESSAGE, "");
return Status::Ok;
case Status::Unchanged:
if (!db.getKV(db::KV_SYSTEM_NAME).empty()) {
db.setKV(db::KV_SYSTEM_NAME_SYNCED, db.getKV(db::KV_SYSTEM_NAME));
}
db.setKV(db::KV_SYSTEM_NAME_PENDING, "0");
db.setKV(db::KV_SYSTEM_NAME_MESSAGE, "");
return Status::Unchanged;
case Status::Conflict:
revertToSynced(db);
db.setKV(db::KV_SYSTEM_NAME_PENDING, "0");
db.setKV(db::KV_SYSTEM_NAME_MESSAGE, CONFLICT_MESSAGE);
return Status::Conflict;
case Status::Invalid:
revertToSynced(db);
db.setKV(db::KV_SYSTEM_NAME_PENDING, "0");
db.setKV(db::KV_SYSTEM_NAME_MESSAGE, INVALID_MESSAGE);
return Status::Invalid;
default:
return Status::Unchanged;
}
}
Status syncWithServer(db::DatabaseManager& db, const PutFn& put) {
const std::string key = db.getKV(db::KV_LICENSE_KEY);
if (key.empty()) {
return Status::NotConfigured;
}
const std::string name = db.getKV(db::KV_SYSTEM_NAME);
if (name.empty()) {
// Nothing to sync; a cleared/empty name is never sent.
db.setKV(db::KV_SYSTEM_NAME_PENDING, "0");
return Status::Unchanged;
}
if (!pendingSync(db)) {
return Status::Unchanged;
}
const api::PutNameResult result = put(key, name);
if (!result.transport_ok) {
return Status::NetworkError; // keep pending for a later retry
}
switch (result.status) {
case 200:
return applyServerStatus(db, "ok");
case 400:
return applyServerStatus(db, "invalid");
case 409:
return applyServerStatus(db, "conflict");
case 404:
return Status::UnknownLicense; // keep pending; license not validated
default:
return Status::NetworkError; // 5xx etc: keep pending
}
}
std::string consumeMessage(db::DatabaseManager& db) {
const std::string message = db.getKV(db::KV_SYSTEM_NAME_MESSAGE);
if (!message.empty()) {
db.setKV(db::KV_SYSTEM_NAME_MESSAGE, "");
}
return message;
}
} // namespace radiantwave::systemname