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:
@@ -0,0 +1,123 @@
|
||||
#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
|
||||
Reference in New Issue
Block a user