mirror of
https://github.com/amnezia-vpn/amnezia-client.git
synced 2026-05-17 08:16:06 +03:00
add ui Configuration Files
This commit is contained in:
@@ -1807,6 +1807,16 @@ Thank you for staying with us!</source>
|
||||
<source>Cancel</source>
|
||||
<translation>Отменить</translation>
|
||||
</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>
|
||||
<name>PageSettingsApiInstructions</name>
|
||||
|
||||
@@ -11,6 +11,8 @@
|
||||
namespace
|
||||
{
|
||||
Logger logger("AccountInfoModel");
|
||||
|
||||
constexpr QLatin1String kCountryConfigSourceType("country_config");
|
||||
}
|
||||
|
||||
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;
|
||||
return qMax(0, spare);
|
||||
}
|
||||
case ConfigurationFilesCountRole: {
|
||||
return m_accountInfoData.configurationFilesCount;
|
||||
}
|
||||
}
|
||||
|
||||
return QVariant();
|
||||
@@ -135,6 +140,15 @@ void ApiAccountInfoModel::updateModel(const QJsonObject &accountInfoObject, cons
|
||||
m_availableCountries = accountInfoObject.value(apiDefs::key::availableCountries).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.maxDeviceCount = accountInfoObject.value(apiDefs::key::maxDeviceCount).toInt();
|
||||
accountInfoData.subscriptionEndDate = accountInfoObject.value(apiDefs::key::subscriptionEndDate).toString();
|
||||
@@ -223,6 +237,7 @@ QHash<int, QByteArray> ApiAccountInfoModel::roleNames() const
|
||||
roles[ActiveDeviceCountRole] = "activeDeviceCount";
|
||||
roles[MaxDeviceCountRole] = "maxDeviceCount";
|
||||
roles[AvailableDeviceSlotsRole] = "availableDeviceSlots";
|
||||
roles[ConfigurationFilesCountRole] = "configurationFilesCount";
|
||||
|
||||
return roles;
|
||||
}
|
||||
|
||||
@@ -28,7 +28,8 @@ public:
|
||||
IsInAppPurchaseRole,
|
||||
ActiveDeviceCountRole,
|
||||
MaxDeviceCountRole,
|
||||
AvailableDeviceSlotsRole
|
||||
AvailableDeviceSlotsRole,
|
||||
ConfigurationFilesCountRole
|
||||
};
|
||||
|
||||
explicit ApiAccountInfoModel(QObject *parent = nullptr);
|
||||
@@ -67,6 +68,7 @@ private:
|
||||
|
||||
bool isInAppPurchase = false;
|
||||
bool isRenewalAvailable = false;
|
||||
int configurationFilesCount = 0;
|
||||
};
|
||||
|
||||
AccountInfoData m_accountInfoData;
|
||||
|
||||
@@ -241,6 +241,26 @@ PageType {
|
||||
|
||||
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) {
|
||||
|
||||
@@ -521,26 +521,51 @@ func handleAccountInfo(w http.ResponseWriter, r *http.Request) {
|
||||
drainBody(r)
|
||||
|
||||
mu.Lock()
|
||||
issuedConfigs := make([]issuedConfigInfo, 0, len(issued))
|
||||
gatewayConfigs := make([]issuedConfigInfo, 0, len(issued))
|
||||
for _, cfg := range issued {
|
||||
issuedConfigs = append(issuedConfigs, cfg)
|
||||
gatewayConfigs = append(gatewayConfigs, cfg)
|
||||
}
|
||||
mu.Unlock()
|
||||
sort.Slice(issuedConfigs, func(i, j int) bool {
|
||||
return issuedConfigs[i].InstallationUUID < issuedConfigs[j].InstallationUUID
|
||||
sort.Slice(gatewayConfigs, func(i, j int) bool {
|
||||
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).
|
||||
endDate := time.Now().UTC().AddDate(1, 0, 0).Format(time.RFC3339)
|
||||
resp := map[string]any{
|
||||
"active_device_count": len(issuedConfigs),
|
||||
"active_device_count": len(gatewayConfigs),
|
||||
"max_device_count": 5,
|
||||
"subscription_end_date": endDate,
|
||||
"subscription_description": "Local mock (tools/local_gateway)",
|
||||
"is_renewal_available": false,
|
||||
"supported_protocols": []string{"awg", "vless"},
|
||||
"available_countries": []any{},
|
||||
"issued_configs": issuedConfigs,
|
||||
"issued_configs": allIssued,
|
||||
"support_info": map[string]any{
|
||||
"telegram": "amnezia_support",
|
||||
"email": "support@example.com",
|
||||
|
||||
Reference in New Issue
Block a user