Compare commits

..

3 Commits

Author SHA1 Message Date
fe8346925a Added proper initial setup 2026-06-22 16:27:40 +03:00
90bfbda9c7 Handle trailing slashes 2026-06-22 15:30:43 +03:00
5ae6fb711c Fix helper naming a bit 2026-06-22 15:30:19 +03:00
4 changed files with 248 additions and 24 deletions

View File

@@ -0,0 +1,11 @@
[
{
// Required
"ID": "MAC ADDRESS",
// Auto managed
"api_key": "api key",
"friendly_id": "917F0B",
"refresh_rate": 600
}
]

View File

@@ -10,28 +10,28 @@ using std::istream;
using std::ostream; using std::ostream;
using std::string; using std::string;
bool read_config_json(json& cfg, const string& filename, ostream* log) bool read_file_json(json& j, const string& filename, ostream* log)
{ {
ifstream cfg_file(filename); ifstream file(filename);
if (!cfg_file.is_open()) if (!file.is_open())
{ {
if (nullptr != log) if (nullptr != log)
{ {
*log << "Could not open config file" << endl; *log << "Could not open file" << endl;
} }
return false; return false;
} }
return read_config_json(cfg, cfg_file, log); return read_stream_json(j, file, log);
} }
bool read_config_json(json& cfg, istream& in, ostream* log) bool read_stream_json(json& j, istream& in, ostream* log)
{ {
// Parse with comments // Parse with comments
try try
{ {
cfg = json::parse(in, nullptr, true, true); j = json::parse(in, nullptr, true, true);
} }
catch (const std::exception &e) catch (const std::exception &e)
{ {

View File

@@ -9,19 +9,19 @@
#include "json.hpp" #include "json.hpp"
// Reads configuration json from supplied filename // Reads json from supplied filename
// cfg - output json // j - output json
// filename - filepath from which to read json // filename - filepath from which to read json
// log - ostream to log human readable errors to - can be nullptr // log - ostream to log human readable errors to - can be nullptr
// Returns - true if read and parse is successful // Returns - true if read and parse is successful
bool read_config_json(nlohmann::json& cfg, const std::string& filename, std::ostream* log); bool read_file_json(nlohmann::json& j, const std::string& filename, std::ostream* log);
// Reads configuration json from supplied istream // Reads configuration json from supplied istream
// cfg - output json // j - output json
// in - istream from which to read json // in - istream from which to read json
// log - ostream to log human readable errors to - can be nullptr // log - ostream to log human readable errors to - can be nullptr
// Returns - true if read and parse is successful // Returns - true if read and parse is successful
bool read_config_json(nlohmann::json& cfg, std::istream& in, std::ostream* log); bool read_stream_json(nlohmann::json& j, std::istream& in, std::ostream* log);
// JSON Extraction Helpers // JSON Extraction Helpers
// out value is modified only if an extraction happened // out value is modified only if an extraction happened

237
main.cpp
View File

@@ -1,6 +1,10 @@
#include <iostream> #include <iostream>
#include <list>
#include <map>
#include <memory> #include <memory>
#include <random>
#include <string> #include <string>
#include <vector>
#include <stdint.h> #include <stdint.h>
@@ -13,6 +17,139 @@ using nlohmann::json;
using namespace std; using namespace std;
struct device
{
string id;
string api_key;
string friendly_id;
int refresh_rate;
// Default constructor
device()
: id(""),
api_key(""),
friendly_id(""),
refresh_rate(600)
{}
};
void to_json(json& j, const device& d)
{
j = json{
{"ID", d.id},
{"api_key", d.api_key},
{"friendly_id", d.friendly_id},
{"refresh_rate", d.refresh_rate}
};
}
bool json_extract(const json& j, const string& key, device& out)
{
bool result = false;
if (!key.empty())
{
if (j.contains(key) && j[key].is_object())
{
result |= json_extract(j[key], "ID", out.id);
result |= json_extract(j[key], "api_key", out.api_key);
result |= json_extract(j[key], "friendly_id", out.friendly_id);
result |= json_extract(j[key], "refresh_rate", out.refresh_rate);
}
}
else
{
result |= json_extract(j, "ID", out.id);
result |= json_extract(j, "api_key", out.api_key);
result |= json_extract(j, "friendly_id", out.friendly_id);
result |= json_extract(j, "refresh_rate", out.refresh_rate);
}
return result;
}
string device_id_to_friendly(string id)
{
size_t pos = id.find(':');
while (pos != string::npos)
{
id.erase(pos, 1);
pos = id.find(':');
}
if (id.size() <= 6)
{
return id;
}
else
{
return id.substr(id.size() - 6);
}
}
class DeviceContainer
{
private:
list<device> m_devices;
map<string, list<device>::iterator> m_by_id;
map<string, list<device>::iterator> m_by_friendly;
public:
DeviceContainer() = default;
void add_device(const device& d)
{
if (d.id.empty())
{
return;
}
auto it = m_devices.insert(m_devices.end(), d);
m_by_id.insert(make_pair(it->id, it));
if (!it->friendly_id.empty())
{
m_by_friendly.insert(make_pair(it->friendly_id, it));
}
}
const device* get_device_by_id(const string& id)
{
if (0 == m_by_id.count(id))
{
return nullptr;
}
else
{
return &(*(*(m_by_id.find(id))).second);
}
}
const device* get_device_by_friendly(const string& friendly)
{
if (0 == m_by_friendly.count(friendly))
{
return nullptr;
}
else
{
return &(*(*(m_by_friendly.find(friendly))).second);
}
}
void set_device_friendly(const string& id, const string& friendly)
{
if (0 == m_by_id.count(id))
{
return;
}
auto it = m_by_id[id];
it->friendly_id = friendly;
m_by_friendly.insert(make_pair(it->friendly_id, it));
}
};
int main(int argc, char **argv) int main(int argc, char **argv)
{ {
string config_filename = "config.json"; string config_filename = "config.json";
@@ -25,13 +162,16 @@ int main(int argc, char **argv)
bool ok; bool ok;
json cfg; json cfg;
json devs;
vector<device> devices;
DeviceContainer container;
if (argc > 2) if (argc > 2)
{ {
config_filename = argv[1]; config_filename = argv[1];
} }
ok = read_config_json(cfg, config_filename, &cout); ok = read_file_json(cfg, config_filename, &cout);
if (!ok) if (!ok)
{ {
return -1; return -1;
@@ -64,21 +204,94 @@ int main(int argc, char **argv)
server = make_shared<httplib::Server>(); server = make_shared<httplib::Server>();
} }
server->Get("/api/setup/", [](const httplib::Request& req, httplib::Response& res) ok = read_file_json(devs, devices_filename, &cout);
if (!ok)
{ {
cout << req.headers.size() << endl; return -1;
for (auto header : req.headers) }
{
cout << header.first << ": " << header.second << endl;
}
res.status = 400;
});
server->Post("/api/log/", [](const httplib::Request& req, httplib::Response& res) if (devs.is_array())
{ {
cout << req.body << endl; for (int i = 0; i < devs.size(); ++i)
{
device d;
json_extract(devs[i], "", d);
devices.push_back(d);
}
}
for (auto device : devices)
{
container.add_device(device);
}
auto setup_handler = [&container](const httplib::Request& req, httplib::Response& res)
{
if (req.has_header("ID"))
{
json response;
string id = req.get_header_value("ID");
const device* dev = container.get_device_by_id(id);
if (nullptr == dev)
{
res.status = 404;
response["status"] = 404;
response["api_key"] = nullptr;
response["friendly_id"] = nullptr;
response["image_url"] = nullptr;
response["filename"] = nullptr;
res.body = response.dump();
}
else
{
res.status = 200;
if (dev->friendly_id.empty())
{
container.set_device_friendly(id, device_id_to_friendly(id));
}
device* dev_mut = const_cast<device*>(dev);
response["status"] = 200;
response["api_key"] = "nullptr";
response["friendly_id"] = dev->friendly_id;
response["image_url"] = "https://trmnl.com/images/setup/setup-logo.bmp";
response["filename"] = "welcome";
res.body = response.dump();
json test = *dev_mut;
cout << test.dump(4) << endl;
}
}
else
{
res.status = 500;
}
};
auto log_handler = [](const httplib::Request& req, httplib::Response& res)
{
try
{
json j = json::parse(req.body);
cout << j.dump(4) << endl;
}
catch (const exception& e)
{
cout << req.body << endl;
}
res.status = 200; res.status = 200;
}); };
server->Get("/api/setup/", setup_handler);
server->Get("/api/setup", setup_handler);
server->Post("/api/log/", log_handler);
server->Post("/api/log", log_handler);
server->listen(host, port); server->listen(host, port);