mirror of
https://github.com/amnezia-vpn/amnezia-client.git
synced 2026-05-08 14:33:23 +00:00
refactor: refactor the application to the mvvm architecture (#2009)
* refactor: move business logic from servers model * refactor: move containersModel initialization * refactor: added protocol ui controller and removed settings class from protocols model * refactor: moved cli management to separate controller * refactor: moved app split to separate controller * refactor: moved site split to separate controller * refactor: moved allowed dns to separate controller * refactor: moved language logic to separate ui controller * refactor: removed Settings from devices model * refactor: moved configs and services api logit to separate core controller * refactor: added a layer with a repository between the storage and controllers * refactor: use child parent system instead of smart pointers for controllers and models initialization * refactor: moved install functions from server controller to install controller * refactor: install controller refactoring * chore: renamed exportController to exportUiController * refactor: separate export controller * refactor: removed VpnConfigurationsController * chore: renamed ServerController to SshSession * refactor: replaced ServerController to SshSession * chore: moved qml controllers to separate folder * chore: include fixes * chore: moved utils from core root to core/utils * chore: include fixes * chore: rename core/utils files to camelCase foramt * chore: include fixes * chore: moved some utils to api and selfhosted folders * chore: include fixes * chore: remove unused file * chore: moved serialization folder to core/utils * chore: include fixes * chore: moved some files from client root to core/utils * chore: include fixes * chore: moved ui utils to ui/utils folder * chore: include fixes * chore: move utils from root to ui/utils * chore: include fixes * chore: moved configurators to core/configurators * chore: include fixes * refactor: moved iap logic from ui controller to core * refactor: moved remaining core logic from ApiConfigsController to SubscriptionController * chore: rename apiNewsController to apiNewsUiController * refactor: moved core logic from news ui controller to core * chore: renamed apiConfigsController to subscriptionUiController * chore: include fixes * refactor: merge ApiSettingsController with SubscriptionUiController * chore: moved ui selfhosted controllers to separate folder * chore: include fixes * chore: rename connectionController to connectiomUiController * refactor: moved core logic from connectionUiController * chore: rename settingsController to settingsUiController * refactor: move core logic from settingsUiController * refactor: moved core controller signal/slot connections to separate class * fix: newsController fixes after refactoring * chore: rename model to camelCase * chore: include fixes * chore: remove unused code * chore: move selfhosted core to separate folder * chore: include fixes * chore: rename importController to importUiController * refactor: move core logic from importUiController * chore: minor fixes * chore: remove prem v1 migration * refactor: remove openvpn over cloak and openvpn over shadowsocks * refactor: removed protocolsForContainer function * refactor: add core models * refactor: replace json with c++ structs for server config * refactor: move getDnsPair to ServerConfigUtils * feat: add admin selfhosted config export test * feat: add multi import test * refactor: use coreController for tests * feat: add few simple tests * chore: qrepos in all core controllers * feat: add test for settings * refactor: remove repo dependency from configurators * chore: moved protocols to core folder * chore: include fixes * refactor: moved containersDefs, defs, apiDefs, protocolsDefs to different places * chore: include fixes * chore: build fixes * chore: build fixes * refactor: remove q repo and interface repo * feat: add test for ui servers model and controller * chore: renamed to camelCase * chore: include fixes * refactor: moved core logic from sites ui controller * fix: fixed api config processing * fix: fixed processed server index processing * refactor: protocol models now use c++ structs instead of json configs * refactor: servers model now use c++ struct instead of json config * fix: fixed default server index processing * fix: fix logs init * fix: fix secure settings load keys * chore: build fixes * fix: fixed clear settings * fix: fixed restore backup * fix: sshSession usage * fix: fixed export functions signatures * fix: return missing part from buildContainerWorker * fix: fixed server description on page home * refactor: add container config helpers functions * refactor: c++ structs instead of json * chore: add dns protocol config struct * refactor: move config utils functions to config structs * feat: add test for selfhosted server setup * refactor: separate resources.qrc * fix: fixed server rename * chore: return nameOverriddenByUser * fix: build fixes * fix: fixed models init * refactor: cleanup models usage * fix: fixed models init * chore: cleanup connections and functions signatures * chore: cleanup updateModel calls * feat: added cache to servers repo * chore: cleanup unused functions * chore: ssxray processing * chore: remove transportProtoWithDefault and portWithDefault functions * chore: removed proto types any and l2tp * refactor: moved some constants * fix: fixed native configs export * refactor: remove json from processConfigWith functions * fix: fixed processed server index usage * fix: qml warning fixes * chore: merge fixes * chore: update tests * fix: fixed xray config processing * fix: fixed split tunneling processing * chore: rename sites controllers and model * chore: rename fixes * chore: minor fixes * chore: remove ability to load backup from "file with connection settings" button * fix: fixed api device revoke * fix: remove full model update when renaming a user * fix: fixed premium/free server rename * fix: fixed selfhosted new server install * fix: fixed updateContainer function * fix: fixed revoke for external premium configs * feat: add native configs qr processing * chore: codestyle fixes * fix: fixed admin config create * chore: again remove ability to load backup from "file with connection settings" button * chore: minor fixes * fix: fixed variables initialization * fix: fixed qml imports * fix: minor fixes * fix: fix vpnConnection function calls * feat: add buckup error handling * fix: fixed admin config revok * fix: fixed selfhosted awg installation * fix: ad visability * feat: add empty check for primary dns * chore: minor fixes
This commit is contained in:
337
client/core/controllers/selfhosted/exportController.cpp
Normal file
337
client/core/controllers/selfhosted/exportController.cpp
Normal file
@@ -0,0 +1,337 @@
|
||||
#include "exportController.h"
|
||||
|
||||
#include <QJsonArray>
|
||||
#include <QJsonDocument>
|
||||
|
||||
#include "core/configurators/configuratorBase.h"
|
||||
#include "core/utils/selfhosted/sshSession.h"
|
||||
#include "core/utils/networkUtilities.h"
|
||||
#include "core/utils/qrCodeUtils.h"
|
||||
#include "core/utils/serialization/serialization.h"
|
||||
#include "core/utils/protocolEnum.h"
|
||||
#include "core/protocols/protocolUtils.h"
|
||||
#include "core/utils/constants/configKeys.h"
|
||||
#include "core/utils/constants/protocolConstants.h"
|
||||
#include "core/models/serverConfig.h"
|
||||
#include "core/models/containerConfig.h"
|
||||
#include "core/models/protocolConfig.h"
|
||||
|
||||
using namespace amnezia;
|
||||
|
||||
ExportController::ExportController(SecureServersRepository* serversRepository,
|
||||
SecureAppSettingsRepository* appSettingsRepository,
|
||||
QObject *parent)
|
||||
: QObject(parent),
|
||||
m_serversRepository(serversRepository),
|
||||
m_appSettingsRepository(appSettingsRepository)
|
||||
{
|
||||
}
|
||||
|
||||
ExportController::ExportResult ExportController::generateFullAccessConfig(int serverIndex)
|
||||
{
|
||||
ExportResult result;
|
||||
|
||||
ServerConfig serverConfig = m_serversRepository->server(serverIndex);
|
||||
serverConfig.visit([](auto& arg) {
|
||||
for (auto it = arg.containers.begin(); it != arg.containers.end(); ++it) {
|
||||
it.value().protocolConfig.clearClientConfig();
|
||||
}
|
||||
});
|
||||
|
||||
QJsonObject serverJson = serverConfig.toJson();
|
||||
QByteArray compressedConfig = QJsonDocument(serverJson).toJson();
|
||||
compressedConfig = qCompress(compressedConfig, 8);
|
||||
result.config = generateVpnUrl(compressedConfig);
|
||||
result.qrCodes = generateQrCodesFromConfig(compressedConfig);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
ExportController::ExportResult ExportController::generateConnectionConfig(int serverIndex, int containerIndex, const QString &clientName)
|
||||
{
|
||||
ExportResult result;
|
||||
|
||||
DockerContainer container = static_cast<DockerContainer>(containerIndex);
|
||||
ServerCredentials credentials = m_serversRepository->serverCredentials(serverIndex);
|
||||
ContainerConfig containerConfig = m_serversRepository->containerConfig(serverIndex, container);
|
||||
|
||||
if (ContainerUtils::containerService(container) != ServiceType::Other) {
|
||||
SshSession sshSession;
|
||||
Proto protocol = ContainerUtils::defaultProtocol(container);
|
||||
|
||||
DnsSettings dnsSettings = {
|
||||
m_appSettingsRepository->primaryDns(),
|
||||
m_appSettingsRepository->secondaryDns()
|
||||
};
|
||||
|
||||
auto configurator = ConfiguratorBase::create(protocol, &sshSession);
|
||||
ProtocolConfig newProtocolConfig = configurator->createConfig(credentials, container, containerConfig, dnsSettings, result.errorCode);
|
||||
if (result.errorCode != ErrorCode::NoError) {
|
||||
return result;
|
||||
}
|
||||
|
||||
containerConfig.protocolConfig = newProtocolConfig;
|
||||
|
||||
QString clientId = newProtocolConfig.clientId();
|
||||
if (!clientId.isEmpty()) {
|
||||
emit appendClientRequested(serverIndex, clientId, clientName, container);
|
||||
}
|
||||
}
|
||||
|
||||
ServerConfig serverConfig = m_serversRepository->server(serverIndex);
|
||||
serverConfig.visit([container, containerConfig](auto& arg) {
|
||||
arg.containers.clear();
|
||||
arg.containers[container] = containerConfig;
|
||||
arg.defaultContainer = container;
|
||||
});
|
||||
|
||||
if (serverConfig.isSelfHosted()) {
|
||||
SelfHostedServerConfig* selfHosted = serverConfig.as<SelfHostedServerConfig>();
|
||||
if (selfHosted) {
|
||||
selfHosted->userName.reset();
|
||||
selfHosted->password.reset();
|
||||
selfHosted->port.reset();
|
||||
}
|
||||
}
|
||||
|
||||
auto dns = serverConfig.getDnsPair(m_appSettingsRepository->useAmneziaDns(),
|
||||
m_appSettingsRepository->primaryDns(),
|
||||
m_appSettingsRepository->secondaryDns());
|
||||
serverConfig.visit([&dns](auto& arg) {
|
||||
arg.dns1 = dns.first;
|
||||
arg.dns2 = dns.second;
|
||||
});
|
||||
|
||||
QJsonObject serverJson = serverConfig.toJson();
|
||||
QByteArray compressedConfig = QJsonDocument(serverJson).toJson();
|
||||
compressedConfig = qCompress(compressedConfig, 8);
|
||||
result.config = generateVpnUrl(compressedConfig);
|
||||
result.qrCodes = generateQrCodesFromConfig(compressedConfig);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
ExportController::NativeConfigResult ExportController::generateNativeConfig(int serverIndex, DockerContainer container,
|
||||
const ContainerConfig &containerConfig,
|
||||
const QString &clientName)
|
||||
{
|
||||
NativeConfigResult result;
|
||||
|
||||
if (ContainerUtils::containerService(container) == ServiceType::Other) {
|
||||
return result;
|
||||
}
|
||||
|
||||
Proto protocol = ContainerUtils::defaultProtocol(container);
|
||||
|
||||
ServerCredentials credentials = m_serversRepository->serverCredentials(serverIndex);
|
||||
ServerConfig serverConfig = m_serversRepository->server(serverIndex);
|
||||
auto dns = serverConfig.getDnsPair(m_appSettingsRepository->useAmneziaDns(),
|
||||
m_appSettingsRepository->primaryDns(),
|
||||
m_appSettingsRepository->secondaryDns());
|
||||
|
||||
ContainerConfig modifiedContainerConfig = containerConfig;
|
||||
modifiedContainerConfig.container = container;
|
||||
|
||||
DnsSettings dnsSettings = {
|
||||
m_appSettingsRepository->primaryDns(),
|
||||
m_appSettingsRepository->secondaryDns()
|
||||
};
|
||||
|
||||
SshSession sshSession;
|
||||
auto configurator = ConfiguratorBase::create(protocol, &sshSession);
|
||||
|
||||
ProtocolConfig newProtocolConfig = configurator->createConfig(credentials, container, modifiedContainerConfig, dnsSettings, result.errorCode);
|
||||
if (result.errorCode != ErrorCode::NoError) {
|
||||
return result;
|
||||
}
|
||||
|
||||
ExportSettings exportSettings = { { dns.first, dns.second } };
|
||||
ProtocolConfig processedConfig = configurator->processConfigWithExportSettings(exportSettings, newProtocolConfig);
|
||||
|
||||
if (protocol == Proto::OpenVpn || protocol == Proto::WireGuard || protocol == Proto::Awg) {
|
||||
result.jsonNativeConfig[configKey::config] = processedConfig.nativeConfig();
|
||||
} else {
|
||||
result.jsonNativeConfig = QJsonDocument::fromJson(processedConfig.nativeConfig().toUtf8()).object();
|
||||
}
|
||||
|
||||
if (protocol == Proto::OpenVpn || protocol == Proto::WireGuard || protocol == Proto::Awg || protocol == Proto::Xray) {
|
||||
QString clientId = newProtocolConfig.clientId();
|
||||
if (!clientId.isEmpty()) {
|
||||
emit appendClientRequested(serverIndex, clientId, clientName, container);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
ExportController::ExportResult ExportController::generateOpenVpnConfig(int serverIndex, const QString &clientName)
|
||||
{
|
||||
ExportResult result;
|
||||
|
||||
DockerContainer container = DockerContainer::OpenVpn;
|
||||
ContainerConfig containerConfig = m_serversRepository->containerConfig(serverIndex, container);
|
||||
|
||||
auto nativeResult = generateNativeConfig(serverIndex, container, containerConfig, clientName);
|
||||
if (nativeResult.errorCode != ErrorCode::NoError) {
|
||||
result.errorCode = nativeResult.errorCode;
|
||||
return result;
|
||||
}
|
||||
|
||||
QStringList lines = nativeResult.jsonNativeConfig.value(configKey::config).toString().replace("\r", "").split("\n");
|
||||
for (const QString &line : std::as_const(lines)) {
|
||||
result.config.append(line + "\n");
|
||||
}
|
||||
|
||||
result.qrCodes = generateQrCodesFromConfig(result.config.toUtf8());
|
||||
return result;
|
||||
}
|
||||
|
||||
ExportController::ExportResult ExportController::generateWireGuardConfig(int serverIndex, const QString &clientName)
|
||||
{
|
||||
ExportResult result;
|
||||
|
||||
ContainerConfig containerConfig = m_serversRepository->containerConfig(serverIndex, DockerContainer::WireGuard);
|
||||
|
||||
auto nativeResult = generateNativeConfig(serverIndex, DockerContainer::WireGuard, containerConfig, clientName);
|
||||
if (nativeResult.errorCode != ErrorCode::NoError) {
|
||||
result.errorCode = nativeResult.errorCode;
|
||||
return result;
|
||||
}
|
||||
|
||||
QStringList lines = nativeResult.jsonNativeConfig.value(configKey::config).toString().replace("\r", "").split("\n");
|
||||
for (const QString &line : std::as_const(lines)) {
|
||||
result.config.append(line + "\n");
|
||||
}
|
||||
|
||||
result.qrCodes << generateSingleQrCode(result.config.toUtf8());
|
||||
return result;
|
||||
}
|
||||
|
||||
ExportController::ExportResult ExportController::generateAwgConfig(int serverIndex, int containerIndex, const QString &clientName)
|
||||
{
|
||||
ExportResult result;
|
||||
|
||||
DockerContainer container = static_cast<DockerContainer>(containerIndex);
|
||||
if (container != DockerContainer::Awg && container != DockerContainer::Awg2) {
|
||||
result.errorCode = ErrorCode::InternalError;
|
||||
return result;
|
||||
}
|
||||
ContainerConfig containerConfig = m_serversRepository->containerConfig(serverIndex, container);
|
||||
|
||||
auto nativeResult = generateNativeConfig(serverIndex, container, containerConfig, clientName);
|
||||
if (nativeResult.errorCode != ErrorCode::NoError) {
|
||||
result.errorCode = nativeResult.errorCode;
|
||||
return result;
|
||||
}
|
||||
|
||||
QStringList lines = nativeResult.jsonNativeConfig.value(configKey::config).toString().replace("\r", "").split("\n");
|
||||
for (const QString &line : std::as_const(lines)) {
|
||||
result.config.append(line + "\n");
|
||||
}
|
||||
|
||||
result.qrCodes << generateSingleQrCode(result.config.toUtf8());
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
ExportController::ExportResult ExportController::generateXrayConfig(int serverIndex, const QString &clientName)
|
||||
{
|
||||
ExportResult result;
|
||||
|
||||
ContainerConfig containerConfig = m_serversRepository->containerConfig(serverIndex, DockerContainer::Xray);
|
||||
|
||||
auto nativeResult = generateNativeConfig(serverIndex, DockerContainer::Xray, containerConfig, clientName);
|
||||
if (nativeResult.errorCode != ErrorCode::NoError) {
|
||||
result.errorCode = nativeResult.errorCode;
|
||||
return result;
|
||||
}
|
||||
|
||||
QStringList lines = QString(QJsonDocument(nativeResult.jsonNativeConfig).toJson()).replace("\r", "").split("\n");
|
||||
for (const QString &line : std::as_const(lines)) {
|
||||
result.config.append(line + "\n");
|
||||
}
|
||||
|
||||
// Parse the Xray data to extract VLESS parameters and generate string
|
||||
QJsonObject xrayConfig = nativeResult.jsonNativeConfig;
|
||||
QJsonArray outbounds = xrayConfig.value(amnezia::protocols::xray::outbounds).toArray();
|
||||
|
||||
if (outbounds.isEmpty()) {
|
||||
result.errorCode = ErrorCode::InternalError;
|
||||
return result;
|
||||
}
|
||||
|
||||
QJsonObject outbound = outbounds[0].toObject();
|
||||
QJsonObject settings = outbound.value(amnezia::protocols::xray::settings).toObject();
|
||||
QJsonObject streamSettings = outbound.value(amnezia::protocols::xray::streamSettings).toObject();
|
||||
|
||||
QJsonArray vnext = settings.value(amnezia::protocols::xray::vnext).toArray();
|
||||
if (vnext.isEmpty()) {
|
||||
result.errorCode = ErrorCode::InternalError;
|
||||
return result;
|
||||
}
|
||||
|
||||
QJsonObject server = vnext[0].toObject();
|
||||
QJsonArray users = server.value(amnezia::protocols::xray::users).toArray();
|
||||
if (users.isEmpty()) {
|
||||
result.errorCode = ErrorCode::InternalError;
|
||||
return result;
|
||||
}
|
||||
|
||||
QJsonObject user = users[0].toObject();
|
||||
|
||||
amnezia::serialization::VlessServerObject vlessServer;
|
||||
vlessServer.address = server.value(amnezia::protocols::xray::address).toString();
|
||||
vlessServer.port = server.value(amnezia::protocols::xray::port).toInt();
|
||||
vlessServer.id = user.value(amnezia::protocols::xray::id).toString();
|
||||
vlessServer.flow = user.value(amnezia::protocols::xray::flow).toString("xtls-rprx-vision");
|
||||
vlessServer.encryption = user.value(amnezia::protocols::xray::encryption).toString("none");
|
||||
|
||||
vlessServer.network = streamSettings.value(amnezia::protocols::xray::network).toString("tcp");
|
||||
vlessServer.security = streamSettings.value(amnezia::protocols::xray::security).toString("reality");
|
||||
|
||||
if (vlessServer.security == "reality") {
|
||||
QJsonObject realitySettings = streamSettings.value(amnezia::protocols::xray::realitySettings).toObject();
|
||||
vlessServer.serverName = realitySettings.value(amnezia::protocols::xray::serverName).toString();
|
||||
vlessServer.publicKey = realitySettings.value(amnezia::protocols::xray::publicKey).toString();
|
||||
vlessServer.shortId = realitySettings.value(amnezia::protocols::xray::shortId).toString();
|
||||
vlessServer.fingerprint = realitySettings.value(amnezia::protocols::xray::fingerprint).toString("chrome");
|
||||
vlessServer.spiderX = realitySettings.value(amnezia::protocols::xray::spiderX).toString("");
|
||||
}
|
||||
|
||||
result.nativeConfigString = amnezia::serialization::vless::Serialize(vlessServer, "AmneziaVPN");
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void ExportController::updateClientManagementModel(int serverIndex, int containerIndex)
|
||||
{
|
||||
DockerContainer container = static_cast<DockerContainer>(containerIndex);
|
||||
emit updateClientsRequested(serverIndex, container);
|
||||
}
|
||||
|
||||
void ExportController::revokeConfig(int row, int serverIndex, int containerIndex)
|
||||
{
|
||||
DockerContainer container = static_cast<DockerContainer>(containerIndex);
|
||||
emit revokeClientRequested(serverIndex, row, container);
|
||||
}
|
||||
|
||||
void ExportController::renameClient(int row, const QString &clientName, int serverIndex, int containerIndex)
|
||||
{
|
||||
DockerContainer container = static_cast<DockerContainer>(containerIndex);
|
||||
emit renameClientRequested(serverIndex, row, clientName, container);
|
||||
}
|
||||
|
||||
QString ExportController::generateVpnUrl(const QByteArray &compressedConfig)
|
||||
{
|
||||
return QString("vpn://%1").arg(QString(compressedConfig.toBase64(QByteArray::Base64UrlEncoding | QByteArray::OmitTrailingEquals)));
|
||||
}
|
||||
|
||||
QList<QString> ExportController::generateQrCodesFromConfig(const QByteArray &data)
|
||||
{
|
||||
return qrCodeUtils::generateQrCodeImageSeries(data);
|
||||
}
|
||||
|
||||
QString ExportController::generateSingleQrCode(const QByteArray &data)
|
||||
{
|
||||
auto qr = qrCodeUtils::generateQrCode(data);
|
||||
return qrCodeUtils::svgToBase64(QString::fromStdString(toSvgString(qr, 1)));
|
||||
}
|
||||
Reference in New Issue
Block a user