From 60d568296a78701eeaa91e55c60f2aa762ab6909 Mon Sep 17 00:00:00 2001 From: Lorow Date: Tue, 4 Nov 2025 21:32:34 +0100 Subject: [PATCH] Refactor the HTTP Rest API commands, fix streaming missing --- .../CommandManager/CommandManager.cpp | 2 +- components/RestAPI/RestAPI/RestAPI.cpp | 181 +++++++----------- components/RestAPI/RestAPI/RestAPI.hpp | 33 ++-- main/openiris_main.cpp | 7 +- 4 files changed, 84 insertions(+), 139 deletions(-) diff --git a/components/CommandManager/CommandManager/CommandManager.cpp b/components/CommandManager/CommandManager/CommandManager.cpp index b666029..5314768 100644 --- a/components/CommandManager/CommandManager/CommandManager.cpp +++ b/components/CommandManager/CommandManager/CommandManager.cpp @@ -26,7 +26,7 @@ std::unordered_map commandTypeMap = { {"get_led_duty_cycle", CommandType::GET_LED_DUTY_CYCLE}, {"get_serial", CommandType::GET_SERIAL}, {"get_led_current", CommandType::GET_LED_CURRENT}, - {"get_who_am_i", CommandType::GET_WHO_AM_I}, + {"get_who_am_i", CommandType::GET_WHO_AM_I}, }; std::function CommandManager::createCommand(const CommandType type, const nlohmann::json &json) const diff --git a/components/RestAPI/RestAPI/RestAPI.cpp b/components/RestAPI/RestAPI/RestAPI.cpp index de002df..e1655f2 100644 --- a/components/RestAPI/RestAPI/RestAPI.cpp +++ b/components/RestAPI/RestAPI/RestAPI.cpp @@ -2,7 +2,10 @@ #include +#define PATCH_METHOD "PATCH" #define POST_METHOD "POST" +#define GET_METHOD "GET" +#define DELETE_METHOD "DELETE" bool getIsSuccess(const nlohmann::json &response) { @@ -19,27 +22,55 @@ bool getIsSuccess(const nlohmann::json &response) RestAPI::RestAPI(std::string url, std::shared_ptr commandManager) : command_manager(commandManager) { + // until we stumble on a simpler way to handle the commands over the rest api + // the formula will be like this: + // each command gets its own endpoint + // each endpoint must include the action it performs in its path + // for example + // /get/ for getters + // /set/ for posts + // /delete/ for deletes + // /update/ for updates + // additional actions on the resource should be appended after the resource name + // like for example /api/set/config/save/ + // + // one endpoint must not contain more than one action + this->url = std::move(url); - // updates - routes.emplace("/api/update/wifi/", &RestAPI::handle_update_wifi); - routes.emplace("/api/update/device/", &RestAPI::handle_update_device); - routes.emplace("/api/update/camera/", &RestAPI::handle_update_camera); + // updates via PATCH + routes.emplace("/api/update/wifi/", RequestBaseData(PATCH_METHOD, CommandType::UPDATE_WIFI, 200, 400)); + routes.emplace("/api/update/device/mode/", RequestBaseData(PATCH_METHOD, CommandType::SWITCH_MODE, 200, 400)); + routes.emplace("/api/update/camera/", RequestBaseData(PATCH_METHOD, CommandType::UPDATE_CAMERA, 200, 400)); + routes.emplace("/api/update/ota/credentials", RequestBaseData(PATCH_METHOD, CommandType::UPDATE_OTA_CREDENTIALS, 200, 400)); + routes.emplace("/api/update/ap/", RequestBaseData(PATCH_METHOD, CommandType::UPDATE_AP_WIFI, 200, 400)); + routes.emplace("/api/update/led_duty_cycle/", RequestBaseData(PATCH_METHOD, CommandType::SET_LED_DUTY_CYCLE, 200, 400)); - // post will reset it - // resets - routes.emplace("/api/reset/config/", &RestAPI::handle_reset_config); - // gets - routes.emplace("/api/get/config/", &RestAPI::handle_get_config); + // POST will set the data + routes.emplace("/api/set/pause/", RequestBaseData(POST_METHOD, CommandType::PAUSE, 200, 400)); + routes.emplace("/api/set/wifi/", RequestBaseData(POST_METHOD, CommandType::SET_WIFI, 200, 400)); + routes.emplace("/api/set/mdns/", RequestBaseData(POST_METHOD, CommandType::SET_MDNS, 200, 400)); + routes.emplace("/api/set/config/save/", RequestBaseData(POST_METHOD, CommandType::SAVE_CONFIG, 200, 400)); + routes.emplace("/api/set/wifi/connect/", RequestBaseData(POST_METHOD, CommandType::CONNECT_WIFI, 200, 400)); - // reboots - routes.emplace("/api/reboot/device/", &RestAPI::handle_reboot); - routes.emplace("/api/reboot/camera/", &RestAPI::handle_camera_reboot); + // resets via POST as well + routes.emplace("/api/reset/config/", RequestBaseData(POST_METHOD, CommandType::RESET_CONFIG, 200, 400)); - // heartbeat - routes.emplace("/api/ping/", &RestAPI::pong); + // gets via GET + routes.emplace("/api/get/config/", RequestBaseData(GET_METHOD, CommandType::GET_CONFIG, 200, 400)); + routes.emplace("/api/get/mdns/", RequestBaseData(GET_METHOD, CommandType::GET_MDNS_NAME, 200, 400)); + routes.emplace("/api/get/led_duty_cycle/", RequestBaseData(GET_METHOD, CommandType::GET_LED_DUTY_CYCLE, 200, 400)); + routes.emplace("/api/get/serial_number/", RequestBaseData(GET_METHOD, CommandType::GET_SERIAL, 200, 400)); + routes.emplace("/api/get/led_current/", RequestBaseData(GET_METHOD, CommandType::GET_LED_CURRENT, 200, 400)); + routes.emplace("/api/get/who_am_i/", RequestBaseData(GET_METHOD, CommandType::GET_WHO_AM_I, 200, 400)); - // special - routes.emplace("/api/save/", &RestAPI::handle_save); + // deletes via DELETE + routes.emplace("/api/delete/wifi", RequestBaseData(DELETE_METHOD, CommandType::DELETE_NETWORK, 200, 400)); + + // reboots via POST + routes.emplace("/api/reboot/device/", RequestBaseData(GET_METHOD, CommandType::RESTART_DEVICE, 200, 500)); + + // heartbeat via GET + routes.emplace("/api/ping/", RequestBaseData(GET_METHOD, CommandType::PING, 200, 400)); } void RestAPI::begin() @@ -58,19 +89,24 @@ void RestAPI::handle_request(struct mg_connection *connection, int event, void * auto const *message = static_cast(event_data); auto const uri = std::string(message->uri.buf, message->uri.len); - if (auto const handler = this->routes[uri]) - { - auto *context = new RequestContext{ - .connection = connection, - .method = std::string(message->method.buf, message->method.len), - .body = std::string(message->body.buf, message->body.len), - }; - (*this.*handler)(context); - } - else + if (this->routes.find(uri) == this->routes.end()) { mg_http_reply(connection, 404, "", "Wrong URL"); + return; } + + auto const base_request_params = this->routes.at(uri); + + auto *context = new RequestContext{ + .connection = connection, + .method = std::string(message->method.buf, message->method.len), + .body = std::string(message->body.buf, message->body.len), + }; + this->handle_endpoint_command(context, + base_request_params.allowed_method, + base_request_params.command_type, + base_request_params.success_code, + base_request_params.error_code); } } @@ -95,97 +131,16 @@ void HandleRestAPIPollTask(void *pvParameter) } } -// COMMANDS -// updates -void RestAPI::handle_update_wifi(RequestContext *context) +void RestAPI::handle_endpoint_command(RequestContext *context, std::string allowed_method, CommandType command_type, int success_code, int error_code) { - if (context->method != POST_METHOD) + + if (context->method != allowed_method) { mg_http_reply(context->connection, 401, JSON_RESPONSE, "{%m:%m}", MG_ESC("error"), "Method not allowed"); return; } - const nlohmann::json result = command_manager->executeFromType(CommandType::UPDATE_WIFI, context->body); - const auto code = getIsSuccess(result) ? 200 : 400; + const nlohmann::json result = command_manager->executeFromType(command_type, context->body); + const auto code = getIsSuccess(result) ? success_code : error_code; mg_http_reply(context->connection, code, JSON_RESPONSE, result.dump().c_str()); -} - -void RestAPI::handle_update_device(RequestContext *context) -{ - if (context->method != POST_METHOD) - { - mg_http_reply(context->connection, 401, JSON_RESPONSE, "{%m:%m}", MG_ESC("error"), "Method not allowed"); - return; - } - - const nlohmann::json result = command_manager->executeFromType(CommandType::UPDATE_OTA_CREDENTIALS, context->body); - const auto code = getIsSuccess(result) ? 200 : 500; - mg_http_reply(context->connection, code, JSON_RESPONSE, result.dump().c_str()); -} - -void RestAPI::handle_update_camera(RequestContext *context) -{ - if (context->method != POST_METHOD) - { - mg_http_reply(context->connection, 401, JSON_RESPONSE, "{%m:%m}", MG_ESC("error"), "Method not allowed"); - return; - } - - const nlohmann::json result = command_manager->executeFromType(CommandType::UPDATE_CAMERA, context->body); - const auto code = getIsSuccess(result) ? 200 : 500; - mg_http_reply(context->connection, code, JSON_RESPONSE, result.dump().c_str()); -} - -// gets - -void RestAPI::handle_get_config(RequestContext *context) -{ - auto const result = this->command_manager->executeFromType(CommandType::GET_CONFIG, ""); - const nlohmann::json jsonResult = result; - mg_http_reply(context->connection, 200, JSON_RESPONSE, "{%m:%m}", MG_ESC("result"), jsonResult.dump().c_str()); -} - -// resets - -void RestAPI::handle_reset_config(RequestContext *context) -{ - if (context->method != POST_METHOD) - { - mg_http_reply(context->connection, 401, JSON_RESPONSE, "{%m:%m}", MG_ESC("error"), "Method not allowed"); - return; - } - - const nlohmann::json result = this->command_manager->executeFromType(CommandType::RESET_CONFIG, "{\"section\": \"all\"}"); - const auto code = getIsSuccess(result) ? 200 : 500; - mg_http_reply(context->connection, code, JSON_RESPONSE, "{%m:%m}", MG_ESC("result"), result.dump().c_str()); -} - -// reboots -void RestAPI::handle_reboot(RequestContext *context) -{ - const auto result = this->command_manager->executeFromType(CommandType::RESTART_DEVICE, ""); - mg_http_reply(context->connection, 200, JSON_RESPONSE, "{%m:%m}", MG_ESC("result"), "Ok"); -} - -void RestAPI::handle_camera_reboot(RequestContext *context) -{ - mg_http_reply(context->connection, 200, JSON_RESPONSE, "{%m:%m}", MG_ESC("result"), "Ok"); -} - -// heartbeat - -void RestAPI::pong(RequestContext *context) -{ - const nlohmann::json result = this->command_manager->executeFromType(CommandType::PING, ""); - const auto code = getIsSuccess(result) ? 200 : 500; - mg_http_reply(context->connection, code, JSON_RESPONSE, result.dump().c_str()); -} - -// special - -void RestAPI::handle_save(RequestContext *context) -{ - const nlohmann::json result = this->command_manager->executeFromType(CommandType::SAVE_CONFIG, ""); - const auto code = getIsSuccess(result) ? 200 : 500; - mg_http_reply(context->connection, code, JSON_RESPONSE, result.dump().c_str()); -} +} \ No newline at end of file diff --git a/components/RestAPI/RestAPI/RestAPI.hpp b/components/RestAPI/RestAPI/RestAPI.hpp index 49aab3e..0768b83 100644 --- a/components/RestAPI/RestAPI/RestAPI.hpp +++ b/components/RestAPI/RestAPI/RestAPI.hpp @@ -18,10 +18,18 @@ struct RequestContext std::string body; }; +struct RequestBaseData +{ + std::string allowed_method; + CommandType command_type; + int success_code; + int error_code; + RequestBaseData(std::string allowed_method, CommandType command_type, int success_code, int error_code) : allowed_method(allowed_method), command_type(command_type), success_code(success_code), error_code(error_code) {}; +}; + class RestAPI { - using route_handler = void (RestAPI::*)(RequestContext *); - typedef std::unordered_map route_map; + typedef std::unordered_map route_map; std::string url; route_map routes; @@ -29,26 +37,7 @@ class RestAPI std::shared_ptr command_manager; private: - // updates - void handle_update_wifi(RequestContext *context); - void handle_update_device(RequestContext *context); - void handle_update_camera(RequestContext *context); - - // gets - void handle_get_config(RequestContext *context); - - // resets - void handle_reset_config(RequestContext *context); - - // reboots - void handle_reboot(RequestContext *context); - void handle_camera_reboot(RequestContext *context); - - // heartbeat - void pong(RequestContext *context); - - // special - void handle_save(RequestContext *context); + void handle_endpoint_command(RequestContext *context, std::string allowed_method, CommandType command_type, int success_code, int error_code); public: // this will also need command manager diff --git a/main/openiris_main.cpp b/main/openiris_main.cpp index de6d1b7..5324ba1 100644 --- a/main/openiris_main.cpp +++ b/main/openiris_main.cpp @@ -36,7 +36,7 @@ #define BLINK_GPIO (gpio_num_t) CONFIG_LED_DEBUG_GPIO #else // Use an invalid / unused GPIO when debug LED disabled to avoid accidental toggles -#define BLINK_GPIO (gpio_num_t) - 1 +#define BLINK_GPIO (gpio_num_t)(-1) #endif #define CONFIG_LED_C_PIN_GPIO (gpio_num_t) CONFIG_LED_EXTERNAL_GPIO @@ -61,7 +61,7 @@ MDNSManager mdnsManager(deviceConfig, eventQueue); std::shared_ptr cameraHandler = std::make_shared(deviceConfig, eventQueue); StreamServer streamServer(80, stateManager); -auto *restAPI = new RestAPI("http://0.0.0.0:81", commandManager); +std::shared_ptr restAPI = std::make_shared("http://0.0.0.0:81", commandManager); #ifdef CONFIG_GENERAL_INCLUDE_UVC_MODE UVCStreamManager uvcStream; @@ -215,6 +215,7 @@ void startWiFiMode() mdnsManager.start(); restAPI->begin(); StreamingMode mode = deviceConfig->getDeviceMode(); + // don't enable in SETUP mode if (mode == StreamingMode::WIFI) { streamServer.startStreamServer(); @@ -223,7 +224,7 @@ void startWiFiMode() HandleRestAPIPollTask, "HandleRestAPIPollTask", 1024 * 2, - restAPI, + restAPI.get(), 1, // it's the rest API, we only serve commands over it so we don't really need a higher priority nullptr); #else