SHA256
127 lines
5.6 KiB
PowerShell
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() }
|
|
}
|