from aiogram import F, Router from aiogram.fsm.context import FSMContext from aiogram.fsm.state import State, StatesGroup from aiogram.types import CallbackQuery, InlineKeyboardButton, Message from aiogram.utils.keyboard import InlineKeyboardBuilder from core.settings.web_config import WEB_CONFIG, update_web_config from database import async_session_maker from handlers.buttons import BACK from ..panel.keyboard import AdminPanelCallback, build_admin_back_btn router = Router(name="admin_settings_web") class WebSettingsState(StatesGroup): waiting_for_url = State() def build_settings_web_kb() -> InlineKeyboardBuilder: builder = InlineKeyboardBuilder() enabled = bool(WEB_CONFIG.get("WEB_ENABLED", False)) url = str(WEB_CONFIG.get("SITE_URL") or "не указан") builder.row( InlineKeyboardButton( text=f"{'✅' if enabled else '❌'} Сайт {'включён' if enabled else 'выключен'}", callback_data=AdminPanelCallback(action="settings_web_toggle").pack(), ) ) builder.row( InlineKeyboardButton( text=f"🌐 URL: {url}", callback_data=AdminPanelCallback(action="settings_web_url").pack(), ) ) email_binding = bool(WEB_CONFIG.get("EMAIL_BINDING_ENABLED", False)) builder.row( InlineKeyboardButton( text=f"{'✅' if email_binding else '❌'} Привязка почты {'включена' if email_binding else 'выключена'}", callback_data=AdminPanelCallback(action="settings_web_email_binding_toggle").pack(), ) ) builder.row( InlineKeyboardButton( text="🔄 Сбросить сайт к исходнику", callback_data=AdminPanelCallback(action="settings_web_reset_ask").pack(), ) ) builder.row(build_admin_back_btn("settings")) return builder def _web_settings_text() -> str: enabled = bool(WEB_CONFIG.get("WEB_ENABLED", False)) url = str(WEB_CONFIG.get("SITE_URL") or "не указан") email_binding = bool(WEB_CONFIG.get("EMAIL_BINDING_ENABLED", False)) return ( "🌐 Настройки веб-сайта\n\n" f"Статус: {'✅ Включён' if enabled else '❌ Выключен'}\n" f"URL: {url}\n" f"Привязка почты: {'✅ Включена' if email_binding else '❌ Выключена'}\n\n" "Сайт может работать на отдельном домене и сервере.\n" "При выключении кнопка «Личный кабинет» скрывается из бота.\n" "Привязка почты — кнопка в боте, через которую пользователь указывает email " "для входа на сайт на случай проблем с Telegram." ) @router.callback_query(AdminPanelCallback.filter(F.action == "settings_web")) async def open_web_settings(callback: CallbackQuery) -> None: await callback.message.edit_text( text=_web_settings_text(), reply_markup=build_settings_web_kb().as_markup(), ) await callback.answer() @router.callback_query(AdminPanelCallback.filter(F.action == "settings_web_toggle")) async def toggle_web_enabled(callback: CallbackQuery) -> None: current = bool(WEB_CONFIG.get("WEB_ENABLED", False)) new_config = dict(WEB_CONFIG) new_config["WEB_ENABLED"] = not current async with async_session_maker() as session: await update_web_config(session, new_config) status = "✅ Сайт включён" if new_config["WEB_ENABLED"] else "❌ Сайт выключен" await callback.answer(status, show_alert=True) await callback.message.edit_text( text=_web_settings_text(), reply_markup=build_settings_web_kb().as_markup(), ) @router.callback_query(AdminPanelCallback.filter(F.action == "settings_web_email_binding_toggle")) async def toggle_email_binding(callback: CallbackQuery) -> None: current = bool(WEB_CONFIG.get("EMAIL_BINDING_ENABLED", False)) new_config = dict(WEB_CONFIG) new_config["EMAIL_BINDING_ENABLED"] = not current async with async_session_maker() as session: await update_web_config(session, new_config) status = "✅ Привязка почты включена" if new_config["EMAIL_BINDING_ENABLED"] else "❌ Привязка почты выключена" await callback.answer(status, show_alert=True) await callback.message.edit_text( text=_web_settings_text(), reply_markup=build_settings_web_kb().as_markup(), ) @router.callback_query(AdminPanelCallback.filter(F.action == "settings_web_url")) async def prompt_web_url(callback: CallbackQuery, state: FSMContext) -> None: current = str(WEB_CONFIG.get("SITE_URL") or "") text = ( "🌐 Введите URL сайта\n\n" f"Текущий: {current or 'не указан'}\n\n" "Отправьте полный URL (с https://).\n" "Пример: https://my-vpn.com\n\n" "Отправьте - чтобы очистить." ) await callback.message.edit_text(text=text) await state.set_state(WebSettingsState.waiting_for_url) await callback.answer() @router.callback_query(AdminPanelCallback.filter(F.action == "settings_web_reset_ask")) async def ask_reset_site(callback: CallbackQuery) -> None: text = ( "⚠️ Сброс сайта к исходнику\n\n" "Действие удалит:\n" "• все страницы, блоки, темы и варианты\n" "• всех веб-пользователей (включая админа сайта)\n" "• флаг «сайт проинициализирован»\n\n" "Биллинг-данные (пользователи бота, ключи, платежи) не трогаются.\n\n" "После сброса админ сайта пересоздаётся из переменных окружения " "WEB_ADMIN_LOGIN / WEB_ADMIN_PASSWORD.\n\n" "Действие необратимо." ) builder = InlineKeyboardBuilder() builder.row( InlineKeyboardButton( text="❌ Отмена", callback_data=AdminPanelCallback(action="settings_web").pack(), ) ) builder.row( InlineKeyboardButton( text="⚠️ Да, сбросить сайт", callback_data=AdminPanelCallback(action="settings_web_reset_do").pack(), ) ) await callback.message.edit_text(text=text, reply_markup=builder.as_markup()) await callback.answer() @router.callback_query(AdminPanelCallback.filter(F.action == "settings_web_reset_do")) async def do_reset_site(callback: CallbackQuery, session=None) -> None: await callback.answer() await callback.message.edit_text(text="⏳ Сбрасываю сайт...") from middlewares.session import release_session_early from services.site_reset import reset_site if session is not None: await release_session_early(session) try: async with async_session_maker() as s: await reset_site(s) await s.commit() except Exception as exc: from html import escape as html_escape safe = html_escape(str(exc))[:2000] await callback.message.edit_text( text=f"❌ Не удалось сбросить сайт\n\n{safe}", reply_markup=build_settings_web_kb().as_markup(), ) return text = ( "✅ Сайт сброшен к исходнику\n\n" "Все веб-страницы, блоки и темы удалены. Админ пересоздан из env.\n" "Откройте сайт и пройдите путь первой установки заново." ) await callback.message.edit_text( text=text, reply_markup=build_settings_web_kb().as_markup(), ) @router.message(WebSettingsState.waiting_for_url) async def set_web_url(message: Message, state: FSMContext) -> None: url = message.text.strip() if message.text else "" if url == "-": url = "" elif url and not url.startswith("http"): await message.answer("❌ URL должен начинаться с http:// или https://") return url = url.rstrip("/") new_config = dict(WEB_CONFIG) new_config["SITE_URL"] = url async with async_session_maker() as session: await update_web_config(session, new_config) await state.clear() await message.answer( text=_web_settings_text(), reply_markup=build_settings_web_kb().as_markup(), )