"""Thin wrapper around RemnawaveSDK.""" from __future__ import annotations import logging from remnawave import RemnawaveSDK from remnawave.models import LoginRequestDto from config import Settings logger = logging.getLogger(__name__) def create_sdk(settings: Settings, *, token: str | None = None) -> RemnawaveSDK: kwargs: dict = { "base_url": settings.remnawave_base_url, "token": token if token is not None else settings.remnawave_token, } if settings.caddy_token: kwargs["caddy_token"] = settings.caddy_token return RemnawaveSDK(**kwargs) async def create_sdk_ready(settings: Settings) -> RemnawaveSDK: """ Собирает SDK. Если заданы REMNAWAVE_USERNAME/PASSWORD — логинится и берёт admin JWT (нужно для create/update users, если API-токен только на чтение). """ if settings.remnawave_username and settings.remnawave_password: bootstrap = create_sdk(settings, token=settings.remnawave_token or "bootstrap") try: login = await bootstrap.auth.login( LoginRequestDto( username=settings.remnawave_username, password=settings.remnawave_password, ) ) logger.info("Remnawave: вошли как %s (admin JWT)", settings.remnawave_username) return create_sdk(settings, token=login.access_token) except Exception as exc: # noqa: BLE001 logger.error( "Не удалось войти в Remnawave как %s: %s. Используем API-токен.", settings.remnawave_username, exc, ) return create_sdk(settings) async def probe_users_write_access(sdk: RemnawaveSDK) -> tuple[bool, str]: """Проверка, что токен может создавать пользователей.""" from datetime import datetime, timedelta, timezone from remnawave.enums import UserStatus from remnawave.models import CreateUserRequestDto username = f"rwprobe_{int(datetime.now(timezone.utc).timestamp()) % 10_000_000}" expire = datetime.now(timezone.utc) + timedelta(hours=1) try: created = await sdk.users.create_user( CreateUserRequestDto( username=username, expire_at=expire, status=UserStatus.ACTIVE, description="write-access probe (auto-delete)", ) ) try: await sdk.users.delete_user(str(created.uuid)) except Exception: # noqa: BLE001 logger.warning("probe user created but not deleted: %s", created.uuid) return True, "OK: users:write доступен" except Exception as exc: # noqa: BLE001 msg = str(exc) if "403" in msg or "Forbidden" in msg: return False, ( "API-токен не может создавать пользователей (HTTP 403).\n" "В панели Remnawave создай новый API Token со scope " "users:* или *,\n" "либо укажи REMNAWAVE_USERNAME / REMNAWAVE_PASSWORD админа в .env." ) return False, f"Проверка записи не удалась: {exc}" def friendly_remnawave_error(exc: BaseException) -> str: text = str(exc) if "403" in text or "Forbidden" in text: return ( "Remnawave запретил операцию (HTTP 403).\n\n" "Скорее всего у API Token нет прав на запись пользователей.\n" "Что сделать:\n" "1) Панель → API Tokens → создай токен со scope users:* или *\n" "2) Вставь новый токен в REMNAWAVE_TOKEN\n" "либо задай логин/пароль админа:\n" "REMNAWAVE_USERNAME / REMNAWAVE_PASSWORD\n\n" f"{exc}" ) return f"{exc}"