feature/api-controller-improvements (#567)
* added error handler for api controller * while downloading the config from the api, the Connecting status is now displayed * added a button to delete container config for api servers * added crc check to avoid re-import of api configs * fixed currentIndex of serversMenuContent after DefaultServerIndexChanged * added closing the import window after re-importing the config from api
This commit is contained in:
parent
dba05aab07
commit
e0863a58aa
20 changed files with 275 additions and 113 deletions
|
@ -3,9 +3,11 @@
|
|||
#include <QEventLoop>
|
||||
#include <QNetworkAccessManager>
|
||||
#include <QNetworkReply>
|
||||
#include <QtConcurrent>
|
||||
|
||||
#include "configurators/openvpn_configurator.h"
|
||||
#include "configurators/wireguard_configurator.h"
|
||||
#include "core/errorstrings.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
|
@ -28,7 +30,8 @@ ApiController::ApiController(const QSharedPointer<ServersModel> &serversModel,
|
|||
{
|
||||
}
|
||||
|
||||
void ApiController::processCloudConfig(const QString &protocol, const ApiController::ApiPayloadData &apiPayloadData, QString &config)
|
||||
void ApiController::processApiConfig(const QString &protocol, const ApiController::ApiPayloadData &apiPayloadData,
|
||||
QString &config)
|
||||
{
|
||||
if (protocol == configKey::cloak) {
|
||||
config.replace("<key>", "<key>\n");
|
||||
|
@ -64,73 +67,91 @@ QJsonObject ApiController::fillApiPayload(const QString &protocol, const ApiCont
|
|||
return obj;
|
||||
}
|
||||
|
||||
bool ApiController::updateServerConfigFromApi()
|
||||
void ApiController::updateServerConfigFromApi()
|
||||
{
|
||||
QtConcurrent::run([this]() {
|
||||
auto serverConfig = m_serversModel->getDefaultServerConfig();
|
||||
auto containerConfig = serverConfig.value(config_key::containers).toArray();
|
||||
|
||||
bool isConfigUpdateStarted = false;
|
||||
|
||||
if (serverConfig.value(config_key::configVersion).toInt() && containerConfig.isEmpty()) {
|
||||
emit updateStarted();
|
||||
isConfigUpdateStarted = true;
|
||||
|
||||
QNetworkAccessManager manager;
|
||||
|
||||
QNetworkRequest request;
|
||||
request.setTransferTimeout(7000);
|
||||
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
|
||||
request.setRawHeader("Authorization",
|
||||
"Api-Key " + serverConfig.value(configKey::accessToken).toString().toUtf8());
|
||||
QString endpoint = serverConfig.value(configKey::apiEdnpoint).toString();
|
||||
request.setUrl(endpoint.replace("https", "http")); // todo remove
|
||||
|
||||
QString protocol = serverConfig.value(configKey::protocol).toString();
|
||||
|
||||
auto apiPayloadData = generateApiPayloadData(protocol);
|
||||
|
||||
QByteArray requestBody = QJsonDocument(fillApiPayload(protocol, apiPayloadData)).toJson();
|
||||
|
||||
QScopedPointer<QNetworkReply> reply;
|
||||
reply.reset(manager.post(request, requestBody));
|
||||
|
||||
QEventLoop wait;
|
||||
QObject::connect(reply.get(), &QNetworkReply::finished, &wait, &QEventLoop::quit);
|
||||
wait.exec();
|
||||
|
||||
if (reply->error() == QNetworkReply::NoError) {
|
||||
QString contents = QString::fromUtf8(reply->readAll());
|
||||
auto data = QJsonDocument::fromJson(contents.toUtf8()).object().value(config_key::config).toString();
|
||||
|
||||
data.replace("vpn://", "");
|
||||
QByteArray ba = QByteArray::fromBase64(data.toUtf8(),
|
||||
QByteArray::Base64UrlEncoding | QByteArray::OmitTrailingEquals);
|
||||
|
||||
QByteArray ba_uncompressed = qUncompress(ba);
|
||||
if (!ba_uncompressed.isEmpty()) {
|
||||
ba = ba_uncompressed;
|
||||
}
|
||||
|
||||
QString configStr = ba;
|
||||
processApiConfig(protocol, apiPayloadData, configStr);
|
||||
|
||||
QJsonObject apiConfig = QJsonDocument::fromJson(configStr.toUtf8()).object();
|
||||
|
||||
serverConfig.insert(config_key::dns1, apiConfig.value(config_key::dns1));
|
||||
serverConfig.insert(config_key::dns2, apiConfig.value(config_key::dns2));
|
||||
serverConfig.insert(config_key::containers, apiConfig.value(config_key::containers));
|
||||
serverConfig.insert(config_key::hostName, apiConfig.value(config_key::hostName));
|
||||
|
||||
auto defaultContainer = apiConfig.value(config_key::defaultContainer).toString();
|
||||
serverConfig.insert(config_key::defaultContainer, defaultContainer);
|
||||
m_serversModel->editServer(serverConfig);
|
||||
emit m_serversModel->defaultContainerChanged(ContainerProps::containerFromString(defaultContainer));
|
||||
} else {
|
||||
qDebug() << reply->error();
|
||||
qDebug() << reply->attribute(QNetworkRequest::HttpStatusCodeAttribute);
|
||||
emit errorOccurred(errorString(ApiConfigDownloadError));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
emit updateFinished(isConfigUpdateStarted);
|
||||
return;
|
||||
});
|
||||
}
|
||||
|
||||
void ApiController::clearApiConfig()
|
||||
{
|
||||
auto serverConfig = m_serversModel->getDefaultServerConfig();
|
||||
|
||||
auto containerConfig = serverConfig.value(config_key::containers).toArray();
|
||||
serverConfig.remove(config_key::dns1);
|
||||
serverConfig.remove(config_key::dns2);
|
||||
serverConfig.remove(config_key::containers);
|
||||
serverConfig.remove(config_key::hostName);
|
||||
|
||||
if (serverConfig.value(config_key::configVersion).toInt() && containerConfig.isEmpty()) {
|
||||
QNetworkAccessManager manager;
|
||||
serverConfig.insert(config_key::defaultContainer, ContainerProps::containerToString(DockerContainer::None));
|
||||
|
||||
QNetworkRequest request;
|
||||
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
|
||||
request.setRawHeader("Authorization",
|
||||
"Api-Key " + serverConfig.value(configKey::accessToken).toString().toUtf8());
|
||||
QString endpoint = serverConfig.value(configKey::apiEdnpoint).toString();
|
||||
request.setUrl(endpoint.replace("https", "http")); // todo remove
|
||||
|
||||
QString protocol = serverConfig.value(configKey::protocol).toString();
|
||||
|
||||
auto apiPayloadData = generateApiPayloadData(protocol);
|
||||
|
||||
QByteArray requestBody = QJsonDocument(fillApiPayload(protocol, apiPayloadData)).toJson();
|
||||
|
||||
QScopedPointer<QNetworkReply> reply;
|
||||
reply.reset(manager.post(request, requestBody));
|
||||
|
||||
QEventLoop wait;
|
||||
QObject::connect(reply.get(), &QNetworkReply::finished, &wait, &QEventLoop::quit);
|
||||
wait.exec();
|
||||
|
||||
if (reply->error() == QNetworkReply::NoError) {
|
||||
QString contents = QString::fromUtf8(reply->readAll());
|
||||
auto data = QJsonDocument::fromJson(contents.toUtf8()).object().value(config_key::config).toString();
|
||||
|
||||
data.replace("vpn://", "");
|
||||
QByteArray ba = QByteArray::fromBase64(data.toUtf8(),
|
||||
QByteArray::Base64UrlEncoding | QByteArray::OmitTrailingEquals);
|
||||
|
||||
QByteArray ba_uncompressed = qUncompress(ba);
|
||||
if (!ba_uncompressed.isEmpty()) {
|
||||
ba = ba_uncompressed;
|
||||
}
|
||||
|
||||
QString configStr = ba;
|
||||
processCloudConfig(protocol, apiPayloadData, configStr);
|
||||
|
||||
QJsonObject cloudConfig = QJsonDocument::fromJson(configStr.toUtf8()).object();
|
||||
|
||||
serverConfig.insert("cloudConfig", cloudConfig);
|
||||
serverConfig.insert(config_key::dns1, cloudConfig.value(config_key::dns1));
|
||||
serverConfig.insert(config_key::dns2, cloudConfig.value(config_key::dns2));
|
||||
serverConfig.insert(config_key::containers, cloudConfig.value(config_key::containers));
|
||||
serverConfig.insert(config_key::hostName, cloudConfig.value(config_key::hostName));
|
||||
|
||||
auto defaultContainer = cloudConfig.value(config_key::defaultContainer).toString();
|
||||
serverConfig.insert(config_key::defaultContainer, defaultContainer);
|
||||
m_serversModel->editServer(serverConfig);
|
||||
emit m_serversModel->defaultContainerChanged(ContainerProps::containerFromString(defaultContainer));
|
||||
} else {
|
||||
QString err = reply->errorString();
|
||||
qDebug() << QString::fromUtf8(reply->readAll()); //todo remove debug output
|
||||
qDebug() << reply->error();
|
||||
qDebug() << err;
|
||||
qDebug() << reply->attribute(QNetworkRequest::HttpStatusCodeAttribute);
|
||||
emit errorOccurred(tr("Error when retrieving configuration from cloud server"));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
m_serversModel->editServer(serverConfig);
|
||||
}
|
||||
|
|
|
@ -16,9 +16,13 @@ public:
|
|||
const QSharedPointer<ContainersModel> &containersModel, QObject *parent = nullptr);
|
||||
|
||||
public slots:
|
||||
bool updateServerConfigFromApi();
|
||||
void updateServerConfigFromApi();
|
||||
|
||||
void clearApiConfig();
|
||||
|
||||
signals:
|
||||
void updateStarted();
|
||||
void updateFinished(bool isConfigUpdateStarted);
|
||||
void errorOccurred(const QString &errorMessage);
|
||||
|
||||
private:
|
||||
|
@ -31,7 +35,7 @@ private:
|
|||
|
||||
ApiPayloadData generateApiPayloadData(const QString &protocol);
|
||||
QJsonObject fillApiPayload(const QString &protocol, const ApiController::ApiPayloadData &apiPayloadData);
|
||||
void processCloudConfig(const QString &protocol, const ApiController::ApiPayloadData &apiPayloadData, QString &config);
|
||||
void processApiConfig(const QString &protocol, const ApiController::ApiPayloadData &apiPayloadData, QString &config);
|
||||
|
||||
QSharedPointer<ServersModel> m_serversModel;
|
||||
QSharedPointer<ContainersModel> m_containersModel;
|
||||
|
|
|
@ -25,6 +25,11 @@ ConnectionController::ConnectionController(const QSharedPointer<ServersModel> &s
|
|||
|
||||
void ConnectionController::openConnection()
|
||||
{
|
||||
if (!m_containersModel->isAnyContainerInstalled()) {
|
||||
emit noInstalledContainers();
|
||||
return;
|
||||
}
|
||||
|
||||
int serverIndex = m_serversModel->getDefaultServerIndex();
|
||||
ServerCredentials credentials = m_serversModel->getServerCredentials(serverIndex);
|
||||
|
||||
|
@ -129,6 +134,17 @@ QString ConnectionController::connectionStateText() const
|
|||
return m_connectionStateText;
|
||||
}
|
||||
|
||||
void ConnectionController::toggleConnection(bool skipConnectionInProgressCheck)
|
||||
{
|
||||
if (!skipConnectionInProgressCheck && isConnectionInProgress()) {
|
||||
closeConnection();
|
||||
} else if (isConnected()) {
|
||||
closeConnection();
|
||||
} else {
|
||||
openConnection();
|
||||
}
|
||||
}
|
||||
|
||||
bool ConnectionController::isConnectionInProgress() const
|
||||
{
|
||||
return m_isConnectionInProgress;
|
||||
|
|
|
@ -26,6 +26,8 @@ public:
|
|||
QString connectionStateText() const;
|
||||
|
||||
public slots:
|
||||
void toggleConnection(bool skipConnectionInProgressCheck);
|
||||
|
||||
void openConnection();
|
||||
void closeConnection();
|
||||
|
||||
|
@ -45,6 +47,8 @@ signals:
|
|||
void connectionErrorOccurred(const QString &errorMessage);
|
||||
void reconnectWithUpdatedContainer(const QString &message);
|
||||
|
||||
void noInstalledContainers();
|
||||
|
||||
private:
|
||||
Vpn::ConnectionState getCurrentConnectionState();
|
||||
|
||||
|
|
|
@ -123,12 +123,19 @@ void ImportController::importConfig()
|
|||
credentials.userName = m_config.value(config_key::userName).toString();
|
||||
credentials.secretData = m_config.value(config_key::password).toString();
|
||||
|
||||
if (credentials.isValid()
|
||||
|| m_config.contains(config_key::containers)
|
||||
|| m_config.contains(config_key::configVersion)) { // todo
|
||||
if (credentials.isValid() || m_config.contains(config_key::containers)) {
|
||||
m_serversModel->addServer(m_config);
|
||||
|
||||
emit importFinished();
|
||||
} else if (m_config.contains(config_key::configVersion)) {
|
||||
quint16 crc = qChecksum(QJsonDocument(m_config).toJson());
|
||||
if (m_serversModel->isServerFromApiAlreadyExists(crc)) {
|
||||
emit importErrorOccurred(errorString(ErrorCode::ApiConfigAlreadyAdded), true);
|
||||
} else {
|
||||
m_config.insert(config_key::crc, crc);
|
||||
|
||||
m_serversModel->addServer(m_config);
|
||||
emit importFinished();
|
||||
}
|
||||
} else {
|
||||
qDebug() << "Failed to import profile";
|
||||
qDebug().noquote() << QJsonDocument(m_config).toJson();
|
||||
|
|
|
@ -39,7 +39,7 @@ public slots:
|
|||
|
||||
signals:
|
||||
void importFinished();
|
||||
void importErrorOccurred(const QString &errorMessage);
|
||||
void importErrorOccurred(const QString &errorMessage, bool goToPageHome = false);
|
||||
|
||||
void qrDecodingFinished();
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue