refactoring: simplified the validity check of the config before connection

- improved project structure
This commit is contained in:
vladimir.kuznetsov 2025-02-15 11:50:42 +07:00
parent db3164223a
commit e9250afd2b
31 changed files with 941 additions and 969 deletions

View file

@ -1,8 +1,13 @@
#include "apiConfigsController.h"
#include <QEventLoop>
#include "amnezia_application.h"
#include "configurators/wireguard_configurator.h"
#include "core/api/apiDefs.h"
#include "core/api/apiUtils.h"
#include "core/controllers/gatewayController.h"
#include "core/networkUtilities.h"
#include "core/qrCodeUtils.h"
#include "ui/controllers/systemController.h"
#include "version.h"
@ -18,6 +23,7 @@ namespace
constexpr char accessToken[] = "api_key";
constexpr char certificate[] = "certificate";
constexpr char publicKey[] = "public_key";
constexpr char protocol[] = "protocol";
constexpr char uuid[] = "installation_uuid";
constexpr char osVersion[] = "os_version";
@ -43,9 +49,10 @@ namespace
}
}
ApiConfigsController::ApiConfigsController(const QSharedPointer<ServersModel> &serversModel, const std::shared_ptr<Settings> &settings,
QObject *parent)
: QObject(parent), m_serversModel(serversModel), m_settings(settings)
ApiConfigsController::ApiConfigsController(const QSharedPointer<ServersModel> &serversModel,
const QSharedPointer<ApiServicesModel> &apiServicesModel,
const std::shared_ptr<Settings> &settings, QObject *parent)
: QObject(parent), m_serversModel(serversModel), m_apiServicesModel(apiServicesModel), m_settings(settings)
{
}
@ -98,6 +105,211 @@ void ApiConfigsController::prepareVpnKeyExport()
emit vpnKeyExportReady();
}
bool ApiConfigsController::fillAvailableServices()
{
GatewayController gatewayController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(), apiDefs::requestTimeoutMsecs);
QJsonObject apiPayload;
apiPayload[configKey::osVersion] = QSysInfo::productType();
QByteArray responseBody;
ErrorCode errorCode = gatewayController.post(QString("%1v1/services"), apiPayload, responseBody);
if (errorCode == ErrorCode::NoError) {
if (!responseBody.contains("services")) {
errorCode = ErrorCode::ApiServicesMissingError;
}
}
if (errorCode != ErrorCode::NoError) {
emit errorOccurred(errorCode);
return false;
}
QJsonObject data = QJsonDocument::fromJson(responseBody).object();
m_apiServicesModel->updateModel(data);
return true;
}
bool ApiConfigsController::importServiceFromGateway()
{
if (m_serversModel->isServerFromApiAlreadyExists(m_apiServicesModel->getCountryCode(), m_apiServicesModel->getSelectedServiceType(),
m_apiServicesModel->getSelectedServiceProtocol())) {
emit errorOccurred(ErrorCode::ApiConfigAlreadyAdded);
return false;
}
GatewayController gatewayController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(), apiDefs::requestTimeoutMsecs);
auto installationUuid = m_settings->getInstallationUuid(true);
auto userCountryCode = m_apiServicesModel->getCountryCode();
auto serviceType = m_apiServicesModel->getSelectedServiceType();
auto serviceProtocol = m_apiServicesModel->getSelectedServiceProtocol();
ApiPayloadData apiPayloadData = generateApiPayloadData(serviceProtocol);
QJsonObject apiPayload = fillApiPayload(serviceProtocol, apiPayloadData);
apiPayload[configKey::userCountryCode] = userCountryCode;
apiPayload[configKey::serviceType] = serviceType;
apiPayload[configKey::uuid] = installationUuid;
QByteArray responseBody;
ErrorCode errorCode = gatewayController.post(QString("%1v1/config"), apiPayload, responseBody);
QJsonObject serverConfig;
if (errorCode == ErrorCode::NoError) {
fillServerConfig(serviceProtocol, apiPayloadData, responseBody, serverConfig);
QJsonObject apiConfig = serverConfig.value(configKey::apiConfig).toObject();
apiConfig.insert(configKey::userCountryCode, m_apiServicesModel->getCountryCode());
apiConfig.insert(configKey::serviceType, m_apiServicesModel->getSelectedServiceType());
apiConfig.insert(configKey::serviceProtocol, m_apiServicesModel->getSelectedServiceProtocol());
serverConfig.insert(configKey::apiConfig, apiConfig);
m_serversModel->addServer(serverConfig);
emit installServerFromApiFinished(tr("%1 installed successfully.").arg(m_apiServicesModel->getSelectedServiceName()));
return true;
} else {
emit errorOccurred(errorCode);
return false;
}
}
bool ApiConfigsController::updateServiceFromGateway(const int serverIndex, const QString &newCountryCode, const QString &newCountryName,
bool reloadServiceConfig)
{
GatewayController gatewayController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(), apiDefs::requestTimeoutMsecs);
auto serverConfig = m_serversModel->getServerConfig(serverIndex);
auto apiConfig = serverConfig.value(configKey::apiConfig).toObject();
auto authData = serverConfig.value(configKey::authData).toObject();
auto installationUuid = m_settings->getInstallationUuid(true);
auto userCountryCode = apiConfig.value(configKey::userCountryCode).toString();
auto serviceType = apiConfig.value(configKey::serviceType).toString();
auto serviceProtocol = apiConfig.value(configKey::serviceProtocol).toString();
ApiPayloadData apiPayloadData = generateApiPayloadData(serviceProtocol);
QJsonObject apiPayload = fillApiPayload(serviceProtocol, apiPayloadData);
apiPayload[configKey::userCountryCode] = userCountryCode;
apiPayload[configKey::serviceType] = serviceType;
apiPayload[configKey::uuid] = installationUuid;
if (!newCountryCode.isEmpty()) {
apiPayload[configKey::serverCountryCode] = newCountryCode;
}
if (!authData.isEmpty()) {
apiPayload[configKey::authData] = authData;
}
QByteArray responseBody;
ErrorCode errorCode = gatewayController.post(QString("%1v1/config"), apiPayload, responseBody);
QJsonObject newServerConfig;
if (errorCode == ErrorCode::NoError) {
fillServerConfig(serviceProtocol, apiPayloadData, responseBody, newServerConfig);
QJsonObject newApiConfig = newServerConfig.value(configKey::apiConfig).toObject();
newApiConfig.insert(configKey::userCountryCode, apiConfig.value(configKey::userCountryCode));
newApiConfig.insert(configKey::serviceType, apiConfig.value(configKey::serviceType));
newApiConfig.insert(configKey::serviceProtocol, apiConfig.value(configKey::serviceProtocol));
newServerConfig.insert(configKey::apiConfig, newApiConfig);
newServerConfig.insert(configKey::authData, authData);
m_serversModel->editServer(newServerConfig, serverIndex);
if (reloadServiceConfig) {
emit reloadServerFromApiFinished(tr("API config reloaded"));
} else if (newCountryName.isEmpty()) {
emit updateServerFromApiFinished();
} else {
emit changeApiCountryFinished(tr("Successfully changed the country of connection to %1").arg(newCountryName));
}
return true;
} else {
emit errorOccurred(errorCode);
return false;
}
}
bool ApiConfigsController::updateServiceFromTelegram(const int serverIndex)
{
auto serverConfig = m_serversModel->getServerConfig(serverIndex);
auto installationUuid = m_settings->getInstallationUuid(true);
#ifdef Q_OS_IOS
IosController::Instance()->requestInetAccess();
QThread::msleep(10);
#endif
if (serverConfig.value(config_key::configVersion).toInt()) {
QNetworkRequest request;
request.setTransferTimeout(apiDefs::requestTimeoutMsecs);
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);
QString protocol = serverConfig.value(configKey::protocol).toString();
ApiPayloadData apiPayloadData = generateApiPayloadData(protocol);
QJsonObject apiPayload = fillApiPayload(protocol, apiPayloadData);
apiPayload[configKey::uuid] = installationUuid;
QByteArray requestBody = QJsonDocument(apiPayload).toJson();
QNetworkReply *reply = amnApp->manager()->post(request, requestBody);
QEventLoop wait;
connect(reply, &QNetworkReply::finished, &wait, &QEventLoop::quit);
QList<QSslError> sslErrors;
connect(reply, &QNetworkReply::sslErrors, [this, &sslErrors](const QList<QSslError> &errors) { sslErrors = errors; });
wait.exec();
auto errorCode = NetworkUtilities::checkNetworkReplyErrors(sslErrors, reply);
if (errorCode != ErrorCode::NoError) {
reply->deleteLater();
emit errorOccurred(errorCode);
return false;
}
auto apiResponseBody = reply->readAll();
reply->deleteLater();
fillServerConfig(protocol, apiPayloadData, apiResponseBody, serverConfig);
m_serversModel->editServer(serverConfig, serverIndex);
emit updateServerFromApiFinished();
}
return true;
}
bool ApiConfigsController::isConfigValid()
{
int serverIndex = m_serversModel->getDefaultServerIndex();
QJsonObject serverConfigObject = m_serversModel->getServerConfig(serverIndex);
auto configSource = apiUtils::getConfigSource(serverConfigObject);
if (configSource == apiDefs::ConfigSource::Telegram
&& !m_serversModel->data(serverIndex, ServersModel::Roles::HasInstalledContainers).toBool()) {
m_serversModel->removeApiConfig(serverIndex);
return updateServiceFromTelegram(serverIndex);
} else if (configSource == apiDefs::ConfigSource::AmneziaGateway
&& !m_serversModel->data(serverIndex, ServersModel::Roles::HasInstalledContainers).toBool()) {
return updateServiceFromGateway(serverIndex, "", "");
} else if (configSource && m_serversModel->isApiKeyExpired(serverIndex)) {
qDebug() << "attempt to update api config by expires_at event";
if (configSource == apiDefs::ConfigSource::Telegram) {
return updateServiceFromGateway(serverIndex, "", "");
} else {
m_serversModel->removeApiConfig(serverIndex);
return updateServiceFromTelegram(serverIndex);
}
}
return true;
}
ApiConfigsController::ApiPayloadData ApiConfigsController::generateApiPayloadData(const QString &protocol)
{
ApiConfigsController::ApiPayloadData apiPayload;
@ -126,6 +338,82 @@ QJsonObject ApiConfigsController::fillApiPayload(const QString &protocol, const
return obj;
}
void ApiConfigsController::fillServerConfig(const QString &protocol, const ApiPayloadData &apiPayloadData,
const QByteArray &apiResponseBody, QJsonObject &serverConfig)
{
QString data = QJsonDocument::fromJson(apiResponseBody).object().value(config_key::config).toString();
data.replace("vpn://", "");
QByteArray ba = QByteArray::fromBase64(data.toUtf8(), QByteArray::Base64UrlEncoding | QByteArray::OmitTrailingEquals);
if (ba.isEmpty()) {
emit errorOccurred(ErrorCode::ApiConfigEmptyError);
return;
}
QByteArray ba_uncompressed = qUncompress(ba);
if (!ba_uncompressed.isEmpty()) {
ba = ba_uncompressed;
}
QString configStr = ba;
if (protocol == configKey::cloak) {
configStr.replace("<key>", "<key>\n");
configStr.replace("$OPENVPN_PRIV_KEY", apiPayloadData.certRequest.privKey);
} else if (protocol == configKey::awg) {
configStr.replace("$WIREGUARD_CLIENT_PRIVATE_KEY", apiPayloadData.wireGuardClientPrivKey);
auto newServerConfig = QJsonDocument::fromJson(configStr.toUtf8()).object();
auto containers = newServerConfig.value(config_key::containers).toArray();
if (containers.isEmpty()) {
return; // todo process error
}
auto container = containers.at(0).toObject();
QString containerName = ContainerProps::containerTypeToString(DockerContainer::Awg);
auto containerConfig = container.value(containerName).toObject();
auto protocolConfig = QJsonDocument::fromJson(containerConfig.value(config_key::last_config).toString().toUtf8()).object();
containerConfig[config_key::junkPacketCount] = protocolConfig.value(config_key::junkPacketCount);
containerConfig[config_key::junkPacketMinSize] = protocolConfig.value(config_key::junkPacketMinSize);
containerConfig[config_key::junkPacketMaxSize] = protocolConfig.value(config_key::junkPacketMaxSize);
containerConfig[config_key::initPacketJunkSize] = protocolConfig.value(config_key::initPacketJunkSize);
containerConfig[config_key::responsePacketJunkSize] = protocolConfig.value(config_key::responsePacketJunkSize);
containerConfig[config_key::initPacketMagicHeader] = protocolConfig.value(config_key::initPacketMagicHeader);
containerConfig[config_key::responsePacketMagicHeader] = protocolConfig.value(config_key::responsePacketMagicHeader);
containerConfig[config_key::underloadPacketMagicHeader] = protocolConfig.value(config_key::underloadPacketMagicHeader);
containerConfig[config_key::transportPacketMagicHeader] = protocolConfig.value(config_key::transportPacketMagicHeader);
container[containerName] = containerConfig;
containers.replace(0, container);
newServerConfig[config_key::containers] = containers;
configStr = QString(QJsonDocument(newServerConfig).toJson());
}
QJsonObject newServerConfig = QJsonDocument::fromJson(configStr.toUtf8()).object();
serverConfig[config_key::dns1] = newServerConfig.value(config_key::dns1);
serverConfig[config_key::dns2] = newServerConfig.value(config_key::dns2);
serverConfig[config_key::containers] = newServerConfig.value(config_key::containers);
serverConfig[config_key::hostName] = newServerConfig.value(config_key::hostName);
if (newServerConfig.value(config_key::configVersion).toInt() == apiDefs::ConfigSource::AmneziaGateway) {
serverConfig[config_key::configVersion] = newServerConfig.value(config_key::configVersion);
serverConfig[config_key::description] = newServerConfig.value(config_key::description);
serverConfig[config_key::name] = newServerConfig.value(config_key::name);
}
auto defaultContainer = newServerConfig.value(config_key::defaultContainer).toString();
serverConfig[config_key::defaultContainer] = defaultContainer;
QVariantMap map = serverConfig.value(configKey::apiConfig).toObject().toVariantMap();
map.insert(newServerConfig.value(configKey::apiConfig).toObject().toVariantMap());
auto apiConfig = QJsonObject::fromVariantMap(map);
if (newServerConfig.value(config_key::configVersion).toInt() == apiDefs::ConfigSource::AmneziaGateway) {
apiConfig.insert(configKey::serviceInfo, QJsonDocument::fromJson(apiResponseBody).object().value(configKey::serviceInfo).toObject());
}
serverConfig[configKey::apiConfig] = apiConfig;
return;
}
QList<QString> ApiConfigsController::getQrCodes()
{
return m_qrCodes;

View file

@ -4,14 +4,15 @@
#include <QObject>
#include "configurators/openvpn_configurator.h"
#include "ui/models/api/apiServicesModel.h"
#include "ui/models/servers_model.h"
class ApiConfigsController : public QObject
{
Q_OBJECT
public:
ApiConfigsController(const QSharedPointer<ServersModel> &serversModel, const std::shared_ptr<Settings> &settings,
QObject *parent = nullptr);
ApiConfigsController(const QSharedPointer<ServersModel> &serversModel, const QSharedPointer<ApiServicesModel> &apiServicesModel,
const std::shared_ptr<Settings> &settings, QObject *parent = nullptr);
Q_PROPERTY(QList<QString> qrCodes READ getQrCodes NOTIFY vpnKeyExportReady)
Q_PROPERTY(int qrCodesCount READ getQrCodesCount NOTIFY vpnKeyExportReady)
@ -21,8 +22,22 @@ public slots:
// bool exportVpnKey(const QString &fileName);
void prepareVpnKeyExport();
bool fillAvailableServices();
bool importServiceFromGateway();
bool updateServiceFromGateway(const int serverIndex, const QString &newCountryCode, const QString &newCountryName,
bool reloadServiceConfig = false);
bool updateServiceFromTelegram(const int serverIndex);
bool isConfigValid();
signals:
void errorOccurred(ErrorCode errorCode);
void installServerFromApiFinished(const QString &message);
void changeApiCountryFinished(const QString &message);
void reloadServerFromApiFinished(const QString &message);
void updateServerFromApiFinished();
void vpnKeyExportReady();
private:
@ -36,6 +51,8 @@ private:
ApiPayloadData generateApiPayloadData(const QString &protocol);
QJsonObject fillApiPayload(const QString &protocol, const ApiPayloadData &apiPayloadData);
void fillServerConfig(const QString &protocol, const ApiPayloadData &apiPayloadData, const QByteArray &apiResponseBody,
QJsonObject &serverConfig);
QList<QString> getQrCodes();
int getQrCodesCount();
@ -43,6 +60,7 @@ private:
QList<QString> m_qrCodes;
QSharedPointer<ServersModel> m_serversModel;
QSharedPointer<ApiServicesModel> m_apiServicesModel;
std::shared_ptr<Settings> m_settings;
};

View file

@ -5,10 +5,8 @@
#else
#include <QApplication>
#endif
#include <QtConcurrent>
#include "core/controllers/vpnConfigurationController.h"
#include "core/api/apiDefs.h"
#include "version.h"
ConnectionController::ConnectionController(const QSharedPointer<ServersModel> &serversModel,
@ -27,7 +25,7 @@ ConnectionController::ConnectionController(const QSharedPointer<ServersModel> &s
connect(this, &ConnectionController::connectToVpn, m_vpnConnection.get(), &VpnConnection::connectToVpn, Qt::QueuedConnection);
connect(this, &ConnectionController::disconnectFromVpn, m_vpnConnection.get(), &VpnConnection::disconnectFromVpn, Qt::QueuedConnection);
connect(this, &ConnectionController::configFromApiUpdated, this, &ConnectionController::continueConnection);
connect(this, &ConnectionController::connectButtonClicked, this, &ConnectionController::toggleConnection, Qt::QueuedConnection);
m_state = Vpn::ConnectionState::Disconnected;
}
@ -35,8 +33,7 @@ ConnectionController::ConnectionController(const QSharedPointer<ServersModel> &s
void ConnectionController::openConnection()
{
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS)
if (!Utils::processIsRunning(Utils::executable(SERVICE_NAME, false), true))
{
if (!Utils::processIsRunning(Utils::executable(SERVICE_NAME, false), true)) {
emit connectionErrorOccurred(ErrorCode::AmneziaServiceNotRunning);
return;
}
@ -44,26 +41,24 @@ void ConnectionController::openConnection()
int serverIndex = m_serversModel->getDefaultServerIndex();
QJsonObject serverConfig = m_serversModel->getServerConfig(serverIndex);
auto configVersion = serverConfig.value(config_key::configVersion).toInt();
emit m_vpnConnection->connectionStateChanged(Vpn::ConnectionState::Preparing);
DockerContainer container = qvariant_cast<DockerContainer>(m_serversModel->data(serverIndex, ServersModel::Roles::DefaultContainerRole));
if (configVersion == apiDefs::ConfigSource::Telegram
&& !m_serversModel->data(serverIndex, ServersModel::Roles::HasInstalledContainers).toBool()) {
emit updateApiConfigFromTelegram();
} else if (configVersion == apiDefs::ConfigSource::AmneziaGateway
&& !m_serversModel->data(serverIndex, ServersModel::Roles::HasInstalledContainers).toBool()) {
emit updateApiConfigFromGateway();
} else if (configVersion && m_serversModel->isApiKeyExpired(serverIndex)) {
qDebug() << "attempt to update api config by expires_at event";
if (configVersion == apiDefs::ConfigSource::Telegram) {
emit updateApiConfigFromTelegram();
} else {
emit updateApiConfigFromGateway();
}
} else {
continueConnection();
if (!m_containersModel->isSupportedByCurrentPlatform(container)) {
emit connectionErrorOccurred(ErrorCode::NotSupportedOnThisPlatform);
return;
}
QSharedPointer<ServerController> serverController(new ServerController(m_settings));
VpnConfigurationsController vpnConfigurationController(m_settings, serverController);
QJsonObject containerConfig = m_containersModel->getContainerConfig(container);
ServerCredentials credentials = m_serversModel->getServerCredentials(serverIndex);
auto dns = m_serversModel->getDnsPair(serverIndex);
auto vpnConfiguration = vpnConfigurationController.createVpnConfiguration(dns, serverConfig, containerConfig, container);
emit connectToVpn(serverIndex, credentials, container, vpnConfiguration);
}
void ConnectionController::closeConnection()
@ -167,7 +162,7 @@ void ConnectionController::toggleConnection()
} else if (isConnected()) {
closeConnection();
} else {
openConnection();
emit prepareConfig();
}
}
@ -180,98 +175,3 @@ bool ConnectionController::isConnected() const
{
return m_isConnected;
}
bool ConnectionController::isProtocolConfigExists(const QJsonObject &containerConfig, const DockerContainer container)
{
for (Proto protocol : ContainerProps::protocolsForContainer(container)) {
QString protocolConfig =
containerConfig.value(ProtocolProps::protoToString(protocol)).toObject().value(config_key::last_config).toString();
if (protocolConfig.isEmpty()) {
return false;
}
}
return true;
}
void ConnectionController::continueConnection()
{
int serverIndex = m_serversModel->getDefaultServerIndex();
QJsonObject serverConfig = m_serversModel->getServerConfig(serverIndex);
auto configVersion = serverConfig.value(config_key::configVersion).toInt();
if (!m_serversModel->data(serverIndex, ServersModel::Roles::HasInstalledContainers).toBool()) {
emit noInstalledContainers();
emit m_vpnConnection->connectionStateChanged(Vpn::ConnectionState::Disconnected);
return;
}
DockerContainer container = qvariant_cast<DockerContainer>(m_serversModel->data(serverIndex, ServersModel::Roles::DefaultContainerRole));
if (!m_containersModel->isSupportedByCurrentPlatform(container)) {
emit connectionErrorOccurred(tr("The selected protocol is not supported on the current platform"));
return;
}
if (container == DockerContainer::None) {
emit connectionErrorOccurred(tr("VPN Protocols is not installed.\n Please install VPN container at first"));
return;
}
QSharedPointer<ServerController> serverController(new ServerController(m_settings));
VpnConfigurationsController vpnConfigurationController(m_settings, serverController);
QJsonObject containerConfig = m_containersModel->getContainerConfig(container);
ServerCredentials credentials = m_serversModel->getServerCredentials(serverIndex);
ErrorCode errorCode = updateProtocolConfig(container, credentials, containerConfig, serverController);
if (errorCode != ErrorCode::NoError) {
emit connectionErrorOccurred(errorCode);
return;
}
auto dns = m_serversModel->getDnsPair(serverIndex);
auto vpnConfiguration = vpnConfigurationController.createVpnConfiguration(dns, serverConfig, containerConfig, container, errorCode);
if (errorCode != ErrorCode::NoError) {
emit connectionErrorOccurred(tr("unable to create configuration"));
return;
}
emit connectToVpn(serverIndex, credentials, container, vpnConfiguration);
}
ErrorCode ConnectionController::updateProtocolConfig(const DockerContainer container, const ServerCredentials &credentials,
QJsonObject &containerConfig, QSharedPointer<ServerController> serverController)
{
QFutureWatcher<ErrorCode> watcher;
if (serverController.isNull()) {
serverController.reset(new ServerController(m_settings));
}
QFuture<ErrorCode> future = QtConcurrent::run([this, container, &credentials, &containerConfig, &serverController]() {
ErrorCode errorCode = ErrorCode::NoError;
if (!isProtocolConfigExists(containerConfig, container)) {
VpnConfigurationsController vpnConfigurationController(m_settings, serverController);
errorCode = vpnConfigurationController.createProtocolConfigForContainer(credentials, container, containerConfig);
if (errorCode != ErrorCode::NoError) {
return errorCode;
}
m_serversModel->updateContainerConfig(container, containerConfig);
errorCode = m_clientManagementModel->appendClient(container, credentials, containerConfig,
QString("Admin [%1]").arg(QSysInfo::prettyProductName()), serverController);
if (errorCode != ErrorCode::NoError) {
return errorCode;
}
}
return errorCode;
});
QEventLoop wait;
connect(&watcher, &QFutureWatcher<ErrorCode>::finished, &wait, &QEventLoop::quit);
watcher.setFuture(future);
wait.exec();
return watcher.result();
}

View file

@ -40,30 +40,20 @@ public slots:
void onTranslationsUpdated();
ErrorCode updateProtocolConfig(const DockerContainer container, const ServerCredentials &credentials, QJsonObject &containerConfig,
QSharedPointer<ServerController> serverController = nullptr);
signals:
void connectToVpn(int serverIndex, const ServerCredentials &credentials, DockerContainer container, const QJsonObject &vpnConfiguration);
void disconnectFromVpn();
void connectionStateChanged();
void connectionErrorOccurred(const QString &errorMessage);
void connectionErrorOccurred(ErrorCode errorCode);
void reconnectWithUpdatedContainer(const QString &message);
void noInstalledContainers();
void connectButtonClicked();
void preparingConfig();
void updateApiConfigFromGateway();
void updateApiConfigFromTelegram();
void configFromApiUpdated();
void prepareConfig();
private:
Vpn::ConnectionState getCurrentConnectionState();
bool isProtocolConfigExists(const QJsonObject &containerConfig, const DockerContainer container);
void continueConnection();

View file

@ -6,8 +6,8 @@
#include <QJsonObject>
#include <QRandomGenerator>
#include <QStandardPaths>
#include <QtConcurrent>
#include "core/controllers/apiController.h"
#include "core/controllers/serverController.h"
#include "core/controllers/vpnConfigurationController.h"
#include "core/networkUtilities.h"
@ -15,6 +15,7 @@
#include "ui/models/protocols/awgConfigModel.h"
#include "ui/models/protocols/wireguardConfigModel.h"
#include "utilities.h"
#include "core/api/apiUtils.h"
namespace
{
@ -39,14 +40,12 @@ namespace
InstallController::InstallController(const QSharedPointer<ServersModel> &serversModel, const QSharedPointer<ContainersModel> &containersModel,
const QSharedPointer<ProtocolsModel> &protocolsModel,
const QSharedPointer<ClientManagementModel> &clientManagementModel,
const QSharedPointer<ApiServicesModel> &apiServicesModel, const std::shared_ptr<Settings> &settings,
QObject *parent)
const std::shared_ptr<Settings> &settings, QObject *parent)
: QObject(parent),
m_serversModel(serversModel),
m_containersModel(containersModel),
m_protocolModel(protocolsModel),
m_clientManagementModel(clientManagementModel),
m_apiServicesModel(apiServicesModel),
m_settings(settings)
{
}
@ -773,110 +772,79 @@ void InstallController::addEmptyServer()
emit installServerFinished(tr("Server added successfully"));
}
bool InstallController::fillAvailableServices()
bool InstallController::isConfigValid()
{
ApiController apiController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv());
int serverIndex = m_serversModel->getDefaultServerIndex();
QJsonObject serverConfigObject = m_serversModel->getServerConfig(serverIndex);
QByteArray responseBody;
ErrorCode errorCode = apiController.getServicesList(responseBody);
if (errorCode != ErrorCode::NoError) {
emit installationErrorOccurred(errorCode);
if (apiUtils::isServerFromApi(serverConfigObject)) {
return true;
}
if (!m_serversModel->data(serverIndex, ServersModel::Roles::HasInstalledContainers).toBool()) {
emit noInstalledContainers();
return false;
}
QJsonObject data = QJsonDocument::fromJson(responseBody).object();
m_apiServicesModel->updateModel(data);
return true;
}
DockerContainer container = qvariant_cast<DockerContainer>(m_serversModel->data(serverIndex, ServersModel::Roles::DefaultContainerRole));
bool InstallController::installServiceFromApi()
{
if (m_serversModel->isServerFromApiAlreadyExists(m_apiServicesModel->getCountryCode(), m_apiServicesModel->getSelectedServiceType(),
m_apiServicesModel->getSelectedServiceProtocol())) {
emit installationErrorOccurred(ErrorCode::ApiConfigAlreadyAdded);
if (container == DockerContainer::None) {
emit installationErrorOccurred(ErrorCode::NoInstalledContainersError);
return false;
}
ApiController apiController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv());
QJsonObject serverConfig;
QSharedPointer<ServerController> serverController(new ServerController(m_settings));
VpnConfigurationsController vpnConfigurationController(m_settings, serverController);
ErrorCode errorCode = apiController.getConfigForService(m_settings->getInstallationUuid(true), m_apiServicesModel->getCountryCode(),
m_apiServicesModel->getSelectedServiceType(),
m_apiServicesModel->getSelectedServiceProtocol(), "", QJsonObject(), serverConfig);
if (errorCode != ErrorCode::NoError) {
emit installationErrorOccurred(errorCode);
return false;
}
QJsonObject containerConfig = m_containersModel->getContainerConfig(container);
ServerCredentials credentials = m_serversModel->getServerCredentials(serverIndex);
auto serviceInfo = m_apiServicesModel->getSelectedServiceInfo();
QJsonObject apiConfig = serverConfig.value(configKey::apiConfig).toObject();
apiConfig.insert(configKey::serviceInfo, serviceInfo);
apiConfig.insert(configKey::userCountryCode, m_apiServicesModel->getCountryCode());
apiConfig.insert(configKey::serviceType, m_apiServicesModel->getSelectedServiceType());
apiConfig.insert(configKey::serviceProtocol, m_apiServicesModel->getSelectedServiceProtocol());
QFutureWatcher<ErrorCode> watcher;
serverConfig.insert(configKey::apiConfig, apiConfig);
QFuture<ErrorCode> future = QtConcurrent::run([this, container, &credentials, &containerConfig, &serverController]() {
ErrorCode errorCode = ErrorCode::NoError;
m_serversModel->addServer(serverConfig);
emit installServerFromApiFinished(tr("%1 installed successfully.").arg(m_apiServicesModel->getSelectedServiceName()));
return true;
}
auto isProtocolConfigExists = [](const QJsonObject &containerConfig, const DockerContainer container) {
for (Proto protocol : ContainerProps::protocolsForContainer(container)) {
QString protocolConfig =
containerConfig.value(ProtocolProps::protoToString(protocol)).toObject().value(config_key::last_config).toString();
bool InstallController::updateServiceFromApi(const int serverIndex, const QString &newCountryCode, const QString &newCountryName,
bool reloadServiceConfig)
{
ApiController apiController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv());
if (protocolConfig.isEmpty()) {
return false;
}
}
return true;
};
auto serverConfig = m_serversModel->getServerConfig(serverIndex);
auto apiConfig = serverConfig.value(configKey::apiConfig).toObject();
auto authData = serverConfig.value(configKey::authData).toObject();
if (!isProtocolConfigExists(containerConfig, container)) {
VpnConfigurationsController vpnConfigurationController(m_settings, serverController);
errorCode = vpnConfigurationController.createProtocolConfigForContainer(credentials, container, containerConfig);
if (errorCode != ErrorCode::NoError) {
return errorCode;
}
m_serversModel->updateContainerConfig(container, containerConfig);
QJsonObject newServerConfig;
ErrorCode errorCode = apiController.getConfigForService(
m_settings->getInstallationUuid(true), apiConfig.value(configKey::userCountryCode).toString(),
apiConfig.value(configKey::serviceType).toString(), apiConfig.value(configKey::serviceProtocol).toString(), newCountryCode,
authData, newServerConfig);
if (errorCode != ErrorCode::NoError) {
emit installationErrorOccurred(errorCode);
return false;
}
QJsonObject newApiConfig = newServerConfig.value(configKey::apiConfig).toObject();
newApiConfig.insert(configKey::userCountryCode, apiConfig.value(configKey::userCountryCode));
newApiConfig.insert(configKey::serviceType, apiConfig.value(configKey::serviceType));
newApiConfig.insert(configKey::serviceProtocol, apiConfig.value(configKey::serviceProtocol));
newServerConfig.insert(configKey::apiConfig, newApiConfig);
newServerConfig.insert(configKey::authData, authData);
m_serversModel->editServer(newServerConfig, serverIndex);
if (reloadServiceConfig) {
emit reloadServerFromApiFinished(tr("API config reloaded"));
} else if (newCountryName.isEmpty()) {
emit updateServerFromApiFinished();
} else {
emit changeApiCountryFinished(tr("Successfully changed the country of connection to %1").arg(newCountryName));
}
return true;
}
void InstallController::updateServiceFromTelegram(const int serverIndex)
{
ApiController *apiController = new ApiController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv());
auto serverConfig = m_serversModel->getServerConfig(serverIndex);
apiController->updateServerConfigFromApi(m_settings->getInstallationUuid(true), serverIndex, serverConfig);
connect(apiController, &ApiController::finished, this, [this, apiController](const QJsonObject &config, const int serverIndex) {
m_serversModel->editServer(config, serverIndex);
emit updateServerFromApiFinished();
apiController->deleteLater();
errorCode = m_clientManagementModel->appendClient(container, credentials, containerConfig,
QString("Admin [%1]").arg(QSysInfo::prettyProductName()), serverController);
if (errorCode != ErrorCode::NoError) {
return errorCode;
}
}
return errorCode;
});
connect(apiController, &ApiController::errorOccurred, this, [this, apiController](ErrorCode errorCode) {
QEventLoop wait;
connect(&watcher, &QFutureWatcher<ErrorCode>::finished, &wait, &QEventLoop::quit);
watcher.setFuture(future);
wait.exec();
ErrorCode errorCode = watcher.result();
if (errorCode != ErrorCode::NoError) {
emit installationErrorOccurred(errorCode);
apiController->deleteLater();
});
return false;
}
return true;
}
bool InstallController::isUpdateDockerContainerRequired(const DockerContainer container, const QJsonObject &oldConfig,

View file

@ -10,7 +10,6 @@
#include "ui/models/containers_model.h"
#include "ui/models/protocols_model.h"
#include "ui/models/servers_model.h"
#include "ui/models/apiServicesModel.h"
class InstallController : public QObject
{
@ -19,7 +18,6 @@ public:
explicit InstallController(const QSharedPointer<ServersModel> &serversModel, const QSharedPointer<ContainersModel> &containersModel,
const QSharedPointer<ProtocolsModel> &protocolsModel,
const QSharedPointer<ClientManagementModel> &clientManagementModel,
const QSharedPointer<ApiServicesModel> &apiServicesModel,
const std::shared_ptr<Settings> &settings, QObject *parent = nullptr);
~InstallController();
@ -52,21 +50,13 @@ public slots:
void addEmptyServer();
bool fillAvailableServices();
bool installServiceFromApi();
bool updateServiceFromApi(const int serverIndex, const QString &newCountryCode, const QString &newCountryName, bool reloadServiceConfig = false);
void updateServiceFromTelegram(const int serverIndex);
bool isConfigValid();
signals:
void installContainerFinished(const QString &finishMessage, bool isServiceInstall);
void installServerFinished(const QString &finishMessage);
void installServerFromApiFinished(const QString &message);
void updateContainerFinished(const QString &message);
void updateServerFromApiFinished();
void changeApiCountryFinished(const QString &message);
void reloadServerFromApiFinished(const QString &message);
void scanServerFinished(bool isInstalledContainerFound);
@ -91,6 +81,8 @@ signals:
void cachedProfileCleared(const QString &message);
void apiConfigRemoved(const QString &message);
void noInstalledContainers();
private:
void installServer(const DockerContainer container, const QMap<DockerContainer, QJsonObject> &installedContainers,
const ServerCredentials &serverCredentials, const QSharedPointer<ServerController> &serverController,
@ -108,7 +100,6 @@ private:
QSharedPointer<ContainersModel> m_containersModel;
QSharedPointer<ProtocolsModel> m_protocolModel;
QSharedPointer<ClientManagementModel> m_clientManagementModel;
QSharedPointer<ApiServicesModel> m_apiServicesModel;
std::shared_ptr<Settings> m_settings;

View file

@ -261,7 +261,7 @@ void ServersModel::setProcessedServerIndex(const int index)
updateContainersModel();
if (data(index, IsServerFromGatewayApiRole).toBool()) {
if (data(index, IsCountrySelectionAvailableRole).toBool()) {
emit updateApiLanguageModel();
emit updateApiCountryModel();
}
emit updateApiServicesModel();
}

View file

@ -140,7 +140,7 @@ signals:
void defaultServerContainersUpdated(const QJsonArray &containers);
void defaultServerDefaultContainerChanged(const int containerIndex);
void updateApiLanguageModel();
void updateApiCountryModel();
void updateApiServicesModel();
private:

View file

@ -136,7 +136,7 @@ PageType {
PageController.showBusyIndicator(true)
var prevIndex = ApiCountryModel.currentIndex
ApiCountryModel.currentIndex = index
if (!InstallController.updateServiceFromApi(ServersModel.defaultIndex, countryCode, countryName)) {
if (!ApiConfigsController.updateServiceFromGateway(ServersModel.defaultIndex, countryCode, countryName)) {
ApiCountryModel.currentIndex = prevIndex
}
}

View file

@ -253,7 +253,7 @@ PageType {
PageController.showNotificationMessage(qsTr("Cannot reload API config during active connection"))
} else {
PageController.showBusyIndicator(true)
InstallController.updateServiceFromApi(ServersModel.processedIndex, "", "", true)
ApiConfigsController.updateServiceFromGateway(ServersModel.processedIndex, "", "", true)
PageController.showBusyIndicator(false)
}
}

View file

@ -138,7 +138,7 @@ PageType {
PageController.closePage()
} else {
PageController.showBusyIndicator(true)
InstallController.installServiceFromApi()
ApiConfigsController.importServiceFromGateway()
PageController.showBusyIndicator(false)
}
}

View file

@ -254,7 +254,7 @@ PageType {
property bool isVisible: true
property var handler: function() {
PageController.showBusyIndicator(true)
var result = InstallController.fillAvailableServices()
var result = ApiConfigsController.fillAvailableServices()
PageController.showBusyIndicator(false)
if (result) {
PageController.goToPage(PageEnum.PageSetupWizardApiServicesList)

View file

@ -46,7 +46,7 @@ PageType {
shareConnectionDrawer.configContentHeaderText = qsTr("File with connection settings to ") + serverSelector.text
shareConnectionDrawer.openTriggered()
shareConnectionDrawer.contentVisible = false
shareConnectionDrawer.contentVisible = true
PageController.showBusyIndicator(true)
switch (type) {

View file

@ -132,29 +132,6 @@ PageType {
PageController.showNotificationMessage(message)
}
function onInstallServerFromApiFinished(message) {
PageController.showBusyIndicator(false)
if (!ConnectionController.isConnected) {
ServersModel.setDefaultServerIndex(ServersModel.getServersCount() - 1);
ServersModel.processedIndex = ServersModel.defaultIndex
}
PageController.goToPageHome()
PageController.showNotificationMessage(message)
}
function onChangeApiCountryFinished(message) {
PageController.showBusyIndicator(false)
PageController.goToPageHome()
PageController.showNotificationMessage(message)
}
function onReloadServerFromApiFinished(message) {
PageController.goToPageHome()
PageController.showNotificationMessage(message)
}
function onRemoveProcessedServerFinished(finishedMessage) {
if (!ServersModel.getServersCount()) {
PageController.goToPageHome()
@ -164,6 +141,14 @@ PageType {
}
PageController.showNotificationMessage(finishedMessage)
}
function onNoInstalledContainers() {
PageController.setTriggeredByConnectButton(true)
ServersModel.processedIndex = ServersModel.getDefaultServerIndex()
InstallController.setShouldCreateServer(false)
PageController.goToPage(PageEnum.PageSetupWizardEasy)
}
}
Connections {
@ -175,14 +160,6 @@ PageType {
PageController.showNotificationMessage(message)
PageController.closePage()
}
function onNoInstalledContainers() {
PageController.setTriggeredByConnectButton(true)
ServersModel.processedIndex = ServersModel.getDefaultServerIndex()
InstallController.setShouldCreateServer(false)
PageController.goToPage(PageEnum.PageSetupWizardEasy)
}
}
Connections {
@ -232,6 +209,37 @@ PageType {
}
}
Connections {
target: ApiConfigsController
function onErrorOccurred(error) {
PageController.showErrorMessage(error)
}
function onInstallServerFromApiFinished(message) {
PageController.showBusyIndicator(false)
if (!ConnectionController.isConnected) {
ServersModel.setDefaultServerIndex(ServersModel.getServersCount() - 1);
ServersModel.processedIndex = ServersModel.defaultIndex
}
PageController.goToPageHome()
PageController.showNotificationMessage(message)
}
function onChangeApiCountryFinished(message) {
PageController.showBusyIndicator(false)
PageController.goToPageHome()
PageController.showNotificationMessage(message)
}
function onReloadServerFromApiFinished(message) {
PageController.goToPageHome()
PageController.showNotificationMessage(message)
}
}
StackViewType {
id: tabBarStackView
objectName: "tabBarStackView"