diff --git a/app/database/crud/server_squad.py b/app/database/crud/server_squad.py
index 15af5746..85f1dc58 100644
--- a/app/database/crud/server_squad.py
+++ b/app/database/crud/server_squad.py
@@ -1,17 +1,11 @@
import logging
from typing import Iterable, List, Optional, Sequence, Tuple
-from sqlalchemy import select, func, update, delete, text
+from sqlalchemy import select, and_, func, update, delete, text
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy.orm import selectinload
-from app.database.models import (
- PromoGroup,
- ServerSquad,
- SubscriptionServer,
- Subscription,
- User,
-)
+from app.database.models import PromoGroup, ServerSquad, SubscriptionServer, Subscription
logger = logging.getLogger(__name__)
@@ -234,48 +228,14 @@ async def delete_server_squad(db: AsyncSession, server_id: int) -> bool:
return True
-async def get_server_users(
- db: AsyncSession,
- server_id: int,
-) -> List[dict]:
-
- result = await db.execute(
- select(User, Subscription, SubscriptionServer)
- .join(Subscription, Subscription.user_id == User.id)
- .join(
- SubscriptionServer,
- SubscriptionServer.subscription_id == Subscription.id,
- )
- .where(SubscriptionServer.server_squad_id == server_id)
- .order_by(User.id)
- )
-
- users_info: List[dict] = []
- for user, subscription, link in result.all():
- users_info.append(
- {
- "user_id": user.id,
- "telegram_id": user.telegram_id,
- "username": user.username,
- "first_name": user.first_name,
- "subscription_id": subscription.id,
- "subscription_status": subscription.status,
- "subscription_end_date": subscription.end_date,
- "connected_at": link.connected_at,
- }
- )
-
- return users_info
-
-
async def sync_with_remnawave(
db: AsyncSession,
remnawave_squads: List[dict]
) -> Tuple[int, int, int]:
-
+
created = 0
updated = 0
- removed = 0
+ disabled = 0
existing_servers = {}
result = await db.execute(select(ServerSquad))
@@ -305,64 +265,15 @@ async def sync_with_remnawave(
)
created += 1
- missing_servers = [
- server for uuid, server in existing_servers.items()
- if uuid not in remnawave_uuids
- ]
-
- if missing_servers:
- missing_ids = [server.id for server in missing_servers]
- missing_uuids = [server.squad_uuid for server in missing_servers]
-
- connections_count_result = await db.execute(
- select(func.count(SubscriptionServer.id)).where(
- SubscriptionServer.server_squad_id.in_(missing_ids)
- )
- )
- removed_connections = connections_count_result.scalar() or 0
-
- await db.execute(
- delete(SubscriptionServer).where(
- SubscriptionServer.server_squad_id.in_(missing_ids)
- )
- )
-
- if removed_connections:
- logger.info(
- "🔁 Удалено %s привязок подписок к отсутствующим серверам",
- removed_connections,
- )
-
- subscriptions_result = await db.execute(
- select(Subscription).where(Subscription.connected_squads.isnot(None))
- )
-
- for subscription in subscriptions_result.scalars().all():
- current_squads = list(subscription.connected_squads or [])
- if not current_squads:
- continue
-
- updated_squads = [
- squad_uuid for squad_uuid in current_squads
- if squad_uuid not in missing_uuids
- ]
-
- if updated_squads != current_squads:
- subscription.connected_squads = updated_squads
-
- for server in missing_servers:
- logger.info(
- "🗑️ Удаляем сервер %s (UUID: %s) — отсутствует в RemnaWave",
- server.display_name,
- server.squad_uuid,
- )
- await db.delete(server)
- removed += 1
-
+ for uuid, server in existing_servers.items():
+ if uuid not in remnawave_uuids and server.is_available:
+ server.is_available = False
+ disabled += 1
+
await db.commit()
-
- logger.info(f"🔄 Синхронизация завершена: +{created} ~{updated} -{removed}")
- return created, updated, removed
+
+ logger.info(f"🔄 Синхронизация завершена: +{created} ~{updated} -{disabled}")
+ return created, updated, disabled
def _generate_display_name(original_name: str) -> str:
diff --git a/app/handlers/admin/servers.py b/app/handlers/admin/servers.py
index 1978236c..9edc3af0 100644
--- a/app/handlers/admin/servers.py
+++ b/app/handlers/admin/servers.py
@@ -15,7 +15,6 @@ from app.database.crud.server_squad import (
create_server_squad,
get_available_server_squads,
update_server_squad_promo_groups,
- get_server_users,
)
from app.database.crud.promo_group import get_promo_groups_with_counts
from app.services.remnawave_service import RemnaWaveService
@@ -82,11 +81,6 @@ def _build_server_edit_view(server):
text="📝 Описание", callback_data=f"admin_server_edit_desc_{server.id}"
),
],
- [
- types.InlineKeyboardButton(
- text="👥 Пользователи", callback_data=f"admin_server_users_{server.id}"
- ),
- ],
[
types.InlineKeyboardButton(
text="❌ Отключить" if server.is_available else "✅ Включить",
@@ -282,7 +276,7 @@ async def sync_servers_with_remnawave(
)
return
- created, updated, removed = await sync_with_remnawave(db, squads)
+ created, updated, disabled = await sync_with_remnawave(db, squads)
await cache.delete_pattern("available_countries*")
@@ -292,7 +286,7 @@ async def sync_servers_with_remnawave(
📊 Результаты:
• Создано новых серверов: {created}
• Обновлено существующих: {updated}
-• Удалено отсутствующих: {removed}
+• Отключено неактивных: {disabled}
• Всего обработано: {len(squads)}
ℹ️ Новые серверы созданы как недоступные.
@@ -1025,119 +1019,9 @@ async def save_server_promo_groups(
reply_markup=keyboard,
parse_mode="HTML",
)
-
await callback.answer("✅ Промогруппы обновлены!")
-@admin_required
-@error_handler
-async def show_server_users(
- callback: types.CallbackQuery,
- db_user: User,
- db: AsyncSession,
-):
-
- server_id = int(callback.data.split('_')[-1])
- server = await get_server_squad_by_id(db, server_id)
-
- if not server:
- await callback.answer("❌ Сервер не найден!", show_alert=True)
- return
-
- users = await get_server_users(db, server_id)
-
- status_emojis = {
- "active": "✅",
- "trial": "🧪",
- "expired": "⏰",
- "disabled": "⛔",
- }
- status_labels = {
- "active": "АКТИВНА",
- "trial": "TRIAL",
- "expired": "ИСТЕКЛА",
- "disabled": "ОТКЛЮЧЕНА",
- }
-
- text_lines = [
- "👥 Пользователи сервера",
- "",
- f"Сервер: {server.display_name}",
- f"UUID: {server.squad_uuid}",
- "",
- ]
-
- keyboard_rows = []
- max_buttons = 50
-
- if not users:
- text_lines.append("На этот сервер пока никто не подключен.")
- else:
- text_lines.append(f"Всего пользователей: {len(users)}")
- text_lines.append("Нажмите на пользователя ниже, чтобы открыть управление.")
- text_lines.append("")
-
- preview_limit = 10
- for user_info in users[:preview_limit]:
- name_parts = []
- if user_info.get("username"):
- name_parts.append(f"@{user_info['username']}")
- if user_info.get("first_name"):
- name_parts.append(user_info["first_name"])
- display_name = " ".join(name_parts) or str(user_info["telegram_id"])
-
- status = (user_info.get("subscription_status") or "unknown").lower()
- status_text = status_labels.get(status, status.upper())
- emoji = status_emojis.get(status, "ℹ️")
-
- text_lines.append(f"• {display_name} — {emoji} {status_text}")
-
- if len(users) > preview_limit:
- remaining = len(users) - preview_limit
- text_lines.append(f"… и ещё {remaining} пользователей")
-
- for user_info in users[:max_buttons]:
- if user_info.get("username"):
- label = f"@{user_info['username']}"
- elif user_info.get("first_name"):
- label = user_info["first_name"]
- else:
- label = str(user_info["telegram_id"])
-
- status = (user_info.get("subscription_status") or "unknown").lower()
- emoji = status_emojis.get(status, "ℹ️")
- button_text = f"{emoji} {label}"[:64]
-
- keyboard_rows.append(
- [
- types.InlineKeyboardButton(
- text=button_text,
- callback_data=f"admin_user_manage_{user_info['user_id']}",
- )
- ]
- )
-
- if len(users) > max_buttons:
- text_lines.append(
- f"⚠️ Показаны первые {max_buttons} пользователей. Используйте фильтры в разделе пользователей для подробностей."
- )
-
- keyboard_rows.append(
- [
- types.InlineKeyboardButton(
- text="⬅️ Назад", callback_data=f"admin_server_edit_{server.id}"
- )
- ]
- )
-
- await callback.message.edit_text(
- "\n".join(text_lines),
- reply_markup=types.InlineKeyboardMarkup(inline_keyboard=keyboard_rows),
- parse_mode="HTML",
- )
- await callback.answer()
-
-
@admin_required
@error_handler
async def sync_server_user_counts_handler(
@@ -1221,7 +1105,6 @@ def register_handlers(dp: Dispatcher):
& ~F.data.contains("promo"),
)
dp.callback_query.register(toggle_server_availability, F.data.startswith("admin_server_toggle_"))
- dp.callback_query.register(show_server_users, F.data.startswith("admin_server_users_"))
dp.callback_query.register(start_server_edit_name, F.data.startswith("admin_server_edit_name_"))
dp.callback_query.register(start_server_edit_price, F.data.startswith("admin_server_edit_price_"))