Files
2026-09-27 16:39:05 +08:00

127 lines
5.6 KiB
PowerShell

param([switch]$NoBrowser)
$ErrorActionPreference = 'Stop'
$launcherLock = $null
$started = $null
$originalPath = $env:PATH
$originalPort = $env:SERVER_PORT
function Open-App([string]$url) {
Write-Host "ashare-view: $url"
if (-not $NoBrowser) {
try { Start-Process -FilePath $url }
catch { Write-Warning "Could not open the browser. Open $url manually." }
}
}
function Find-Node {
$candidates = @()
$current = Get-Command node.exe -ErrorAction SilentlyContinue
if ($current) { $candidates += $current.Source }
$fnmRoots = @($env:FNM_DIR, (Join-Path $env:APPDATA 'fnm'), (Join-Path $env:LOCALAPPDATA 'fnm'))
foreach ($fnmRoot in $fnmRoots) {
if ($fnmRoot) {
$candidates += Get-ChildItem -Path (Join-Path $fnmRoot 'node-versions\*\installation\node.exe') -ErrorAction SilentlyContinue |
Sort-Object FullName -Descending | Select-Object -ExpandProperty FullName
}
}
$candidates += Join-Path $env:ProgramFiles 'nodejs\node.exe'
foreach ($candidate in ($candidates | Select-Object -Unique)) {
if (-not (Test-Path -LiteralPath $candidate)) { continue }
$versionText = & $candidate --version
if ($LASTEXITCODE -ne 0) { continue }
$version = [version]($versionText.Trim().TrimStart('v'))
if (($version.Major -eq 20 -and $version.Minor -ge 19) -or
($version.Major -eq 22 -and $version.Minor -ge 12) -or $version.Major -gt 22) {
$nodeDirectory = Split-Path -Parent $candidate
if (Test-Path -LiteralPath (Join-Path $nodeDirectory 'npm.cmd')) { return $nodeDirectory }
}
}
throw 'No compatible installed Node.js found (requires 20.19+ or 22.12+). Nothing was installed.'
}
try {
. (Join-Path $PSScriptRoot 'launcher-common.ps1')
$launcherLock = Open-LauncherLock
$state = Get-ServerState
$owned = Get-OwnedServer $state
if ($null -ne $owned) {
if (-not (Test-ServerHealth $state.url)) { throw 'Existing server is not healthy. Run stop.cmd, then start.cmd.' }
Open-App $state.url
exit 0
}
Remove-Item -LiteralPath $statePath -Force -ErrorAction SilentlyContinue
$web = Join-Path $PSScriptRoot 'web'
$server = Join-Path $PSScriptRoot 'server'
if (-not (Test-Path -LiteralPath (Join-Path $web 'node_modules\.bin\vite.cmd'))) {
throw 'Frontend dependencies are missing (web/node_modules). Nothing was installed.'
}
$nodeDirectory = Find-Node
$env:PATH = $nodeDirectory + ';' + $env:PATH
$goCommand = Get-Command go.exe -ErrorAction SilentlyContinue
$go = if ($goCommand) { $goCommand.Source } else { Join-Path $env:ProgramFiles 'Go\bin\go.exe' }
if (-not (Test-Path -LiteralPath $go)) { throw 'Go is not installed or available on PATH.' }
Write-Host 'Building frontend...'
Push-Location $web
try {
& (Join-Path $nodeDirectory 'npm.cmd') run build
if ($LASTEXITCODE -ne 0) { throw "Frontend build failed (exit $LASTEXITCODE)." }
} finally { Pop-Location }
# Build with fresh assets without rewriting the checked-in embedded bundle.
$embedded = Join-Path $server 'router\webdist'
$dist = Join-Path $web 'dist'
$replacements = @{}
Get-ChildItem -LiteralPath $embedded -File -Recurse | ForEach-Object { $replacements[$_.FullName] = '' }
Get-ChildItem -LiteralPath $dist -File -Recurse | ForEach-Object {
$relative = $_.FullName.Substring($dist.Length + 1)
$replacements[(Join-Path $embedded $relative)] = $_.FullName
}
$overlayPath = Join-Path $runtime 'build-overlay.json'
$utf8 = New-Object System.Text.UTF8Encoding($false)
[IO.File]::WriteAllText($overlayPath, (@{ Replace = $replacements } | ConvertTo-Json -Depth 4), $utf8)
Write-Host 'Building Go server...'
Push-Location $server
try {
& $go build -buildvcs=false -overlay $overlayPath -o $executable .
if ($LASTEXITCODE -ne 0) { throw "Go build failed (exit $LASTEXITCODE)." }
} finally { Pop-Location }
$stdout = Join-Path $runtime 'server.out.log'
$stderr = Join-Path $runtime 'server.err.log'
$env:SERVER_PORT = '0'
$started = Start-Process -FilePath $executable -WorkingDirectory $server -WindowStyle Hidden -PassThru `
-RedirectStandardOutput $stdout -RedirectStandardError $stderr
$state = @{ pid = $started.Id; startTime = $started.StartTime.ToUniversalTime().ToString('o'); executable = $executable; url = '' }
[IO.File]::WriteAllText($statePath, ($state | ConvertTo-Json), $utf8)
$deadline = [DateTime]::UtcNow.AddSeconds(60)
do {
if ($started.HasExited) { throw "Server exited. See $stderr" }
$output = Get-Content -LiteralPath $stdout -Raw -ErrorAction SilentlyContinue
if ($output -match 'Server running on (http://127\.0\.0\.1:\d+/)') {
$state.url = $Matches[1]
if (Test-ServerHealth $state.url) {
[IO.File]::WriteAllText($statePath, ($state | ConvertTo-Json), $utf8)
Open-App $state.url
exit 0
}
}
Start-Sleep -Milliseconds 300
} while ([DateTime]::UtcNow -lt $deadline)
throw "Server startup timed out. See $stderr"
} catch {
if ($null -ne $started) {
if (-not $started.HasExited) { $started.Kill(); $started.WaitForExit() }
Remove-Item -LiteralPath $statePath -Force -ErrorAction SilentlyContinue
}
Write-Host ('Startup failed: ' + $_.Exception.Message) -ForegroundColor Red
exit 1
} finally {
$env:PATH = $originalPath
$env:SERVER_PORT = $originalPort
if ($null -ne $launcherLock) { $launcherLock.Dispose() }
}