metis-desk/scripts/enable-lan-access.ps1

121 lines
5.1 KiB
PowerShell
Raw Permalink Normal View History

#Requires -Version 5.1
#Requires -RunAsAdministrator
<#
.SYNOPSIS
Publica Vite desde WSL2 en la red local mediante el reenvio TCP de Windows.
.DESCRIPTION
Ejecutar en PowerShell de Windows como administrador, con pnpm dev activo en WSL.
Volver a ejecutar si cambia la IP de WSL. Solo admite el perfil de red privado.
La API sigue siendo accesible a traves del proxy /api de Vite.
Reinicia IP Helper para aplicar las reglas; interrumpe brevemente otros reenvios.
#>
[CmdletBinding()]
param(
[System.Net.IPAddress]$ListenAddress = '192.168.0.2',
[ValidateRange(1, 65535)]
[int]$Port = 5173,
[ValidateNotNullOrEmpty()]
[string]$Distribution = 'Ubuntu-24.04'
)
Set-StrictMode -Version Latest
$ErrorActionPreference = 'Stop'
if ($ListenAddress.AddressFamily -ne [System.Net.Sockets.AddressFamily]::InterNetwork) {
throw 'ListenAddress debe ser una direccion IPv4 del ordenador Windows.'
}
$localAddress = Get-NetIPAddress -AddressFamily IPv4 -IPAddress $ListenAddress.IPAddressToString
$profiles = @(Get-NetConnectionProfile -InterfaceIndex $localAddress.InterfaceIndex)
if (-not ($profiles | Where-Object { $_.NetworkCategory -eq 'Private' })) {
throw 'La interfaz debe tener perfil Privado en Windows. Revisar la red antes de ejecutar el script.'
}
# eth0 evita seleccionar la direccion de un bridge de Docker con hostname -I.
$addressOutput = & wsl.exe --distribution $Distribution --exec ip -4 -o addr show dev eth0
if ($LASTEXITCODE -ne 0) {
throw "No se pudo consultar eth0 en la distribucion $Distribution."
}
$addressMatch = [regex]::Match(($addressOutput -join "`n"), '\binet\s+(\d{1,3}(?:\.\d{1,3}){3})/')
if (-not $addressMatch.Success) {
throw 'No se encontro una direccion IPv4 en eth0 de WSL.'
}
$wslAddress = [System.Net.IPAddress]::Parse($addressMatch.Groups[1].Value)
if (-not (Test-NetConnection -ComputerName $wslAddress.IPAddressToString -Port $Port -InformationLevel Quiet -WarningAction SilentlyContinue)) {
throw "WSL no responde en ${wslAddress}:$Port. Comprobar pnpm dev y el puerto anunciado por Vite."
}
# add actualiza tambien el destino del reenvio existente para esta direccion y puerto.
& netsh.exe interface portproxy add v4tov4 `
listenaddress=$($ListenAddress.IPAddressToString) listenport=$Port `
connectaddress=$($wslAddress.IPAddressToString) connectport=$Port protocol=tcp
if ($LASTEXITCODE -ne 0) {
throw 'No se pudo configurar el reenvio TCP de Windows.'
}
$ruleName = "MetisDesk-WSL-LAN-$($ListenAddress.IPAddressToString)-$Port"
$existingRule = Get-NetFirewallRule -Name $ruleName -ErrorAction SilentlyContinue
if ($existingRule) {
$existingRule | Remove-NetFirewallRule
}
New-NetFirewallRule -Name $ruleName -DisplayName "Metis Desk WSL LAN ($Port)" `
-Direction Inbound -Action Allow -Enabled True -Profile Private `
-Protocol TCP -LocalAddress $ListenAddress.IPAddressToString -LocalPort $Port `
-RemoteAddress LocalSubnet | Out-Null
# El servicio puede estar activo sin haber aplicado los reenvios registrados.
Write-Host 'Reiniciando IP Helper para aplicar el reenvio...'
Restart-Service -Name iphlpsvc
$accessUrl = "http://${ListenAddress}:$Port/"
# Una regla creada no garantiza que Windows este escuchando o reenviando HTTP.
# Evitar el proxy del sistema permite comprobar directamente el reenvio local.
$request = [System.Net.WebRequest]::Create($accessUrl)
$request.Proxy = $null
$request.Timeout = 10000
$request.ReadWriteTimeout = 10000
$request.AllowAutoRedirect = $false
$response = $null
try {
# Running no garantiza que el listener este listo: esperar hasta diez segundos.
$listenerTimer = [System.Diagnostics.Stopwatch]::StartNew()
do {
$listener = Get-NetTCPConnection -State Listen -LocalPort $Port -ErrorAction SilentlyContinue |
Where-Object { $_.LocalAddress -eq $ListenAddress.IPAddressToString -or $_.LocalAddress -eq '0.0.0.0' }
if ($listener) {
break
}
if ($listenerTimer.Elapsed.TotalSeconds -ge 10) {
throw "Windows no ha abierto el listener IPv4 ${ListenAddress}:$Port tras reiniciar IP Helper."
}
Start-Sleep -Milliseconds 200
} while ($true)
$response = $request.GetResponse()
if ([int]$response.StatusCode -ne 200) {
throw "Respuesta HTTP inesperada: $([int]$response.StatusCode)."
}
}
catch {
Write-Warning 'Las reglas se han creado, pero la comprobacion HTTP desde Windows ha fallado. Se conservan para diagnostico.'
& netsh.exe interface portproxy show all
Get-NetTCPConnection -State Listen -LocalPort $Port -ErrorAction SilentlyContinue |
Format-Table LocalAddress, LocalPort, OwningProcess | Out-Host
throw "No se pudo verificar ${accessUrl}: $($_.Exception.Message)"
}
finally {
if ($null -ne $response) {
$response.Close()
}
}
Write-Host "HTTP verificado desde Windows: $accessUrl -> ${wslAddress}:$Port"
Write-Host 'Comprobar ahora el acceso desde otro dispositivo de la red local.'
Write-Host "En WSL, establecer APP_ORIGIN=http://${ListenAddress}:$Port en .env y reiniciar pnpm dev."
Write-Host 'Volver a ejecutar este script si cambia la IP de WSL.'