File indexing completed on 2024-04-21 14:45:04

0001 /* EkosLive Node
0002 
0003     SPDX-FileCopyrightText: 2023 Jasem Mutlaq <mutlaqja@ikarustech.com>
0004 
0005     Node Manager
0006 
0007     SPDX-License-Identifier: GPL-2.0-or-later
0008 */
0009 
0010 #include "nodemanager.h"
0011 #include "version.h"
0012 
0013 #include <QWebSocket>
0014 #include <QUrlQuery>
0015 #include <QTimer>
0016 #include <QJsonDocument>
0017 
0018 #include <KActionCollection>
0019 #include <KLocalizedString>
0020 #include <basedevice.h>
0021 #include <QUuid>
0022 
0023 namespace EkosLive
0024 {
0025 
0026 ///////////////////////////////////////////////////////////////////////////////////////////
0027 ///
0028 ///////////////////////////////////////////////////////////////////////////////////////////
0029 NodeManager::NodeManager(uint32_t mask)
0030 {
0031     m_NetworkManager = new QNetworkAccessManager(this);
0032     connect(m_NetworkManager, &QNetworkAccessManager::finished, this, &NodeManager::onResult);
0033 
0034     // Configure nodes
0035     if (mask & Message)
0036         m_Nodes[Message] = new Node("message");
0037     if (mask & Media)
0038         m_Nodes[Media] = new Node("media");
0039     if (mask & Cloud)
0040         m_Nodes[Cloud] = new Node("cloud");
0041 
0042     for (auto &node : m_Nodes)
0043     {
0044         connect(node, &Node::connected, this, &NodeManager::setConnected);
0045         connect(node, &Node::disconnected, this, &NodeManager::setDisconnected);
0046     }
0047 }
0048 
0049 ///////////////////////////////////////////////////////////////////////////////////////////
0050 ///
0051 ///////////////////////////////////////////////////////////////////////////////////////////
0052 void NodeManager::setURLs(const QUrl &service, const QUrl &websocket)
0053 {
0054     m_ServiceURL = service;
0055     m_WebsocketURL = websocket;
0056     for (auto &node : m_Nodes)
0057         node->setProperty("url", m_WebsocketURL);
0058 }
0059 
0060 ///////////////////////////////////////////////////////////////////////////////////////////
0061 ///
0062 ///////////////////////////////////////////////////////////////////////////////////////////
0063 bool NodeManager::isConnected() const
0064 {
0065     return std::all_of(m_Nodes.begin(), m_Nodes.end(), [](const auto & node)
0066     {
0067         return node->isConnected();
0068     });
0069 }
0070 
0071 ///////////////////////////////////////////////////////////////////////////////////////////
0072 ///
0073 ///////////////////////////////////////////////////////////////////////////////////////////
0074 void NodeManager::setConnected()
0075 {
0076     auto node = qobject_cast<Node*>(sender());
0077     if (!node)
0078         return;
0079 
0080     auto isConnected = std::all_of(m_Nodes.begin(), m_Nodes.end(), [](const auto & node)
0081     {
0082         return node->isConnected();
0083     });
0084 
0085     // Only emit once all nodes are connected.
0086     if (isConnected)
0087         emit connected();
0088 }
0089 
0090 ///////////////////////////////////////////////////////////////////////////////////////////
0091 ///
0092 ///////////////////////////////////////////////////////////////////////////////////////////
0093 void NodeManager::setDisconnected()
0094 {
0095     auto node = qobject_cast<Node*>(sender());
0096     if (!node)
0097         return;
0098 
0099     // Only emit once all nodes are disconnected.
0100     if (isConnected() == false)
0101         emit disconnected();
0102 }
0103 
0104 ///////////////////////////////////////////////////////////////////////////////////////////
0105 ///
0106 ///////////////////////////////////////////////////////////////////////////////////////////
0107 void NodeManager::disconnectNodes()
0108 {
0109     for (auto &node : m_Nodes)
0110         node->disconnectServer();
0111 }
0112 
0113 ///////////////////////////////////////////////////////////////////////////////////////////
0114 ///
0115 ///////////////////////////////////////////////////////////////////////////////////////////
0116 void NodeManager::setCredentials(const QString &username, const QString &password)
0117 {
0118     m_Username = username;
0119     m_Password = password;
0120 }
0121 
0122 ///////////////////////////////////////////////////////////////////////////////////////////
0123 ///
0124 ///////////////////////////////////////////////////////////////////////////////////////////
0125 void NodeManager::authenticate()
0126 {
0127     QNetworkRequest request;
0128     request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
0129 
0130     QUrl authURL(m_ServiceURL);
0131     authURL.setPath("/api/authenticate");
0132 
0133     request.setUrl(authURL);
0134 
0135     QJsonObject json = {{"username", m_Username}, {"password", m_Password}};
0136 
0137     auto postData = QJsonDocument(json).toJson(QJsonDocument::Compact);
0138 
0139     m_NetworkManager->post(request, postData);
0140 }
0141 
0142 ///////////////////////////////////////////////////////////////////////////////////////////
0143 ///
0144 ///////////////////////////////////////////////////////////////////////////////////////////
0145 void NodeManager::onResult(QNetworkReply *reply)
0146 {
0147     if (reply->error() != QNetworkReply::NoError)
0148     {
0149         // If connection refused, retry up to 3 times
0150         if (reply->error() == QNetworkReply::ConnectionRefusedError && m_ReconnectTries++ < RECONNECT_MAX_TRIES)
0151         {
0152             reply->deleteLater();
0153             QTimer::singleShot(RECONNECT_INTERVAL, this, &NodeManager::authenticate);
0154             return;
0155         }
0156 
0157         m_ReconnectTries = 0;
0158         emit authenticationError(i18n("Error authentication with Ekos Live server: %1", reply->errorString()));
0159         reply->deleteLater();
0160         return;
0161     }
0162 
0163     m_ReconnectTries = 0;
0164     QJsonParseError error;
0165     auto response = QJsonDocument::fromJson(reply->readAll(), &error);
0166 
0167     if (error.error != QJsonParseError::NoError)
0168     {
0169         emit authenticationError(i18n("Error parsing server response: %1", error.errorString()));
0170         reply->deleteLater();
0171         return;
0172     }
0173 
0174     m_AuthResponse = response.object();
0175 
0176     if (m_AuthResponse["success"].toBool() == false)
0177     {
0178         emit authenticationError(m_AuthResponse["message"].toString());
0179         reply->deleteLater();
0180         return;
0181     }
0182 
0183     // Cloud only supported for plan_id = 1
0184     if (m_AuthResponse["plan_id"].toString("2") == "2")
0185         m_Nodes.remove(Cloud);
0186 
0187     for (auto &node : m_Nodes)
0188     {
0189         node->setAuthResponse(m_AuthResponse);
0190         node->connectServer();
0191     }
0192 
0193     reply->deleteLater();
0194 }
0195 
0196 }