mirror of
https://github.com/amnezia-vpn/amnezia-client.git
synced 2026-06-24 02:00:24 +07:00
add ui Configuration Files
This commit is contained in:
@@ -1807,6 +1807,16 @@ Thank you for staying with us!</source>
|
|||||||
<source>Cancel</source>
|
<source>Cancel</source>
|
||||||
<translation>Отменить</translation>
|
<translation>Отменить</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../ui/qml/Pages2/PageSettingsApiDevices.qml" line="252"/>
|
||||||
|
<source>Configuration Files: %1</source>
|
||||||
|
<translation>Файлы конфигурации: %1</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../ui/qml/Pages2/PageSettingsApiDevices.qml" line="253"/>
|
||||||
|
<source>Generated configuration files also count towards the device limit</source>
|
||||||
|
<translation>Сгенерированные файлы конфигурации тоже учитываются в лимите устройств</translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>PageSettingsApiInstructions</name>
|
<name>PageSettingsApiInstructions</name>
|
||||||
|
|||||||
@@ -11,6 +11,8 @@
|
|||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
Logger logger("AccountInfoModel");
|
Logger logger("AccountInfoModel");
|
||||||
|
|
||||||
|
constexpr QLatin1String kCountryConfigSourceType("country_config");
|
||||||
}
|
}
|
||||||
|
|
||||||
ApiAccountInfoModel::ApiAccountInfoModel(QObject *parent) : QAbstractListModel(parent)
|
ApiAccountInfoModel::ApiAccountInfoModel(QObject *parent) : QAbstractListModel(parent)
|
||||||
@@ -121,6 +123,9 @@ QVariant ApiAccountInfoModel::data(const QModelIndex &index, int role) const
|
|||||||
const int spare = m_accountInfoData.maxDeviceCount - m_accountInfoData.activeDeviceCount;
|
const int spare = m_accountInfoData.maxDeviceCount - m_accountInfoData.activeDeviceCount;
|
||||||
return qMax(0, spare);
|
return qMax(0, spare);
|
||||||
}
|
}
|
||||||
|
case ConfigurationFilesCountRole: {
|
||||||
|
return m_accountInfoData.configurationFilesCount;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return QVariant();
|
return QVariant();
|
||||||
@@ -135,6 +140,15 @@ void ApiAccountInfoModel::updateModel(const QJsonObject &accountInfoObject, cons
|
|||||||
m_availableCountries = accountInfoObject.value(apiDefs::key::availableCountries).toArray();
|
m_availableCountries = accountInfoObject.value(apiDefs::key::availableCountries).toArray();
|
||||||
m_issuedConfigsInfo = accountInfoObject.value(apiDefs::key::issuedConfigs).toArray();
|
m_issuedConfigsInfo = accountInfoObject.value(apiDefs::key::issuedConfigs).toArray();
|
||||||
|
|
||||||
|
int configurationFilesCount = 0;
|
||||||
|
for (int i = 0; i < m_issuedConfigsInfo.size(); ++i) {
|
||||||
|
const QJsonObject issued = m_issuedConfigsInfo.at(i).toObject();
|
||||||
|
if (issued.value(apiDefs::key::sourceType).toString() == kCountryConfigSourceType) {
|
||||||
|
++configurationFilesCount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
accountInfoData.configurationFilesCount = configurationFilesCount;
|
||||||
|
|
||||||
accountInfoData.activeDeviceCount = accountInfoObject.value(apiDefs::key::activeDeviceCount).toInt();
|
accountInfoData.activeDeviceCount = accountInfoObject.value(apiDefs::key::activeDeviceCount).toInt();
|
||||||
accountInfoData.maxDeviceCount = accountInfoObject.value(apiDefs::key::maxDeviceCount).toInt();
|
accountInfoData.maxDeviceCount = accountInfoObject.value(apiDefs::key::maxDeviceCount).toInt();
|
||||||
accountInfoData.subscriptionEndDate = accountInfoObject.value(apiDefs::key::subscriptionEndDate).toString();
|
accountInfoData.subscriptionEndDate = accountInfoObject.value(apiDefs::key::subscriptionEndDate).toString();
|
||||||
@@ -223,6 +237,7 @@ QHash<int, QByteArray> ApiAccountInfoModel::roleNames() const
|
|||||||
roles[ActiveDeviceCountRole] = "activeDeviceCount";
|
roles[ActiveDeviceCountRole] = "activeDeviceCount";
|
||||||
roles[MaxDeviceCountRole] = "maxDeviceCount";
|
roles[MaxDeviceCountRole] = "maxDeviceCount";
|
||||||
roles[AvailableDeviceSlotsRole] = "availableDeviceSlots";
|
roles[AvailableDeviceSlotsRole] = "availableDeviceSlots";
|
||||||
|
roles[ConfigurationFilesCountRole] = "configurationFilesCount";
|
||||||
|
|
||||||
return roles;
|
return roles;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,7 +28,8 @@ public:
|
|||||||
IsInAppPurchaseRole,
|
IsInAppPurchaseRole,
|
||||||
ActiveDeviceCountRole,
|
ActiveDeviceCountRole,
|
||||||
MaxDeviceCountRole,
|
MaxDeviceCountRole,
|
||||||
AvailableDeviceSlotsRole
|
AvailableDeviceSlotsRole,
|
||||||
|
ConfigurationFilesCountRole
|
||||||
};
|
};
|
||||||
|
|
||||||
explicit ApiAccountInfoModel(QObject *parent = nullptr);
|
explicit ApiAccountInfoModel(QObject *parent = nullptr);
|
||||||
@@ -67,6 +68,7 @@ private:
|
|||||||
|
|
||||||
bool isInAppPurchase = false;
|
bool isInAppPurchase = false;
|
||||||
bool isRenewalAvailable = false;
|
bool isRenewalAvailable = false;
|
||||||
|
int configurationFilesCount = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
AccountInfoData m_accountInfoData;
|
AccountInfoData m_accountInfoData;
|
||||||
|
|||||||
@@ -241,6 +241,26 @@ PageType {
|
|||||||
|
|
||||||
DividerType {}
|
DividerType {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
footer: ColumnLayout {
|
||||||
|
width: listView.width
|
||||||
|
|
||||||
|
LabelWithButtonType {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.topMargin: 8
|
||||||
|
|
||||||
|
text: qsTr("Configuration Files: %1").arg(ApiAccountInfoModel.data("configurationFilesCount"))
|
||||||
|
descriptionText: qsTr("Generated configuration files also count towards the device limit")
|
||||||
|
rightImageSource: "qrc:/images/controls/chevron-right.svg"
|
||||||
|
|
||||||
|
clickedFunction: function() {
|
||||||
|
SubscriptionUiController.updateApiCountryModel()
|
||||||
|
PageController.goToPage(PageEnum.PageSettingsApiNativeConfigs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DividerType {}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function deactivateExternalDevice(serverIndex, supportTag, countryCode) {
|
function deactivateExternalDevice(serverIndex, supportTag, countryCode) {
|
||||||
|
|||||||
@@ -521,26 +521,51 @@ func handleAccountInfo(w http.ResponseWriter, r *http.Request) {
|
|||||||
drainBody(r)
|
drainBody(r)
|
||||||
|
|
||||||
mu.Lock()
|
mu.Lock()
|
||||||
issuedConfigs := make([]issuedConfigInfo, 0, len(issued))
|
gatewayConfigs := make([]issuedConfigInfo, 0, len(issued))
|
||||||
for _, cfg := range issued {
|
for _, cfg := range issued {
|
||||||
issuedConfigs = append(issuedConfigs, cfg)
|
gatewayConfigs = append(gatewayConfigs, cfg)
|
||||||
}
|
}
|
||||||
mu.Unlock()
|
mu.Unlock()
|
||||||
sort.Slice(issuedConfigs, func(i, j int) bool {
|
sort.Slice(gatewayConfigs, func(i, j int) bool {
|
||||||
return issuedConfigs[i].InstallationUUID < issuedConfigs[j].InstallationUUID
|
return gatewayConfigs[i].InstallationUUID < gatewayConfigs[j].InstallationUUID
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Seed country_config rows so the client can verify "Configuration Files: N" (ApiAccountInfoModel counts these).
|
||||||
|
// active_device_count must reflect gateway devices only, not these synthetic file rows.
|
||||||
|
nowISO := time.Now().UTC().Format(time.RFC3339)
|
||||||
|
mockCountryConfigs := []issuedConfigInfo{
|
||||||
|
{
|
||||||
|
InstallationUUID: "mock-country-config-de",
|
||||||
|
WorkerLastUpdated: nowISO,
|
||||||
|
LastDownloaded: nowISO,
|
||||||
|
SourceType: "country_config",
|
||||||
|
OSVersion: "",
|
||||||
|
ServerCountryCode: "de",
|
||||||
|
ServerCountryName: "Germany",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
InstallationUUID: "mock-country-config-nl",
|
||||||
|
WorkerLastUpdated: nowISO,
|
||||||
|
LastDownloaded: nowISO,
|
||||||
|
SourceType: "country_config",
|
||||||
|
OSVersion: "",
|
||||||
|
ServerCountryCode: "nl",
|
||||||
|
ServerCountryName: "Netherlands",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
allIssued := append(append([]issuedConfigInfo{}, gatewayConfigs...), mockCountryConfigs...)
|
||||||
|
|
||||||
// Keys match client/core/utils/constants/apiKeys.h (snake_case).
|
// Keys match client/core/utils/constants/apiKeys.h (snake_case).
|
||||||
endDate := time.Now().UTC().AddDate(1, 0, 0).Format(time.RFC3339)
|
endDate := time.Now().UTC().AddDate(1, 0, 0).Format(time.RFC3339)
|
||||||
resp := map[string]any{
|
resp := map[string]any{
|
||||||
"active_device_count": len(issuedConfigs),
|
"active_device_count": len(gatewayConfigs),
|
||||||
"max_device_count": 5,
|
"max_device_count": 5,
|
||||||
"subscription_end_date": endDate,
|
"subscription_end_date": endDate,
|
||||||
"subscription_description": "Local mock (tools/local_gateway)",
|
"subscription_description": "Local mock (tools/local_gateway)",
|
||||||
"is_renewal_available": false,
|
"is_renewal_available": false,
|
||||||
"supported_protocols": []string{"awg", "vless"},
|
"supported_protocols": []string{"awg", "vless"},
|
||||||
"available_countries": []any{},
|
"available_countries": []any{},
|
||||||
"issued_configs": issuedConfigs,
|
"issued_configs": allIssued,
|
||||||
"support_info": map[string]any{
|
"support_info": map[string]any{
|
||||||
"telegram": "amnezia_support",
|
"telegram": "amnezia_support",
|
||||||
"email": "support@example.com",
|
"email": "support@example.com",
|
||||||
|
|||||||
Reference in New Issue
Block a user