1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
<#
  一键启动前后端(可靠方式)
  用法:powershell -ExecutionPolicy Bypass -File start-dev.ps1
  说明:
  - 后端:cmd /c mvn.cmd spring-boot:run,日志 backend.log,端口 8090
  - 前端:cmd /c npm.cmd run serve,日志 traffic-audit-web/frontend.log,端口 8080
  - 为什么用 cmd /c:本机环境变量同时存在 Path 与 PATH,
    会导致 Start-Process 抛"已添加项"异常;cmd /c 方式可绕过。
  - 前端必须在沙箱外运行(node 访问 C:\Users\jcxiong 会被沙箱拦截)。
#>
$ErrorActionPreference = 'Stop'
$root = Split-Path -Parent $MyInvocation.MyCommand.Path
$webDir = Join-Path $root 'traffic-audit-web'
 
# ---------- 密钥:从 Windows 用户环境变量补齐(真实值不落文件)----------
# 若当前 shell 是旧进程、拿不到新设的变量,则从注册表 HKCU\Environment 读一次,
# 供后端子进程继承(避免"密钥不在任何文件里、又读不到环境变量"的空档)。
foreach ($name in @('TRAFFIC_DB_PASSWORD', 'DEEPSEEK_API_KEY', 'TRAFFIC_DB_URL', 'TRAFFIC_DB_USER', 'TRAFFIC_JWT_SECRET')) {
  if (-not [Environment]::GetEnvironmentVariable($name, 'Process')) {
    $val = [Environment]::GetEnvironmentVariable($name, 'User')
    if ($val) {
      [Environment]::SetEnvironmentVariable($name, $val, 'Process')
      Write-Host "已从用户环境变量载入 $name"
    }
  }
}
 
function Test-PortListening($port) {
  try {
    $c = Get-NetTCPConnection -LocalPort $port -State Listen -ErrorAction Stop
    return [bool]$c
  } catch {
    return $false
  }
}
 
# ---------- 后端 ----------
$blog = Join-Path $root 'backend.log'
if (Test-PortListening 8090) {
  Write-Host '后端已在运行(8090 有监听),跳过启动。'
} else {
  if (Test-Path $blog) { Remove-Item -LiteralPath $blog -Force -ErrorAction SilentlyContinue }
  $psi = [System.Diagnostics.ProcessStartInfo]::new()
  $psi.FileName = 'cmd.exe'
  $psi.Arguments = '/c mvn.cmd -q spring-boot:run -f traffic-audit-server\pom.xml > backend.log 2>&1'
  $psi.WorkingDirectory = $root
  $psi.UseShellExecute = $false
  $psi.CreateNoWindow = $true
  $p = [System.Diagnostics.Process]::new()
  $p.StartInfo = $psi
  [void]$p.Start()
  Write-Host "后端启动中(PID $($p.Id)),日志:backend.log"
}
 
# ---------- 前端 ----------
$flog = Join-Path $webDir 'frontend.log'
if (Test-PortListening 8080) {
  Write-Host '前端已在运行(8080 有监听),跳过启动。'
} else {
  if (Test-Path $flog) { Remove-Item -LiteralPath $flog -Force -ErrorAction SilentlyContinue }
  $psi2 = [System.Diagnostics.ProcessStartInfo]::new()
  $psi2.FileName = 'cmd.exe'
  $psi2.Arguments = '/c npm.cmd run serve > frontend.log 2>&1'
  $psi2.WorkingDirectory = $webDir
  $psi2.UseShellExecute = $false
  $psi2.CreateNoWindow = $true
  $p2 = [System.Diagnostics.Process]::new()
  $p2.StartInfo = $psi2
  [void]$p2.Start()
  Write-Host "前端启动中(PID $($p2.Id)),日志:traffic-audit-web/frontend.log"
}
 
# ---------- 等待并验证 ----------
Write-Host ''
Write-Host '等待服务就绪(最多 120 秒)...'
$deadline = (Get-Date).AddSeconds(120)
$backendOk = $false
$frontendOk = $false
while ((Get-Date) -lt $deadline) {
  if (-not $backendOk -and (Test-Path $blog)) {
    $b = Get-Content $blog -Raw -ErrorAction SilentlyContinue
    if ($b -match 'Started TrafficAuditApplication') { $backendOk = $true }
  }
  if (-not $frontendOk -and (Test-Path $flog)) {
    $f = Get-Content $flog -Raw -ErrorAction SilentlyContinue
    if ($f -match 'App running at') { $frontendOk = $true }
  }
  if ($backendOk -and $frontendOk) { break }
  Start-Sleep -Seconds 3
}
$backendMsg = if ($backendOk) { 'OK,已就绪' } else { '未确认(请查看 backend.log)' }
$frontendMsg = if ($frontendOk) { 'OK,已就绪' } else { '未确认(请查看 frontend.log)' }
Write-Host "后端: $backendMsg"
Write-Host "前端: $frontendMsg"
Write-Host '访问地址:http://localhost:8080'