Массовая синхронизация пользователей с ремнем!

This commit is contained in:
gy9vin
2025-11-02 15:07:28 +03:00
parent f26df389b4
commit 005fc95433
4 changed files with 425 additions and 47 deletions
+57
View File
@@ -1148,6 +1148,63 @@ async def check_and_update_subscription_status(
return subscription return subscription
async def create_subscription_no_commit(
db: AsyncSession,
user_id: int,
status: str = "trial",
is_trial: bool = True,
end_date: datetime = None,
traffic_limit_gb: int = 10,
traffic_used_gb: float = 0.0,
device_limit: int = 1,
connected_squads: list = None,
remnawave_short_uuid: str = None,
subscription_url: str = "",
subscription_crypto_link: str = "",
autopay_enabled: Optional[bool] = None,
autopay_days_before: Optional[int] = None,
) -> Subscription:
"""
Создает подписку без немедленного коммита для пакетной обработки
"""
if end_date is None:
end_date = datetime.utcnow() + timedelta(days=3)
if connected_squads is None:
connected_squads = []
subscription = Subscription(
user_id=user_id,
status=status,
is_trial=is_trial,
end_date=end_date,
traffic_limit_gb=traffic_limit_gb,
traffic_used_gb=traffic_used_gb,
device_limit=device_limit,
connected_squads=connected_squads,
remnawave_short_uuid=remnawave_short_uuid,
subscription_url=subscription_url,
subscription_crypto_link=subscription_crypto_link,
autopay_enabled=(
settings.is_autopay_enabled_by_default()
if autopay_enabled is None
else autopay_enabled
),
autopay_days_before=(
settings.DEFAULT_AUTOPAY_DAYS_BEFORE
if autopay_days_before is None
else autopay_days_before
),
)
db.add(subscription)
# Не коммитим сразу, оставляем для пакетной обработки
logger.info(f"✅ Подготовлена подписка для пользователя {user_id} (ожидает коммита)")
return subscription
async def create_subscription( async def create_subscription(
db: AsyncSession, db: AsyncSession,
user_id: int, user_id: int,
+45 -2
View File
@@ -122,6 +122,51 @@ async def _get_or_create_default_promo_group(db: AsyncSession) -> PromoGroup:
return default_group return default_group
async def create_user_no_commit(
db: AsyncSession,
telegram_id: int,
username: str = None,
first_name: str = None,
last_name: str = None,
language: str = "ru",
referred_by_id: int = None,
referral_code: str = None
) -> User:
"""
Создает пользователя без немедленного коммита для пакетной обработки
"""
if not referral_code:
referral_code = await create_unique_referral_code(db)
default_group = await _get_or_create_default_promo_group(db)
promo_group_id = default_group.id
safe_first = sanitize_telegram_name(first_name)
safe_last = sanitize_telegram_name(last_name)
user = User(
telegram_id=telegram_id,
username=username,
first_name=safe_first,
last_name=safe_last,
language=language,
referred_by_id=referred_by_id,
referral_code=referral_code,
balance_kopeks=0,
has_had_paid_subscription=False,
has_made_first_topup=False,
promo_group_id=promo_group_id,
)
db.add(user)
# Не коммитим сразу, оставляем для пакетной обработки
logger.info(
f"✅ Подготовлен пользователь {telegram_id} с реферальным кодом {referral_code} (ожидает коммита)"
)
return user
async def create_user( async def create_user(
db: AsyncSession, db: AsyncSession,
telegram_id: int, telegram_id: int,
@@ -515,7 +560,6 @@ async def get_users_list(
if search.isdigit(): if search.isdigit():
conditions.append(User.telegram_id == int(search)) conditions.append(User.telegram_id == int(search))
conditions.append(User.id == int(search)) # Add support for searching by internal user ID
query = query.where(or_(*conditions)) query = query.where(or_(*conditions))
@@ -613,7 +657,6 @@ async def get_users_count(
if search.isdigit(): if search.isdigit():
conditions.append(User.telegram_id == int(search)) conditions.append(User.telegram_id == int(search))
conditions.append(User.id == int(search)) # Add support for searching by internal user ID
query = query.where(or_(*conditions)) query = query.where(or_(*conditions))
+316 -36
View File
@@ -18,6 +18,7 @@ from sqlalchemy.orm import selectinload
from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy.ext.asyncio import AsyncSession
from app.database.crud.user import ( from app.database.crud.user import (
create_user, create_user,
create_user_no_commit,
get_users_list, get_users_list,
get_user_by_telegram_id, get_user_by_telegram_id,
update_user, update_user,
@@ -203,6 +204,87 @@ class RemnaWaveService:
return unique_users return unique_users
def _extract_user_data_from_description(self, description: str) -> Tuple[Optional[str], Optional[str], Optional[str]]:
"""
Извлекает имя, фамилию и username из описания пользователя в панели Remnawave.
Args:
description: Описание пользователя из панели
Returns:
Tuple[first_name, last_name, username] - извлеченные данные
"""
logger.debug(f"📥 Парсинг описания пользователя: '{description}'")
if not description:
logger.debug("❌ Пустое описание пользователя")
return None, None, None
# Ищем строки в формате "Bot user: ..."
import re
# Паттерн для извлечения данных из "Bot user: Name @username" или "Bot user: Name"
# Также поддерживаем просто "Name @username" без префикса
bot_user_patterns = [
r"Bot user:\s*(.+)", # С префиксом
r"^([\w\s]+(?:@[\w_]+)?)$", # Без префикса
]
user_info = None
for pattern in bot_user_patterns:
match = re.search(pattern, description)
if match:
user_info = match.group(1).strip()
logger.debug(f"🔍 Найдена информация о пользователе: '{user_info}'")
break
if not user_info:
logger.debug("❌ Не удалось найти информацию о пользователе в описании")
return None, None, None
# Паттерн для извлечения username (@username в конце)
username_pattern = r"\s+(@[\w_]+)$"
username_match = re.search(username_pattern, user_info)
if username_match:
username_with_at = username_match.group(1)
username = username_with_at[1:] if username_with_at.startswith('@') else username_with_at # Убираем символ @
# Убираем username из основной информации
name_part = user_info[:username_match.start()].strip()
logger.debug(f"📱 Найден username: '{username_with_at}' (обработанный: '{username}'), остаток: '{name_part}'")
else:
username = None
name_part = user_info
logger.debug(f"📱 Username не найден, имя: '{name_part}'")
# Разделяем имя и фамилию
if name_part and not name_part.startswith("@"):
# Если есть имя (не начинается с @), используем его
name_parts = name_part.split()
logger.debug(f"🔤 Части имени: {name_parts}")
if len(name_parts) >= 2:
# Первое слово - имя, остальные - фамилия
first_name = name_parts[0]
last_name = " ".join(name_parts[1:]) if len(name_parts) > 1 else None
logger.debug(f"👤 Имя: '{first_name}', Фамилия: '{last_name}'")
elif len(name_parts) == 1 and not name_parts[0].startswith("@"):
# Только имя
first_name = name_parts[0]
last_name = None
logger.debug(f"👤 Только имя: '{first_name}'")
else:
first_name = None
last_name = None
logger.debug("👤 Имя не определено")
else:
first_name = None
last_name = None
logger.debug("👤 Имя не определено (начинается с @)")
logger.debug(f"✅ Результат парсинга: first_name='{first_name}', last_name='{last_name}', username='{username}'")
return first_name, last_name, username
async def _get_or_create_bot_user_from_panel( async def _get_or_create_bot_user_from_panel(
self, self,
db: AsyncSession, db: AsyncSession,
@@ -218,14 +300,30 @@ class RemnaWaveService:
if telegram_id is None: if telegram_id is None:
return None, False return None, False
username = panel_user.get("username") or f"user_{telegram_id}" # Извлекаем настоящее имя пользователя из описания
description = panel_user.get("description") or ""
first_name_from_desc, last_name_from_desc, username_from_desc = self._extract_user_data_from_description(description)
# Используем извлеченное имя или дефолтное значение
if first_name_from_desc and last_name_from_desc:
full_first_name = first_name_from_desc
full_last_name = last_name_from_desc
elif first_name_from_desc:
full_first_name = first_name_from_desc
full_last_name = last_name_from_desc
else:
full_first_name = f"User {telegram_id}"
full_last_name = None
username = panel_user.get("username") or username_from_desc or f"user_{telegram_id}"
try: try:
db_user = await create_user( db_user = await create_user_no_commit(
db=db, db=db,
telegram_id=telegram_id, telegram_id=telegram_id,
username=username, username=username,
first_name=f"Panel User {telegram_id}", first_name=full_first_name,
last_name=full_last_name,
language="ru", language="ru",
) )
return db_user, True return db_user, True
@@ -241,16 +339,28 @@ class RemnaWaveService:
# create_user уже выполняет rollback при необходимости # create_user уже выполняет rollback при необходимости
pass pass
existing_user = await get_user_by_telegram_id(db, telegram_id) try:
if existing_user is None: existing_user = await get_user_by_telegram_id(db, telegram_id)
raise create_error if existing_user is None:
logger.error("❌ Не удалось найти существующего пользователя с telegram_id %s", telegram_id)
return None, False
logger.debug( logger.debug(
"Используется существующий пользователь %s после конфликта уникальности: %s", "Используется существующий пользователь %s после конфликта уникальности: %s",
telegram_id, telegram_id,
create_error, create_error,
) )
return existing_user, False return existing_user, False
except Exception as load_error:
logger.error("❌ Ошибка загрузки существующего пользователя %s: %s", telegram_id, load_error)
return None, False
except Exception as general_error:
logger.error("❌ Общая ошибка создания/загрузки пользователя %s: %s", telegram_id, general_error)
try:
await db.rollback()
except:
pass
return None, False
async def get_system_statistics(self) -> Dict[str, Any]: async def get_system_statistics(self) -> Dict[str, Any]:
try: try:
@@ -886,7 +996,17 @@ class RemnaWaveService:
logger.info(f"✅ Всего загружено пользователей из панели: {len(panel_users)}") logger.info(f"✅ Всего загружено пользователей из панели: {len(panel_users)}")
bot_users = await get_users_list(db, offset=0, limit=10000) # Загрузка пользователей с их подписками за один запрос (bulk loading)
from sqlalchemy.orm import selectinload
from app.database.models import User, Subscription
from sqlalchemy import select
# Получаем всех пользователей с их подписками за один запрос
bot_users_result = await db.execute(
select(User)
.options(selectinload(User.subscription))
)
bot_users = bot_users_result.scalars().all()
bot_users_by_telegram_id = {user.telegram_id: user for user in bot_users} bot_users_by_telegram_id = {user.telegram_id: user for user in bot_users}
logger.info(f"📊 Пользователей в боте: {len(bot_users)}") logger.info(f"📊 Пользователей в боте: {len(bot_users)}")
@@ -910,6 +1030,21 @@ class RemnaWaveService:
panel_telegram_ids = set(unique_panel_users_map.keys()) panel_telegram_ids = set(unique_panel_users_map.keys())
# Для ускорения - подготовим данные о подписках
# Соберем все существующие подписки за один запрос
existing_subscriptions_result = await db.execute(
select(Subscription)
.join(User)
.options(selectinload(Subscription.user))
)
existing_subscriptions = existing_subscriptions_result.scalars().all()
# Создадим словарь для быстрого доступа к подпискам
subscriptions_by_user_id = {sub.user_id: sub for sub in existing_subscriptions}
# Для оптимизации коммитим изменения каждые N пользователей
batch_size = 50
for i, panel_user in enumerate(unique_panel_users): for i, panel_user in enumerate(unique_panel_users):
try: try:
telegram_id = panel_user.get('telegramId') telegram_id = panel_user.get('telegramId')
@@ -937,18 +1072,56 @@ class RemnaWaveService:
bot_users_by_telegram_id[telegram_id] = db_user bot_users_by_telegram_id[telegram_id] = db_user
# Обновляем имя пользователя из описания панели
description = panel_user.get("description") or ""
first_name_from_desc, last_name_from_desc, username_from_desc = self._extract_user_data_from_description(description)
# Используем извлеченное имя или текущее
if first_name_from_desc:
full_first_name = first_name_from_desc
else:
full_first_name = db_user.first_name or f"User {telegram_id}"
if last_name_from_desc:
full_last_name = last_name_from_desc
else:
full_last_name = db_user.last_name
extracted_username = username_from_desc or panel_user.get("username")
if extracted_username:
username = extracted_username
else:
username = db_user.username or f"user_{telegram_id}"
updated_fields = []
if db_user.first_name != full_first_name:
db_user.first_name = full_first_name
updated_fields.append("first_name")
if db_user.last_name != full_last_name:
db_user.last_name = full_last_name
updated_fields.append("last_name")
if db_user.username != username:
db_user.username = username
updated_fields.append("username")
# Если пользователь был обновлен, сохраняем изменения
if updated_fields:
logger.info(f"🔄 Обновлены поля {updated_fields} для пользователя {telegram_id}")
await db.flush() # Сохраняем изменения без коммита
if not db_user.remnawave_uuid: if not db_user.remnawave_uuid:
await update_user(db, db_user, remnawave_uuid=panel_user.get('uuid')) await update_user(db, db_user, remnawave_uuid=panel_user.get('uuid'))
if is_created or not getattr(db_user, "subscription", None):
await self._create_subscription_from_panel_data(db, db_user, panel_user)
else:
await self._update_subscription_from_panel_data(db, db_user, panel_user)
if is_created: if is_created:
await self._create_subscription_from_panel_data(db, db_user, panel_user)
stats["created"] += 1 stats["created"] += 1
logger.info(f"✅ Создан пользователь {telegram_id} с подпиской") logger.info(f"✅ Создан пользователь {telegram_id} с подпиской")
else: else:
# Обновляем данные существующего пользователя
# Но теперь мы уже загрузили подписку с пользователем, нет необходимости перезагружать
await self._update_subscription_from_panel_data(db, db_user, panel_user)
stats["updated"] += 1 stats["updated"] += 1
logger.info( logger.info(
f"♻️ Обновлена подписка существующего пользователя {telegram_id}" f"♻️ Обновлена подписка существующего пользователя {telegram_id}"
@@ -958,24 +1131,93 @@ class RemnaWaveService:
if sync_type in ["update_only", "all"]: if sync_type in ["update_only", "all"]:
logger.debug(f"🔄 Обновление пользователя {telegram_id}") logger.debug(f"🔄 Обновление пользователя {telegram_id}")
# Обновляем имя пользователя из описания панели
description = panel_user.get("description") or ""
first_name_from_desc, last_name_from_desc, username_from_desc = self._extract_user_data_from_description(description)
# Используем извлеченное имя или текущее
if first_name_from_desc:
full_first_name = first_name_from_desc
else:
full_first_name = db_user.first_name or f"User {telegram_id}"
if last_name_from_desc:
full_last_name = last_name_from_desc
else:
full_last_name = db_user.last_name
extracted_username = username_from_desc or panel_user.get("username")
if extracted_username:
username = extracted_username
else:
username = db_user.username or f"user_{telegram_id}"
updated_fields = []
if db_user.first_name != full_first_name:
db_user.first_name = full_first_name
updated_fields.append("first_name")
if db_user.last_name != full_last_name:
db_user.last_name = full_last_name
updated_fields.append("last_name")
if db_user.username != username:
db_user.username = username
updated_fields.append("username")
# Если пользователь был обновлен, сохраняем изменения
if updated_fields:
logger.info(f"🔄 Обновлены поля {updated_fields} для пользователя {telegram_id}")
await db.flush() # Сохраняем изменения без коммита
# Проверяем, есть ли у пользователя подписка, загруженная с пользователем
if hasattr(db_user, 'subscription') and db_user.subscription:
# Используем уже загруженную подписку
await self._update_subscription_from_panel_data(db, db_user, panel_user)
else:
# Если подписки нет, создаем новую
await self._create_subscription_from_panel_data(db, db_user, panel_user)
if not db_user.remnawave_uuid: if not db_user.remnawave_uuid:
await update_user(db, db_user, remnawave_uuid=panel_user.get('uuid')) await update_user(db, db_user, remnawave_uuid=panel_user.get('uuid'))
await self._update_subscription_from_panel_data(db, db_user, panel_user)
stats["updated"] += 1 stats["updated"] += 1
logger.debug(f"✅ Обновлён пользователь {telegram_id}") logger.debug(f"✅ Обновлён пользователь {telegram_id}")
except Exception as user_error: except Exception as user_error:
logger.error(f"❌ Ошибка обработки пользователя {telegram_id}: {user_error}") logger.error(f"❌ Ошибка обработки пользователя {telegram_id}: {user_error}")
stats["errors"] += 1 stats["errors"] += 1
try:
await db.rollback() # Выполняем rollback при ошибке
except:
pass
continue continue
# Коммитим изменения каждые N пользователей для ускорения
if (i + 1) % batch_size == 0:
try:
await db.commit()
logger.debug(f"📦 Коммит изменений после обработки {i+1} пользователей")
except Exception as commit_error:
logger.error(f"❌ Ошибка коммита после обработки {i+1} пользователей: {commit_error}")
await db.rollback()
stats["errors"] += batch_size # Учитываем ошибки за всю группу
# Коммитим оставшиеся изменения
try:
await db.commit()
except Exception as final_commit_error:
logger.error(f"❌ Ошибка финального коммита: {final_commit_error}")
await db.rollback()
if sync_type == "all": if sync_type == "all":
logger.info("🗑️ Деактивация подписок пользователей, отсутствующих в панели...") logger.info("🗑️ Деактивация подписок пользователей, отсутствующих в панели...")
batch_size = 50
processed_count = 0
for telegram_id, db_user in bot_users_by_telegram_id.items(): for telegram_id, db_user in bot_users_by_telegram_id.items():
if telegram_id not in panel_telegram_ids and db_user.subscription: if telegram_id not in panel_telegram_ids and hasattr(db_user, 'subscription') and db_user.subscription:
try: try:
logger.info(f"🗑️ Деактивация подписки пользователя {telegram_id} (нет в панели)") logger.info(f"🗑️ Деактивация подписки пользователя {telegram_id} (нет в панели)")
@@ -1021,15 +1263,39 @@ class RemnaWaveService:
db_user.remnawave_uuid = None db_user.remnawave_uuid = None
await db.commit()
stats["deleted"] += 1 stats["deleted"] += 1
logger.info(f"✅ Деактивирована подписка пользователя {telegram_id} (сохранен баланс)") logger.info(f"✅ Деактивирована подписка пользователя {telegram_id} (сохранен баланс)")
processed_count += 1
# Коммитим изменения каждые N пользователей
if processed_count % batch_size == 0:
try:
await db.commit()
logger.debug(f"📦 Коммит изменений после деактивации {processed_count} подписок")
except Exception as commit_error:
logger.error(f"❌ Ошибка коммита после деактивации {processed_count} подписок: {commit_error}")
await db.rollback()
stats["errors"] += batch_size
break # Прерываем цикл при ошибке коммита
except Exception as delete_error: except Exception as delete_error:
logger.error(f"❌ Ошибка деактивации подписки {telegram_id}: {delete_error}") logger.error(f"❌ Ошибка деактивации подписки {telegram_id}: {delete_error}")
stats["errors"] += 1 stats["errors"] += 1
await db.rollback() try:
await db.rollback()
except:
pass
else:
# Увеличиваем счетчик для отслеживания прогресса
processed_count += 1
# Коммитим оставшиеся изменения
try:
await db.commit()
except Exception as final_commit_error:
logger.error(f"❌ Ошибка финального коммита при деактивации: {final_commit_error}")
await db.rollback()
logger.info(f"🎯 Синхронизация завершена: создано {stats['created']}, обновлено {stats['updated']}, деактивировано {stats['deleted']}, ошибок {stats['errors']}") logger.info(f"🎯 Синхронизация завершена: создано {stats['created']}, обновлено {stats['updated']}, деактивировано {stats['deleted']}, ошибок {stats['errors']}")
return stats return stats
@@ -1040,7 +1306,7 @@ class RemnaWaveService:
async def _create_subscription_from_panel_data(self, db: AsyncSession, user, panel_user): async def _create_subscription_from_panel_data(self, db: AsyncSession, user, panel_user):
try: try:
from app.database.crud.subscription import create_subscription from app.database.crud.subscription import create_subscription_no_commit
from app.database.models import SubscriptionStatus from app.database.models import SubscriptionStatus
expire_at_str = panel_user.get('expireAt', '') expire_at_str = panel_user.get('expireAt', '')
@@ -1088,16 +1354,16 @@ class RemnaWaveService:
) )
} }
subscription = await create_subscription(db, **subscription_data) subscription = await create_subscription_no_commit(db, **subscription_data)
logger.info(f"Создана подписка для пользователя {user.telegram_id} до {expire_at}") logger.info(f"Подготовлена подписка для пользователя {user.telegram_id} до {expire_at}")
except Exception as e: except Exception as e:
logger.error(f"❌ Ошибка создания подписки для пользователя {user.telegram_id}: {e}") logger.error(f"❌ Ошибка создания подписки для пользователя {user.telegram_id}: {e}")
try: try:
from app.database.crud.subscription import create_subscription from app.database.crud.subscription import create_subscription_no_commit
from app.database.models import SubscriptionStatus from app.database.models import SubscriptionStatus
basic_subscription = await create_subscription( basic_subscription = await create_subscription_no_commit(
db=db, db=db,
user_id=user.id, user_id=user.id,
status=SubscriptionStatus.ACTIVE.value, status=SubscriptionStatus.ACTIVE.value,
@@ -1114,7 +1380,7 @@ class RemnaWaveService:
or (panel_user.get('happ') or {}).get('cryptoLink', '') or (panel_user.get('happ') or {}).get('cryptoLink', '')
) )
) )
logger.info(f"Создана базовая подписка для пользователя {user.telegram_id}") logger.info(f"Подготовлена базовая подписка для пользователя {user.telegram_id}")
except Exception as basic_error: except Exception as basic_error:
logger.error(f"❌ Ошибка создания базовой подписки: {basic_error}") logger.error(f"❌ Ошибка создания базовой подписки: {basic_error}")
@@ -1122,8 +1388,19 @@ class RemnaWaveService:
try: try:
from app.database.crud.subscription import get_subscription_by_user_id from app.database.crud.subscription import get_subscription_by_user_id
from app.database.models import SubscriptionStatus from app.database.models import SubscriptionStatus
subscription = await get_subscription_by_user_id(db, user.id) # Сначала пытаемся использовать уже загруженную подписку, если она есть
subscription = None
try:
# Проверяем, что подписка уже загружена (была загружена через selectinload)
if hasattr(user, 'subscription') and user.subscription:
subscription = user.subscription
else:
# В противном случае, получаем подписку через CRUD метод
subscription = await get_subscription_by_user_id(db, user.id)
except:
# Если не удалось получить подписку через ленивую загрузку
subscription = await get_subscription_by_user_id(db, user.id)
if not subscription: if not subscription:
await self._create_subscription_from_panel_data(db, user, panel_user) await self._create_subscription_from_panel_data(db, user, panel_user)
@@ -1202,12 +1479,14 @@ class RemnaWaveService:
subscription.connected_squads = squad_uuids subscription.connected_squads = squad_uuids
logger.debug(f"Обновлены подключенные сквады: {squad_uuids}") logger.debug(f"Обновлены подключенные сквады: {squad_uuids}")
await db.commit() # Коммитим изменения позже, в основном цикле, чтобы уменьшить количество транзакций
logger.debug(f"✅ Обновлена подписка для пользователя {user.telegram_id}") logger.debug(f"✅ Обновлена подписка для пользователя {user.telegram_id}")
except Exception as e: except Exception as e:
logger.error(f"❌ Ошибка обновления подписки для пользователя {user.telegram_id}: {e}") logger.error(f"❌ Ошибка обновления подписки для пользователя {user.telegram_id}: {e}")
await db.rollback() # Не делаем rollback, так как это может повлиять на другие операции
# Ошибку прокидываем выше для корректной обработки в основном цикле
raise
async def sync_users_to_panel(self, db: AsyncSession) -> Dict[str, int]: async def sync_users_to_panel(self, db: AsyncSession) -> Dict[str, int]:
try: try:
@@ -1269,7 +1548,8 @@ class RemnaWaveService:
await update_user(db, user, remnawave_uuid=new_user.uuid) await update_user(db, user, remnawave_uuid=new_user.uuid)
subscription.remnawave_short_uuid = new_user.short_uuid subscription.remnawave_short_uuid = new_user.short_uuid
await db.commit() # Убираем немедленный коммит для пакетной обработки
# await db.commit()
stats["created"] += 1 stats["created"] += 1
+7 -9
View File
@@ -11,7 +11,7 @@ services:
volumes: volumes:
- postgres_data:/var/lib/postgresql/data - postgres_data:/var/lib/postgresql/data
networks: networks:
- bot_network - remnawave-network
healthcheck: healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-remnawave_user} -d ${POSTGRES_DB:-remnawave_bot}"] test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-remnawave_user} -d ${POSTGRES_DB:-remnawave_bot}"]
interval: 30s interval: 30s
@@ -27,7 +27,7 @@ services:
volumes: volumes:
- redis_data:/data - redis_data:/data
networks: networks:
- bot_network - remnawave-network
healthcheck: healthcheck:
test: ["CMD", "redis-cli", "ping"] test: ["CMD", "redis-cli", "ping"]
interval: 30s interval: 30s
@@ -80,7 +80,7 @@ services:
- "${WATA_WEBHOOK_PORT:-8085}:8085" - "${WATA_WEBHOOK_PORT:-8085}:8085"
- "${HELEKET_WEBHOOK_PORT:-8086}:8086" - "${HELEKET_WEBHOOK_PORT:-8086}:8086"
networks: networks:
- bot_network - remnawave-network
healthcheck: healthcheck:
test: ["CMD-SHELL", "python -c 'import requests; requests.get(\"http://localhost:8081/health\", timeout=5)' || exit 1"] test: ["CMD-SHELL", "python -c 'import requests; requests.get(\"http://localhost:8081/health\", timeout=5)' || exit 1"]
interval: 60s interval: 60s
@@ -95,9 +95,7 @@ volumes:
driver: local driver: local
networks: networks:
bot_network: remnawave-network:
driver: bridge name: remnawave-network
ipam: driver: bridge
config: external: true
- subnet: 172.20.0.0/16
gateway: 172.20.0.1