diff --git a/app/.DS_Store b/app/.DS_Store new file mode 100644 index 00000000..7de69c5a Binary files /dev/null and b/app/.DS_Store differ diff --git a/app/handlers/.DS_Store b/app/handlers/.DS_Store new file mode 100644 index 00000000..43d7cb4f Binary files /dev/null and b/app/handlers/.DS_Store differ diff --git a/app/handlers/admin/blacklist.py b/app/handlers/admin/blacklist.py index 38201916..82d08d57 100644 --- a/app/handlers/admin/blacklist.py +++ b/app/handlers/admin/blacklist.py @@ -25,6 +25,8 @@ async def show_blacklist_settings( """ Показывает настройки черного списка """ + logger.info(f"Вызван обработчик show_blacklist_settings для пользователя {callback.from_user.id}") + is_enabled = blacklist_service.is_blacklist_check_enabled() github_url = blacklist_service.get_blacklist_github_url() blacklist_count = len(await blacklist_service.get_all_blacklisted_users()) @@ -324,7 +326,7 @@ async def process_blacklist_url( await state.clear() -async def register_blacklist_handlers(dp): +def register_blacklist_handlers(dp): """ Регистрация обработчиков черного списка """ @@ -360,4 +362,4 @@ async def register_blacklist_handlers(dp): dp.message.register( process_blacklist_url, lambda m: True # Фильтр будет внутри функции - ) \ No newline at end of file + ) diff --git a/app/handlers/admin/bulk_ban.py b/app/handlers/admin/bulk_ban.py index 37d82794..2f7a28fd 100644 --- a/app/handlers/admin/bulk_ban.py +++ b/app/handlers/admin/bulk_ban.py @@ -60,8 +60,17 @@ async def process_bulk_ban_list( """ Обработка списка Telegram ID и выполнение массовой блокировки """ + if not message.text: + await message.answer( + "❌ Отправьте текстовое сообщение со списком Telegram ID", + reply_markup=types.InlineKeyboardMarkup(inline_keyboard=[ + [types.InlineKeyboardButton(text="🔙 Назад", callback_data="admin_users")] + ]) + ) + return + input_text = message.text.strip() - + if not input_text: await message.answer( "❌ Введите корректный список Telegram ID", @@ -152,7 +161,7 @@ async def process_bulk_ban_list( await state.clear() -async def register_bulk_ban_handlers(dp): +def register_bulk_ban_handlers(dp): """ Регистрация обработчиков команд для массовой блокировки """ @@ -165,5 +174,5 @@ async def register_bulk_ban_handlers(dp): # Обработчик текстового сообщения с ID для блокировки dp.message.register( process_bulk_ban_list, - lambda m: m.text and AdminStates.waiting_for_bulk_ban_list - ) \ No newline at end of file + AdminStates.waiting_for_bulk_ban_list + ) diff --git a/app/handlers/balance/cryptobot.py b/app/handlers/balance/cryptobot.py index b1291c9d..bd033920 100644 --- a/app/handlers/balance/cryptobot.py +++ b/app/handlers/balance/cryptobot.py @@ -7,6 +7,7 @@ from app.config import settings from app.database.models import User from app.keyboards.inline import get_back_keyboard from app.localization.texts import get_texts +from app.services.blacklist_service import blacklist_service from app.services.payment_service import PaymentService from app.utils.decorators import error_handler from app.states import BalanceStates @@ -96,8 +97,26 @@ async def process_cryptobot_payment_amount( amount_kopeks: int, state: FSMContext ): + # Проверяем, находится ли пользователь в черном списке + is_blacklisted, blacklist_reason = await blacklist_service.is_user_blacklisted( + message.from_user.id, + message.from_user.username + ) + + if is_blacklisted: + logger.warning(f"🚫 Пользователь {message.from_user.id} находится в черном списке: {blacklist_reason}") + try: + await message.answer( + f"🚫 Оплата невозможна\n\n" + f"Причина: {blacklist_reason}\n\n" + f"Если вы считаете, что это ошибка, обратитесь в поддержку." + ) + except Exception as e: + logger.error(f"Ошибка при отправке сообщения о блокировке: {e}") + return + texts = get_texts(db_user.language) - + if not settings.is_cryptobot_enabled(): await message.answer("❌ Оплата криптовалютой временно недоступна") return diff --git a/app/handlers/balance/stars.py b/app/handlers/balance/stars.py index a8cac0f8..71ae3149 100644 --- a/app/handlers/balance/stars.py +++ b/app/handlers/balance/stars.py @@ -6,6 +6,7 @@ from app.config import settings from app.database.models import User from app.keyboards.inline import get_back_keyboard from app.localization.texts import get_texts +from app.services.blacklist_service import blacklist_service from app.services.payment_service import PaymentService from app.states import BalanceStates from app.utils.decorators import error_handler @@ -68,6 +69,24 @@ async def process_stars_payment_amount( amount_kopeks: int, state: FSMContext ): + # Проверяем, находится ли пользователь в черном списке + is_blacklisted, blacklist_reason = await blacklist_service.is_user_blacklisted( + message.from_user.id, + message.from_user.username + ) + + if is_blacklisted: + logger.warning(f"🚫 Пользователь {message.from_user.id} находится в черном списке: {blacklist_reason}") + try: + await message.answer( + f"🚫 Оплата невозможна\n\n" + f"Причина: {blacklist_reason}\n\n" + f"Если вы считаете, что это ошибка, обратитесь в поддержку." + ) + except Exception as e: + logger.error(f"Ошибка при отправке сообщения о блокировке: {e}") + return + texts = get_texts(db_user.language) if not settings.TELEGRAM_STARS_ENABLED: diff --git a/app/handlers/balance/yookassa.py b/app/handlers/balance/yookassa.py index fb6a345c..2ee88ef0 100644 --- a/app/handlers/balance/yookassa.py +++ b/app/handlers/balance/yookassa.py @@ -10,6 +10,7 @@ from app.config import settings from app.database.models import User from app.keyboards.inline import get_back_keyboard from app.localization.texts import get_texts +from app.services.blacklist_service import blacklist_service from app.services.payment_service import PaymentService from app.utils.decorators import error_handler from app.states import BalanceStates @@ -133,8 +134,26 @@ async def process_yookassa_payment_amount( amount_kopeks: int, state: FSMContext ): + # Проверяем, находится ли пользователь в черном списке + is_blacklisted, blacklist_reason = await blacklist_service.is_user_blacklisted( + message.from_user.id, + message.from_user.username + ) + + if is_blacklisted: + logger.warning(f"🚫 Пользователь {message.from_user.id} находится в черном списке: {blacklist_reason}") + try: + await message.answer( + f"🚫 Оплата невозможна\n\n" + f"Причина: {blacklist_reason}\n\n" + f"Если вы считаете, что это ошибка, обратитесь в поддержку." + ) + except Exception as e: + logger.error(f"Ошибка при отправке сообщения о блокировке: {e}") + return + texts = get_texts(db_user.language) - + if not settings.is_yookassa_enabled(): await message.answer("❌ Оплата через YooKassa временно недоступна") return @@ -261,8 +280,26 @@ async def process_yookassa_sbp_payment_amount( amount_kopeks: int, state: FSMContext ): + # Проверяем, находится ли пользователь в черном списке + is_blacklisted, blacklist_reason = await blacklist_service.is_user_blacklisted( + message.from_user.id, + message.from_user.username + ) + + if is_blacklisted: + logger.warning(f"🚫 Пользователь {message.from_user.id} находится в черном списке: {blacklist_reason}") + try: + await message.answer( + f"🚫 Оплата невозможна\n\n" + f"Причина: {blacklist_reason}\n\n" + f"Если вы считаете, что это ошибка, обратитесь в поддержку." + ) + except Exception as e: + logger.error(f"Ошибка при отправке сообщения о блокировке: {e}") + return + texts = get_texts(db_user.language) - + if not settings.is_yookassa_enabled() or not settings.YOOKASSA_SBP_ENABLED: await message.answer("❌ Оплата через СБП временно недоступна") return diff --git a/app/handlers/promocode.py b/app/handlers/promocode.py index 960bd10b..f8303acb 100644 --- a/app/handlers/promocode.py +++ b/app/handlers/promocode.py @@ -7,6 +7,7 @@ from app.states import PromoCodeStates from app.database.models import User from app.keyboards.inline import get_back_keyboard from app.localization.texts import get_texts +from app.services.blacklist_service import blacklist_service from app.services.promocode_service import PromoCodeService from app.services.admin_notification_service import AdminNotificationService from app.utils.decorators import error_handler @@ -79,6 +80,24 @@ async def process_promocode( state: FSMContext, db: AsyncSession ): + # Проверяем, находится ли пользователь в черном списке + is_blacklisted, blacklist_reason = await blacklist_service.is_user_blacklisted( + message.from_user.id, + message.from_user.username + ) + + if is_blacklisted: + logger.warning(f"🚫 Пользователь {message.from_user.id} находится в черном списке: {blacklist_reason}") + try: + await message.answer( + f"🚫 Активация промокода невозможна\n\n" + f"Причина: {blacklist_reason}\n\n" + f"Если вы считаете, что это ошибка, обратитесь в поддержку." + ) + except Exception as e: + logger.error(f"Ошибка при отправке сообщения о блокировке: {e}") + return + texts = get_texts(db_user.language) code = message.text.strip() diff --git a/app/handlers/subscription/purchase.py b/app/handlers/subscription/purchase.py index b10733a5..dbf6b0d7 100644 --- a/app/handlers/subscription/purchase.py +++ b/app/handlers/subscription/purchase.py @@ -51,6 +51,7 @@ from app.services.user_cart_service import user_cart_service from app.localization.texts import get_texts from app.services.admin_notification_service import AdminNotificationService from app.services.remnawave_service import RemnaWaveConfigurationError, RemnaWaveService +from app.services.blacklist_service import blacklist_service from app.services.subscription_checkout_service import ( clear_subscription_checkout_draft, get_subscription_checkout_draft, @@ -995,7 +996,7 @@ async def save_cart_and_redirect_to_topup( 'return_to_cart': True, 'user_id': db_user.id } - + await user_cart_service.save_user_cart(db_user.id, cart_data) await callback.message.edit_text( @@ -1020,7 +1021,7 @@ async def return_to_saved_cart( ): # Получаем данные корзины из Redis cart_data = await user_cart_service.get_user_cart(db_user.id) - + if not cart_data: await callback.answer("❌ Сохраненная корзина не найдена", show_alert=True) return @@ -1347,6 +1348,25 @@ async def confirm_extend_subscription( db_user: User, db: AsyncSession ): + # Проверяем, находится ли пользователь в черном списке + is_blacklisted, blacklist_reason = await blacklist_service.is_user_blacklisted( + callback.from_user.id, + callback.from_user.username + ) + + if is_blacklisted: + logger.warning(f"🚫 Пользователь {callback.from_user.id} находится в черном списке: {blacklist_reason}") + try: + await callback.answer( + f"🚫 Продление подписки невозможно\n\n" + f"Причина: {blacklist_reason}\n\n" + f"Если вы считаете, что это ошибка, обратитесь в поддержку.", + show_alert=True + ) + except Exception as e: + logger.error(f"Ошибка при отправке сообщения о блокировке: {e}") + return + from app.services.admin_notification_service import AdminNotificationService days = int(callback.data.split('_')[2]) @@ -1528,7 +1548,7 @@ async def confirm_extend_subscription( 'description': f"Продление подписки на {days} дней", 'consume_promo_offer': bool(promo_component["discount"] > 0), } - + await user_cart_service.save_user_cart(db_user.id, cart_data) await callback.message.edit_text( @@ -1811,6 +1831,25 @@ async def confirm_purchase( ): from app.services.admin_notification_service import AdminNotificationService + # Проверяем, находится ли пользователь в черном списке + is_blacklisted, blacklist_reason = await blacklist_service.is_user_blacklisted( + callback.from_user.id, + callback.from_user.username + ) + + if is_blacklisted: + logger.warning(f"🚫 Пользователь {callback.from_user.id} находится в черном списке: {blacklist_reason}") + try: + await callback.answer( + f"🚫 Покупка подписки невозможна\n\n" + f"Причина: {blacklist_reason}\n\n" + f"Если вы считаете, что это ошибка, обратитесь в поддержку.", + show_alert=True + ) + except Exception as e: + logger.error(f"Ошибка при отправке сообщения о блокировке: {e}") + return + data = await state.get_data() texts = get_texts(db_user.language) @@ -2101,7 +2140,7 @@ async def confirm_purchase( 'return_to_cart': True, 'user_id': db_user.id } - + await user_cart_service.save_user_cart(db_user.id, cart_data) await callback.message.edit_text( @@ -2210,31 +2249,25 @@ async def confirm_purchase( if should_update_devices: existing_subscription.device_limit = selected_devices # Проверяем, что при обновлении существующей подписки есть хотя бы одна страна - selected_countries = data.get('countries', []) + selected_countries = data.get('countries') if not selected_countries: - # В случае если подписка уже существовала, не разрешаем отключать все страны - # Если подписка новая, разрешаем, но обычно через UI пользователь должен выбрать хотя бы один сервер - if existing_subscription and existing_subscription.connected_squads is not None: - # Проверим, что в данных есть информация о том, что это обновление существующей подписки - # или что-то указывает, что не нужно отключать все страны - pass # Для простоты в этом случае просто проверим, что список стран не пустой - else: - # Для новой подписки разрешаем пустой список, если не является обновлением - pass + # Иногда после возврата к оформлению из сохраненной корзины список стран не передается. + # В таком случае повторно используем текущие подключенные страны подписки. + selected_countries = existing_subscription.connected_squads or [] + if selected_countries: + data['countries'] = selected_countries # чтобы далее использовать фактический список стран - # Но для безопасности - если список стран пустой, проверим, что это разрешено - # иначе вернем ошибку - if not selected_countries: - texts = get_texts(db_user.language) - await callback.message.edit_text( - texts.t( - "COUNTRIES_MINIMUM_REQUIRED", - "❌ Нельзя отключить все страны. Должна быть подключена хотя бы одна страна." - ), - reply_markup=get_back_keyboard(db_user.language) - ) - await callback.answer() - return + if not selected_countries: + texts = get_texts(db_user.language) + await callback.message.edit_text( + texts.t( + "COUNTRIES_MINIMUM_REQUIRED", + "❌ Нельзя отключить все страны. Должна быть подключена хотя бы одна страна." + ), + reply_markup=get_back_keyboard(db_user.language) + ) + await callback.answer() + return existing_subscription.connected_squads = selected_countries @@ -2266,7 +2299,7 @@ async def confirm_purchase( resolved_device_limit = default_device_limit # Проверяем, что для новой подписки также есть хотя бы одна страна, если пользователь проходит через интерфейс стран - new_subscription_countries = data.get('countries', []) + new_subscription_countries = data.get('countries') if not new_subscription_countries: # Проверяем, была ли это покупка через интерфейс стран, и если да, то требуем хотя бы одну страну # Если в данных явно указано, что это интерфейс стран, или есть другие признаки - требуем страну @@ -2304,11 +2337,11 @@ async def confirm_purchase( await add_user_to_servers(db, server_ids) logger.info(f"Сохранены цены серверов за весь период: {server_prices}") - + await db.refresh(db_user) - + subscription_service = SubscriptionService() - + if db_user.remnawave_uuid: remnawave_user = await subscription_service.update_remnawave_user( db, @@ -2323,7 +2356,7 @@ async def confirm_purchase( reset_traffic=settings.RESET_TRAFFIC_ON_PAYMENT, reset_reason="покупка подписки", ) - + if not remnawave_user: logger.error(f"Не удалось создать/обновить RemnaWave пользователя для {db_user.telegram_id}") remnawave_user = await subscription_service.create_remnawave_user( @@ -2332,7 +2365,7 @@ async def confirm_purchase( reset_traffic=settings.RESET_TRAFFIC_ON_PAYMENT, reset_reason="покупка подписки (повторная попытка)", ) - + transaction = await create_transaction( db=db, user_id=db_user.id, @@ -2939,7 +2972,7 @@ def register_handlers(dp: Dispatcher): show_device_connection_help, F.data == "device_connection_help" ) - + # Регистрируем обработчик для простой покупки dp.callback_query.register( handle_simple_subscription_purchase, @@ -2954,12 +2987,31 @@ async def handle_simple_subscription_purchase( db: AsyncSession, ): """Обрабатывает простую покупку подписки.""" + # Проверяем, находится ли пользователь в черном списке + is_blacklisted, blacklist_reason = await blacklist_service.is_user_blacklisted( + callback.from_user.id, + callback.from_user.username + ) + + if is_blacklisted: + logger.warning(f"🚫 Пользователь {callback.from_user.id} находится в черном списке: {blacklist_reason}") + try: + await callback.answer( + f"🚫 Простая покупка подписки невозможна\n\n" + f"Причина: {blacklist_reason}\n\n" + f"Если вы считаете, что это ошибка, обратитесь в поддержку.", + show_alert=True + ) + except Exception as e: + logger.error(f"Ошибка при отправке сообщения о блокировке: {e}") + return + texts = get_texts(db_user.language) - + if not settings.SIMPLE_SUBSCRIPTION_ENABLED: await callback.answer("❌ Простая покупка подписки временно недоступна", show_alert=True) return - + # Определяем ограничение по устройствам для текущего режима simple_device_limit = resolve_simple_subscription_device_limit() @@ -2989,10 +3041,10 @@ async def handle_simple_subscription_purchase( "traffic_limit_gb": settings.SIMPLE_SUBSCRIPTION_TRAFFIC_GB, "squad_uuid": settings.SIMPLE_SUBSCRIPTION_SQUAD_UUID } - + # Сохраняем параметры в состояние await state.update_data(subscription_params=subscription_params) - + # Проверяем баланс пользователя user_balance_kopeks = getattr(db_user, "balance_kopeks", 0) # Рассчитываем цену подписки @@ -3017,7 +3069,7 @@ async def handle_simple_subscription_purchase( if subscription_params["traffic_limit_gb"] == 0 else f"{subscription_params['traffic_limit_gb']} ГБ" ) - + if user_balance_kopeks >= price_kopeks: # Если баланс достаточный, предлагаем оплатить с баланса simple_lines = [ @@ -3040,7 +3092,7 @@ async def handle_simple_subscription_purchase( ]) message_text = "\n".join(simple_lines) - + keyboard = types.InlineKeyboardMarkup(inline_keyboard=[ [types.InlineKeyboardButton(text="✅ Оплатить с баланса", callback_data="simple_subscription_pay_with_balance")], [types.InlineKeyboardButton(text="💳 Другие способы оплаты", callback_data="simple_subscription_other_payment_methods")], @@ -3068,19 +3120,19 @@ async def handle_simple_subscription_purchase( ]) message_text = "\n".join(simple_lines) - + keyboard = _get_simple_subscription_payment_keyboard(db_user.language) - + await callback.message.edit_text( message_text, reply_markup=keyboard, parse_mode="HTML" ) - + await state.set_state(SubscriptionStates.waiting_for_simple_subscription_payment_method) await callback.answer() - + async def _calculate_simple_subscription_price( @@ -3105,14 +3157,14 @@ def _get_simple_subscription_payment_keyboard(language: str) -> types.InlineKeyb """Создает клавиатуру с методами оплаты для простой подписки.""" texts = get_texts(language) keyboard = [] - + # Добавляем доступные методы оплаты if settings.TELEGRAM_STARS_ENABLED: keyboard.append([types.InlineKeyboardButton( text="⭐ Telegram Stars", callback_data="simple_subscription_stars" )]) - + if settings.is_yookassa_enabled(): yookassa_methods = [] if settings.YOOKASSA_SBP_ENABLED: @@ -3126,38 +3178,38 @@ def _get_simple_subscription_payment_keyboard(language: str) -> types.InlineKeyb )) if yookassa_methods: keyboard.append(yookassa_methods) - + if settings.is_cryptobot_enabled(): keyboard.append([types.InlineKeyboardButton( text="🪙 CryptoBot", callback_data="simple_subscription_cryptobot" )]) - + if settings.is_mulenpay_enabled(): mulenpay_name = settings.get_mulenpay_display_name() keyboard.append([types.InlineKeyboardButton( text=f"💳 {mulenpay_name}", callback_data="simple_subscription_mulenpay" )]) - + if settings.is_pal24_enabled(): keyboard.append([types.InlineKeyboardButton( text="💳 PayPalych", callback_data="simple_subscription_pal24" )]) - + if settings.is_wata_enabled(): keyboard.append([types.InlineKeyboardButton( text="💳 WATA", callback_data="simple_subscription_wata" )]) - + # Кнопка назад keyboard.append([types.InlineKeyboardButton( text=texts.BACK, callback_data="subscription_purchase" )]) - + return types.InlineKeyboardMarkup(inline_keyboard=keyboard) @@ -3179,9 +3231,9 @@ async def _extend_existing_subscription( from app.services.subscription_service import SubscriptionService from app.utils.pricing_utils import calculate_months_from_days from datetime import datetime, timedelta - + texts = get_texts(db_user.language) - + # Рассчитываем цену подписки subscription_params = { "period_days": period_days, @@ -3205,7 +3257,7 @@ async def _extend_existing_subscription( price_breakdown.get("servers_price", 0), price_breakdown.get("total_discount", 0), ) - + # Проверяем баланс пользователя if db_user.balance_kopeks < price_kopeks: missing_kopeks = price_kopeks - db_user.balance_kopeks @@ -3223,7 +3275,7 @@ async def _extend_existing_subscription( balance=texts.format_price(db_user.balance_kopeks), missing=texts.format_price(missing_kopeks), ) - + # Подготовим данные для сохранения в корзину from app.services.user_cart_service import user_cart_service cart_data = { @@ -3241,9 +3293,9 @@ async def _extend_existing_subscription( 'squad_uuid': squad_uuid, 'consume_promo_offer': False, } - + await user_cart_service.save_user_cart(db_user.id, cart_data) - + await callback.message.edit_text( message_text, reply_markup=get_insufficient_balance_keyboard( @@ -3255,7 +3307,7 @@ async def _extend_existing_subscription( ) await callback.answer() return - + # Списываем средства success = await subtract_user_balance( db, @@ -3264,15 +3316,15 @@ async def _extend_existing_subscription( f"Продление подписки на {period_days} дней", consume_promo_offer=False, # Простая покупка не использует промо-скидки ) - + if not success: await callback.answer("⚠ Ошибка списания средств", show_alert=True) return - + # Обновляем параметры подписки current_time = datetime.utcnow() old_end_date = current_subscription.end_date - + # Обновляем параметры в зависимости от типа текущей подписки if current_subscription.is_trial: # При продлении триальной подписки переводим её в обычную @@ -3296,7 +3348,7 @@ async def _extend_existing_subscription( if squad_uuid and squad_uuid not in current_subscription.connected_squads: # Используем += для безопасного добавления в список SQLAlchemy current_subscription.connected_squads = current_subscription.connected_squads + [squad_uuid] - + # Продлеваем подписку if current_subscription.end_date > current_time: # Если подписка ещё активна, добавляем дни к текущей дате окончания @@ -3304,15 +3356,15 @@ async def _extend_existing_subscription( else: # Если подписка уже истекла, начинаем от текущего времени new_end_date = current_time + timedelta(days=period_days) - + current_subscription.end_date = new_end_date current_subscription.updated_at = current_time - + # Сохраняем изменения await db.commit() await db.refresh(current_subscription) await db.refresh(db_user) - + # Обновляем пользователя в Remnawave subscription_service = SubscriptionService() try: @@ -3328,7 +3380,7 @@ async def _extend_existing_subscription( logger.error("⚠ ОШИБКА ОБНОВЛЕНИЯ REMNAWAVE") except Exception as e: logger.error(f"⚠ ИСКЛЮЧЕНИЕ ПРИ ОБНОВЛЕНИИ REMNAWAVE: {e}") - + # Создаём транзакцию transaction = await create_transaction( db=db, @@ -3337,7 +3389,7 @@ async def _extend_existing_subscription( amount_kopeks=price_kopeks, description=f"Продление подписки на {period_days} дней" ) - + # Отправляем уведомление админу try: notification_service = AdminNotificationService(callback.bot) @@ -3353,7 +3405,7 @@ async def _extend_existing_subscription( ) except Exception as e: logger.error(f"Ошибка отправки уведомления о продлении: {e}") - + # Отправляем сообщение пользователю success_message = ( "✅ Подписка успешно продлена!\n\n" @@ -3361,15 +3413,15 @@ async def _extend_existing_subscription( f"Действует до: {format_local_datetime(new_end_date, '%d.%m.%Y %H:%M')}\n\n" f"💰 Списано: {texts.format_price(price_kopeks)}" ) - + # Если это была триальная подписка, добавляем информацию о преобразовании if current_subscription.is_trial: success_message += "\n🎯 Триальная подписка преобразована в платную" - + await callback.message.edit_text( success_message, reply_markup=get_back_keyboard(db_user.language) ) - + logger.info(f"✅ Пользователь {db_user.telegram_id} продлил подписку на {period_days} дней за {price_kopeks / 100}₽") await callback.answer() diff --git a/docker-compose.yml b/docker-compose.yml index c62611f2..71c766bd 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -11,7 +11,7 @@ services: volumes: - postgres_data:/var/lib/postgresql/data networks: - - bot_network + - remnawave-network healthcheck: test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-remnawave_user} -d ${POSTGRES_DB:-remnawave_bot}"] interval: 30s @@ -27,7 +27,7 @@ services: volumes: - redis_data:/data networks: - - bot_network + - remnawave-network healthcheck: test: ["CMD", "redis-cli", "ping"] interval: 30s @@ -74,7 +74,7 @@ services: ports: - "${WEB_API_PORT:-8080}:8080" networks: - - bot_network + - remnawave-network healthcheck: test: ["CMD-SHELL", "python -c \"import requests, os; requests.get('http://localhost:8080/health', headers={'X-API-Key': os.environ.get('WEB_API_DEFAULT_TOKEN')}, timeout=5) or exit(1)\""] interval: 60s @@ -89,9 +89,7 @@ volumes: driver: local networks: - bot_network: - driver: bridge - ipam: - config: - - subnet: 172.20.0.0/16 - gateway: 172.20.0.1 + remnawave-network: + name: remnawave-network + driver: bridge + external: true