From 29ba4625e813a260604bbe3410585aa1fee5efdf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=BCrz?= Date: Wed, 12 Nov 2025 10:51:19 +0100 Subject: [PATCH] Add resource enable/disable handling --- libnymea-core/debugserverhandler.cpp | 9 +++------ libnymea-core/debugserverhandler.h | 2 -- libnymea-core/servers/webserver.cpp | 9 ++++++++- libnymea/integrations/thing.cpp | 9 +++++++++ libnymea/integrations/thing.h | 1 + libnymea/webserver/httpreply.cpp | 17 +++++++++++++++++ libnymea/webserver/httpreply.h | 5 +++++ libnymea/webserver/webserverresource.cpp | 18 +++++++++++++++++- libnymea/webserver/webserverresource.h | 7 ++++++- 9 files changed, 66 insertions(+), 11 deletions(-) diff --git a/libnymea-core/debugserverhandler.cpp b/libnymea-core/debugserverhandler.cpp index baa5bebf..105413a6 100644 --- a/libnymea-core/debugserverhandler.cpp +++ b/libnymea-core/debugserverhandler.cpp @@ -51,14 +51,9 @@ DebugServerHandler::DebugServerHandler(QObject *parent) : onDebugServerEnabledChanged(NymeaCore::instance()->configuration()->debugServerEnabled()); } -bool DebugServerHandler::authenticationRequired() const -{ - return false; -} - HttpReply *DebugServerHandler::processRequest(const HttpRequest &request) { - if (NymeaCore::instance()->configuration()->debugServerEnabled()) { + if (m_enabled) { // Verify methods if (request.method() != HttpRequest::Get && request.method() != HttpRequest::Options) { @@ -658,6 +653,8 @@ void DebugServerHandler::onDebugServerEnabledChanged(bool enabled) m_websocketServer = nullptr; } } + + setEnabled(enabled); } void DebugServerHandler::onWebsocketClientConnected() diff --git a/libnymea-core/debugserverhandler.h b/libnymea-core/debugserverhandler.h index c3928110..dd9777f6 100644 --- a/libnymea-core/debugserverhandler.h +++ b/libnymea-core/debugserverhandler.h @@ -43,8 +43,6 @@ class DebugServerHandler : public WebServerResource public: explicit DebugServerHandler(QObject *parent = nullptr); - bool authenticationRequired() const override; - HttpReply *processRequest(const HttpRequest &request) override; private: diff --git a/libnymea-core/servers/webserver.cpp b/libnymea-core/servers/webserver.cpp index e983971f..e01910fd 100644 --- a/libnymea-core/servers/webserver.cpp +++ b/libnymea-core/servers/webserver.cpp @@ -379,7 +379,14 @@ void WebServer::readClient() // Verify if we habe a resource for this request foreach (WebServerResource *resource, m_resources) { if (request.url().path().startsWith(resource->basePath())) { - qCDebug(dcWebServer()) << "Let the resource handle this request"; + if (!resource->enabled()) { + qCDebug(dcWebServer()) << "The corresponding resource exists but is not enabled. Respond with 404 Not Found."; + HttpReply *reply = HttpReply::createErrorReply(HttpReply::NotFound); + reply->setClientId(clientId); + sendHttpReply(reply); + reply->deleteLater(); + } + qCDebug(dcDebugServer()) << "Request:" << request.url().toString(); HttpReply *reply = resource->processRequest(request); reply->setClientId(clientId); diff --git a/libnymea/integrations/thing.cpp b/libnymea/integrations/thing.cpp index c70f8a69..7963fddc 100644 --- a/libnymea/integrations/thing.cpp +++ b/libnymea/integrations/thing.cpp @@ -317,6 +317,15 @@ States Thing::states() const return m_states; } + +/*! Returns true, a \l{Param} with the given \a paramTypeId exists for this thing. */ +bool Thing::hasParam(const QString ¶mName) const +{ + ParamTypeId paramTypeId = m_thingClass.paramTypes().findByName(paramName).id(); + return m_params.hasParam(paramTypeId); +} + + /*! Returns true, a \l{Param} with the given \a paramTypeId exists for this thing. */ bool Thing::hasParam(const ParamTypeId ¶mTypeId) const { diff --git a/libnymea/integrations/thing.h b/libnymea/integrations/thing.h index 3c862213..c28e3337 100644 --- a/libnymea/integrations/thing.h +++ b/libnymea/integrations/thing.h @@ -112,6 +112,7 @@ public: ParamList params() const; bool hasParam(const ParamTypeId ¶mTypeId) const; + bool hasParam(const QString ¶mName) const; void setParams(const ParamList ¶ms); QVariant paramValue(const ParamTypeId ¶mTypeId) const; diff --git a/libnymea/webserver/httpreply.cpp b/libnymea/webserver/httpreply.cpp index 4e152175..a76d8a4a 100644 --- a/libnymea/webserver/httpreply.cpp +++ b/libnymea/webserver/httpreply.cpp @@ -197,6 +197,14 @@ HttpReply *HttpReply::createSuccessReply() return reply; } +HttpReply *HttpReply::createJsonReply(const QJsonDocument &jsonDoc, const HttpReply::HttpStatusCode &statusCode) +{ + HttpReply *reply = new HttpReply(statusCode, HttpReply::TypeSync); + reply->setPayload(jsonDoc.toJson(QJsonDocument::Compact)); + reply->setHeader(HttpReply::ContentTypeHeader, "application/json; charset=\"utf-8\";"); + return reply; +} + HttpReply *HttpReply::createErrorReply(const HttpReply::HttpStatusCode &statusCode) { HttpReply *reply = new HttpReply(statusCode, HttpReply::TypeSync); @@ -389,6 +397,12 @@ QByteArray HttpReply::getHttpReasonPhrase(const HttpReply::HttpStatusCode &statu case BadRequest: response = QString("Bad Request").toUtf8(); break; + case Unauthorized: + response = QString("Unauthorized").toUtf8(); + break; + case PaymentRequired: + response = QString("Payment required").toUtf8(); + break; case Forbidden: response = QString("Forbidden").toUtf8(); break; @@ -398,6 +412,9 @@ QByteArray HttpReply::getHttpReasonPhrase(const HttpReply::HttpStatusCode &statu case MethodNotAllowed: response = QString("Method Not Allowed").toUtf8(); break; + case NotAcceptable: + response = QString("Not Acceptable").toUtf8(); + break; case RequestTimeout: response = QString("Request Timeout").toUtf8(); break; diff --git a/libnymea/webserver/httpreply.h b/libnymea/webserver/httpreply.h index 8d8d6df6..c584a6a4 100644 --- a/libnymea/webserver/httpreply.h +++ b/libnymea/webserver/httpreply.h @@ -30,6 +30,7 @@ #include #include #include +#include // Note: RFC 7231 HTTP/1.1 Semantics and Content -> http://tools.ietf.org/html/rfc7231 @@ -45,9 +46,12 @@ public: Found = 302, PermanentRedirect = 308, BadRequest = 400, + Unauthorized = 401, + PaymentRequired = 402, Forbidden = 403, NotFound = 404, MethodNotAllowed = 405, + NotAcceptable = 406, RequestTimeout = 408, Conflict = 409, InternalServerError = 500, @@ -80,6 +84,7 @@ public: static HttpReply *createSuccessReply(); static HttpReply *createErrorReply(const HttpReply::HttpStatusCode &statusCode); + static HttpReply *createJsonReply(const QJsonDocument &jsonDoc, const HttpReply::HttpStatusCode &statusCode = HttpStatusCode::Ok); static HttpReply *createAsyncReply(); void setHttpStatusCode(const HttpStatusCode &statusCode); diff --git a/libnymea/webserver/webserverresource.cpp b/libnymea/webserver/webserverresource.cpp index 520621ed..3c4aa720 100644 --- a/libnymea/webserver/webserverresource.cpp +++ b/libnymea/webserver/webserverresource.cpp @@ -45,10 +45,24 @@ QString WebServerResource::basePath() const return m_basePath; } +bool WebServerResource::enabled() const +{ + return m_enabled; +} + +void WebServerResource::setEnabled(bool enabled) +{ + if (m_enabled == enabled) + return; + + qCDebug(dcWebServer()) << "The resource" << m_basePath << "is now" << (enabled ? "enabled" : "disabled"); + m_enabled = enabled; + emit enabledChanged(m_enabled); +} + HttpReply *WebServerResource::createFileReply(const QString fileName) { qCDebug(dcWebServer()) << "Create file reply for" << fileName; - HttpReply *reply = HttpReply::createSuccessReply(); QFile file(fileName); if (!file.open(QFile::ReadOnly)) { @@ -56,6 +70,8 @@ HttpReply *WebServerResource::createFileReply(const QString fileName) return HttpReply::createErrorReply(HttpReply::Forbidden); } + HttpReply *reply = HttpReply::createSuccessReply(); + // Check content type if (file.fileName().endsWith(".html")) { reply->setHeader(HttpReply::ContentTypeHeader, "text/html; charset=\"utf-8\";"); diff --git a/libnymea/webserver/webserverresource.h b/libnymea/webserver/webserverresource.h index 0f29fc83..879e84fb 100644 --- a/libnymea/webserver/webserverresource.h +++ b/libnymea/webserver/webserverresource.h @@ -46,14 +46,19 @@ public: QString basePath() const; - virtual bool authenticationRequired() const = 0; + bool enabled() const; + void setEnabled(bool enabled); virtual HttpReply *processRequest(const HttpRequest &request) = 0; static HttpReply *createFileReply(const QString fileName); +signals: + void enabledChanged(bool enabled); + protected: QString m_basePath; + bool m_enabled = true; };