[CmdletBinding()] param( [switch]$OpenFirewall, [switch]$StartServer ) $ErrorActionPreference = "Stop" $ProjectRoot = (Resolve-Path (Join-Path $PSScriptRoot "..")).Path function Write-Step([string]$Message) { Write-Host "" Write-Host "==> $Message" -ForegroundColor Cyan } function Refresh-ProcessPath { $machinePath = [Environment]::GetEnvironmentVariable("Path", "Machine") $userPath = [Environment]::GetEnvironmentVariable("Path", "User") $env:Path = "$machinePath;$userPath" } function Get-NodeMajor { $node = Get-Command node.exe -ErrorAction SilentlyContinue if (-not $node) { return $null } $rawVersion = (& $node.Source --version).Trim().TrimStart("v") $majorText = $rawVersion.Split(".")[0] $major = 0 if ([int]::TryParse($majorText, [ref]$major)) { return $major } return $null } function Install-NodeLts { $winget = Get-Command winget.exe -ErrorAction SilentlyContinue if (-not $winget) { throw "Node.js 22/24 LTS is required. Install it from https://nodejs.org/en/download and run this script again." } Write-Step "Installing Node.js LTS with winget" & $winget.Source install --id OpenJS.NodeJS.LTS --exact --accept-source-agreements --accept-package-agreements if ($LASTEXITCODE -ne 0) { throw "winget could not install Node.js LTS (exit code $LASTEXITCODE)." } Refresh-ProcessPath } function Install-VcRuntime { $winget = Get-Command winget.exe -ErrorAction SilentlyContinue if (-not $winget) { Write-Warning "winget was not found; Microsoft Visual C++ 2015-2022 x64 Runtime could not be checked." return } Write-Step "Installing Microsoft Visual C++ 2015-2022 x64 Runtime" & $winget.Source install --id "Microsoft.VCRedist.2015+.x64" --exact --silent --accept-source-agreements --accept-package-agreements if ($LASTEXITCODE -ne 0) { Write-Warning "Visual C++ Runtime install/check returned exit code $LASTEXITCODE. AMMC may still work if it is already installed." } } Write-Host "RC RaceCore - Windows 11 setup" -ForegroundColor White Write-Host "Project: $ProjectRoot" $nodeMajor = Get-NodeMajor if ($null -eq $nodeMajor -or $nodeMajor -lt 22 -or $nodeMajor -ge 27) { Install-NodeLts $nodeMajor = Get-NodeMajor } if ($null -eq $nodeMajor -or $nodeMajor -lt 22 -or $nodeMajor -ge 27) { throw "A supported Node.js version was not found after installation. Reopen PowerShell and run windows\setup.ps1 again." } Install-VcRuntime $nodeCommand = (Get-Command node.exe).Source $npmCommand = (Get-Command npm.cmd -ErrorAction SilentlyContinue).Source if (-not $npmCommand) { throw "npm.cmd was not found. Repair the Node.js installation and run this script again." } Write-Step "Checking runtime" Write-Host "Node: $(& $nodeCommand --version)" Write-Host "npm: $(& $npmCommand --version)" Write-Host "OS: $([Environment]::OSVersion.VersionString) / $env:PROCESSOR_ARCHITECTURE" Set-Location $ProjectRoot Write-Step "Installing locked npm dependencies" if (Test-Path (Join-Path $ProjectRoot "package-lock.json")) { & $npmCommand ci --no-audit --no-fund } else { & $npmCommand install --no-audit --no-fund } if ($LASTEXITCODE -ne 0) { Write-Host "" Write-Host "If better-sqlite3 attempted a source build, run this once as Administrator:" -ForegroundColor Yellow Write-Host ' & "C:\Program Files\nodejs\install_tools.bat"' -ForegroundColor Yellow throw "npm dependency installation failed (exit code $LASTEXITCODE)." } Write-Step "Verifying Express, SQLite and bundled AMMC" & $nodeCommand scripts\verify-install.js if ($LASTEXITCODE -ne 0) { throw "Runtime verification failed (exit code $LASTEXITCODE)." } if ($OpenFirewall) { Write-Step "Adding Windows Firewall rules" $isAdmin = ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole( [Security.Principal.WindowsBuiltInRole]::Administrator ) if (-not $isAdmin) { Write-Warning "Firewall rules require an Administrator PowerShell. Setup continues without them." } else { foreach ($port in @(8081, 9000, 9001, 9002, 9003)) { $ruleName = "RC RaceCore TCP $port" if (-not (Get-NetFirewallRule -DisplayName $ruleName -ErrorAction SilentlyContinue)) { New-NetFirewallRule -DisplayName $ruleName -Direction Inbound -Protocol TCP -LocalPort $port -Action Allow | Out-Null } Write-Host "Firewall: TCP $port allowed" } } } if ($StartServer) { Write-Step "Restarting RC RaceCore after installation" & $npmCommand restart if ($LASTEXITCODE -ne 0) { throw "RC RaceCore did not restart (exit code $LASTEXITCODE)." } } Write-Host "" Write-Host "Windows setup completed." -ForegroundColor Green Write-Host "Start: npm.cmd start" Write-Host "Restart: npm.cmd restart" Write-Host "Status: npm.cmd run status" Write-Host "Stop: npm.cmd stop" Write-Host "Open: http://localhost:8081"