xyc
2 天以前 0d3f36094d5fbeb91026fd3a1649ce0759ea6212
deploy-templates/backend/install-service.ps1
@@ -1,8 +1,8 @@
<#
<#
  Install / update / remove the TrafficAudit backend as a Windows service.
  The service is a WinSW (Windows Service Wrapper) host: trafficAudit.exe reads
  trafficAudit.xml and runs "java -jar traffic-audit-server-<version>.jar".
  trafficAudit.xml and runs "java -jar traffic-audit-server.jar".
  Because it is a real Windows service it starts at boot with no user logged on,
  is listed in services.msc / Get-Service, and is restarted by the Service
  Control Manager when the java process dies.
@@ -44,6 +44,21 @@
$config    = Join-Path $root 'trafficAudit.xml'
$appConfig = Join-Path $root 'application.yml'
$serviceId = 'TrafficAuditBackend'
# Any terminating error below lands here instead of dumping a raw stack trace
# at the operator. The line number is kept so a copy of this window is enough
# to diagnose a report.
trap {
  Write-Host ''
  Write-Host '=============================================================='
  Write-Host ' ERROR: the installer stopped before finishing.'
  Write-Host ('        ' + $_.Exception.Message)
  Write-Host ('        at line ' + $_.InvocationInfo.ScriptLineNumber + ' of install-service.ps1')
  Write-Host ' The service may not be installed. Send this window text to the'
  Write-Host ' developer if the reason is not obvious.'
  Write-Host '=============================================================='
  exit 1
}
function Assert-Admin {
  $identity  = [Security.Principal.WindowsIdentity]::GetCurrent()
@@ -129,6 +144,62 @@
  Get-Service -Name $serviceId -ErrorAction SilentlyContinue
}
function Test-LocalPort {
  param([int]$Port, [string]$TargetHost = '127.0.0.1')
  # Probe TCP instead of HTTP: Invoke-WebRequest does not exist on Windows
  # PowerShell 2 (Windows 7), and this works on every PowerShell version.
  $client = New-Object System.Net.Sockets.TcpClient
  try {
    $async = $client.BeginConnect($TargetHost, $Port, $null, $null)
    if ($async.AsyncWaitHandle.WaitOne(1000, $false) -and $client.Connected) { return $true }
    return $false
  } catch {
    return $false
  } finally {
    $client.Close()
  }
}
function Test-AppConfigPassword {
  param([string]$Path)
  # Returns $true when the datasource password looks usable. Prints a WARN and
  # returns $false for the states that break startup with
  # "Access denied ... (using password: NO)": missing file / empty value / placeholder.
  if (-not (Test-Path -LiteralPath $Path)) {
    Write-Host 'WARN: application.yml is missing next to the jar.'
    Write-Host '      The built-in password placeholder resolves to an empty string and startup will fail with'
    Write-Host "      Java SQLException: Access denied for user 'root'@'localhost' (using password: NO)."
    return $false
  }
  $line = Select-String -LiteralPath $Path -Pattern '^\s*password\s*:' | Select-Object -First 1
  if (-not $line) {
    Write-Host "WARN: no 'password:' entry found in application.yml; check the datasource block."
    return $false
  }
  # Trim() takes a single char, so strip whitespace, then one quote kind at a
  # time. Passing a 2-char string here throws
  # "cannot convert value ... to type System.Char" and aborts the script.
  $value = $line.Line -replace '^\s*password\s*:\s*', ''
  $value = $value.Trim()
  $value = $value.Trim("'")
  $value = $value.Trim('"')
  if ($value -eq '') {
    Write-Host "WARN: spring.datasource.password is empty in application.yml; startup may fail with '(using password: NO)'."
    return $false
  }
  if ($value -match '[^\x00-\x7F]') {
    Write-Host 'WARN: the datasource password contains non-ASCII characters.'
    Write-Host '      If it is still the Chinese placeholder text, replace it with the real MySQL password first.'
    return $false
  }
  return $true
}
# ---------- read-only status: no elevation needed ----------
if ($Status) {
  $service = Get-ServiceState
@@ -180,24 +251,7 @@
if (-not (Test-Path $wrapper)) {
  throw "Missing wrapper executable: $wrapper`nRebuild the deploy package with pack-deploy.ps1 (it provides WinSW), or copy WinSW-x64.exe here renamed to trafficAudit.exe."
}
if (-not (Test-Path $appConfig)) {
  Write-Host "WARN: application.yml is missing next to the jar."
  Write-Host "      The built-in password placeholder resolves to an empty string and startup will fail with"
  Write-Host "      Java SQLException: Access denied for user 'root'@'localhost' (using password: NO)."
} else {
  $line = Select-String -LiteralPath $appConfig -Pattern '^\s*password\s*:' | Select-Object -First 1
  if (-not $line) {
    Write-Host "WARN: no 'password:' entry found in application.yml; check the datasource block."
  } else {
    $value = ($line.Line -replace '^\s*password\s*:\s*', '').Trim(' ''', '"')
    if ($value -eq '') {
      Write-Host "WARN: spring.datasource.password is empty in application.yml; startup may fail with '(using password: NO)'."
    } elseif ($value -match '[^\x00-\x7F]') {
      Write-Host "WARN: the datasource password contains non-ASCII characters."
      Write-Host "      If it is still the Chinese placeholder text, replace it with the real MySQL password first."
    }
  }
}
$passwordOk = Test-AppConfigPassword -Path $appConfig
$java = Resolve-JavaPath -Explicit $JavaPath
Set-WrapperJavaPath -JavaExe $java
@@ -218,18 +272,59 @@
if ($LASTEXITCODE -ne 0) { throw "WinSW install failed with exit code $LASTEXITCODE" }
if (-not $NoStart) {
  Write-Host "Starting service '$serviceId' (delayed auto-start is used at boot)..."
  Write-Host "Starting service '$serviceId' (auto-start at boot is enabled)..."
  & $wrapper start
  Start-Sleep -Seconds 8
  Start-Sleep -Seconds 5
  $service = Get-ServiceState
  $service | Select-Object Name, DisplayName, Status, StartType | Format-List
  $serviceDir = Join-Path $root 'logs'
  # Read the port from application.yml so the check matches the real config.
  $port = 8090
  if (Test-Path $appConfig) {
    $portLine = Select-String -LiteralPath $appConfig -Pattern '^\s*port\s*:' | Select-Object -First 1
    if ($portLine -and $portLine.Line -match '(\d{2,5})') { $port = [int]$matches[1] }
  }
  $ready = $false
  for ($i = 1; $i -le 30; $i++) {
    if (Test-LocalPort -Port $port) { $ready = $true; break }
    Start-Sleep -Seconds 2
    $state = Get-ServiceState
    if ($state -and $state.Status -ne 'Running') { break }
  }
  $logDir = Join-Path $root 'logs'
  Write-Host ''
  Write-Host "Logs        : $serviceDir\trafficAudit.out.log / trafficAudit.err.log"
  Write-Host "Health check: (Invoke-WebRequest http://127.0.0.1:8090 -UseBasicParsing).StatusCode"
  if ($ready) {
    Write-Host "OK  : the backend is listening on port $port."
    Write-Host "      On this server   : http://127.0.0.1:$port"
    Write-Host "      For other people : http://<server-ip>:$port"
  } else {
    Write-Host "WARN: nothing is listening on port $port yet - startup is still running, or it failed."
    Write-Host "      Fix application.yml / MySQL, then run: .\install-service.ps1 -Restart"
    foreach ($name in @('trafficAudit.err.log', 'trafficAudit.out.log')) {
      $logFile = Join-Path $logDir $name
      if (Test-Path -LiteralPath $logFile) {
        Write-Host ''
        Write-Host "---- last 15 lines of $name ----"
        Get-Content -LiteralPath $logFile -Tail 15 -ErrorAction SilentlyContinue | ForEach-Object { Write-Host "  $_" }
      }
    }
  }
  if (-not $passwordOk) {
    Write-Host ''
    Write-Host '=============================================================='
    Write-Host ' IMPORTANT: backend\application.yml still looks unset.'
    Write-Host ' Put the real MySQL url / username / password in it, then run'
    Write-Host '   .\install-service.ps1 -Restart'
    Write-Host '=============================================================='
  }
  Write-Host ''
  Write-Host "Manage it with: Stop-Service $serviceId | Start-Service $serviceId"
  Write-Host "Or: .\install-service.ps1 -Status | -Restart | -Remove"
  Write-Host "Logs  : $logDir\trafficAudit.out.log / trafficAudit.err.log"
  Write-Host "Manage: .\install-service.ps1 -Status | -Restart | -Remove"
  Write-Host "        Stop-Service $serviceId | Start-Service $serviceId"
  Write-Host ''
  Write-Host 'The service auto-starts on every boot (Automatic, delayed start).'
  Write-Host 'This service starts automatically on every boot (Automatic + delayed start).'
}