Merge pull request #2242 from BEDOLAGA-DEV/dev5

Api update / Miniapp fix
This commit is contained in:
Egor
2026-01-07 17:39:27 +03:00
committed by GitHub
4 changed files with 98 additions and 6 deletions

View File

@@ -538,10 +538,25 @@ class RemnaWaveAPI:
user = self._parse_user(response['response'])
return await self.enrich_user_with_happ_link(user)
async def revoke_user_subscription(self, uuid: str, new_short_uuid: Optional[str] = None) -> RemnaWaveUser:
async def revoke_user_subscription(
self,
uuid: str,
new_short_uuid: Optional[str] = None,
revoke_only_passwords: bool = False
) -> RemnaWaveUser:
"""
Отзывает подписку пользователя (меняет ссылку/пароли).
Args:
uuid: UUID пользователя
new_short_uuid: Новый короткий UUID (опционально, рекомендуется генерировать автоматически)
revoke_only_passwords: Если True, меняются только пароли без изменения URL подписки
"""
data = {}
if new_short_uuid:
data['shortUuid'] = new_short_uuid
if revoke_only_passwords:
data['revokeOnlyPasswords'] = True
response = await self._make_request('POST', f'/api/users/{uuid}/actions/revoke', data)
user = self._parse_user(response['response'])
@@ -809,6 +824,19 @@ class RemnaWaveAPI:
async def get_system_stats(self) -> Dict[str, Any]:
response = await self._make_request('GET', '/api/system/stats')
return response['response']
async def get_system_metadata(self) -> Dict[str, Any]:
"""
Получает метаданные системы Remnawave.
Returns:
Dict с полями:
- version: версия Remnawave
- build: {time, number} - информация о сборке
- git: {backend: {commitSha}, node: {commitSha}} - информация о коммитах
"""
response = await self._make_request('GET', '/api/system/metadata')
return response['response']
async def get_bandwidth_stats(self) -> Dict[str, Any]:
response = await self._make_request('GET', '/api/system/stats/bandwidth')

View File

@@ -5052,6 +5052,8 @@ async def get_subscription_renewal_options_endpoint(
autopay_days_options=renewal_autopay_days_options,
autopay=renewal_autopay_payload,
autopay_settings=renewal_autopay_payload,
is_trial=bool(getattr(subscription, "is_trial", False)),
sales_mode=settings.get_sales_mode(),
**renewal_autopay_extras,
)

View File

@@ -201,6 +201,9 @@ class MiniAppSubscriptionRenewalOptionsResponse(BaseModel):
autopay_days_options: List[int] = Field(default_factory=list)
autopay: Optional[MiniAppSubscriptionAutopay] = None
autopay_settings: Optional[MiniAppSubscriptionAutopay] = None
# Флаги для определения типа действия (покупка vs продление)
is_trial: bool = Field(default=False, alias="isTrial")
sales_mode: str = Field(default="classic", alias="salesMode")
model_config = ConfigDict(populate_by_name=True, extra="allow")

View File

@@ -5903,7 +5903,11 @@
'subscription_settings.confirm.months.one': '{count} month',
'subscription_settings.confirm.months.other': '{count} months',
'subscription_renewal.title': 'Renew subscription',
'subscription_renewal.title.trial_classic': 'Buy subscription',
'subscription_renewal.title.trial_tariffs': 'Buy tariff',
'subscription_renewal.subtitle': 'Choose how long you want to extend access.',
'subscription_renewal.subtitle.trial_classic': 'Choose a period to get started.',
'subscription_renewal.subtitle.trial_tariffs': 'Choose a period to get started.',
'subscription_renewal.status.loading': 'Loading renewal options…',
'subscription_renewal.status.empty': 'No renewal options are currently available.',
'subscription_renewal.error.generic': 'Unable to load renewal options. Please try again later.',
@@ -5915,9 +5919,12 @@
'subscription_renewal.summary.discount': 'You save {percent}%',
'subscription_renewal.summary.insufficient': 'Not enough balance — missing {missing}.',
'subscription_renewal.submit': 'Renew',
'subscription_renewal.submit.trial': 'Buy',
'subscription_renewal.submit.loading': 'Processing…',
'subscription_renewal.confirm.title': 'Confirm renewal',
'subscription_renewal.confirm.title.trial': 'Confirm purchase',
'subscription_renewal.confirm.message': 'Renew the subscription for {period} and pay {amount}?',
'subscription_renewal.confirm.message.trial': 'Buy subscription for {period} and pay {amount}?',
'subscription_renewal.confirm.balance': 'Current balance: {balance}',
'subscription_renewal.confirm.accept': 'Pay',
'subscription_renewal.confirm.cancel': 'Cancel',
@@ -6327,7 +6334,11 @@
'subscription_settings.confirm.months.one': '{count} месяц',
'subscription_settings.confirm.months.other': '{count} месяцев',
'subscription_renewal.title': 'Продление подписки',
'subscription_renewal.title.trial_classic': 'Покупка подписки',
'subscription_renewal.title.trial_tariffs': 'Покупка тарифа',
'subscription_renewal.subtitle': 'Выберите период продления.',
'subscription_renewal.subtitle.trial_classic': 'Выберите период для начала.',
'subscription_renewal.subtitle.trial_tariffs': 'Выберите период для начала.',
'subscription_renewal.status.loading': 'Загружаем доступные периоды…',
'subscription_renewal.status.empty': 'Доступных периодов продления нет.',
'subscription_renewal.error.generic': 'Не удалось загрузить параметры продления. Попробуйте позже.',
@@ -6339,9 +6350,12 @@
'subscription_renewal.summary.discount': 'Выгода {percent}%',
'subscription_renewal.summary.insufficient': 'Недостаточно средств — не хватает {missing}.',
'subscription_renewal.submit': 'Продлить',
'subscription_renewal.submit.trial': 'Купить',
'subscription_renewal.submit.loading': 'Продление…',
'subscription_renewal.confirm.title': 'Подтвердите продление',
'subscription_renewal.confirm.title.trial': 'Подтвердите покупку',
'subscription_renewal.confirm.message': 'Продлить подписку на {period} и списать {amount}?',
'subscription_renewal.confirm.message.trial': 'Купить подписку на {period} и списать {amount}?',
'subscription_renewal.confirm.balance': 'Текущий баланс: {balance}',
'subscription_renewal.confirm.accept': 'Оплатить',
'subscription_renewal.confirm.cancel': 'Отмена',
@@ -14919,6 +14933,44 @@
statusText.classList.add('hidden');
}
// Обновляем заголовок, подзаголовок и кнопку в зависимости от типа подписки
const isTrial = subscriptionRenewalData.isTrial || subscriptionRenewalData.is_trial || false;
const salesMode = subscriptionRenewalData.salesMode || subscriptionRenewalData.sales_mode || 'classic';
// Обновляем заголовок карточки
const titleEl = card.querySelector('[data-i18n="subscription_renewal.title"]');
if (titleEl) {
let titleKey = 'subscription_renewal.title';
if (isTrial) {
titleKey = salesMode === 'tariffs'
? 'subscription_renewal.title.trial_tariffs'
: 'subscription_renewal.title.trial_classic';
}
const titleText = t(titleKey);
titleEl.textContent = titleText !== titleKey ? titleText : (isTrial ? (salesMode === 'tariffs' ? 'Buy tariff' : 'Buy subscription') : 'Renew subscription');
}
// Обновляем подзаголовок
const subtitleEl = card.querySelector('[data-i18n="subscription_renewal.subtitle"]');
if (subtitleEl) {
let subtitleKey = 'subscription_renewal.subtitle';
if (isTrial) {
subtitleKey = salesMode === 'tariffs'
? 'subscription_renewal.subtitle.trial_tariffs'
: 'subscription_renewal.subtitle.trial_classic';
}
const subtitleText = t(subtitleKey);
subtitleEl.textContent = subtitleText !== subtitleKey ? subtitleText : (isTrial ? 'Choose a period to get started.' : 'Choose how long you want to extend access.');
}
// Обновляем текст кнопки
const submitBtn = document.getElementById('subscriptionRenewalSubmit');
if (submitBtn && !subscriptionRenewalSubmitting) {
const btnKey = isTrial ? 'subscription_renewal.submit.trial' : 'subscription_renewal.submit';
const btnText = t(btnKey);
submitBtn.textContent = btnText !== btnKey ? btnText : (isTrial ? 'Buy' : 'Renew');
}
renderSubscriptionRenewalMeta(subscriptionRenewalData);
renderSubscriptionRenewalOptions(subscriptionRenewalData);
renderSubscriptionRenewalSummary(subscriptionRenewalData);
@@ -14930,10 +14982,17 @@
|| (option.priceKopeks !== null ? formatPriceFromKopeks(option.priceKopeks, data.currency) : '');
const periodLabel = buildRenewalPeriodLabel(option);
const messageTemplate = t('subscription_renewal.confirm.message');
const message = messageTemplate && messageTemplate !== 'subscription_renewal.confirm.message'
? messageTemplate.replace('{period}', periodLabel).replace('{amount}', amountLabel)
// Определяем, триал ли это
const isTrial = data?.isTrial || data?.is_trial || false;
const messageKey = isTrial ? 'subscription_renewal.confirm.message.trial' : 'subscription_renewal.confirm.message';
const messageTemplate = t(messageKey);
const defaultMessage = isTrial
? `Buy subscription for ${periodLabel} and pay ${amountLabel}?`
: `Renew the subscription for ${periodLabel} and pay ${amountLabel}?`;
const message = messageTemplate && messageTemplate !== messageKey
? messageTemplate.replace('{period}', periodLabel).replace('{amount}', amountLabel)
: defaultMessage;
const balanceLabel = data?.balanceLabel
|| (data.balanceKopeks !== null ? formatPriceFromKopeks(data.balanceKopeks, data.currency) : null);
@@ -14947,7 +15006,7 @@
finalMessage = `${message}\n${balanceText}`;
}
const titleKey = 'subscription_renewal.confirm.title';
const titleKey = isTrial ? 'subscription_renewal.confirm.title.trial' : 'subscription_renewal.confirm.title';
const confirmKey = 'subscription_renewal.confirm.accept';
const cancelKey = 'subscription_renewal.confirm.cancel';
@@ -14956,7 +15015,7 @@
const cancelText = t(cancelKey);
return showConfirmationPopup({
title: title === titleKey ? 'Confirm renewal' : title,
title: title === titleKey ? (isTrial ? 'Confirm purchase' : 'Confirm renewal') : title,
message: finalMessage,
confirmText: confirmText === confirmKey ? 'Pay' : confirmText,
cancelText: cancelText === cancelKey ? 'Cancel' : cancelText,