migration between external squads/ make format

This commit is contained in:
Vladless
2025-12-10 07:34:48 +03:00
parent 9bacc0c8e3
commit 1b1780df55
9 changed files with 47 additions and 24 deletions
+13 -4
View File
@@ -105,8 +105,9 @@ async def handle_key_edit(
is_configurable = False is_configurable = False
if key_obj.tariff_id: if key_obj.tariff_id:
result = await session.execute( result = await session.execute(
select(Tariff.name, Tariff.subgroup_title, Tariff.device_limit, Tariff.traffic_limit, Tariff.configurable) select(
.where(Tariff.id == key_obj.tariff_id) Tariff.name, Tariff.subgroup_title, Tariff.device_limit, Tariff.traffic_limit, Tariff.configurable
).where(Tariff.id == key_obj.tariff_id)
) )
row = result.first() row = result.first()
if row: if row:
@@ -122,13 +123,21 @@ async def handle_key_edit(
sel_dev, cur_dev = key_obj.selected_device_limit, key_obj.current_device_limit sel_dev, cur_dev = key_obj.selected_device_limit, key_obj.current_device_limit
if sel_dev is not None or cur_dev is not None: if sel_dev is not None or cur_dev is not None:
base_dev = sel_dev if sel_dev is not None else (base_devices if base_devices is not None else cur_dev) base_dev = sel_dev if sel_dev is not None else (base_devices if base_devices is not None else cur_dev)
extra = f" + {cur_dev - base_dev} (докуплено)" if (base_dev is not None and cur_dev is not None and cur_dev > base_dev) else "" extra = (
f" + {cur_dev - base_dev} (докуплено)"
if (base_dev is not None and cur_dev is not None and cur_dev > base_dev)
else ""
)
devices_line = f"📱 <b>Устройства:</b> {base_dev}{extra}\n" devices_line = f"📱 <b>Устройства:</b> {base_dev}{extra}\n"
sel_traf, cur_traf = key_obj.selected_traffic_limit, key_obj.current_traffic_limit sel_traf, cur_traf = key_obj.selected_traffic_limit, key_obj.current_traffic_limit
if sel_traf is not None or cur_traf is not None: if sel_traf is not None or cur_traf is not None:
base_traf = sel_traf if sel_traf is not None else (base_traffic if base_traffic is not None else cur_traf) base_traf = sel_traf if sel_traf is not None else (base_traffic if base_traffic is not None else cur_traf)
extra = f" + {cur_traf - base_traf} ГБ (докуплено)" if (base_traf is not None and cur_traf is not None and cur_traf > base_traf) else "" extra = (
f" + {cur_traf - base_traf} ГБ (докуплено)"
if (base_traf is not None and cur_traf is not None and cur_traf > base_traf)
else ""
)
traffic_line = f"📊 <b>Трафик:</b> {base_traf} ГБ{extra}\n" traffic_line = f"📊 <b>Трафик:</b> {base_traf} ГБ{extra}\n"
text = ( text = (
+1 -1
View File
@@ -42,11 +42,11 @@ from hooks.processors import (
process_purchase_tariff_group_override, process_purchase_tariff_group_override,
process_tariff_menu, process_tariff_menu,
) )
from .utils import add_tariff_button_generic
from logger import logger from logger import logger
from .key_mode.key_cluster_mode import key_cluster_mode from .key_mode.key_cluster_mode import key_cluster_mode
from .key_mode.key_country_mode import key_country_mode from .key_mode.key_country_mode import key_country_mode
from .utils import add_tariff_button_generic
router = Router() router = Router()
+1 -3
View File
@@ -95,9 +95,7 @@ async def key_country_mode(
if state and plan: if state and plan:
await state.update_data(tariff_id=plan) await state.update_data(tariff_id=plan)
if state and any( if state and any(value is not None for value in (selected_device_limit, selected_traffic_gb, selected_price_rub)):
value is not None for value in (selected_device_limit, selected_traffic_gb, selected_price_rub)
):
data = await state.get_data() data = await state.get_data()
if selected_device_limit is not None: if selected_device_limit is not None:
data["config_selected_device_limit"] = selected_device_limit data["config_selected_device_limit"] = selected_device_limit
+2 -1
View File
@@ -53,9 +53,10 @@ from hooks.processors import (
process_renewal_complete, process_renewal_complete,
process_renewal_forbidden_groups, process_renewal_forbidden_groups,
) )
from .utils import add_tariff_button_generic
from logger import logger from logger import logger
from .utils import add_tariff_button_generic
router = Router() router = Router()
moscow_tz = pytz.timezone("Europe/Moscow") moscow_tz = pytz.timezone("Europe/Moscow")
+17 -12
View File
@@ -10,10 +10,10 @@ from database import (
filter_cluster_by_subgroup, filter_cluster_by_subgroup,
get_key_details, get_key_details,
get_servers, get_servers,
get_tariff_by_id,
resolve_device_limit_from_group, resolve_device_limit_from_group,
update_key_expiry, update_key_expiry,
update_key_link, update_key_link,
get_tariff_by_id,
) )
from hooks.processors import process_get_cryptolink_after_renewal from hooks.processors import process_get_cryptolink_after_renewal
from logger import ( from logger import (
@@ -57,7 +57,6 @@ async def renew_on_remnawave(
target_server_name: str | None = None, target_server_name: str | None = None,
external_squad_uuid: str | None = None, external_squad_uuid: str | None = None,
) -> bool: ) -> bool:
"""Продлевает подписку на Remnawave-узлах кластера."""
remnawave_nodes = [ remnawave_nodes = [
s for s in cluster if str(s.get("panel_type", "3x-ui")).lower() == "remnawave" and s.get("inbound_id") s for s in cluster if str(s.get("panel_type", "3x-ui")).lower() == "remnawave" and s.get("inbound_id")
] ]
@@ -67,23 +66,24 @@ async def renew_on_remnawave(
remnawave_nodes = [s for s in remnawave_nodes if s.get("server_name") == target_server_name] or remnawave_nodes[ remnawave_nodes = [s for s in remnawave_nodes if s.get("server_name") == target_server_name] or remnawave_nodes[
:1 :1
] ]
remna = RemnawaveAPI(remnawave_nodes[0]["api_url"]) remna = RemnawaveAPI(remnawave_nodes[0]["api_url"])
if not await remna.login(REMNAWAVE_LOGIN, REMNAWAVE_PASSWORD): if not await remna.login(REMNAWAVE_LOGIN, REMNAWAVE_PASSWORD):
logger.error(f"{PANEL_REMNA} Не удалось войти в Remnawave API") logger.error(f"{PANEL_REMNA} Не удалось войти в Remnawave API")
return False return False
expire_iso = datetime.utcfromtimestamp(new_expiry_time // 1000).isoformat() + "Z" expire_iso = datetime.utcfromtimestamp(new_expiry_time // 1000).isoformat() + "Z"
traffic_limit_bytes = total_gb * 1024 * 1024 * 1024 if total_gb else 0 traffic_limit_bytes = total_gb * 1024 * 1024 * 1024 if total_gb else 0
active_inbounds = [s["inbound_id"] for s in remnawave_nodes] active_inbounds = [s["inbound_id"] for s in remnawave_nodes]
update_kwargs = dict( update_kwargs = {
uuid=client_id, "uuid": client_id,
expire_at=expire_iso, "expire_at": expire_iso,
active_user_inbounds=active_inbounds, "active_user_inbounds": active_inbounds,
traffic_limit_bytes=traffic_limit_bytes, "traffic_limit_bytes": traffic_limit_bytes,
hwid_device_limit=hwid_device_limit, "hwid_device_limit": hwid_device_limit,
) "external_squad_uuid": external_squad_uuid,
if external_squad_uuid: }
update_kwargs["external_squad_uuid"] = external_squad_uuid
updated = await remna.update_user(**update_kwargs) updated = await remna.update_user(**update_kwargs)
if updated: if updated:
@@ -222,7 +222,11 @@ async def renew_key_in_cluster(
if plan is not None: if plan is not None:
tariff = await get_tariff_by_id(session, plan) tariff = await get_tariff_by_id(session, plan)
if tariff: if tariff:
external_squad_uuid = tariff.get("external_squad") raw_external_squad = tariff.get("external_squad")
if raw_external_squad:
external_squad_uuid = raw_external_squad
else:
external_squad_uuid = ""
is_configurable = tariff.get("configurable", False) is_configurable = tariff.get("configurable", False)
if not is_configurable: if not is_configurable:
tariff_device_limit = tariff.get("device_limit") tariff_device_limit = tariff.get("device_limit")
@@ -247,6 +251,7 @@ async def renew_key_in_cluster(
reset_traffic=reset_traffic, reset_traffic=reset_traffic,
old_subgroup=old_subgroup, old_subgroup=old_subgroup,
target_subgroup=target_subgroup, target_subgroup=target_subgroup,
external_squad_uuid=external_squad_uuid,
) )
await update_key_expiry(session, new_client_id or client_id, new_expiry_time) await update_key_expiry(session, new_client_id or client_id, new_expiry_time)
@@ -29,6 +29,7 @@ async def ensure_on_remnawave(
hwid_device_limit: int, hwid_device_limit: int,
reset_traffic: bool, reset_traffic: bool,
attempt_update_first: bool, attempt_update_first: bool,
external_squad_uuid: str | None = None,
) -> tuple[str | None, str | None]: ) -> tuple[str | None, str | None]:
if not servers: if not servers:
return None, None return None, None
@@ -74,6 +75,7 @@ async def ensure_on_remnawave(
active_user_inbounds=inbounds, active_user_inbounds=inbounds,
traffic_limit_bytes=traffic_bytes, traffic_limit_bytes=traffic_bytes,
hwid_device_limit=hwid_device_limit, hwid_device_limit=hwid_device_limit,
external_squad_uuid=external_squad_uuid,
) )
if not updated: if not updated:
return None, None return None, None
@@ -113,6 +115,8 @@ async def ensure_on_remnawave(
if traffic_bytes > 0: if traffic_bytes > 0:
payload["trafficLimitBytes"] = traffic_bytes payload["trafficLimitBytes"] = traffic_bytes
payload["hwidDeviceLimit"] = hwid_device_limit payload["hwidDeviceLimit"] = hwid_device_limit
if external_squad_uuid is not None:
payload["externalSquadUuid"] = external_squad_uuid or None
created = await api.create_user(payload) created = await api.create_user(payload)
if not created: if not created:
@@ -237,6 +241,7 @@ async def migrate_between_subgroups(
reset_traffic: bool, reset_traffic: bool,
old_subgroup: str, old_subgroup: str,
target_subgroup: str, target_subgroup: str,
external_squad_uuid: str | None = None,
) -> tuple[str, str | None]: ) -> tuple[str, str | None]:
target = await filter_cluster_by_subgroup(session, cluster_all, target_subgroup, cluster_id) target = await filter_cluster_by_subgroup(session, cluster_all, target_subgroup, cluster_id)
xui_tgt, remna_tgt = split_by_panel(target) xui_tgt, remna_tgt = split_by_panel(target)
@@ -293,6 +298,7 @@ async def migrate_between_subgroups(
hwid_device_limit=hwid_device_limit, hwid_device_limit=hwid_device_limit,
reset_traffic=reset_traffic, reset_traffic=reset_traffic,
attempt_update_first=was_on_remna_before, attempt_update_first=was_on_remna_before,
external_squad_uuid=external_squad_uuid,
) )
if remna_old_non: if remna_old_non:
await delete_on_remnawave(remna_old_non, client_id) await delete_on_remnawave(remna_old_non, client_id)
@@ -315,6 +321,7 @@ async def migrate_between_subgroups(
hwid_device_limit=hwid_device_limit, hwid_device_limit=hwid_device_limit,
reset_traffic=reset_traffic, reset_traffic=reset_traffic,
attempt_update_first=was_on_remna_before, attempt_update_first=was_on_remna_before,
external_squad_uuid=external_squad_uuid,
) )
if remna_old_non: if remna_old_non:
+1 -1
View File
@@ -1 +1 @@
DEFAULT_HOOK_TIMEOUT = 4.0 DEFAULT_HOOK_TIMEOUT = 4.0
+5 -2
View File
@@ -49,6 +49,7 @@ async def run_hooks(name: str, require_enabled: bool = True, **kwargs) -> list[A
if require_enabled and owner: if require_enabled and owner:
try: try:
from utils.modules_manager import manager from utils.modules_manager import manager
if not manager.is_enabled(owner): if not manager.is_enabled(owner):
continue continue
except Exception: except Exception:
@@ -57,14 +58,16 @@ async def run_hooks(name: str, require_enabled: bool = True, **kwargs) -> list[A
if inspect.iscoroutinefunction(func): if inspect.iscoroutinefunction(func):
coro = func(**kwargs) coro = func(**kwargs)
else: else:
async def _run_sync(): async def _run_sync():
return func(**kwargs) return func(**kwargs)
coro = _run_sync() coro = _run_sync()
result = await asyncio.wait_for(coro, timeout=DEFAULT_HOOK_TIMEOUT) result = await asyncio.wait_for(coro, timeout=DEFAULT_HOOK_TIMEOUT)
if result: if result:
results.append(result) results.append(result)
except asyncio.TimeoutError: except TimeoutError:
logger.error( logger.error(
f"[HOOK:{name}] Таймаут в {getattr(func, '__name__', func)} при timeout={DEFAULT_HOOK_TIMEOUT}", f"[HOOK:{name}] Таймаут в {getattr(func, '__name__', func)} при timeout={DEFAULT_HOOK_TIMEOUT}",
exc_info=True, exc_info=True,
@@ -74,4 +77,4 @@ async def run_hooks(name: str, require_enabled: bool = True, **kwargs) -> list[A
f"[HOOK:{name}] Ошибка в {getattr(func, '__name__', func)}: {e}", f"[HOOK:{name}] Ошибка в {getattr(func, '__name__', func)}: {e}",
exc_info=True, exc_info=True,
) )
return results return results
Binary file not shown.