#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.'