#include #include #include #include #include #include #include #include #include "httplib.h" #include "json.hpp" #include "helpers.h" #include "TRMNL.h" using nlohmann::json; using namespace std; void reload_container(TRMNLContainer& container, const string& filename) { json j; bool ok = read_file_json(j, filename, &cout); if (!ok) { return; } container.clear(); container = j; } int main(int argc, char **argv) { string config_filename = "config.json"; string devices_filename = "devices.json"; string host = ""; string folder_images = "images"; string cert_file = ""; string key_file = ""; uint16_t port = 0; shared_ptr server = nullptr; bool ok; json cfg; json devs; TRMNLContainer container; if (argc > 2) { config_filename = argv[1]; } ok = read_file_json(cfg, config_filename, &cout); if (!ok) { return -1; } json_extract(cfg, "devices_filename", devices_filename); json_extract(cfg, "host", host); json_extract(cfg, "port", port); json_extract(cfg, "folder_images", folder_images); // json_extract(cfg, "cert_file", cert_file); // json_extract(cfg, "key_file", cert_file); if (host.empty()) { cout << "host not provided" << endl; return -1; } if (0 == port) { cout << "port number not provided" << endl; return -1; } if (folder_images.empty()) { cout << "folder for images is empty" << endl; return -1; } if (!cert_file.empty() && !key_file.empty()) { // TODO: Implement SSL Server Properly } else { server = make_shared(); } ok = read_file_json(devs, devices_filename, &cout); if (!ok) { return -1; } container.clear(); container = devs; auto setup_handler = [&container, &devices_filename](const httplib::Request& req, httplib::Response& res) { json response; if (!req.has_header("ID")) { // Bad Request - No ID header res.status = 400; response["status"] = 400; response["error"] = "No ID header"; res.set_header("Content-Type", "application/json"); res.body = response.dump(); return; } // Refresh data from file // Someone might have put a new device in reload_container(container, devices_filename); string id = req.get_header_value("ID"); TRMNL* trmnl = container.get_device_by_id(id); if (nullptr == trmnl) { res.status = 404; response["status"] = 404; response["api_key"] = nullptr; response["friendly_id"] = nullptr; response["image_url"] = nullptr; response["filename"] = nullptr; res.set_header("Content-Type", "application/json"); res.body = response.dump(); return; } bool should_dump = false; res.status = 200; if (trmnl->friendly_id().empty()) { should_dump = true; trmnl->friendly_id(TRMNL::friendly_from_id(id)); } if (trmnl->api_key().empty()) { should_dump = true; // TODO: Randomly generate key here trmnl->api_key("nullptr"); } response["status"] = 200; response["api_key"] = trmnl->api_key(); response["friendly_id"] = trmnl->friendly_id(); // TODO: Check for image in folder response["image_url"] = "https://trmnl.com/images/setup/setup-logo.bmp"; response["filename"] = "welcome"; res.set_header("Content-Type", "application/json"); res.body = response.dump(); if (should_dump) { write_file_json(container, devices_filename, &cout); } }; auto display_handler = [&container, &devices_filename](const httplib::Request& req, httplib::Response& res) { json response; if (!req.has_header("ID") || !req.has_header("Access-Token")) { // Bad Request res.status = 400; response["status"] = 400; response["error"] = "No ID and Access-Token headers set"; res.body = response.dump(); res.set_header("Content-Type", "application/json"); return; } // Refresh data from file // Someone might have put a new device in reload_container(container, devices_filename); string id = req.get_header_value("ID"); string api_key = req.get_header_value("Access-Token"); TRMNL* trmnl = container.get_device_by_id(id); if (nullptr == trmnl) { // Not Found res.status = 404; response["status"] = 404; response["error"] = "Device not found"; res.set_header("Content-Type", "application/json"); res.body = response.dump(); return; } if (trmnl->api_key() != api_key) { // Forbidden res.status = 403; response["status"] = 403; response["error"] = "Wrong credentials for this device"; res.set_header("Content-Type", "application/json"); res.body = response.dump(); return; } // TODO: Check for image in folder // string image; // The ID and api_key match here res.status = 200; // From docs: will be 202 if no user_id is attached to device response["status"] = 0; response["image_url"] = "https://trmnl.com/images/setup/setup-logo.bmp"; response["filename"] = "2024-09-20T00:00:00"; response["update_firmware"] = false; response["firmware_url"] = nullptr; response["refresh_rate"] = to_string(trmnl->refresh_rate()); response["reset_firmware"] = false; res.set_header("Content-Type", "application/json"); res.body = response.dump(); }; 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; }; server->Get("/api/setup/*", setup_handler); server->Get("/api/display/*", display_handler); server->Post("/api/log/*", log_handler); ok = server->set_mount_point("/images", folder_images); if (!ok) { cout << "Could not mount images folder" << endl; return -1; } cout << "Server listening on " << host << ":" << port << endl; server->listen(host, port); return 0; }