mirror of
https://github.com/andrey271192/PCAtelegram_web.git
synced 2026-09-22 12:11:57 +00:00
feat: install warp on enable
This commit is contained in:
@@ -94,12 +94,12 @@ PCATELEGRAM_WEB_ADMIN_PASSWORD='strong-password' bash bootstrap.sh
|
|||||||
- `Off` — WARP выключен.
|
- `Off` — WARP выключен.
|
||||||
- `WARP` — обычный Cloudflare WARP.
|
- `WARP` — обычный Cloudflare WARP.
|
||||||
- `WARP+` — WARP+ с license key.
|
- `WARP+` — WARP+ с license key.
|
||||||
- `All clients` — применяет WARP на весь proxy-трафик через `warp-cli`, если Cloudflare WARP установлен на сервере.
|
- `All clients` — если `warp-cli` не установлен, web-admin ставит `cloudflare-warp`, затем применяет WARP на весь proxy-трафик через `warp-cli`.
|
||||||
- `One client` — сохраняет WARP/WARP+ профиль и key для выбранного клиента в `/opt/pcatelegram_web/warp.json`.
|
- `One client` — если `warp-cli` не установлен, web-admin ставит `cloudflare-warp`, затем сохраняет WARP/WARP+ профиль и key для выбранного клиента в `/opt/pcatelegram_web/warp.json`.
|
||||||
|
|
||||||
WARP+ key не отдается в API целиком: web показывает только маску. Файл `warp.json` хранится с правами `0600` и входит в backup.
|
WARP+ key не отдается в API целиком: web показывает только маску. Файл `warp.json` хранится с правами `0600` и входит в backup.
|
||||||
|
|
||||||
Для реального global WARP на сервере нужен установленный `cloudflare-warp` и доступная команда `warp-cli`. По документации Cloudflare: регистрация `warp-cli registration new`, WARP+ key `warp-cli registration license <KEY>`, подключение `warp-cli connect`.
|
Для real global WARP web-admin использует официальный Cloudflare Linux repo: добавляет GPG key, repo `pkg.cloudflareclient.com`, ставит пакет `cloudflare-warp`, затем выполняет регистрацию `warp-cli registration new`, WARP+ key `warp-cli registration license <KEY>`, подключение `warp-cli connect`.
|
||||||
|
|
||||||
Per-client runtime routing в текущем telemt не включается автоматически: публичные параметры telemt дают users/limits/quotas/ad tags, но не документируют привязку upstream к конкретному user. Для настоящего WARP только одному клиенту нужен отдельный telemt route/service или upstream-схема.
|
Per-client runtime routing в текущем telemt не включается автоматически: публичные параметры telemt дают users/limits/quotas/ad tags, но не документируют привязку upstream к конкретному user. Для настоящего WARP только одному клиенту нужен отдельный telemt route/service или upstream-схема.
|
||||||
|
|
||||||
|
|||||||
@@ -604,15 +604,74 @@ def user_warp_payload(name: str) -> dict[str, Any]:
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def install_warp_cli() -> dict[str, Any]:
|
||||||
|
result: dict[str, Any] = {"attempted": False, "installed": bool(shutil.which("warp-cli")), "commands": [], "warnings": []}
|
||||||
|
if result["installed"]:
|
||||||
|
return result
|
||||||
|
if os.geteuid() != 0:
|
||||||
|
result["warnings"].append("root required to install cloudflare-warp")
|
||||||
|
return result
|
||||||
|
|
||||||
|
def call(label: str, cmd: list[str], timeout: int = 120) -> tuple[int, str, str]:
|
||||||
|
result["attempted"] = True
|
||||||
|
code, out, err = run(cmd, timeout=timeout)
|
||||||
|
result["commands"].append({"cmd": label, "exit_code": code})
|
||||||
|
return code, out, err
|
||||||
|
|
||||||
|
apt_get = shutil.which("apt-get")
|
||||||
|
dnf = shutil.which("dnf")
|
||||||
|
yum = shutil.which("yum")
|
||||||
|
|
||||||
|
if apt_get:
|
||||||
|
script = r"""
|
||||||
|
set -e
|
||||||
|
export DEBIAN_FRONTEND=noninteractive
|
||||||
|
apt-get update
|
||||||
|
apt-get install -y curl gpg lsb-release ca-certificates
|
||||||
|
install -d -m 0755 /usr/share/keyrings
|
||||||
|
curl -fsSL https://pkg.cloudflareclient.com/pubkey.gpg | gpg --yes --dearmor --output /usr/share/keyrings/cloudflare-warp-archive-keyring.gpg
|
||||||
|
echo "deb [signed-by=/usr/share/keyrings/cloudflare-warp-archive-keyring.gpg] https://pkg.cloudflareclient.com/ $(lsb_release -cs) main" > /etc/apt/sources.list.d/cloudflare-client.list
|
||||||
|
apt-get update
|
||||||
|
apt-get install -y cloudflare-warp
|
||||||
|
"""
|
||||||
|
code, _, err = call("install cloudflare-warp via apt", ["bash", "-lc", script], timeout=420)
|
||||||
|
if code != 0:
|
||||||
|
result["warnings"].append(err.strip() or "apt install failed")
|
||||||
|
elif dnf or yum:
|
||||||
|
manager = dnf or yum
|
||||||
|
repo_url = "https://pkg.cloudflareclient.com/cloudflare-warp-ascii.repo"
|
||||||
|
script = f"""
|
||||||
|
set -e
|
||||||
|
rpm --import https://pkg.cloudflareclient.com/pubkey.gpg || true
|
||||||
|
curl -fsSL {shlex.quote(repo_url)} > /etc/yum.repos.d/cloudflare-warp.repo
|
||||||
|
{shlex.quote(manager)} install -y cloudflare-warp
|
||||||
|
"""
|
||||||
|
code, _, err = call("install cloudflare-warp via rpm repo", ["bash", "-lc", script], timeout=420)
|
||||||
|
if code != 0:
|
||||||
|
result["warnings"].append(err.strip() or "rpm install failed")
|
||||||
|
else:
|
||||||
|
result["warnings"].append("supported package manager not found")
|
||||||
|
|
||||||
|
result["installed"] = bool(shutil.which("warp-cli"))
|
||||||
|
if not result["installed"] and not result["warnings"]:
|
||||||
|
result["warnings"].append("warp-cli still not available after install")
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
def apply_warp_runtime(cfg: dict[str, Any]) -> dict[str, Any]:
|
def apply_warp_runtime(cfg: dict[str, Any]) -> dict[str, Any]:
|
||||||
warp_cli = shutil.which("warp-cli")
|
warp_cli = shutil.which("warp-cli")
|
||||||
result: dict[str, Any] = {"applied": False, "commands": [], "warnings": []}
|
result: dict[str, Any] = {"applied": False, "install": {"attempted": False, "installed": bool(warp_cli)}, "commands": [], "warnings": []}
|
||||||
|
if cfg["enabled"] and cfg["mode"] != "off" and not warp_cli:
|
||||||
|
install_result = install_warp_cli()
|
||||||
|
result["install"] = install_result
|
||||||
|
result["warnings"].extend(install_result.get("warnings", []))
|
||||||
|
warp_cli = shutil.which("warp-cli")
|
||||||
|
if (not cfg["enabled"] or cfg["mode"] == "off") and not warp_cli:
|
||||||
|
result["applied"] = True
|
||||||
|
return result
|
||||||
if not warp_cli:
|
if not warp_cli:
|
||||||
result["warnings"].append("warp-cli not installed")
|
result["warnings"].append("warp-cli not installed")
|
||||||
return result
|
return result
|
||||||
if cfg["scope"] == "user":
|
|
||||||
result["warnings"].append("per-user WARP route saved only; telemt per-user upstream routing is not documented")
|
|
||||||
return result
|
|
||||||
|
|
||||||
def call(args: list[str], timeout: int = 20) -> tuple[int, str, str]:
|
def call(args: list[str], timeout: int = 20) -> tuple[int, str, str]:
|
||||||
code, out, err = run([warp_cli, *args], timeout=timeout)
|
code, out, err = run([warp_cli, *args], timeout=timeout)
|
||||||
@@ -627,6 +686,10 @@ def apply_warp_runtime(cfg: dict[str, Any]) -> dict[str, Any]:
|
|||||||
result["applied"] = True
|
result["applied"] = True
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
if cfg["scope"] == "user":
|
||||||
|
result["warnings"].append("per-user WARP route saved only; telemt per-user upstream routing is not documented")
|
||||||
|
return result
|
||||||
|
|
||||||
run(["systemctl", "enable", "--now", "warp-svc"], timeout=20)
|
run(["systemctl", "enable", "--now", "warp-svc"], timeout=20)
|
||||||
code, _, _ = call(["registration", "show"], timeout=10)
|
code, _, _ = call(["registration", "show"], timeout=10)
|
||||||
if code != 0:
|
if code != 0:
|
||||||
|
|||||||
@@ -118,9 +118,9 @@ const i18n = {
|
|||||||
warpSave: "Save WARP",
|
warpSave: "Save WARP",
|
||||||
warpSaved: "WARP settings saved",
|
warpSaved: "WARP settings saved",
|
||||||
warpInstalled: "warp-cli installed",
|
warpInstalled: "warp-cli installed",
|
||||||
warpNotInstalled: "warp-cli not installed",
|
warpNotInstalled: "will install on save",
|
||||||
warpPerUserNote: "One-client WARP profile is stored here. Runtime per-client routing needs a dedicated telemt route; global WARP applies to all clients.",
|
warpPerUserNote: "One-client WARP profile is stored here. If warp-cli is missing, it will be installed on save. Runtime per-client routing needs a dedicated telemt route; global WARP applies to all clients.",
|
||||||
warpAllNote: "Global WARP applies to all proxy traffic when warp-cli is installed and connected.",
|
warpAllNote: "Global WARP applies to all proxy traffic. If warp-cli is missing, it will be installed on save, then connected.",
|
||||||
savedKey: "saved key",
|
savedKey: "saved key",
|
||||||
dashboard: "Dashboard",
|
dashboard: "Dashboard",
|
||||||
noKeys: "No keys yet",
|
noKeys: "No keys yet",
|
||||||
@@ -362,9 +362,9 @@ const i18n = {
|
|||||||
warpSave: "Сохранить WARP",
|
warpSave: "Сохранить WARP",
|
||||||
warpSaved: "Настройки WARP сохранены",
|
warpSaved: "Настройки WARP сохранены",
|
||||||
warpInstalled: "warp-cli установлен",
|
warpInstalled: "warp-cli установлен",
|
||||||
warpNotInstalled: "warp-cli не установлен",
|
warpNotInstalled: "установится при сохранении",
|
||||||
warpPerUserNote: "Профиль WARP для одного клиента сохраняется здесь. Runtime-маршрут на одного клиента требует отдельный маршрут telemt; global WARP действует на всех клиентов.",
|
warpPerUserNote: "Профиль WARP для одного клиента сохраняется здесь. Если warp-cli нет, он установится при сохранении. Runtime-маршрут на одного клиента требует отдельный маршрут telemt; global WARP действует на всех клиентов.",
|
||||||
warpAllNote: "Global WARP применится ко всему proxy-трафику, если warp-cli установлен и подключён.",
|
warpAllNote: "Global WARP применится ко всему proxy-трафику. Если warp-cli нет, он установится при сохранении и затем подключится.",
|
||||||
savedKey: "ключ сохранён",
|
savedKey: "ключ сохранён",
|
||||||
dashboard: "Обзор",
|
dashboard: "Обзор",
|
||||||
noKeys: "Ключей пока нет",
|
noKeys: "Ключей пока нет",
|
||||||
|
|||||||
@@ -440,6 +440,6 @@
|
|||||||
<button id="qrCopyBtn" type="button" class="soft" data-i18n="copyLink">Copy link</button>
|
<button id="qrCopyBtn" type="button" class="soft" data-i18n="copyLink">Copy link</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<script src="/app.js?v=2.5.0-admin21" type="module"></script>
|
<script src="/app.js?v=2.5.0-admin22" type="module"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
Reference in New Issue
Block a user