fix(infra): gradle_run detecta android-sdk — issue 0076 #2
@@ -0,0 +1,37 @@
|
||||
---
|
||||
name: install_nordvpn
|
||||
kind: function
|
||||
lang: bash
|
||||
domain: infra
|
||||
version: "1.0.0"
|
||||
purity: impure
|
||||
signature: "install_nordvpn() -> void"
|
||||
description: "Instala NordVPN CLI en Ubuntu/Debian (incluido WSL2). Configura repositorio oficial, instala paquete y habilita servicio nordvpnd. Idempotente."
|
||||
tags: [vpn, nordvpn, install, infra, wsl2]
|
||||
uses_functions: []
|
||||
uses_types: []
|
||||
returns: []
|
||||
returns_optional: false
|
||||
error_type: "error_go_core"
|
||||
imports: []
|
||||
tested: false
|
||||
tests: []
|
||||
test_file_path: ""
|
||||
file_path: "bash/functions/infra/install_nordvpn.sh"
|
||||
---
|
||||
|
||||
## Ejemplo
|
||||
|
||||
```bash
|
||||
source install_nordvpn.sh
|
||||
install_nordvpn
|
||||
# nordvpn ya instalado: NordVPN Version 3.x.x
|
||||
# — o —
|
||||
# Instalando NordVPN CLI...
|
||||
# NordVPN instalado: NordVPN Version 3.x.x
|
||||
# NOTA: ejecuta 'nordvpn login' para autenticarte
|
||||
```
|
||||
|
||||
## Notas
|
||||
|
||||
Usa el script de instalacion oficial de NordVPN. En WSL2 sin systemd, levanta nordvpnd manualmente. Agrega el usuario al grupo nordvpn para evitar sudo en comandos posteriores. Despues de instalar, se requiere `nordvpn login` para autenticarse.
|
||||
@@ -0,0 +1,41 @@
|
||||
# install_nordvpn
|
||||
# ---------------
|
||||
# Instala NordVPN CLI en Ubuntu/Debian (incluido WSL2).
|
||||
# Configura el repositorio oficial, instala el paquete y habilita el servicio.
|
||||
# Si ya esta instalado, no hace nada.
|
||||
#
|
||||
# USO (sourced):
|
||||
# source install_nordvpn.sh
|
||||
# install_nordvpn
|
||||
|
||||
install_nordvpn() {
|
||||
if command -v nordvpn &>/dev/null; then
|
||||
echo "nordvpn ya instalado: $(nordvpn version 2>/dev/null)"
|
||||
return 0
|
||||
fi
|
||||
|
||||
echo "Instalando NordVPN CLI..."
|
||||
|
||||
# Descargar e instalar via script oficial
|
||||
sh <(curl -sSf https://downloads.nordcdn.com/apps/linux/install.sh) 2>&1
|
||||
|
||||
if ! command -v nordvpn &>/dev/null; then
|
||||
echo "install_nordvpn: fallo la instalacion" >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Agregar usuario al grupo nordvpn para evitar sudo
|
||||
sudo usermod -aG nordvpn "$USER" 2>/dev/null || true
|
||||
|
||||
# Habilitar servicio (systemd o manual para WSL2)
|
||||
if command -v systemctl &>/dev/null && systemctl is-system-running &>/dev/null 2>&1; then
|
||||
sudo systemctl enable --now nordvpnd 2>/dev/null || true
|
||||
else
|
||||
# WSL2 sin systemd — levantar daemon manualmente
|
||||
sudo nordvpnd &>/dev/null &
|
||||
sleep 2
|
||||
fi
|
||||
|
||||
echo "NordVPN instalado: $(nordvpn version 2>/dev/null)"
|
||||
echo "NOTA: ejecuta 'nordvpn login' para autenticarte"
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
---
|
||||
name: nordvpn_connect
|
||||
kind: function
|
||||
lang: bash
|
||||
domain: infra
|
||||
version: "1.0.0"
|
||||
purity: impure
|
||||
signature: "nordvpn_connect(country?: string, city?: string) -> json"
|
||||
description: "Conecta a NordVPN por pais, ciudad o servidor especifico. Sin argumentos conecta al mejor servidor disponible. Devuelve JSON con resultado."
|
||||
tags: [vpn, nordvpn, connect, infra, network]
|
||||
uses_functions: []
|
||||
uses_types: []
|
||||
returns: []
|
||||
returns_optional: false
|
||||
error_type: "error_go_core"
|
||||
imports: []
|
||||
tested: false
|
||||
tests: []
|
||||
test_file_path: ""
|
||||
file_path: "bash/functions/infra/nordvpn_connect.sh"
|
||||
---
|
||||
|
||||
## Ejemplo
|
||||
|
||||
```bash
|
||||
source nordvpn_connect.sh
|
||||
|
||||
nordvpn_connect
|
||||
# {"ok":true,"server":"us1234.nordvpn.com","country":"auto","city":"auto"}
|
||||
|
||||
nordvpn_connect Spain
|
||||
# {"ok":true,"server":"es42.nordvpn.com","country":"Spain","city":"auto"}
|
||||
|
||||
nordvpn_connect Spain Madrid
|
||||
# {"ok":true,"server":"es15.nordvpn.com","country":"Spain","city":"Madrid"}
|
||||
```
|
||||
|
||||
## Notas
|
||||
|
||||
Requiere NordVPN CLI instalado y autenticado (`nordvpn login`). La salida JSON facilita composicion con otros scripts y pipelines. Si ya hay una conexion activa, NordVPN reconecta automaticamente al nuevo destino.
|
||||
@@ -0,0 +1,39 @@
|
||||
# nordvpn_connect
|
||||
# ---------------
|
||||
# Conecta a NordVPN. Acepta pais, ciudad o servidor especifico.
|
||||
# Sin argumentos conecta al mejor servidor disponible.
|
||||
# Imprime JSON con el resultado de la conexion.
|
||||
#
|
||||
# USO (sourced):
|
||||
# source nordvpn_connect.sh
|
||||
# nordvpn_connect # mejor servidor
|
||||
# nordvpn_connect Spain # por pais
|
||||
# nordvpn_connect Spain Madrid # por ciudad
|
||||
# nordvpn_connect Spain '#42' # servidor especifico
|
||||
|
||||
nordvpn_connect() {
|
||||
local country="${1:-}"
|
||||
local city="${2:-}"
|
||||
|
||||
if ! command -v nordvpn &>/dev/null; then
|
||||
echo '{"ok":false,"error":"nordvpn no instalado"}' >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
local args=()
|
||||
[ -n "$country" ] && args+=("$country")
|
||||
[ -n "$city" ] && args+=("$city")
|
||||
|
||||
local output
|
||||
output=$(nordvpn connect "${args[@]}" 2>&1)
|
||||
local rc=$?
|
||||
|
||||
if [ $rc -eq 0 ] && echo "$output" | grep -qi "connected"; then
|
||||
local server
|
||||
server=$(echo "$output" | grep -oP '(?<=to )\S+' | head -1)
|
||||
echo "{\"ok\":true,\"server\":\"${server}\",\"country\":\"${country:-auto}\",\"city\":\"${city:-auto}\"}"
|
||||
else
|
||||
echo "{\"ok\":false,\"error\":$(echo "$output" | python3 -c 'import sys,json; print(json.dumps(sys.stdin.read().strip()))' 2>/dev/null || echo "\"$output\"")}"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
---
|
||||
name: nordvpn_disconnect
|
||||
kind: function
|
||||
lang: bash
|
||||
domain: infra
|
||||
version: "1.0.0"
|
||||
purity: impure
|
||||
signature: "nordvpn_disconnect() -> json"
|
||||
description: "Desconecta de NordVPN. Idempotente — si no hay conexion activa retorna ok. Devuelve JSON con resultado."
|
||||
tags: [vpn, nordvpn, disconnect, infra, network]
|
||||
uses_functions: []
|
||||
uses_types: []
|
||||
returns: []
|
||||
returns_optional: false
|
||||
error_type: "error_go_core"
|
||||
imports: []
|
||||
tested: false
|
||||
tests: []
|
||||
test_file_path: ""
|
||||
file_path: "bash/functions/infra/nordvpn_disconnect.sh"
|
||||
---
|
||||
|
||||
## Ejemplo
|
||||
|
||||
```bash
|
||||
source nordvpn_disconnect.sh
|
||||
|
||||
nordvpn_disconnect
|
||||
# {"ok":true,"status":"disconnected"}
|
||||
```
|
||||
|
||||
## Notas
|
||||
|
||||
Idempotente: si no hay conexion activa, retorna ok sin error. Requiere NordVPN CLI instalado.
|
||||
@@ -0,0 +1,26 @@
|
||||
# nordvpn_disconnect
|
||||
# ------------------
|
||||
# Desconecta de NordVPN. Idempotente — si no hay conexion activa, retorna ok.
|
||||
# Imprime JSON con el resultado.
|
||||
#
|
||||
# USO (sourced):
|
||||
# source nordvpn_disconnect.sh
|
||||
# nordvpn_disconnect
|
||||
|
||||
nordvpn_disconnect() {
|
||||
if ! command -v nordvpn &>/dev/null; then
|
||||
echo '{"ok":false,"error":"nordvpn no instalado"}' >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
local output
|
||||
output=$(nordvpn disconnect 2>&1)
|
||||
local rc=$?
|
||||
|
||||
if [ $rc -eq 0 ] || echo "$output" | grep -qi "not connected\|disconnected"; then
|
||||
echo '{"ok":true,"status":"disconnected"}'
|
||||
else
|
||||
echo "{\"ok\":false,\"error\":$(echo "$output" | python3 -c 'import sys,json; print(json.dumps(sys.stdin.read().strip()))' 2>/dev/null || echo "\"$output\"")}"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
---
|
||||
name: nordvpn_get_ip
|
||||
kind: function
|
||||
lang: bash
|
||||
domain: infra
|
||||
version: "1.0.0"
|
||||
purity: impure
|
||||
signature: "nordvpn_get_ip() -> json"
|
||||
description: "Obtiene IP publica actual con fallback entre multiples servicios. Indica si la conexion VPN esta activa y el servidor usado."
|
||||
tags: [vpn, nordvpn, ip, infra, network, verification]
|
||||
uses_functions: []
|
||||
uses_types: []
|
||||
returns: []
|
||||
returns_optional: false
|
||||
error_type: "error_go_core"
|
||||
imports: []
|
||||
tested: false
|
||||
tests: []
|
||||
test_file_path: ""
|
||||
file_path: "bash/functions/infra/nordvpn_get_ip.sh"
|
||||
---
|
||||
|
||||
## Ejemplo
|
||||
|
||||
```bash
|
||||
source nordvpn_get_ip.sh
|
||||
|
||||
# Con VPN activa:
|
||||
nordvpn_get_ip
|
||||
# {"ok":true,"ip":"185.x.x.x","vpn_connected":true,"vpn_server":"es42.nordvpn.com","source":"https://api.ipify.org"}
|
||||
|
||||
# Sin VPN:
|
||||
nordvpn_get_ip
|
||||
# {"ok":true,"ip":"88.x.x.x","vpn_connected":false,"vpn_server":"","source":"https://api.ipify.org"}
|
||||
```
|
||||
|
||||
## Notas
|
||||
|
||||
Usa ipify.org como servicio primario con fallback a ifconfig.me e icanhazip.com. Timeout de 5 segundos por servicio. Util para verificar que el tunel VPN esta activo antes de ejecutar operaciones sensibles a la IP.
|
||||
@@ -0,0 +1,42 @@
|
||||
# nordvpn_get_ip
|
||||
# --------------
|
||||
# Obtiene la IP publica actual para verificar que el tunel VPN funciona.
|
||||
# Usa multiples servicios como fallback.
|
||||
#
|
||||
# USO (sourced):
|
||||
# source nordvpn_get_ip.sh
|
||||
# nordvpn_get_ip
|
||||
|
||||
nordvpn_get_ip() {
|
||||
local ip=""
|
||||
local source=""
|
||||
|
||||
# Intentar multiples servicios
|
||||
for svc in "https://api.ipify.org" "https://ifconfig.me" "https://icanhazip.com"; do
|
||||
ip=$(curl -s --max-time 5 "$svc" 2>/dev/null)
|
||||
if echo "$ip" | grep -qP '^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$'; then
|
||||
source="$svc"
|
||||
break
|
||||
fi
|
||||
ip=""
|
||||
done
|
||||
|
||||
if [ -z "$ip" ]; then
|
||||
echo '{"ok":false,"error":"no se pudo obtener IP publica"}' >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Si nordvpn esta disponible, incluir info de conexion
|
||||
local connected="false"
|
||||
local vpn_server=""
|
||||
if command -v nordvpn &>/dev/null; then
|
||||
local status_output
|
||||
status_output=$(nordvpn status 2>/dev/null)
|
||||
if echo "$status_output" | grep -qi "connected"; then
|
||||
connected="true"
|
||||
vpn_server=$(echo "$status_output" | grep -iP "hostname|server" | head -1 | sed 's/.*: *//')
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "{\"ok\":true,\"ip\":\"$ip\",\"vpn_connected\":$connected,\"vpn_server\":\"$vpn_server\",\"source\":\"$source\"}"
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
---
|
||||
name: nordvpn_list_cities
|
||||
kind: function
|
||||
lang: bash
|
||||
domain: infra
|
||||
version: "1.0.0"
|
||||
purity: impure
|
||||
signature: "nordvpn_list_cities(country: string) -> json"
|
||||
description: "Lista ciudades disponibles de un pais en NordVPN como array JSON ordenado."
|
||||
tags: [vpn, nordvpn, cities, infra, network]
|
||||
uses_functions: []
|
||||
uses_types: []
|
||||
returns: []
|
||||
returns_optional: false
|
||||
error_type: "error_go_core"
|
||||
imports: []
|
||||
tested: false
|
||||
tests: []
|
||||
test_file_path: ""
|
||||
file_path: "bash/functions/infra/nordvpn_list_cities.sh"
|
||||
---
|
||||
|
||||
## Ejemplo
|
||||
|
||||
```bash
|
||||
source nordvpn_list_cities.sh
|
||||
|
||||
nordvpn_list_cities Spain
|
||||
# {"ok":true,"country":"Spain","count":2,"cities":["Barcelona","Madrid"]}
|
||||
|
||||
nordvpn_list_cities "United_States"
|
||||
# {"ok":true,"country":"United_States","count":15,"cities":["Atlanta","Buffalo",...]}
|
||||
```
|
||||
|
||||
## Notas
|
||||
|
||||
El nombre de pais debe coincidir con lo que devuelve `nordvpn countries`. Usa underscores para paises compuestos (ej: United_States). Las ciudades se devuelven con espacios.
|
||||
@@ -0,0 +1,38 @@
|
||||
# nordvpn_list_cities
|
||||
# -------------------
|
||||
# Lista las ciudades disponibles de un pais en NordVPN como array JSON.
|
||||
#
|
||||
# USO (sourced):
|
||||
# source nordvpn_list_cities.sh
|
||||
# nordvpn_list_cities Spain
|
||||
|
||||
nordvpn_list_cities() {
|
||||
local country="${1:?nordvpn_list_cities: se requiere pais como argumento}"
|
||||
|
||||
if ! command -v nordvpn &>/dev/null; then
|
||||
echo '{"ok":false,"error":"nordvpn no instalado"}' >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
local output
|
||||
output=$(nordvpn cities "$country" 2>&1)
|
||||
local rc=$?
|
||||
|
||||
if [ $rc -ne 0 ]; then
|
||||
echo "{\"ok\":false,\"error\":$(echo "$output" | python3 -c 'import sys,json; print(json.dumps(sys.stdin.read().strip()))' 2>/dev/null || echo "\"$output\"")}"
|
||||
return 1
|
||||
fi
|
||||
|
||||
echo "$output" | python3 -c '
|
||||
import sys, json, re
|
||||
|
||||
country = "'"$country"'"
|
||||
text = sys.stdin.read()
|
||||
text = re.sub(r"\x1b\[[0-9;]*m", "", text)
|
||||
text = re.sub(r"[\t\r]", " ", text)
|
||||
cities = [c.strip().replace("_", " ") for c in re.split(r"[,\n]+", text) if c.strip() and c.strip() != "-"]
|
||||
cities = [c for c in cities if len(c) > 1]
|
||||
cities.sort()
|
||||
print(json.dumps({"ok": True, "country": country, "count": len(cities), "cities": cities}))
|
||||
'
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
---
|
||||
name: nordvpn_list_countries
|
||||
kind: function
|
||||
lang: bash
|
||||
domain: infra
|
||||
version: "1.0.0"
|
||||
purity: impure
|
||||
signature: "nordvpn_list_countries() -> json"
|
||||
description: "Lista paises disponibles en NordVPN como array JSON ordenado alfabeticamente."
|
||||
tags: [vpn, nordvpn, countries, infra, network]
|
||||
uses_functions: []
|
||||
uses_types: []
|
||||
returns: []
|
||||
returns_optional: false
|
||||
error_type: "error_go_core"
|
||||
imports: []
|
||||
tested: false
|
||||
tests: []
|
||||
test_file_path: ""
|
||||
file_path: "bash/functions/infra/nordvpn_list_countries.sh"
|
||||
---
|
||||
|
||||
## Ejemplo
|
||||
|
||||
```bash
|
||||
source nordvpn_list_countries.sh
|
||||
|
||||
nordvpn_list_countries
|
||||
# {"ok":true,"count":60,"countries":["Albania","Argentina","Australia",...,"United States","Vietnam"]}
|
||||
```
|
||||
|
||||
## Notas
|
||||
|
||||
Parsea la salida de `nordvpn countries` eliminando codigos ANSI y normalizando separadores. Los nombres de paises se devuelven con espacios en vez de underscores.
|
||||
@@ -0,0 +1,36 @@
|
||||
# nordvpn_list_countries
|
||||
# ----------------------
|
||||
# Lista los paises disponibles en NordVPN como array JSON.
|
||||
#
|
||||
# USO (sourced):
|
||||
# source nordvpn_list_countries.sh
|
||||
# nordvpn_list_countries
|
||||
|
||||
nordvpn_list_countries() {
|
||||
if ! command -v nordvpn &>/dev/null; then
|
||||
echo '{"ok":false,"error":"nordvpn no instalado"}' >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
local output
|
||||
output=$(nordvpn countries 2>&1)
|
||||
local rc=$?
|
||||
|
||||
if [ $rc -ne 0 ]; then
|
||||
echo "{\"ok\":false,\"error\":$(echo "$output" | python3 -c 'import sys,json; print(json.dumps(sys.stdin.read().strip()))' 2>/dev/null || echo "\"$output\"")}"
|
||||
return 1
|
||||
fi
|
||||
|
||||
echo "$output" | python3 -c '
|
||||
import sys, json, re
|
||||
|
||||
text = sys.stdin.read()
|
||||
text = re.sub(r"\x1b\[[0-9;]*m", "", text)
|
||||
text = re.sub(r"[\t\r]", " ", text)
|
||||
# Split by comma, whitespace, or newline and clean
|
||||
countries = [c.strip().replace("_", " ") for c in re.split(r"[,\n]+", text) if c.strip() and c.strip() != "-"]
|
||||
countries = [c for c in countries if len(c) > 1]
|
||||
countries.sort()
|
||||
print(json.dumps({"ok": True, "count": len(countries), "countries": countries}))
|
||||
'
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
---
|
||||
name: nordvpn_set_protocol
|
||||
kind: function
|
||||
lang: bash
|
||||
domain: infra
|
||||
version: "1.0.0"
|
||||
purity: impure
|
||||
signature: "nordvpn_set_protocol(protocol: string) -> json"
|
||||
description: "Cambia el protocolo de NordVPN entre NordLynx (WireGuard) y OpenVPN. NordLynx recomendado por velocidad."
|
||||
tags: [vpn, nordvpn, protocol, nordlynx, wireguard, openvpn, infra]
|
||||
uses_functions: []
|
||||
uses_types: []
|
||||
returns: []
|
||||
returns_optional: false
|
||||
error_type: "error_go_core"
|
||||
imports: []
|
||||
tested: false
|
||||
tests: []
|
||||
test_file_path: ""
|
||||
file_path: "bash/functions/infra/nordvpn_set_protocol.sh"
|
||||
---
|
||||
|
||||
## Ejemplo
|
||||
|
||||
```bash
|
||||
source nordvpn_set_protocol.sh
|
||||
|
||||
nordvpn_set_protocol NordLynx
|
||||
# {"ok":true,"protocol":"NordLynx"}
|
||||
|
||||
nordvpn_set_protocol OpenVPN
|
||||
# {"ok":true,"protocol":"OpenVPN"}
|
||||
```
|
||||
|
||||
## Notas
|
||||
|
||||
NordLynx es WireGuard wrapeado por NordVPN — mas rapido y moderno. OpenVPN es mas compatible con redes restrictivas. El cambio de protocolo requiere reconectar si hay una conexion activa.
|
||||
@@ -0,0 +1,38 @@
|
||||
# nordvpn_set_protocol
|
||||
# --------------------
|
||||
# Cambia el protocolo de NordVPN (NordLynx o OpenVPN).
|
||||
# NordLynx = WireGuard (recomendado por velocidad).
|
||||
#
|
||||
# USO (sourced):
|
||||
# source nordvpn_set_protocol.sh
|
||||
# nordvpn_set_protocol NordLynx
|
||||
# nordvpn_set_protocol OpenVPN
|
||||
|
||||
nordvpn_set_protocol() {
|
||||
local protocol="${1:?nordvpn_set_protocol: se requiere protocolo (NordLynx|OpenVPN)}"
|
||||
|
||||
if ! command -v nordvpn &>/dev/null; then
|
||||
echo '{"ok":false,"error":"nordvpn no instalado"}' >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
case "$protocol" in
|
||||
NordLynx|nordlynx|NORDLYNX) protocol="NordLynx" ;;
|
||||
OpenVPN|openvpn|OPENVPN) protocol="OpenVPN" ;;
|
||||
*)
|
||||
echo "{\"ok\":false,\"error\":\"protocolo invalido: $protocol (usar NordLynx o OpenVPN)\"}"
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
|
||||
local output
|
||||
output=$(nordvpn set protocol "$protocol" 2>&1)
|
||||
local rc=$?
|
||||
|
||||
if [ $rc -eq 0 ] || echo "$output" | grep -qi "already set\|successfully"; then
|
||||
echo "{\"ok\":true,\"protocol\":\"$protocol\"}"
|
||||
else
|
||||
echo "{\"ok\":false,\"error\":$(echo "$output" | python3 -c 'import sys,json; print(json.dumps(sys.stdin.read().strip()))' 2>/dev/null || echo "\"$output\"")}"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
---
|
||||
name: nordvpn_status
|
||||
kind: function
|
||||
lang: bash
|
||||
domain: infra
|
||||
version: "1.0.0"
|
||||
purity: impure
|
||||
signature: "nordvpn_status() -> json"
|
||||
description: "Obtiene estado actual de NordVPN como JSON estructurado. Incluye servidor, IP, pais, protocolo y estado de conexion."
|
||||
tags: [vpn, nordvpn, status, infra, network]
|
||||
uses_functions: []
|
||||
uses_types: []
|
||||
returns: []
|
||||
returns_optional: false
|
||||
error_type: "error_go_core"
|
||||
imports: []
|
||||
tested: false
|
||||
tests: []
|
||||
test_file_path: ""
|
||||
file_path: "bash/functions/infra/nordvpn_status.sh"
|
||||
---
|
||||
|
||||
## Ejemplo
|
||||
|
||||
```bash
|
||||
source nordvpn_status.sh
|
||||
|
||||
nordvpn_status
|
||||
# {"ok":true,"connected":true,"status":"Connected","hostname":"es42.nordvpn.com","ip":"185.x.x.x","country":"Spain","city":"Madrid","current_technology":"NordLynx","current_protocol":"nordlynx","transfer":"1.2 MiB received, 500 KiB sent","uptime":"5 minutes 32 seconds"}
|
||||
|
||||
# Desconectado:
|
||||
# {"ok":true,"connected":false,"status":"Disconnected"}
|
||||
```
|
||||
|
||||
## Notas
|
||||
|
||||
Parsea la salida clave-valor de `nordvpn status` eliminando codigos ANSI. Los campos disponibles dependen del estado de conexion — cuando esta desconectado solo devuelve status y connected.
|
||||
@@ -0,0 +1,43 @@
|
||||
# nordvpn_status
|
||||
# --------------
|
||||
# Obtiene el estado actual de NordVPN como JSON estructurado.
|
||||
# Parsea la salida clave-valor de `nordvpn status` a campos JSON.
|
||||
#
|
||||
# USO (sourced):
|
||||
# source nordvpn_status.sh
|
||||
# nordvpn_status
|
||||
|
||||
nordvpn_status() {
|
||||
if ! command -v nordvpn &>/dev/null; then
|
||||
echo '{"ok":false,"error":"nordvpn no instalado"}' >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
local output
|
||||
output=$(nordvpn status 2>&1)
|
||||
local rc=$?
|
||||
|
||||
if [ $rc -ne 0 ]; then
|
||||
echo "{\"ok\":false,\"error\":$(echo "$output" | python3 -c 'import sys,json; print(json.dumps(sys.stdin.read().strip()))' 2>/dev/null || echo "\"$output\"")}"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Parsear output clave: valor a JSON con python3
|
||||
echo "$output" | python3 -c '
|
||||
import sys, json, re
|
||||
|
||||
lines = sys.stdin.read().strip().split("\n")
|
||||
data = {"ok": True}
|
||||
for line in lines:
|
||||
line = re.sub(r"\x1b\[[0-9;]*m", "", line).strip()
|
||||
line = line.lstrip("- ")
|
||||
if ":" in line:
|
||||
key, _, val = line.partition(":")
|
||||
key = key.strip().lower().replace(" ", "_")
|
||||
val = val.strip()
|
||||
if key == "status":
|
||||
data["connected"] = val.lower() == "connected"
|
||||
data[key] = val
|
||||
print(json.dumps(data))
|
||||
'
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package infra
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// NordVPNContainerRunOpts opciones para ejecutar un container a traves del gateway NordVPN.
|
||||
type NordVPNContainerRunOpts struct {
|
||||
Image string // Imagen Docker a ejecutar (obligatorio)
|
||||
Cmd []string // Comando a ejecutar en el container
|
||||
Env map[string]string // Variables de entorno
|
||||
Volumes []string // Bind mounts
|
||||
Name string // Nombre del container (opcional)
|
||||
Gateway string // Nombre del container NordVPN gateway (default: "nordvpn")
|
||||
Detach bool // Ejecutar en background
|
||||
Remove bool // Eliminar al terminar (--rm)
|
||||
}
|
||||
|
||||
// NordVPNContainerRun ejecuta un container Docker cuyo trafico de red
|
||||
// pasa por el container gateway NordVPN usando --network=container:<gateway>.
|
||||
// Devuelve el ID del container creado.
|
||||
func NordVPNContainerRun(opts NordVPNContainerRunOpts) (string, error) {
|
||||
if opts.Image == "" {
|
||||
return "", fmt.Errorf("image required")
|
||||
}
|
||||
if opts.Gateway == "" {
|
||||
opts.Gateway = "nordvpn"
|
||||
}
|
||||
|
||||
id, err := DockerRunContainer(opts.Image, DockerRunOpts{
|
||||
Name: opts.Name,
|
||||
Env: opts.Env,
|
||||
Volumes: opts.Volumes,
|
||||
Detach: opts.Detach,
|
||||
Remove: opts.Remove,
|
||||
Network: "container:" + opts.Gateway,
|
||||
Cmd: opts.Cmd,
|
||||
})
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("nordvpn container run %s: %w", opts.Image, err)
|
||||
}
|
||||
|
||||
return id, nil
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
---
|
||||
name: nordvpn_container_run
|
||||
kind: function
|
||||
lang: go
|
||||
domain: infra
|
||||
version: "1.0.0"
|
||||
purity: impure
|
||||
signature: "func NordVPNContainerRun(opts NordVPNContainerRunOpts) (string, error)"
|
||||
description: "Ejecuta un container Docker cuyo trafico pasa por el gateway NordVPN usando --network=container:<gateway>. El container hereda la IP y tunel VPN del gateway."
|
||||
tags: [vpn, nordvpn, docker, container, run, infra, network]
|
||||
uses_functions: ["docker_run_container_go_infra"]
|
||||
uses_types: []
|
||||
returns: []
|
||||
returns_optional: false
|
||||
error_type: "error_go_core"
|
||||
imports: [fmt]
|
||||
tested: false
|
||||
tests: []
|
||||
test_file_path: ""
|
||||
file_path: "functions/infra/nordvpn_container_run.go"
|
||||
---
|
||||
|
||||
## Ejemplo
|
||||
|
||||
```go
|
||||
// Verificar IP desde VPN
|
||||
id, err := NordVPNContainerRun(NordVPNContainerRunOpts{
|
||||
Image: "curlimages/curl",
|
||||
Cmd: []string{"https://api.ipify.org"},
|
||||
Remove: true,
|
||||
Gateway: "nordvpn",
|
||||
})
|
||||
|
||||
// Ejecutar scraper bajo VPN
|
||||
id, err := NordVPNContainerRun(NordVPNContainerRunOpts{
|
||||
Image: "my-scraper:latest",
|
||||
Env: map[string]string{"TARGET_URL": "https://example.com"},
|
||||
Volumes: []string{"/tmp/output:/output"},
|
||||
Detach: true,
|
||||
Name: "scraper-vpn",
|
||||
})
|
||||
|
||||
// Navegador headless bajo VPN
|
||||
id, err := NordVPNContainerRun(NordVPNContainerRunOpts{
|
||||
Image: "chromedp/headless-shell",
|
||||
Detach: true,
|
||||
Name: "chrome-vpn",
|
||||
})
|
||||
```
|
||||
|
||||
## Notas
|
||||
|
||||
Requiere que el container gateway NordVPN este corriendo (usar `NordVPNContainerStart` primero). El container cliente no necesita capabilities especiales — hereda la red del gateway. Con `--network=container:X` el container no puede exponer puertos propios; los puertos deben mapearse en el gateway.
|
||||
@@ -0,0 +1,73 @@
|
||||
package infra
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// NordVPNContainerOpts opciones para el container gateway NordVPN.
|
||||
type NordVPNContainerOpts struct {
|
||||
Token string // Token de acceso NordVPN (obligatorio)
|
||||
Country string // Pais al que conectar (opcional, ej: "Spain")
|
||||
City string // Ciudad (opcional, ej: "Madrid")
|
||||
Protocol string // "NordLynx" o "OpenVPN" (default: NordLynx)
|
||||
Name string // Nombre del container (default: "nordvpn")
|
||||
}
|
||||
|
||||
// NordVPNContainerStart levanta un container Docker con NordVPN como gateway.
|
||||
// Otros containers pueden usar su red con --network=container:<name>.
|
||||
// Espera hasta que el tunel este activo o timeout de 30s.
|
||||
func NordVPNContainerStart(opts NordVPNContainerOpts) (string, error) {
|
||||
if opts.Token == "" {
|
||||
return "", fmt.Errorf("nordvpn token required")
|
||||
}
|
||||
if opts.Name == "" {
|
||||
opts.Name = "nordvpn"
|
||||
}
|
||||
if opts.Protocol == "" {
|
||||
opts.Protocol = "NordLynx"
|
||||
}
|
||||
|
||||
env := map[string]string{
|
||||
"TOKEN": opts.Token,
|
||||
"TECHNOLOGY": opts.Protocol,
|
||||
}
|
||||
if opts.Country != "" {
|
||||
connect := opts.Country
|
||||
if opts.City != "" {
|
||||
connect += " " + opts.City
|
||||
}
|
||||
env["CONNECT"] = connect
|
||||
}
|
||||
|
||||
// Limpiar container previo con el mismo nombre si existe
|
||||
_ = DockerRemoveContainer(opts.Name, true)
|
||||
|
||||
id, err := DockerRunContainer("ghcr.io/bubuntux/nordvpn", DockerRunOpts{
|
||||
Name: opts.Name,
|
||||
Env: env,
|
||||
Detach: true,
|
||||
CapAdd: []string{"NET_ADMIN", "NET_RAW"},
|
||||
})
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("nordvpn container start: %w", err)
|
||||
}
|
||||
|
||||
// Esperar a que el tunel este activo
|
||||
for i := 0; i < 30; i++ {
|
||||
time.Sleep(1 * time.Second)
|
||||
logs, logErr := DockerContainerLogs(opts.Name, 20)
|
||||
if logErr != nil {
|
||||
continue
|
||||
}
|
||||
if strings.Contains(logs, "Connected") || strings.Contains(logs, "connected") {
|
||||
return id, nil
|
||||
}
|
||||
if strings.Contains(logs, "error") || strings.Contains(logs, "failed") {
|
||||
return id, fmt.Errorf("nordvpn connection failed, check logs: docker logs %s", opts.Name)
|
||||
}
|
||||
}
|
||||
|
||||
return id, fmt.Errorf("nordvpn connection timeout after 30s, check logs: docker logs %s", opts.Name)
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
---
|
||||
name: nordvpn_container_start
|
||||
kind: function
|
||||
lang: go
|
||||
domain: infra
|
||||
version: "1.0.0"
|
||||
purity: impure
|
||||
signature: "func NordVPNContainerStart(opts NordVPNContainerOpts) (string, error)"
|
||||
description: "Levanta un container Docker con NordVPN como gateway de red. Otros containers pueden rutear su trafico a traves de este con --network=container:<name>. Espera hasta 30s a que el tunel este activo."
|
||||
tags: [vpn, nordvpn, docker, container, gateway, infra, network]
|
||||
uses_functions: ["docker_run_container_go_infra", "docker_remove_container_go_infra", "docker_container_logs_go_infra"]
|
||||
uses_types: []
|
||||
returns: []
|
||||
returns_optional: false
|
||||
error_type: "error_go_core"
|
||||
imports: [fmt, strings, time]
|
||||
tested: false
|
||||
tests: []
|
||||
test_file_path: ""
|
||||
file_path: "functions/infra/nordvpn_container_start.go"
|
||||
---
|
||||
|
||||
## Ejemplo
|
||||
|
||||
```go
|
||||
id, err := NordVPNContainerStart(NordVPNContainerOpts{
|
||||
Token: os.Getenv("NORDVPN_TOKEN"),
|
||||
Country: "Spain",
|
||||
City: "Madrid",
|
||||
})
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
fmt.Println("VPN gateway:", id)
|
||||
|
||||
// Ahora otros containers pueden usar la VPN:
|
||||
// docker run --network=container:nordvpn curlimages/curl https://api.ipify.org
|
||||
```
|
||||
|
||||
## Notas
|
||||
|
||||
Usa la imagen `ghcr.io/bubuntux/nordvpn`. Requiere un token de acceso NordVPN (obtener con `nordvpn token` desde CLI o desde la web de NordVPN). Limpia containers previos con el mismo nombre automaticamente. El protocolo por defecto es NordLynx (WireGuard).
|
||||
@@ -0,0 +1,29 @@
|
||||
package infra
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// NordVPNContainerStop detiene y elimina el container gateway NordVPN.
|
||||
// Tambien detiene containers que usen su red si se proporcionan.
|
||||
func NordVPNContainerStop(gateway string, clientNames ...string) error {
|
||||
if gateway == "" {
|
||||
gateway = "nordvpn"
|
||||
}
|
||||
|
||||
// Primero parar los clientes que usan la red del gateway
|
||||
for _, name := range clientNames {
|
||||
_ = DockerStopContainer(name, 5)
|
||||
_ = DockerRemoveContainer(name, true)
|
||||
}
|
||||
|
||||
// Parar y eliminar el gateway
|
||||
if err := DockerStopContainer(gateway, 10); err != nil {
|
||||
return fmt.Errorf("nordvpn container stop: %w", err)
|
||||
}
|
||||
if err := DockerRemoveContainer(gateway, true); err != nil {
|
||||
return fmt.Errorf("nordvpn container remove: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
---
|
||||
name: nordvpn_container_stop
|
||||
kind: function
|
||||
lang: go
|
||||
domain: infra
|
||||
version: "1.0.0"
|
||||
purity: impure
|
||||
signature: "func NordVPNContainerStop(gateway string, clientNames ...string) error"
|
||||
description: "Detiene y elimina el container gateway NordVPN y opcionalmente los containers cliente que usan su red."
|
||||
tags: [vpn, nordvpn, docker, container, stop, cleanup, infra]
|
||||
uses_functions: ["docker_stop_container_go_infra", "docker_remove_container_go_infra"]
|
||||
uses_types: []
|
||||
returns: []
|
||||
returns_optional: false
|
||||
error_type: "error_go_core"
|
||||
imports: [fmt]
|
||||
tested: false
|
||||
tests: []
|
||||
test_file_path: ""
|
||||
file_path: "functions/infra/nordvpn_container_stop.go"
|
||||
---
|
||||
|
||||
## Ejemplo
|
||||
|
||||
```go
|
||||
// Parar solo el gateway
|
||||
err := NordVPNContainerStop("nordvpn")
|
||||
|
||||
// Parar gateway y clientes asociados
|
||||
err := NordVPNContainerStop("nordvpn", "chrome-vpn", "scraper-vpn")
|
||||
```
|
||||
|
||||
## Notas
|
||||
|
||||
Detiene primero los containers cliente (si se proporcionan) y luego el gateway. Importante: si los clientes usan `--network=container:nordvpn`, deben pararse antes que el gateway para evitar errores de red. Los clientes se paran con timeout de 5s, el gateway con 10s.
|
||||
@@ -0,0 +1,68 @@
|
||||
package infra
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// NordVPNStatus representa el estado parseado de nordvpn status.
|
||||
type NordVPNStatus struct {
|
||||
Connected bool // true si hay conexion activa
|
||||
Status string // "Connected" o "Disconnected"
|
||||
Hostname string // ej: "es42.nordvpn.com"
|
||||
IP string // IP del servidor VPN
|
||||
Country string // ej: "Spain"
|
||||
City string // ej: "Madrid"
|
||||
Technology string // ej: "NordLynx"
|
||||
Protocol string // ej: "nordlynx"
|
||||
Transfer string // ej: "1.2 MiB received, 500 KiB sent"
|
||||
Uptime string // ej: "5 minutes 32 seconds"
|
||||
}
|
||||
|
||||
// ansiRegexp elimina codigos de escape ANSI.
|
||||
var ansiRegexp = regexp.MustCompile(`\x1b\[[0-9;]*m`)
|
||||
|
||||
// ParseNordVPNStatus parsea la salida de texto de `nordvpn status`
|
||||
// a un struct tipado. Funcion pura — no ejecuta comandos.
|
||||
func ParseNordVPNStatus(output string) NordVPNStatus {
|
||||
var s NordVPNStatus
|
||||
|
||||
lines := strings.Split(output, "\n")
|
||||
for _, line := range lines {
|
||||
line = ansiRegexp.ReplaceAllString(line, "")
|
||||
line = strings.TrimSpace(line)
|
||||
line = strings.TrimLeft(line, "- ")
|
||||
|
||||
idx := strings.Index(line, ":")
|
||||
if idx < 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
key := strings.ToLower(strings.TrimSpace(line[:idx]))
|
||||
val := strings.TrimSpace(line[idx+1:])
|
||||
|
||||
switch key {
|
||||
case "status":
|
||||
s.Status = val
|
||||
s.Connected = strings.EqualFold(val, "connected")
|
||||
case "hostname", "server":
|
||||
s.Hostname = val
|
||||
case "ip":
|
||||
s.IP = val
|
||||
case "country":
|
||||
s.Country = val
|
||||
case "city":
|
||||
s.City = val
|
||||
case "current technology":
|
||||
s.Technology = val
|
||||
case "current protocol":
|
||||
s.Protocol = val
|
||||
case "transfer":
|
||||
s.Transfer = val
|
||||
case "uptime":
|
||||
s.Uptime = val
|
||||
}
|
||||
}
|
||||
|
||||
return s
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
---
|
||||
name: parse_nordvpn_status
|
||||
kind: function
|
||||
lang: go
|
||||
domain: infra
|
||||
version: "1.0.0"
|
||||
purity: pure
|
||||
signature: "func ParseNordVPNStatus(output string) NordVPNStatus"
|
||||
description: "Parsea la salida de texto de nordvpn status a un struct tipado. Elimina codigos ANSI y normaliza claves."
|
||||
tags: [vpn, nordvpn, parser, pure, infra]
|
||||
uses_functions: []
|
||||
uses_types: ["NordVPNStatus_go_infra"]
|
||||
returns: []
|
||||
returns_optional: false
|
||||
error_type: ""
|
||||
imports: ["regexp", "strings"]
|
||||
tested: true
|
||||
tests: ["TestParseNordVPNStatus_Connected", "TestParseNordVPNStatus_Disconnected", "TestParseNordVPNStatus_WithANSI"]
|
||||
test_file_path: "functions/infra/parse_nordvpn_status_test.go"
|
||||
file_path: "functions/infra/parse_nordvpn_status.go"
|
||||
---
|
||||
|
||||
## Ejemplo
|
||||
|
||||
```go
|
||||
output := `Status: Connected
|
||||
Hostname: es42.nordvpn.com
|
||||
IP: 185.230.124.42
|
||||
Country: Spain
|
||||
City: Madrid
|
||||
Current Technology: NordLynx`
|
||||
|
||||
s := ParseNordVPNStatus(output)
|
||||
// s.Connected == true
|
||||
// s.Hostname == "es42.nordvpn.com"
|
||||
// s.Country == "Spain"
|
||||
```
|
||||
|
||||
## Notas
|
||||
|
||||
Funcion pura — no ejecuta comandos ni accede a red. Maneja codigos ANSI que NordVPN CLI emite en terminal. Campos no presentes en la salida quedan como zero value.
|
||||
@@ -0,0 +1,62 @@
|
||||
package infra
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestParseNordVPNStatus_Connected(t *testing.T) {
|
||||
input := `Status: Connected
|
||||
Hostname: es42.nordvpn.com
|
||||
IP: 185.230.124.42
|
||||
Country: Spain
|
||||
City: Madrid
|
||||
Current Technology: NordLynx
|
||||
Current Protocol: nordlynx
|
||||
Transfer: 1.2 MiB received, 500 KiB sent
|
||||
Uptime: 5 minutes 32 seconds`
|
||||
|
||||
got := ParseNordVPNStatus(input)
|
||||
|
||||
if !got.Connected {
|
||||
t.Error("expected Connected=true")
|
||||
}
|
||||
if got.Hostname != "es42.nordvpn.com" {
|
||||
t.Errorf("Hostname = %q, want es42.nordvpn.com", got.Hostname)
|
||||
}
|
||||
if got.IP != "185.230.124.42" {
|
||||
t.Errorf("IP = %q, want 185.230.124.42", got.IP)
|
||||
}
|
||||
if got.Country != "Spain" {
|
||||
t.Errorf("Country = %q, want Spain", got.Country)
|
||||
}
|
||||
if got.City != "Madrid" {
|
||||
t.Errorf("City = %q, want Madrid", got.City)
|
||||
}
|
||||
if got.Technology != "NordLynx" {
|
||||
t.Errorf("Technology = %q, want NordLynx", got.Technology)
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseNordVPNStatus_Disconnected(t *testing.T) {
|
||||
input := `Status: Disconnected`
|
||||
|
||||
got := ParseNordVPNStatus(input)
|
||||
|
||||
if got.Connected {
|
||||
t.Error("expected Connected=false")
|
||||
}
|
||||
if got.Status != "Disconnected" {
|
||||
t.Errorf("Status = %q, want Disconnected", got.Status)
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseNordVPNStatus_WithANSI(t *testing.T) {
|
||||
input := "\x1b[32mStatus: Connected\x1b[0m\n\x1b[32m- Hostname: us1234.nordvpn.com\x1b[0m"
|
||||
|
||||
got := ParseNordVPNStatus(input)
|
||||
|
||||
if !got.Connected {
|
||||
t.Error("expected Connected=true with ANSI codes")
|
||||
}
|
||||
if got.Hostname != "us1234.nordvpn.com" {
|
||||
t.Errorf("Hostname = %q, want us1234.nordvpn.com", got.Hostname)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
---
|
||||
name: NordVPNStatus
|
||||
lang: go
|
||||
domain: infra
|
||||
version: "1.0.0"
|
||||
algebraic: product
|
||||
definition: "type NordVPNStatus struct { Connected bool; Status string; Hostname string; IP string; Country string; City string; Technology string; Protocol string; Transfer string; Uptime string }"
|
||||
description: "Estado parseado de nordvpn status. Contiene informacion de conexion, servidor, ubicacion y protocolo."
|
||||
tags: [vpn, nordvpn, status, infra]
|
||||
uses_types: []
|
||||
file_path: "functions/infra/parse_nordvpn_status.go"
|
||||
---
|
||||
|
||||
## Ejemplos
|
||||
|
||||
```go
|
||||
s := NordVPNStatus{
|
||||
Connected: true,
|
||||
Status: "Connected",
|
||||
Hostname: "es42.nordvpn.com",
|
||||
IP: "185.230.124.42",
|
||||
Country: "Spain",
|
||||
City: "Madrid",
|
||||
Technology: "NordLynx",
|
||||
Protocol: "nordlynx",
|
||||
}
|
||||
```
|
||||
|
||||
## Notas
|
||||
|
||||
Producido por ParseNordVPNStatus. Los campos corresponden a las claves de la salida de `nordvpn status`. Transfer y Uptime son strings sin parsear — incluir parseo numerico si se necesita.
|
||||
Reference in New Issue
Block a user