$ErrorActionPreference = 'Stop' $repoPath = (Resolve-Path (Join-Path $PSScriptRoot '../..')).Path $runtimePath = Join-Path $PSScriptRoot '.runtime' function Read-LocalSettings { param([string]$Path = (Join-Path $PSScriptRoot 'secrets.psd1')) if (-not (Test-Path -LiteralPath $Path)) { throw 'Copy secrets.example.psd1 to secrets.psd1 and fill in the local database password first.' } $settings = Import-PowerShellDataFile -LiteralPath $Path if ($settings.DatabaseName -notmatch '^elder_yudao_[a-z0-9_]+$') { throw 'Use a separate database named elder_yudao_*; existing application databases are not supported by this initializer.' } return $settings } function Resolve-LocalProgram { param([string]$ConfiguredPath, [string]$CommandName, [string]$FallbackPath = '') if ($ConfiguredPath) { if (-not (Test-Path -LiteralPath $ConfiguredPath -PathType Leaf)) { throw "Program not found: $ConfiguredPath" } return (Resolve-Path -LiteralPath $ConfiguredPath).Path } $program = Get-Command $CommandName -ErrorAction SilentlyContinue if ($program) { return $program.Source } if ($FallbackPath -and (Test-Path -LiteralPath $FallbackPath)) { return $FallbackPath } throw "Program not found: $CommandName. Install it or set its path in secrets.psd1." } function Invoke-LocalMySql { param([hashtable]$Settings, [string]$Query, [string]$Database = '') $mysqlExe = Resolve-LocalProgram $Settings.MySqlExe 'mysql.exe' 'C:\Program Files\MySQL\MySQL Server 8.0\bin\mysql.exe' $arguments = @('--protocol=TCP', "--host=$($Settings.DatabaseHost)", "--port=$($Settings.DatabasePort)", "--user=$($Settings.DatabaseUser)", '--connect-timeout=5', '--default-character-set=utf8mb4', '--batch', '--skip-column-names') if ($Database) { $arguments += "--database=$Database" } $arguments += "--execute=$Query" $previousPassword = $env:MYSQL_PWD try { $env:MYSQL_PWD = $Settings.DatabasePassword $result = @(& $mysqlExe @arguments) if ($LASTEXITCODE -ne 0) { throw 'MySQL command failed; see the error above. No password is printed by this script.' } return $result } finally { if ($null -eq $previousPassword) { Remove-Item Env:\MYSQL_PWD -ErrorAction SilentlyContinue } else { $env:MYSQL_PWD = $previousPassword } } } function Get-LocalServerProcess { $pidFile = Join-Path $runtimePath 'server.pid' if (-not (Test-Path -LiteralPath $pidFile)) { return $null } $serverPid = 0 if (-not [int]::TryParse((Get-Content -LiteralPath $pidFile -Raw).Trim(), [ref]$serverPid)) { throw 'Invalid server.pid; inspect the runtime directory before continuing.' } $process = Get-CimInstance Win32_Process -Filter "ProcessId=$serverPid" -ErrorAction SilentlyContinue if (-not $process) { return $null } $jarPath = Join-Path $repoPath 'yudao-server/target/yudao-server.jar' if ($process.Name -notmatch '^java(w)?\.exe$' -or -not $process.CommandLine.Contains($jarPath)) { throw 'The recorded PID belongs to another process; refusing to operate on it.' } return $process } function Get-LocalUiProcess { $pidFile = Join-Path $runtimePath 'ui.pid' if (-not (Test-Path -LiteralPath $pidFile)) { return $null } $uiPid = 0 if (-not [int]::TryParse((Get-Content -LiteralPath $pidFile -Raw).Trim(), [ref]$uiPid)) { throw 'Invalid ui.pid.' } $process = Get-CimInstance Win32_Process -Filter "ProcessId=$uiPid" -ErrorAction SilentlyContinue if (-not $process) { return $null } $vitePaths = @((Join-Path $repoPath 'yudao-ui-admin-vue3/node_modules/vite/bin/vite.js'), (Join-Path $runtimePath 'ui-src/node_modules/vite/bin/vite.js')) $matchesUiPath = @($vitePaths | Where-Object { $process.CommandLine.Contains($_) }).Count -gt 0 if ($process.Name -ne 'node.exe' -or -not $matchesUiPath) { throw 'The recorded UI PID belongs to another process; refusing to operate on it.' } return $process }