From 6ff7c8a6cb48512939eb63e9893e0c21a911d31d Mon Sep 17 00:00:00 2001 From: nedko Date: Mon, 22 Jun 2026 13:20:10 +0300 Subject: [PATCH] First server executable --- .gitignore | 2 + Makefile | 7 +++ README.md | 12 +++++ config_example.json | 10 +++++ devices_example.json | 0 helpers.cpp | 101 +++++++++++++++++++++++++++++++++++++++++++ helpers.h | 34 +++++++++++++++ main.cpp | 79 +++++++++++++++++++++++++++++++++ 8 files changed, 245 insertions(+) create mode 100644 .gitignore create mode 100644 Makefile create mode 100644 config_example.json create mode 100644 devices_example.json create mode 100644 helpers.cpp create mode 100644 helpers.h create mode 100644 main.cpp diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..16caf03 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +server +*.json diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..e4f242f --- /dev/null +++ b/Makefile @@ -0,0 +1,7 @@ +srcs += main.cpp +srcs += helpers.cpp + +all: + g++ ${srcs} -o server + +.PHONY: all diff --git a/README.md b/README.md index 1a37471..cbd2a2c 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,15 @@ +# Required packages for building (Ubuntu 24.04) +`build-essential` + +# Usage +### Configuration +Use file named `config.json` in working directory or supply your own via first argument. +For configuration example see `config_example.json`. + +### Devices +Use file named `devices.json` in working directory or supply your own via configuration file. +**Note: This file will be managed by the server when devices successfully register.** + # Libraries ### JSON diff --git a/config_example.json b/config_example.json new file mode 100644 index 0000000..81e9faf --- /dev/null +++ b/config_example.json @@ -0,0 +1,10 @@ +{ + // Required + "host": "localhost", + "port": 0, + + // Optional + "devices_filename": "devices.json", + "cert_file": "path/to/file", + "key_file": "path/to/file" +} diff --git a/devices_example.json b/devices_example.json new file mode 100644 index 0000000..e69de29 diff --git a/helpers.cpp b/helpers.cpp new file mode 100644 index 0000000..fe84b72 --- /dev/null +++ b/helpers.cpp @@ -0,0 +1,101 @@ +#include "helpers.h" + +#include + +using nlohmann::json; + +using std::endl; +using std::ifstream; +using std::istream; +using std::ostream; +using std::string; + +bool read_config_json(json& cfg, const string& filename, ostream* log) +{ + ifstream cfg_file(filename); + + if (!cfg_file.is_open()) + { + if (nullptr != log) + { + *log << "Could not open config file" << endl; + } + return false; + } + + return read_config_json(cfg, cfg_file, log); +} + +bool read_config_json(json& cfg, istream& in, ostream* log) +{ + // Parse with comments + try + { + cfg = json::parse(in, nullptr, true, true); + } + catch (const std::exception &e) + { + if (nullptr != log) + { + *log << e.what() << endl; + } + return false; + } + + return true; +} + +bool json_extract(const json& j, const string& key, string& out) +{ + bool result = false; + + if (j.contains(key) && j[key].is_string()) + { + out = j[key]; + result = true; + } + + return result; +} + +bool json_extract(const json& j, const string& key, int& out) +{ + bool result = false; + + if (j.contains(key) && j[key].is_number_integer()) + { + out = j[key]; + result = true; + } + + return result; +} + +bool json_extract(const json& j, const string& key, bool& out) +{ + bool result = false; + + if (j.contains(key) && j[key].is_boolean()) + { + out = j[key]; + result = true; + } + + return result; +} + +bool json_extract(const json& j, const string& key, uint16_t& out) +{ + bool result = false; + + if (j.contains(key) && j[key].is_number_integer()) + { + if ((j[key] >= 0) && (j[key] <= UINT16_MAX)) + { + out = j[key]; + result = true; + } + } + + return result; +} diff --git a/helpers.h b/helpers.h new file mode 100644 index 0000000..3255214 --- /dev/null +++ b/helpers.h @@ -0,0 +1,34 @@ +#ifndef HELPERS_H_ +#define HELPERS_H_ + +#include +#include + +#include + +#include "json.hpp" + + +// Reads configuration json from supplied filename +// cfg - output json +// filename - filepath from which to read json +// log - ostream to log human readable errors to - can be nullptr +// Returns - true if read and parse is successful +bool read_config_json(nlohmann::json& cfg, const std::string& filename, std::ostream* log); + +// Reads configuration json from supplied istream +// cfg - output json +// in - istream from which to read json +// log - ostream to log human readable errors to - can be nullptr +// Returns - true if read and parse is successful +bool read_config_json(nlohmann::json& cfg, std::istream& in, std::ostream* log); + +// JSON Extraction Helpers +// out value is modified only if an extraction happened +// Returns - whether an extraction happened +bool json_extract(const nlohmann::json& j, const std::string& key, std::string& out); +bool json_extract(const nlohmann::json& j, const std::string& key, int& out); +bool json_extract(const nlohmann::json& j, const std::string& key, bool& out); +bool json_extract(const nlohmann::json& j, const std::string& key, uint16_t& out); + +#endif // HELPERS_H_ diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..463b270 --- /dev/null +++ b/main.cpp @@ -0,0 +1,79 @@ +#include +#include +#include + +#include + +#include "httplib.h" +#include "json.hpp" + +#include "helpers.h" + +using nlohmann::json; + +using namespace std; + +int main(int argc, char **argv) +{ + string config_filename = "config.json"; + string devices_filename = "devices.json"; + string host = ""; + string cert_file = ""; + string key_file = ""; + uint16_t port = 0; + shared_ptr server = nullptr; + + bool ok; + json cfg; + + if (argc > 2) + { + config_filename = argv[1]; + } + + ok = read_config_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, "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 (!cert_file.empty() && !key_file.empty()) + { + // TODO: Implement SSL Server Properly + } + else + { + server = make_shared(); + } + + server->listen(host, port); + server->Get("/api/setup", [](const httplib::Request& req, httplib::Response& res) + { + cout << req.headers.size() << endl; + for (auto header : req.headers) + { + cout << header.first << ": " << header.second << endl; + } + res.status = 400; + }); + + return 0; +}