all support, core,cli,gui,essentiials
This commit is contained in:
parent
693c3404ad
commit
047c5b2ed0
139 changed files with 850 additions and 647 deletions
72
README.md
72
README.md
|
|
@ -1,10 +1,29 @@
|
||||||
|
# hydra-veil — usage
|
||||||
|
|
||||||
|
## 1. Create billing code / Crear billing code
|
||||||
|
|
||||||
|
```python
|
||||||
python3 -c "
|
python3 -c "
|
||||||
from core.services.WebServiceApiService import WebServiceApiService
|
from core.services.WebServiceApiService import WebServiceApiService
|
||||||
subscription = WebServiceApiService.post_subscription(2, operator_id=3)
|
subscription = WebServiceApiService.post_subscription(2, operator_id=<OPERATOR_ID>)
|
||||||
print('billing code:', subscription.billing_code)
|
print('billing code:', subscription.billing_code)
|
||||||
"
|
"
|
||||||
|
```
|
||||||
|
|
||||||
parchear
|
> Copy the printed `billing_code` — you'll need it in the connect step.
|
||||||
|
> Copiá el `billing_code` impreso, lo vas a necesitar en el paso de conexión.
|
||||||
|
|
||||||
|
---
|
||||||
|
consultar estado de invoive
|
||||||
|
python3 -c "
|
||||||
|
from core.services.WebServiceApiService import WebServiceApiService
|
||||||
|
|
||||||
|
invoice = WebServiceApiService.get_invoice('BILLING_CODE_HERE')
|
||||||
|
print('status:', invoice.status)
|
||||||
|
"
|
||||||
|
## 2. Patch subscription / Parchear suscripción
|
||||||
|
|
||||||
|
```bash
|
||||||
sudo docker compose exec laravel.test php artisan tinker --execute="
|
sudo docker compose exec laravel.test php artisan tinker --execute="
|
||||||
\$sub = \App\Models\Subscription::orderBy('id', 'desc')->first();
|
\$sub = \App\Models\Subscription::orderBy('id', 'desc')->first();
|
||||||
\$sub->expires_at = '2027-12-31 23:59:59';
|
\$sub->expires_at = '2027-12-31 23:59:59';
|
||||||
|
|
@ -13,7 +32,15 @@ sudo docker compose exec laravel.test php artisan tinker --execute="
|
||||||
\$sub->save();
|
\$sub->save();
|
||||||
echo 'OK' . PHP_EOL;
|
echo 'OK' . PHP_EOL;
|
||||||
"
|
"
|
||||||
conectar
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Connect / Conectar
|
||||||
|
|
||||||
|
### VLESS
|
||||||
|
|
||||||
|
```python
|
||||||
python3 -c "
|
python3 -c "
|
||||||
from core.services.WebServiceApiService import WebServiceApiService
|
from core.services.WebServiceApiService import WebServiceApiService
|
||||||
from core.controllers.encrypted_proxy.VlessController import VlessController
|
from core.controllers.encrypted_proxy.VlessController import VlessController
|
||||||
|
|
@ -24,11 +51,48 @@ observer.subscribe('connected', lambda e: print('connected:', e.subject))
|
||||||
observer.subscribe('error', lambda e: print('error:', e.subject))
|
observer.subscribe('error', lambda e: print('error:', e.subject))
|
||||||
observer.subscribe('disconnected', lambda e: print('disconnected:', e.subject))
|
observer.subscribe('disconnected', lambda e: print('disconnected:', e.subject))
|
||||||
|
|
||||||
session = WebServiceApiService.post_operator_proxy('617N-LPKB-MVPT-YRQM', 3, 'vless')
|
session = WebServiceApiService.post_operator_proxy('<BILLING_CODE>', <OPERATOR_ID>, 'vless')
|
||||||
print('session:', session)
|
print('session:', session)
|
||||||
|
|
||||||
controller = VlessController(1080)
|
controller = VlessController(1080)
|
||||||
controller.enable(session.links[0], session.username, observer)
|
controller.enable(session.links[0], session.username, observer)
|
||||||
"
|
"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Hysteria2
|
||||||
|
|
||||||
|
```python
|
||||||
|
python3 -c "
|
||||||
|
from core.services.WebServiceApiService import WebServiceApiService
|
||||||
|
from core.controllers.encrypted_proxy.HysteriaController import HysteriaController
|
||||||
|
from core.observers.EncryptedProxyObserver import EncryptedProxyObserver
|
||||||
|
|
||||||
|
observer = EncryptedProxyObserver()
|
||||||
|
observer.subscribe('connected', lambda e: print('connected:', e.subject))
|
||||||
|
observer.subscribe('error', lambda e: print('error:', e.subject))
|
||||||
|
observer.subscribe('disconnected', lambda e: print('disconnected:', e.subject))
|
||||||
|
|
||||||
|
session = WebServiceApiService.post_operator_proxy('<BILLING_CODE>', <OPERATOR_ID>, 'hysteria2')
|
||||||
|
print('session:', session)
|
||||||
|
|
||||||
|
controller = HysteriaController(1080)
|
||||||
|
controller.enable(session.username, session.password, session.operator_hysteria2_host, observer)
|
||||||
|
"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Disconnect / Desconectar
|
||||||
|
|
||||||
|
```python
|
||||||
|
python3 -c "
|
||||||
|
from core.controllers.encrypted_proxy.HysteriaController import HysteriaController
|
||||||
|
from core.observers.EncryptedProxyObserver import EncryptedProxyObserver
|
||||||
|
|
||||||
|
observer = EncryptedProxyObserver()
|
||||||
|
observer.subscribe('disconnected', lambda e: print('disconnected'))
|
||||||
|
|
||||||
|
controller = HysteriaController(1080)
|
||||||
|
controller.disable(observer)
|
||||||
|
"
|
||||||
|
```
|
||||||
|
|
|
||||||
|
|
@ -1,50 +0,0 @@
|
||||||
from dataclasses import dataclass
|
|
||||||
from typing import Final
|
|
||||||
from dotenv import load_dotenv
|
|
||||||
import os
|
|
||||||
|
|
||||||
load_dotenv(os.path.join(os.path.dirname(__file__), '../.env'))
|
|
||||||
|
|
||||||
_env = os.environ.get('APP_ENV', 'production')
|
|
||||||
_sp_api_base_url = (
|
|
||||||
os.environ.get('SP_API_BASE_URL_LOCAL', 'http://simplifiedprivacy.test/api/v1')
|
|
||||||
if _env == 'local'
|
|
||||||
else os.environ.get('SP_API_BASE_URL_PRODUCTION', 'https://api.hydraveil.net/api/v1')
|
|
||||||
)
|
|
||||||
|
|
||||||
@dataclass(frozen=True)
|
|
||||||
class Constants:
|
|
||||||
TICKET_API_BASE_URL: Final[str] = os.environ.get(
|
|
||||||
"TICKET_API_BASE_URL", "https://ticket.hydraveil.net"
|
|
||||||
)
|
|
||||||
SP_API_BASE_URL: Final[str] = _sp_api_base_url
|
|
||||||
PING_URL: Final[str] = os.environ.get('PING_URL', f'{_sp_api_base_url}/health')
|
|
||||||
CONNECTION_RETRY_INTERVAL: Final[int] = int(os.environ.get('CONNECTION_RETRY_INTERVAL', '5'))
|
|
||||||
MAX_CONNECTION_ATTEMPTS: Final[int] = int(os.environ.get('MAX_CONNECTION_ATTEMPTS', '2'))
|
|
||||||
HV_CLIENT_PATH: Final[str] = os.environ.get('HV_CLIENT_PATH')
|
|
||||||
HV_CLIENT_VERSION_NUMBER: Final[str] = os.environ.get('HV_CLIENT_VERSION_NUMBER')
|
|
||||||
HOME: Final[str] = os.path.expanduser('~')
|
|
||||||
SYSTEM_CONFIG_PATH: Final[str] = '/etc'
|
|
||||||
INSTALL_DIR: Final[str] = os.environ.get('INSTALL_DIR', os.path.expanduser('~'))
|
|
||||||
CACHE_HOME: Final[str] = os.environ.get('XDG_CACHE_HOME', f'{INSTALL_DIR}/.cache')
|
|
||||||
CONFIG_HOME: Final[str] = os.environ.get('XDG_CONFIG_HOME', f'{INSTALL_DIR}/.config')
|
|
||||||
DATA_HOME: Final[str] = os.environ.get('XDG_DATA_HOME', f'{INSTALL_DIR}/data')
|
|
||||||
STATE_HOME: Final[str] = os.environ.get('XDG_STATE_HOME', f'{INSTALL_DIR}/.state')
|
|
||||||
HV_SYSTEM_CONFIG_PATH: Final[str] = f'{SYSTEM_CONFIG_PATH}/hydra-veil'
|
|
||||||
HV_CACHE_HOME: Final[str] = f'{CACHE_HOME}/hydra-veil'
|
|
||||||
HV_CONFIG_HOME: Final[str] = f'{CONFIG_HOME}/hydra-veil'
|
|
||||||
HV_DATA_HOME: Final[str] = f'{DATA_HOME}/hydra-veil'
|
|
||||||
HV_STATE_HOME: Final[str] = f'{STATE_HOME}/hydra-veil'
|
|
||||||
HV_SYSTEM_PROFILE_CONFIG_PATH: Final[str] = f'{HV_SYSTEM_CONFIG_PATH}/profiles'
|
|
||||||
HV_PROFILE_CONFIG_HOME: Final[str] = f'{HV_CONFIG_HOME}/profiles'
|
|
||||||
HV_PROFILE_DATA_HOME: Final[str] = f'{HV_DATA_HOME}/profiles'
|
|
||||||
HV_TICKETING_CONFIG_HOME: Final[str] = f"{HV_CONFIG_HOME}/ticketing"
|
|
||||||
HV_TICKETING_DATA_HOME: Final[str] = f"{HV_DATA_HOME}/ticket_data"
|
|
||||||
HV_APPLICATION_DATA_HOME: Final[str] = f'{HV_DATA_HOME}/applications'
|
|
||||||
HV_INCIDENT_DATA_HOME: Final[str] = f'{HV_DATA_HOME}/incidents'
|
|
||||||
HV_RUNTIME_DATA_HOME: Final[str] = f'{HV_DATA_HOME}/runtime'
|
|
||||||
HV_STORAGE_DATABASE_PATH: Final[str] = f'{HV_DATA_HOME}/storage.db'
|
|
||||||
HV_CAPABILITY_POLICY_PATH: Final[str] = f'{SYSTEM_CONFIG_PATH}/apparmor.d/hydra-veil'
|
|
||||||
HV_PRIVILEGE_POLICY_PATH: Final[str] = f'{SYSTEM_CONFIG_PATH}/sudoers.d/hydra-veil'
|
|
||||||
HV_SESSION_STATE_HOME: Final[str] = f'{HV_STATE_HOME}/sessions'
|
|
||||||
HV_TOR_STATE_HOME: Final[str] = f'{HV_STATE_HOME}/tor'
|
|
||||||
|
|
@ -1,44 +0,0 @@
|
||||||
import socket
|
|
||||||
import subprocess
|
|
||||||
import time
|
|
||||||
|
|
||||||
|
|
||||||
def resolve(host: str) -> str | None:
|
|
||||||
try:
|
|
||||||
return socket.gethostbyname(host)
|
|
||||||
except Exception:
|
|
||||||
return None
|
|
||||||
|
|
||||||
|
|
||||||
def get_real_ip(timeout: int = 10) -> str:
|
|
||||||
try:
|
|
||||||
r = subprocess.run(
|
|
||||||
["curl", "-s", "--max-time", str(timeout), "https://ifconfig.me/ip"],
|
|
||||||
capture_output=True, timeout=timeout + 2,
|
|
||||||
)
|
|
||||||
return r.stdout.decode().strip() or "unknown (empty)"
|
|
||||||
except Exception as e:
|
|
||||||
return f"unknown ({e})"
|
|
||||||
|
|
||||||
|
|
||||||
def get_proxied_ip(socks5_port: int, timeout: int = 10) -> str:
|
|
||||||
try:
|
|
||||||
r = subprocess.run(
|
|
||||||
["curl", "-s", "--max-time", str(timeout),
|
|
||||||
"-x", f"socks5h://127.0.0.1:{socks5_port}",
|
|
||||||
"https://ifconfig.me/ip"],
|
|
||||||
capture_output=True, timeout=timeout + 2,
|
|
||||||
)
|
|
||||||
return r.stdout.decode().strip() or "unknown (empty)"
|
|
||||||
except Exception as e:
|
|
||||||
return f"unknown ({e})"
|
|
||||||
|
|
||||||
|
|
||||||
def verify_ip(socks5_port: int, retries: int = 3, delay: float = 2.0) -> str:
|
|
||||||
for attempt in range(1, retries + 1):
|
|
||||||
ip = get_proxied_ip(socks5_port)
|
|
||||||
if not ip.startswith("unknown"):
|
|
||||||
return ip
|
|
||||||
if attempt < retries:
|
|
||||||
time.sleep(delay)
|
|
||||||
return "unknown"
|
|
||||||
70
core/Constants.py
Normal file
70
core/Constants.py
Normal file
|
|
@ -0,0 +1,70 @@
|
||||||
|
from dataclasses import dataclass
|
||||||
|
from typing import Final
|
||||||
|
from dotenv import load_dotenv
|
||||||
|
import os
|
||||||
|
|
||||||
|
load_dotenv(os.path.join(os.path.dirname(__file__), '../.env'))
|
||||||
|
|
||||||
|
_env = os.environ.get('APP_ENV', 'production')
|
||||||
|
|
||||||
|
_sp_api_base_url = (
|
||||||
|
os.environ.get('SP_API_BASE_URL_LOCAL', 'http://simplifiedprivacy.test/api/v1')
|
||||||
|
if _env == 'local'
|
||||||
|
else os.environ.get('SP_API_BASE_URL_PRODUCTION', 'https://api.hydraveil.net/api/v1')
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass(frozen=True)
|
||||||
|
class Constants:
|
||||||
|
TICKET_API_BASE_URL: Final[str] = os.environ.get(
|
||||||
|
"TICKET_API_BASE_URL", "https://ticket.hydraveil.net"
|
||||||
|
)
|
||||||
|
SP_API_BASE_URL: Final[str] = _sp_api_base_url
|
||||||
|
PING_URL: Final[str] = os.environ.get('PING_URL', f'{_sp_api_base_url}/health')
|
||||||
|
CONNECTION_RETRY_INTERVAL: Final[int] = int(os.environ.get('CONNECTION_RETRY_INTERVAL', '5'))
|
||||||
|
MAX_CONNECTION_ATTEMPTS: Final[int] = int(os.environ.get('MAX_CONNECTION_ATTEMPTS', '2'))
|
||||||
|
HV_CLIENT_PATH: Final[str] = os.environ.get('HV_CLIENT_PATH')
|
||||||
|
HV_CLIENT_VERSION_NUMBER: Final[str] = os.environ.get('HV_CLIENT_VERSION_NUMBER')
|
||||||
|
|
||||||
|
# ── Paths base ────────────────────────────────────────────────────────────
|
||||||
|
HOME: Final[str] = os.path.expanduser('~')
|
||||||
|
SYSTEM_CONFIG_PATH: Final[str] = '/etc'
|
||||||
|
INSTALL_DIR: Final[str] = os.environ.get('INSTALL_DIR', os.path.expanduser('~'))
|
||||||
|
|
||||||
|
# ── XDG ───────────────────────────────────────────────────────────────────
|
||||||
|
CACHE_HOME: Final[str] = os.environ.get('XDG_CACHE_HOME', f'{INSTALL_DIR}/.cache')
|
||||||
|
CONFIG_HOME: Final[str] = os.environ.get('XDG_CONFIG_HOME', f'{INSTALL_DIR}/.config')
|
||||||
|
DATA_HOME: Final[str] = os.environ.get('XDG_DATA_HOME', f'{INSTALL_DIR}/data')
|
||||||
|
STATE_HOME: Final[str] = os.environ.get('XDG_STATE_HOME', f'{INSTALL_DIR}/.state')
|
||||||
|
|
||||||
|
# ── hydra-veil dirs ───────────────────────────────────────────────────────
|
||||||
|
HV_SYSTEM_CONFIG_PATH: Final[str] = f'{SYSTEM_CONFIG_PATH}/hydra-veil'
|
||||||
|
HV_CACHE_HOME: Final[str] = f'{CACHE_HOME}/hydra-veil'
|
||||||
|
HV_CONFIG_HOME: Final[str] = f'{CONFIG_HOME}/hydra-veil'
|
||||||
|
HV_DATA_HOME: Final[str] = f'{DATA_HOME}/hydra-veil'
|
||||||
|
HV_STATE_HOME: Final[str] = f'{STATE_HOME}/hydra-veil'
|
||||||
|
|
||||||
|
HV_SYSTEM_PROFILE_CONFIG_PATH: Final[str] = f'{HV_SYSTEM_CONFIG_PATH}/profiles'
|
||||||
|
HV_PROFILE_CONFIG_HOME: Final[str] = f'{HV_CONFIG_HOME}/profiles'
|
||||||
|
HV_PROFILE_DATA_HOME: Final[str] = f'{HV_DATA_HOME}/profiles'
|
||||||
|
HV_TICKETING_CONFIG_HOME: Final[str] = f'{HV_CONFIG_HOME}/ticketing'
|
||||||
|
HV_TICKETING_DATA_HOME: Final[str] = f'{HV_DATA_HOME}/ticket_data'
|
||||||
|
HV_APPLICATION_DATA_HOME: Final[str] = f'{HV_DATA_HOME}/applications'
|
||||||
|
HV_INCIDENT_DATA_HOME: Final[str] = f'{HV_DATA_HOME}/incidents'
|
||||||
|
HV_RUNTIME_DATA_HOME: Final[str] = f'{HV_DATA_HOME}/runtime'
|
||||||
|
HV_STORAGE_DATABASE_PATH: Final[str] = f'{HV_DATA_HOME}/storage.db'
|
||||||
|
HV_CAPABILITY_POLICY_PATH: Final[str] = f'{SYSTEM_CONFIG_PATH}/apparmor.d/hydra-veil'
|
||||||
|
HV_PRIVILEGE_POLICY_PATH: Final[str] = f'{SYSTEM_CONFIG_PATH}/sudoers.d/hydra-veil'
|
||||||
|
HV_SESSION_STATE_HOME: Final[str] = f'{HV_STATE_HOME}/sessions'
|
||||||
|
HV_TOR_STATE_HOME: Final[str] = f'{HV_STATE_HOME}/tor'
|
||||||
|
|
||||||
|
# ── sing-box ──────────────────────────────────────────────────────────────
|
||||||
|
SINGBOX_WRAPPER: Final[str] = os.environ.get(
|
||||||
|
'SINGBOX_WRAPPER', '/usr/local/bin/hydraveil-singbox'
|
||||||
|
)
|
||||||
|
SINGBOX_BIN: Final[str] = os.environ.get(
|
||||||
|
'SINGBOX_BIN', '/usr/bin/sing-box'
|
||||||
|
)
|
||||||
|
SINGBOX_CONFIG_DIR: Final[str] = f'{HV_DATA_HOME}/configs'
|
||||||
|
SINGBOX_PID_FILE: Final[str] = f'{HV_RUNTIME_DATA_HOME}/singbox.pid'
|
||||||
|
SINGBOX_LOG_FILE: Final[str] = f'{HV_RUNTIME_DATA_HOME}/singbox.log'
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
import socket
|
import socket
|
||||||
|
import time
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from core.Constants import Constants
|
from core.Constants import Constants
|
||||||
from core.utils.encrypted_proxy.net import get_real_ip, verify_ip
|
from core.utils.encrypted_proxy.net import get_real_ip, verify_ip
|
||||||
|
|
@ -12,6 +13,7 @@ def _resolve(host: str) -> str:
|
||||||
def build_hysteria_config(username: str, password: str,
|
def build_hysteria_config(username: str, password: str,
|
||||||
server_host: str, socks5_port: int) -> dict:
|
server_host: str, socks5_port: int) -> dict:
|
||||||
server_ip = _resolve(server_host)
|
server_ip = _resolve(server_host)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"dns": {
|
"dns": {
|
||||||
"servers": [{"tag": "local", "type": "udp", "server": "9.9.9.9"}],
|
"servers": [{"tag": "local", "type": "udp", "server": "9.9.9.9"}],
|
||||||
|
|
@ -55,6 +57,8 @@ def build_hysteria_config(username: str, password: str,
|
||||||
"rules": [
|
"rules": [
|
||||||
{"protocol": "dns", "action": "hijack-dns"},
|
{"protocol": "dns", "action": "hijack-dns"},
|
||||||
{"ip_cidr": ["172.19.0.0/30"], "action": "hijack-dns"},
|
{"ip_cidr": ["172.19.0.0/30"], "action": "hijack-dns"},
|
||||||
|
# DNS y servidor salen directo — nunca por el TUN
|
||||||
|
{"ip_cidr": ["9.9.9.9/32"], "outbound": "direct"},
|
||||||
{"ip_cidr": [f"{server_ip}/32"], "outbound": "direct"},
|
{"ip_cidr": [f"{server_ip}/32"], "outbound": "direct"},
|
||||||
{"ip_is_private": True, "outbound": "direct"},
|
{"ip_is_private": True, "outbound": "direct"},
|
||||||
{"ip_version": 6, "outbound": "block"},
|
{"ip_version": 6, "outbound": "block"},
|
||||||
|
|
@ -85,7 +89,11 @@ def enable_hysteria(username: str, password: str, server_host: str,
|
||||||
if observer:
|
if observer:
|
||||||
observer.notify("error", "sing-box not active after start")
|
observer.notify("error", "sing-box not active after start")
|
||||||
return False
|
return False
|
||||||
proxy_ip = verify_ip(socks5_port)
|
|
||||||
|
# Esperar que el proxy esté listo para rutear tráfico
|
||||||
|
time.sleep(3)
|
||||||
|
|
||||||
|
proxy_ip = verify_ip(socks5_port, retries=5, delay=3.0)
|
||||||
if observer:
|
if observer:
|
||||||
observer.notify("connected", {
|
observer.notify("connected", {
|
||||||
"real_ip": real_ip,
|
"real_ip": real_ip,
|
||||||
|
|
@ -4,6 +4,7 @@ from core.Constants import Constants
|
||||||
from core.utils.encrypted_proxy.net import get_real_ip, verify_ip
|
from core.utils.encrypted_proxy.net import get_real_ip, verify_ip
|
||||||
from core.utils.encrypted_proxy.singbox import SingboxRunner
|
from core.utils.encrypted_proxy.singbox import SingboxRunner
|
||||||
import socket
|
import socket
|
||||||
|
import time
|
||||||
|
|
||||||
|
|
||||||
def parse_vless_link(link: str) -> dict:
|
def parse_vless_link(link: str) -> dict:
|
||||||
|
|
@ -36,7 +37,13 @@ def build_vless_config(vless: dict, socks5_port: int) -> dict:
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"dns": {
|
"dns": {
|
||||||
"servers": [{"tag": "local", "type": "udp", "server": "9.9.9.9"}],
|
"servers": [
|
||||||
|
{
|
||||||
|
"tag": "local",
|
||||||
|
"type": "udp",
|
||||||
|
"server": "9.9.9.9",
|
||||||
|
},
|
||||||
|
],
|
||||||
"final": "local",
|
"final": "local",
|
||||||
"strategy": "ipv4_only",
|
"strategy": "ipv4_only",
|
||||||
},
|
},
|
||||||
|
|
@ -82,6 +89,7 @@ def build_vless_config(vless: dict, socks5_port: int) -> dict:
|
||||||
"rules": [
|
"rules": [
|
||||||
{"protocol": "dns", "action": "hijack-dns"},
|
{"protocol": "dns", "action": "hijack-dns"},
|
||||||
{"ip_cidr": ["172.19.0.0/30"], "action": "hijack-dns"},
|
{"ip_cidr": ["172.19.0.0/30"], "action": "hijack-dns"},
|
||||||
|
{"ip_cidr": ["9.9.9.9/32"], "outbound": "direct"},
|
||||||
{"ip_cidr": [f"{server_ip}/32"], "outbound": "direct"},
|
{"ip_cidr": [f"{server_ip}/32"], "outbound": "direct"},
|
||||||
{"ip_is_private": True, "outbound": "direct"},
|
{"ip_is_private": True, "outbound": "direct"},
|
||||||
{"ip_version": 6, "outbound": "block"},
|
{"ip_version": 6, "outbound": "block"},
|
||||||
|
|
@ -113,6 +121,10 @@ def enable_vless(vless_link: str, username: str,
|
||||||
if observer:
|
if observer:
|
||||||
observer.notify("error", "sing-box not active after start")
|
observer.notify("error", "sing-box not active after start")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
# Esperar que el proxy esté listo para rutear tráfico
|
||||||
|
time.sleep(3)
|
||||||
|
|
||||||
proxy_ip = verify_ip(socks5_port, retries=5, delay=3.0)
|
proxy_ip = verify_ip(socks5_port, retries=5, delay=3.0)
|
||||||
if observer:
|
if observer:
|
||||||
observer.notify("connected", {
|
observer.notify("connected", {
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue