feat: Добавлены веб-интерфейс управления и скрипт для генерации клиентских конфигураций VLESS из URL.

This commit is contained in:
2025-12-24 11:42:23 +03:00
parent dbeb1b9cc0
commit 6a9d454d2a
4 changed files with 42 additions and 94 deletions

View File

@@ -17,7 +17,7 @@
- Пользователи, которым нужен VPN для работы или доступа к заблокированным ресурсам - Пользователи, которым нужен VPN для работы или доступа к заблокированным ресурсам
- Разработчики, которые хотят направить трафик VS Code или других программ через VPN - Разработчики, которые хотят направить трафик VS Code или других программ через VPN
- Люди, которые получили "ссылку подписки" от VPN-провайдера - Люди, которые получили VLESS ссылку от VPN-провайдера
--- ---
@@ -57,9 +57,7 @@
### Что вам понадобится ### Что вам понадобится
1. **VPN-ссылка** — получите её от вашего VPN-провайдера. Бывает двух видов: 1. **VLESS-ссылка** — получите её от вашего VPN-провайдера. Она начинается с `vless://...`
- **Прямая ссылка**: начинается с `vless://...`
- **Ссылка подписки**: обычный URL (начинается с `https://...`), который содержит список серверов
2. **Docker** — программа для запуска изолированных приложений 2. **Docker** — программа для запуска изолированных приложений
- [Скачать Docker Desktop](https://www.docker.com/products/docker-desktop/) (бесплатно) - [Скачать Docker Desktop](https://www.docker.com/products/docker-desktop/) (бесплатно)
@@ -90,7 +88,7 @@ docker compose up -d
### После запуска ### После запуска
1. **Откройте веб-интерфейс**: http://localhost:3456 1. **Откройте веб-интерфейс**: http://localhost:3456
2. **Вставьте вашу VPN-ссылку** (vless:// или https://) 2. **Вставьте вашу VLESS-ссылку** (vless://)
3. **Нажмите "Применить"** 3. **Нажмите "Применить"**
4. Готово! Прокси работает на порту **8082** 4. Готово! Прокси работает на порту **8082**
@@ -174,24 +172,14 @@ http.proxy: http://127.0.0.1:8082
--- ---
## 🔄 Смена сервера
### Через веб-интерфейс (рекомендуется)
### Через веб-интерфейс
1. Откройте http://localhost:3456 1. Откройте http://localhost:3456
2. Вставьте новую ссылку 2. Вставьте новую VLESS ссылку
3. Нажмите "Применить" 3. Нажмите "Применить"
### Через консоль (если нужен выбор из списка)
Если у вас ссылка подписки с несколькими серверами:
```bash
docker exec -it sing-proxy ./menu.sh "https://ваша-ссылка-подписки..."
```
Появится список серверов для выбора.
--- ---
## 📋 Управление контейнером ## 📋 Управление контейнером
@@ -250,7 +238,7 @@ docker compose up -d
**Решение**: **Решение**:
- Попробуйте другой сервер — вставьте другую ссылку в веб-интерфейсе - Попробуйте другой сервер — вставьте другую ссылку в веб-интерфейсе
- Проверьте, что ссылка подписки актуальна - Проверьте, что VLESS ссылка актуальна
### Как узнать, работает ли VPN? ### Как узнать, работает ли VPN?
@@ -311,7 +299,7 @@ environment:
| **Контейнер** | Изолированное приложение со всеми необходимыми компонентами | | **Контейнер** | Изолированное приложение со всеми необходимыми компонентами |
| **VLESS** | Современный протокол VPN-соединения | | **VLESS** | Современный протокол VPN-соединения |
| **Reality** | Технология маскировки VPN-трафика под обычный интернет-трафик | | **Reality** | Технология маскировки VPN-трафика под обычный интернет-трафик |
| **Ссылка подписки** | URL, который содержит список VPN-серверов и их настройки |
| **Порт** | "Номер двери" для сетевых соединений. Прокси: 8082, Веб-интерфейс: 3456 | | **Порт** | "Номер двери" для сетевых соединений. Прокси: 8082, Веб-интерфейс: 3456 |
--- ---

View File

@@ -19,60 +19,12 @@ if [[ ! -f "$TEMPLATE_FILE" ]]; then
exit 1 exit 1
fi fi
# Detect if input is a subscription link (HTTP/HTTPS) # Check if input starts with vless://
if [[ "$URL_INPUT" =~ ^http ]]; then if [[ "$URL_INPUT" != vless://* ]]; then
echo "Detecting subscription link..." echo "Error: Only vless:// URLs are supported." >&2
# Build URL with client parameter for APIs that require it
SUB_URL="$URL_INPUT"
# Try fetching as-is first
SUB_CONTENT=$(curl -sSL "$SUB_URL")
# If empty, try adding client parameter (some APIs require this)
if [[ -z "$SUB_CONTENT" ]]; then
echo "Empty response, trying with client=v2rayng parameter..."
if [[ "$SUB_URL" == *"?"* ]]; then
SUB_URL="${URL_INPUT}&client=v2rayng"
else
SUB_URL="${URL_INPUT}?client=v2rayng"
fi
SUB_CONTENT=$(curl -sSL "$SUB_URL")
fi
if [[ -z "$SUB_CONTENT" ]]; then
echo "Error: Failed to download subscription from $SUB_URL" >&2
exit 1 exit 1
fi fi
# Check if base64 encoded (simple check: no spaces, looks like b64)
# Trying to decode. If fails, assume it's plain text lists
if DECODED=$(echo "$SUB_CONTENT" | base64 -d 2>/dev/null); then
echo "Decoded base64 subscription."
RAW_CONFIGS="$DECODED"
else
echo "Using plain text subscription."
RAW_CONFIGS="$SUB_CONTENT"
fi
# Find first vless reality link (vless://... + security=reality or just vless://)
# We try to find one that explicitly has reality, if not, pick ANY vless
TARGET_URL=$(echo "$RAW_CONFIGS" | grep -o 'vless://[^[:space:]]*' | grep 'security=reality' | head -n 1)
if [[ -z "$TARGET_URL" ]]; then
echo "No VLESS Reality link found, trying any VLESS..."
TARGET_URL=$(echo "$RAW_CONFIGS" | grep -o 'vless://[^[:space:]]*' | head -n 1)
fi
if [[ -z "$TARGET_URL" ]]; then
echo "Error: No VLESS URL found in subscription." >&2
exit 1
fi
echo "Selected URL from subscription: ${TARGET_URL:0:30}..."
URL_INPUT="$TARGET_URL"
fi
# Strip scheme # Strip scheme
URL_NOSCHEME=${URL_INPUT#vless://} URL_NOSCHEME=${URL_INPUT#vless://}

View File

@@ -1,5 +1,6 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="ru"> <html lang="ru">
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
@@ -113,8 +114,15 @@
} }
@keyframes pulse { @keyframes pulse {
0%, 100% { opacity: 1; }
50% { opacity: 0.6; } 0%,
100% {
opacity: 1;
}
50% {
opacity: 0.6;
}
} }
.status-text { .status-text {
@@ -223,7 +231,9 @@
} }
@keyframes spin { @keyframes spin {
to { transform: rotate(360deg); } to {
transform: rotate(360deg);
}
} }
.message { .message {
@@ -242,6 +252,7 @@
opacity: 0; opacity: 0;
transform: translateY(-8px); transform: translateY(-8px);
} }
to { to {
opacity: 1; opacity: 1;
transform: translateY(0); transform: translateY(0);
@@ -346,6 +357,7 @@
} }
</style> </style>
</head> </head>
<body> <body>
<div class="container"> <div class="container">
<div class="card"> <div class="card">
@@ -365,17 +377,12 @@
<form id="proxyForm"> <form id="proxyForm">
<div class="form-group"> <div class="form-group">
<label for="urlInput">VLESS / Subscription URL</label> <label for="urlInput">VLESS Key</label>
<div class="input-wrapper"> <div class="input-wrapper">
<input <input type="text" id="urlInput" placeholder="vless://..." autocomplete="off"
type="text" spellcheck="false">
id="urlInput"
placeholder="vless://... или https://subscription.link"
autocomplete="off"
spellcheck="false"
>
</div> </div>
<p class="hint">Вставьте VLESS ссылку или URL подписки</p> <p class="hint">Вставьте VLESS ссылку</p>
</div> </div>
<button type="submit" class="btn btn-primary" id="submitBtn"> <button type="submit" class="btn btn-primary" id="submitBtn">
@@ -513,4 +520,5 @@
}); });
</script> </script>
</body> </body>
</html> </html>

View File

@@ -115,8 +115,8 @@ class ProxyControlHandler(http.server.BaseHTTPRequestHandler):
self.send_json({"success": False, "error": "URL не указан"}, 400) self.send_json({"success": False, "error": "URL не указан"}, 400)
return return
if not (url.startswith("vless://") or url.startswith("http://") or url.startswith("https://")): if not url.startswith("vless://"):
self.send_json({"success": False, "error": "Неверный формат URL. Ожидается vless:// или http(s):// ссылка"}, 400) self.send_json({"success": False, "error": "Неверный формат. Поддерживаются только vless:// ссылки"}, 400)
return return
# Run gen-client-from-url.sh # Run gen-client-from-url.sh