diff --git a/admin.html b/admin.html index 677a502..5e6cd1f 100644 --- a/admin.html +++ b/admin.html @@ -294,6 +294,7 @@ + + +
+
Настройки
+
Смена телеграм-бота без переустановки панели
+
+

Telegram-бот

+

Сейчас: — (токен: —)

+
+
+
+
+

Панель сама проверит токен у Telegram (запрос getMe) перед применением и подставит настоящий юзернейм бота — придумывать не нужно. После смены перезапустится только бот; если уведомления от api (например об оплате) продолжат идти со старого бота, выполни на сервере mbs restart.

+
+
+
@@ -573,6 +589,7 @@ function showView(name) { if (name === "nodes") loadNodes(); if (name === "traffic") loadTraffic(); if (name === "payments") loadPayments(); + if (name === "settings") loadBotSettings(); } const COUNTRIES = [ @@ -827,6 +844,29 @@ async function checkPayment(id, btn) { } } +async function loadBotSettings() { + const data = await api("/admin/api/settings/bot"); + document.getElementById("settings-bot-username").textContent = "@" + data.username; + document.getElementById("settings-bot-token").textContent = data.token_masked; + document.getElementById("settings-bot-result").innerHTML = ""; +} + +async function saveBotSettings() { + const input = document.getElementById("settings-bot-token-input"); + const token = input.value.trim(); + const result = document.getElementById("settings-bot-result"); + if (!token) return; + result.innerHTML = '

Проверяю токен у Telegram…

'; + try { + const res = await api("/admin/api/settings/bot", { method: "POST", body: JSON.stringify({ token }) }); + result.innerHTML = `

Готово: бот сменён на @${esc(res.username)}${res.restarted ? "" : " (сохранено, но авто-рестарт не удался — перезапусти вручную: mbs restart)"}

`; + input.value = ""; + loadBotSettings(); + } catch (e) { + result.innerHTML = '

Не получилось — проверь токен и попробуй снова

'; + } +} + function fmtDate(iso) { return iso ? iso.slice(0, 10) : "—"; } function statusBadge(active, daysLeft) { if (!active) return 'истекла'; diff --git a/api.py b/api.py index 1356c75..12fa772 100644 --- a/api.py +++ b/api.py @@ -2,6 +2,8 @@ import datetime import json import os import re +import subprocess +import urllib.request from fastapi import FastAPI, HTTPException, Request, Response from fastapi.middleware.cors import CORSMiddleware from fastapi import Body @@ -14,14 +16,32 @@ import payments import xray_manager from config import ( PLANS, PLANS_BY_CODE, SITE_DOMAIN, SUB_DOMAIN, PANEL_DOMAIN, - ADMIN_PANEL_PASSWORD, BOT_USERNAME, BOT_TOKEN, + ADMIN_PANEL_PASSWORD, BOT_USERNAME, BOT_TOKEN, BASE_DIR, HWID_LIMIT_ENABLED, HWID_FALLBACK_LIMIT, ) HWID_RE = re.compile(r"^[a-zA-Z0-9=-]{10,64}$") +ENV_PATH = os.path.join(BASE_DIR, ".env") db.init_db() + +def _update_env_var(key: str, value: str): + lines = [] + if os.path.exists(ENV_PATH): + with open(ENV_PATH, encoding="utf-8") as f: + lines = f.readlines() + found = False + for i, line in enumerate(lines): + if line.strip().startswith(f"{key}="): + lines[i] = f"{key}={value}\n" + found = True + break + if not found: + lines.append(f"{key}={value}\n") + with open(ENV_PATH, "w", encoding="utf-8") as f: + f.writelines(lines) + app = FastAPI(title="mbs-api") app.add_middleware( @@ -275,7 +295,6 @@ def install_script(token: str): def _tg_send_message(tg_id: int, text: str): - import urllib.request url = f"https://api.telegram.org/bot{BOT_TOKEN}/sendMessage" data = json.dumps({"chat_id": tg_id, "text": text, "parse_mode": "HTML"}).encode() req = urllib.request.Request(url, data=data, method="POST", headers={"Content-Type": "application/json"}) @@ -432,6 +451,37 @@ def admin_me(request: Request): +@app.get("/admin/api/settings/bot") +def admin_get_bot_settings(request: Request): + require_admin(request) + masked = f"{BOT_TOKEN[:8]}...{BOT_TOKEN[-4:]}" if len(BOT_TOKEN) > 14 else "***" + return {"username": BOT_USERNAME, "token_masked": masked} + + +@app.post("/admin/api/settings/bot") +def admin_set_bot_settings(request: Request, body: dict = Body(...)): + require_admin(request) + token = (body.get("token") or "").strip() + if not token: + raise HTTPException(400, "token required") + try: + with urllib.request.urlopen(f"https://api.telegram.org/bot{token}/getMe", timeout=10) as resp: + data = json.loads(resp.read()) + except Exception: + raise HTTPException(400, "не получилось проверить токен — нет связи с Telegram") + if not data.get("ok"): + raise HTTPException(400, "Telegram отклонил этот токен") + username = data["result"]["username"] + _update_env_var("BOT_TOKEN", token) + _update_env_var("BOT_USERNAME", username) + try: + subprocess.run(["systemctl", "restart", "mbs-bot"], check=True, timeout=15) + restarted = True + except Exception: + restarted = False + return {"ok": True, "username": username, "restarted": restarted} + + @app.get("/admin/api/stats") def admin_stats(request: Request): require_admin(request) diff --git a/mbs b/mbs index c17b209..8e5a245 100644 --- a/mbs +++ b/mbs @@ -10,7 +10,7 @@ mbs — управление MBS Panel mbs pass [новый_пароль] сменить пароль админ-панели (без аргумента — сгенерировать случайный) mbs status статус всех сервисов (bot, api, xray, nginx) - mbs restart перезапустить bot + api + mbs restart перезапустить всё (bot, api, xray, reload nginx) mbs logs [bot|api|xray] последние строки лога (по умолчанию api) mbs domain показать текущий домен панели mbs update обновить код с GitHub и перезапустить (не трогает .env и базу) @@ -36,8 +36,9 @@ cmd_status() { } cmd_restart() { - systemctl restart mbs-bot mbs-api - echo "Перезапущено." + systemctl restart mbs-bot mbs-api xray + systemctl reload nginx 2>/dev/null || true + echo "Перезапущено: bot, api, xray (+ reload nginx)." } cmd_logs() {