fixed captcha

This commit is contained in:
dranik
2026-04-30 12:26:41 +03:00
parent 2729682309
commit 63cc1e6a8e
8 changed files with 192 additions and 9 deletions

View File

@@ -218,7 +218,8 @@ ErrorCode SubscriptionController::executeRequest(const QString &endpoint, const
ErrorCode SubscriptionController::importServiceFromGateway(const QString &userCountryCode, const QString &serviceType,
const QString &serviceProtocol, const ProtocolData &protocolData,
ServerConfig &serverConfig)
ServerConfig &serverConfig,
CaptchaInfo &captchaInfo)
{
GatewayRequestData gatewayRequestData { QSysInfo::productType(),
QString(APP_VERSION),
@@ -235,6 +236,19 @@ ErrorCode SubscriptionController::importServiceFromGateway(const QString &userCo
QByteArray responseBody;
ErrorCode errorCode = executeRequest(QString("%1v1/config"), apiPayload, responseBody);
if (errorCode == ErrorCode::ApiCaptchaRequiredError) {
QJsonDocument jsonDoc = QJsonDocument::fromJson(responseBody);
if (jsonDoc.isObject()) {
QJsonObject jsonObj = jsonDoc.object();
captchaInfo.captchaId = jsonObj.value("captcha_id").toString();
captchaInfo.captchaImageBase64 = jsonObj.value("captcha_image").toString();
captchaInfo.hint = jsonObj.value("hint").toString();
captchaInfo.isRequired = true;
}
return errorCode;
}
if (errorCode != ErrorCode::NoError) {
return errorCode;
}
@@ -244,11 +258,11 @@ ErrorCode SubscriptionController::importServiceFromGateway(const QString &userCo
if (errorCode != ErrorCode::NoError) {
return errorCode;
}
updateApiConfigInJson(serverConfigJson, serviceType, serviceProtocol, userCountryCode, responseBody);
ServerConfig serverConfigModel = ServerConfig::fromJson(serverConfigJson);
if (!serverConfigModel.isApiV2()) {
return ErrorCode::InternalError;
}
@@ -1092,3 +1106,50 @@ QFuture<QPair<ErrorCode, QString>> SubscriptionController::getRenewalLink(int se
return promise->future();
}
ErrorCode SubscriptionController::resolveImportServiceCaptcha(const QString &userCountryCode,
const QString &serviceType,
const QString &serviceProtocol,
const ProtocolData &protocolData,
const QString &captchaId,
const QString &captchaSolution,
ServerConfig &serverConfig) {
GatewayRequestData gatewayRequestData{QSysInfo::productType(),
QString(APP_VERSION),
m_appSettingsRepository->getAppLanguage().name().split("_").first(),
m_appSettingsRepository->getInstallationUuid(true),
userCountryCode,
"",
serviceType,
serviceProtocol,
QJsonObject()};
QJsonObject apiPayload = gatewayRequestData.toJsonObject();
appendProtocolDataToApiPayload(serviceProtocol, protocolData, apiPayload);
apiPayload["captcha_id"] = captchaId;
apiPayload["captcha_solution"] = captchaSolution;
QByteArray responseBody;
ErrorCode errorCode = executeRequest(QString("%1v1/config"), apiPayload, responseBody);
if (errorCode != ErrorCode::NoError) {
return errorCode;
}
QJsonObject serverConfigJson;
errorCode = extractServerConfigJsonFromResponse(responseBody, serviceProtocol, protocolData, serverConfigJson);
if (errorCode != ErrorCode::NoError) {
return errorCode;
}
updateApiConfigInJson(serverConfigJson, serviceType, serviceProtocol, userCountryCode, responseBody);
ServerConfig serverConfigModel = ServerConfig::fromJson(serverConfigJson);
if (!serverConfigModel.isApiV2()) {
return ErrorCode::InternalError;
}
m_serversRepository->addServer(serverConfigModel);
serverConfig = serverConfigModel;
return ErrorCode::NoError;
}

View File

@@ -43,6 +43,13 @@ public:
QJsonObject toJsonObject() const;
};
struct CaptchaInfo {
QString captchaId;
QString captchaImageBase64;
QString hint;
bool isRequired = false;
};
explicit SubscriptionController(SecureServersRepository* serversRepository,
SecureAppSettingsRepository* appSettingsRepository);
@@ -52,7 +59,8 @@ public:
ErrorCode importServiceFromGateway(const QString &userCountryCode, const QString &serviceType,
const QString &serviceProtocol, const ProtocolData &protocolData,
ServerConfig &serverConfig);
ServerConfig &serverConfig,
CaptchaInfo &captchaInfo);
ErrorCode importTrialFromGateway(const QString &userCountryCode, const QString &serviceType,
const QString &serviceProtocol, const QString &email,
ServerConfig &serverConfig);
@@ -104,6 +112,11 @@ public:
AppStoreRestoreResult processAppStoreRestore(const QString &userCountryCode, const QString &serviceType,
const QString &serviceProtocol);
ErrorCode resolveImportServiceCaptcha(const QString &userCountryCode, const QString &serviceType,
const QString &serviceProtocol, const ProtocolData &protocolData,
const QString &captchaId, const QString &captchaSolution,
ServerConfig &serverConfig);
private:
ErrorCode executeRequest(const QString &endpoint, const QJsonObject &apiPayload, QByteArray &responseBody, bool isTestPurchase = false);
bool isApiKeyExpired(int serverIndex) const;

View File

@@ -16,7 +16,11 @@
using namespace amnezia;
namespace {
#ifdef AMNEZIA_LOCAL_GATEWAY
constexpr char gatewayEndpoint[] = "http://localhost:8080/";
#else
constexpr char gatewayEndpoint[] = "http://gw.amnezia.org:80/";
#endif
}
SecureAppSettingsRepository::SecureAppSettingsRepository(SecureQSettings* settings, QObject *parent)
@@ -260,7 +264,11 @@ void SecureAppSettingsRepository::setGatewayEndpoint(const QString &endpoint)
void SecureAppSettingsRepository::resetGatewayEndpoint()
{
m_gatewayEndpoint = gatewayEndpoint;
#ifdef AMNEZIA_LOCAL_GATEWAY
setValue(QStringLiteral("Conf/devGatewayEnv"), true);
#else
setValue("Conf/gatewayEndpoint", gatewayEndpoint);
#endif
}
void SecureAppSettingsRepository::setDevGatewayEndpoint()

View File

@@ -76,6 +76,11 @@ SubscriptionUiController::SubscriptionUiController(ServersController* serversCon
});
}
bool SubscriptionUiController::isCaptchaAwaitingUser() const
{
return m_captchaState.isPending;
}
bool SubscriptionUiController::exportVpnKey(int serverIndex, const QString &fileName)
{
if (fileName.isEmpty()) {
@@ -260,21 +265,99 @@ bool SubscriptionUiController::importFreeFromGateway()
}
SubscriptionController::ProtocolData protocolData = m_subscriptionController->generateProtocolData(serviceProtocol);
SubscriptionController::CaptchaInfo captchaInfo;
ServerConfig serverConfig;
ErrorCode errorCode = m_subscriptionController->importServiceFromGateway(userCountryCode, serviceType,
serviceProtocol, protocolData,
serverConfig);
serverConfig, captchaInfo);
if (errorCode == ErrorCode::NoError) {
emit installServerFromApiFinished(tr("%1 installed successfully.").arg(m_apiServicesModel->getSelectedServiceName()));
return true;
} else if (errorCode == ErrorCode::ApiCaptchaRequiredError && captchaInfo.isRequired) {
m_captchaState.userCountryCode = userCountryCode;
m_captchaState.serviceType = serviceType;
m_captchaState.serviceProtocol = serviceProtocol;
m_captchaState.openvpnPrivKey = protocolData.certPrivKey;
m_captchaState.wireguardClientPrivKey = protocolData.wireGuardClientPrivKey;
m_captchaState.wireguardClientPubKey = protocolData.wireGuardClientPubKey;
m_captchaState.xrayUuid = protocolData.xrayUuid;
m_captchaState.isPending = true;
emit captchaRequired(captchaInfo.captchaId, captchaInfo.captchaImageBase64,
captchaInfo.hint.isEmpty() ? tr("Please solve the CAPTCHA to continue") : captchaInfo.hint);
return false;
} else {
emit errorOccurred(errorCode);
return false;
}
}
void SubscriptionUiController::onCaptchaSolved(const QString &captchaId, const QString &solution)
{
if (!m_captchaState.isPending) {
return;
}
SubscriptionController::ProtocolData protocolData;
protocolData.certPrivKey = m_captchaState.openvpnPrivKey;
protocolData.wireGuardClientPrivKey = m_captchaState.wireguardClientPrivKey;
protocolData.wireGuardClientPubKey = m_captchaState.wireguardClientPubKey;
protocolData.xrayUuid = m_captchaState.xrayUuid;
ServerConfig serverConfig;
ErrorCode errorCode = m_subscriptionController->resolveImportServiceCaptcha(
m_captchaState.userCountryCode,
m_captchaState.serviceType,
m_captchaState.serviceProtocol,
protocolData,
captchaId,
solution,
serverConfig);
m_captchaState.isPending = false;
if (errorCode == ErrorCode::NoError) {
m_serversController->addServer(serverConfig);
emit installServerFromApiFinished(tr("%1 installed successfully.").arg(m_apiServicesModel->getSelectedServiceName()));
} else {
emit errorOccurred(errorCode);
}
}
void SubscriptionUiController::onRefreshCaptchaRequested()
{
if (!m_captchaState.isPending) {
return;
}
SubscriptionController::ProtocolData protocolData;
protocolData.certPrivKey = m_captchaState.openvpnPrivKey;
protocolData.wireGuardClientPrivKey = m_captchaState.wireguardClientPrivKey;
protocolData.wireGuardClientPubKey = m_captchaState.wireguardClientPubKey;
protocolData.xrayUuid = m_captchaState.xrayUuid;
SubscriptionController::CaptchaInfo captchaInfo;
ServerConfig serverConfig;
ErrorCode errorCode = m_subscriptionController->importServiceFromGateway(
m_captchaState.userCountryCode,
m_captchaState.serviceType,
m_captchaState.serviceProtocol,
protocolData,
serverConfig,
captchaInfo);
if (errorCode == ErrorCode::ApiCaptchaRequiredError && captchaInfo.isRequired) {
emit captchaRequired(captchaInfo.captchaId, captchaInfo.captchaImageBase64,
captchaInfo.hint.isEmpty() ? tr("Please solve the CAPTCHA to continue") : captchaInfo.hint);
} else if (errorCode != ErrorCode::NoError) {
m_captchaState.isPending = false;
emit errorOccurred(errorCode);
}
}
bool SubscriptionUiController::importTrialFromGateway(const QString &email)
{
emit trialEmailError(QString());

View File

@@ -56,6 +56,10 @@ public slots:
void setCurrentProtocol(int serverIndex, const QString &protocolName);
bool isVlessProtocol(int serverIndex);
bool isCaptchaAwaitingUser() const;
void onCaptchaSolved(const QString &captchaId, const QString &solution);
void onRefreshCaptchaRequested();
void removeApiConfig(int serverIndex);
bool getAccountInfo(int serverIndex, bool reload);
@@ -79,6 +83,19 @@ signals:
void apiConfigRemoved(const QString &message);
void vpnKeyExportReady();
void captchaRequired(const QString &captchaId, const QString &captchaImageBase64, const QString &hint);
private:
struct CaptchaState {
QString userCountryCode;
QString serviceType;
QString serviceProtocol;
QString openvpnPrivKey;
QString wireguardClientPrivKey;
QString wireguardClientPubKey;
QString xrayUuid;
bool isPending = false;
} m_captchaState;
private:
QList<QString> getQrCodes();

View File

@@ -130,7 +130,7 @@ PageType {
PageController.showBusyIndicator(false)
if (!result) {
if (ApiConfigsController.isCaptchaAwaitingUser()) {
if (SubscriptionUiController.isCaptchaAwaitingUser()) {
return
}
var endpoint = ApiServicesModel.getStoreEndpoint()

View File

@@ -211,11 +211,11 @@ Window {
onCaptchaSolved: function(captchaId, solution) {
captchaDialog.close()
ApiConfigsController.onCaptchaSolved(captchaId, solution)
SubscriptionUiController.onCaptchaSolved(captchaId, solution)
}
onRefreshCaptchaRequested: function() {
ApiConfigsController.onRefreshCaptchaRequested()
SubscriptionUiController.onRefreshCaptchaRequested()
}
}
}

View File

@@ -23,6 +23,7 @@
<file>Controls2/BackButtonType.qml</file>
<file>Controls2/BasicButtonType.qml</file>
<file>Controls2/BusyIndicatorType.qml</file>
<file>Controls2/CaptchaDialogType.qml</file>
<file>Controls2/CardType.qml</file>
<file>Controls2/CardWithIconsType.qml</file>
<file>Controls2/CheckBoxType.qml</file>