diff --git a/assets/schema.sql b/assets/schema.sql
index 0f84a171..f7885de4 100644
--- a/assets/schema.sql
+++ b/assets/schema.sql
@@ -133,6 +133,9 @@ CREATE TABLE IF NOT EXISTS servers
ALTER TABLE servers ADD COLUMN IF NOT EXISTS panel_type TEXT NOT NULL DEFAULT '3x-ui';
ALTER TABLE servers
ALTER COLUMN subscription_url DROP NOT NULL;
+ALTER TABLE servers
+ADD COLUMN IF NOT EXISTS enabled BOOLEAN NOT NULL DEFAULT TRUE;
+
CREATE TABLE IF NOT EXISTS gifts
diff --git a/database.py b/database.py
index 7f219fe2..ccc6ea54 100644
--- a/database.py
+++ b/database.py
@@ -1341,17 +1341,21 @@ async def get_last_notification_time(tg_id: int, notification_type: str, session
await conn.close()
-async def get_servers(session: Any = None):
+async def get_servers(session: Any = None, include_enabled: bool = False):
conn = None
try:
conn = session if session is not None else await asyncpg.connect(DATABASE_URL)
- result = await conn.fetch(
- """
+ query = """
SELECT cluster_name, server_name, api_url, subscription_url, inbound_id, panel_type
- FROM servers
- """
- )
+ """
+ if include_enabled:
+ query += ", enabled"
+
+ query += " FROM servers"
+
+ result = await conn.fetch(query)
+
servers = {}
for row in result:
cluster_name = row["cluster_name"]
@@ -1364,6 +1368,7 @@ async def get_servers(session: Any = None):
"subscription_url": row["subscription_url"],
"inbound_id": row["inbound_id"],
"panel_type": row["panel_type"],
+ "enabled": row.get("enabled", True),
})
return servers
diff --git a/handlers/admin/servers/keyboard.py b/handlers/admin/servers/keyboard.py
index 31d4c7d2..b9b568cb 100644
--- a/handlers/admin/servers/keyboard.py
+++ b/handlers/admin/servers/keyboard.py
@@ -10,14 +10,20 @@ class AdminServerCallback(CallbackData, prefix="admin_server"):
data: str
-def build_manage_server_kb(server_name: str, cluster_name: str) -> InlineKeyboardMarkup:
+def build_manage_server_kb(server_name: str, cluster_name: str, enabled: bool) -> InlineKeyboardMarkup:
from ..clusters.keyboard import AdminClusterCallback
builder = InlineKeyboardBuilder()
- builder.button(text="🗑️ Удалить", callback_data=AdminServerCallback(action="delete", data=server_name).pack())
+
+ toggle_text = "🔴 Отключить" if enabled else "🟢 Включить"
+ toggle_action = "disable" if enabled else "enable"
+
builder.button(
- text="✏️ Сменить название", callback_data=AdminServerCallback(action="rename", data=server_name).pack()
+ text=toggle_text, callback_data=AdminServerCallback(action=toggle_action, data=server_name).pack()
)
+ builder.button(text="🗑️ Удалить", callback_data=AdminServerCallback(action="delete", data=server_name).pack())
+ builder.button(text="✏️ Сменить название", callback_data=AdminServerCallback(action="rename", data=server_name).pack())
builder.button(text=BACK, callback_data=AdminClusterCallback(action="manage", data=cluster_name).pack())
+
builder.adjust(1)
- return builder.as_markup()
+ return builder.as_markup()
\ No newline at end of file
diff --git a/handlers/admin/servers/servers_handler.py b/handlers/admin/servers/servers_handler.py
index 816404fb..cd1ace22 100644
--- a/handlers/admin/servers/servers_handler.py
+++ b/handlers/admin/servers/servers_handler.py
@@ -5,7 +5,7 @@ from aiogram.fsm.context import FSMContext
from aiogram.types import CallbackQuery, InlineKeyboardButton
from aiogram.utils.keyboard import InlineKeyboardBuilder
-from database import delete_server, get_servers
+from database import get_servers
from filters.admin import IsAdminFilter
from handlers.buttons import BACK
@@ -22,7 +22,7 @@ router = Router()
@router.callback_query(AdminServerCallback.filter(F.action == "manage"), IsAdminFilter())
async def handle_server_manage(callback_query: CallbackQuery, callback_data: AdminServerCallback):
server_name = callback_data.data
- servers = await get_servers()
+ servers = await get_servers(include_enabled=True)
cluster_name, server = next(
((c, s) for c, cs in servers.items() for s in cs if s["server_name"] == server_name), (None, None)
@@ -42,7 +42,7 @@ async def handle_server_manage(callback_query: CallbackQuery, callback_data: Adm
await callback_query.message.edit_text(
text=text,
- reply_markup=build_manage_server_kb(server_name, cluster_name),
+ reply_markup=build_manage_server_kb(server_name, cluster_name, enabled=server.get("enabled", True)),
)
else:
await callback_query.message.edit_text(text="❌ Сервер не найден.")
@@ -170,3 +170,41 @@ async def process_callback_delete_server(
text=f"✅ Сервер '{server_name}' удален.",
reply_markup=build_admin_back_kb("clusters"),
)
+
+
+@router.callback_query(
+ AdminServerCallback.filter(F.action.in_(["enable", "disable"])), IsAdminFilter()
+)
+async def toggle_server_enabled(callback_query: CallbackQuery, callback_data: AdminServerCallback, session: Any):
+ server_name = callback_data.data
+ action = callback_data.action
+
+ new_status = action == "enable"
+
+ await session.execute(
+ "UPDATE servers SET enabled = $1 WHERE server_name = $2",
+ new_status,
+ server_name
+ )
+
+ servers = await get_servers(include_enabled=True)
+
+ cluster_name, server = next(
+ ((c, s) for c, cs in servers.items() for s in cs if s["server_name"] == server_name), (None, None)
+ )
+
+ if not server:
+ await callback_query.message.edit_text("❌ Сервер не найден.")
+ return
+
+ text = (
+ f"🔧 Информация о сервере {server_name}:\n\n"
+ f"📡 API URL: {server['api_url']}\n"
+ f"🌐 Subscription URL: {server['subscription_url']}\n"
+ f"🔑 Inbound ID: {server['inbound_id']}"
+ )
+
+ await callback_query.message.edit_text(
+ text=text,
+ reply_markup=build_manage_server_kb(server_name, cluster_name, enabled=new_status)
+ )
diff --git a/handlers/keys/key_utils.py b/handlers/keys/key_utils.py
index ae512e83..8d13cb8d 100644
--- a/handlers/keys/key_utils.py
+++ b/handlers/keys/key_utils.py
@@ -43,7 +43,7 @@ async def create_key_on_cluster(
remnawave_link: str = None,
):
try:
- servers = await get_servers()
+ servers = await get_servers(include_enabled=True)
cluster = servers.get(cluster_id)
server_id_to_store = cluster_id
@@ -59,10 +59,15 @@ async def create_key_on_cluster(
else:
raise ValueError(f"Кластер или сервер с ID/именем {cluster_id} не найден.")
+ enabled_servers = [s for s in cluster if s.get("enabled", True)]
+ if not enabled_servers:
+ logger.warning(f"[Key Creation] Нет доступных серверов в кластере {cluster_id}")
+ return
+
semaphore = asyncio.Semaphore(2)
- remnawave_servers = [s for s in cluster if s.get("panel_type", "3x-ui").lower() == "remnawave"]
- xui_servers = [s for s in cluster if s.get("panel_type", "3x-ui").lower() == "3x-ui"]
+ remnawave_servers = [s for s in enabled_servers if s.get("panel_type", "3x-ui").lower() == "remnawave"]
+ xui_servers = [s for s in enabled_servers if s.get("panel_type", "3x-ui").lower() == "3x-ui"]
remnawave_created = False
remnawave_key = None