<# .SYNOPSIS Tailnet 전용 Vignette 접속점을 켠다. .DESCRIPTION - 현재 Tailscale 노드의 MagicDNS URL을 읽는다. - dev 스택(API 8000 + web 5173 + gateway 9099)을 기동하면서 Tailnet URL에서 dev-login을 허용한다. - tailscale serve 루트 HTTPS를 로컬 web 포트로 연결한다. 공개 인터넷 노출이 아니라 같은 Tailnet에 로그인한 PC/모바일 전용이다. #> param( [string]$Workspace = "D:\workspace\vignette", [int]$ApiPort = 8010, [int]$WebPort = 5173, [switch]$SkipStackRestart, [switch]$SkipServeUpdate ) $ErrorActionPreference = "Stop" function Get-TailnetOrigin { $raw = & tailscale status --json if ($LASTEXITCODE -ne 0) { throw "tailscale status failed" } $status = $raw | ConvertFrom-Json $dnsName = [string]$status.Self.DNSName if (!$dnsName) { throw "Tailscale MagicDNS name was not reported. Enable MagicDNS or check tailscale status." } "https://$($dnsName.TrimEnd('.'))" } function Wait-HttpOk { param( [string]$Uri, [int]$TimeoutSec = 45 ) $deadline = (Get-Date).AddSeconds($TimeoutSec) do { try { $response = Invoke-WebRequest -UseBasicParsing -Uri $Uri -TimeoutSec 8 if ($response.StatusCode -ge 200 -and $response.StatusCode -lt 500) { return $response } } catch { Start-Sleep -Seconds 1 } } while ((Get-Date) -lt $deadline) throw "Timed out waiting for $Uri" } function Set-EnvFileValue { param( [string]$EnvPath, [string]$Key, [string]$Value ) $line = "$Key=$Value" if (Test-Path -LiteralPath $EnvPath) { $lines = Get-Content -LiteralPath $EnvPath -Encoding UTF8 } else { $lines = @() } $updated = $false $next = foreach ($existing in $lines) { if ($existing -match "^$([regex]::Escape($Key))=") { $updated = $true $line } else { $existing } } if (!$updated) { $next += $line } Set-Content -LiteralPath $EnvPath -Encoding UTF8 -Value $next } if (!(Get-Command tailscale -ErrorAction SilentlyContinue)) { throw "tailscale CLI not found" } if (!(Test-Path -LiteralPath $Workspace)) { throw "Workspace not found: $Workspace" } $tailnetOrigin = Get-TailnetOrigin $tailnetHost = ([uri]$tailnetOrigin).Host $env:AUTH_DEV_LOGIN_EXTRA_ORIGINS = ConvertTo-Json -InputObject @($tailnetOrigin) -Compress $env:VITE_ALLOWED_HOSTS = $tailnetHost $env:VITE_API_PROXY_TARGET = "http://127.0.0.1:$ApiPort" Set-EnvFileValue ` -EnvPath (Join-Path $Workspace "apps\api\.env") ` -Key "AUTH_DEV_LOGIN_EXTRA_ORIGINS" ` -Value $env:AUTH_DEV_LOGIN_EXTRA_ORIGINS Write-Output "Tailnet origin: $tailnetOrigin" Write-Output "AUTH_DEV_LOGIN_EXTRA_ORIGINS=$env:AUTH_DEV_LOGIN_EXTRA_ORIGINS" Write-Output "VITE_ALLOWED_HOSTS=$env:VITE_ALLOWED_HOSTS" Write-Output "VITE_API_PROXY_TARGET=$env:VITE_API_PROXY_TARGET" if (!$SkipStackRestart) { $devUp = Join-Path $Workspace "scripts\dev-up.ps1" if (!(Test-Path -LiteralPath $devUp)) { throw "dev-up script not found: $devUp" } & $devUp -ApiPort $ApiPort } if (!$SkipServeUpdate) { $target = "http://127.0.0.1:$WebPort" Write-Output "Updating tailscale serve root -> $target" & tailscale serve --bg --yes $target if ($LASTEXITCODE -ne 0) { throw "tailscale serve update failed" } } Wait-HttpOk -Uri $tailnetOrigin | Out-Null $authConfig = Invoke-RestMethod -Uri "$tailnetOrigin/api/auth/config" -TimeoutSec 15 if ($authConfig.dev_login_enabled -ne $true) { throw "Tailnet auth config is reachable, but dev_login_enabled is not true" } Write-Output "Tailscale Vignette URL: $tailnetOrigin" Write-Output "Login path: $tailnetOrigin/login" Write-Output "Serve status:" & tailscale serve status