chore: 初始化 app-cli,本地开发环境脚本
- dev/dev.ps1: 一键启动后端(编译后运行)+前端(4321),校验 CORS 与 astro dev 单实例锁 - dev/stop.ps1: 按 PID 登记停止,-Force 清残留 - dev/status.ps1: 端口/PID/健康检查一览 - dev/doctor.ps1: 工具链/路径/依赖/数据库/端口/CORS 体检 - vision/vision.ps1: CLIP 向量服务起停 - lib/common.ps1: 路径探测、端口、进程登记、HTTP 探测 - config/local.env.example: 路径与端口覆盖模板
This commit is contained in:
190
dev/doctor.ps1
Normal file
190
dev/doctor.ps1
Normal file
@ -0,0 +1,190 @@
|
||||
# dev/doctor.ps1 — 本地环境体检
|
||||
#
|
||||
# 只读检查,不改任何东西。把「启动后才发现」的问题提前暴露:
|
||||
# 工具链版本 / 仓库路径 / 配置文件 / 依赖安装 / 数据库连通 / 端口占用 / CORS 白名单
|
||||
#
|
||||
# 退出码:0 = 可以开工;1 = 有必须先处理的问题。
|
||||
|
||||
$ErrorActionPreference = 'Stop'
|
||||
$CliRoot = Split-Path -Parent $PSScriptRoot
|
||||
. (Join-Path $CliRoot 'lib\common.ps1')
|
||||
|
||||
$envMap = Import-LocalEnv (Join-Path $CliRoot 'config\local.env')
|
||||
$svc = Resolve-Services -EnvMap $envMap -Root $CliRoot
|
||||
|
||||
$backendPort = [int](Get-Setting $envMap 'BACKEND_PORT' '8090')
|
||||
$frontendPort = [int](Get-Setting $envMap 'FRONTEND_PORT' '4321')
|
||||
$visionPort = [int](Get-Setting $envMap 'VISION_PORT' '8900')
|
||||
|
||||
$script:Fail = 0
|
||||
$script:Warn = 0
|
||||
|
||||
# Check:$Value 只在通过时显示(版本号、路径等);$Hint 只在未通过时显示(怎么修)。
|
||||
function Check {
|
||||
param(
|
||||
[string]$Name,
|
||||
[bool]$Ok,
|
||||
[string]$Value = '',
|
||||
[string]$Hint = '',
|
||||
[bool]$Fatal = $true
|
||||
)
|
||||
if ($Ok) {
|
||||
if ($Value -ne '') { Write-Ok "$Name($Value)" } else { Write-Ok $Name }
|
||||
return
|
||||
}
|
||||
if ($Fatal) {
|
||||
Write-Fail $Name
|
||||
$script:Fail++
|
||||
} else {
|
||||
Write-Warn $Name
|
||||
$script:Warn++
|
||||
}
|
||||
if ($Hint -ne '') { Write-Note " $Hint" }
|
||||
}
|
||||
|
||||
Write-Host ''
|
||||
Write-Host ' app-cli / 本地环境体检' -ForegroundColor White
|
||||
Write-Host ' ----------------------------------------' -ForegroundColor DarkGray
|
||||
|
||||
# ---------------------------------------------------------------- 1. 工具链
|
||||
Write-Step '工具链'
|
||||
$goCmd = Get-Command go -ErrorAction SilentlyContinue
|
||||
if ($goCmd) {
|
||||
$goVer = (& go version) -replace 'go version ', '' -replace ' windows.*', ''
|
||||
Check -Name 'Go' -Ok $true -Value $goVer
|
||||
} else {
|
||||
Check -Name 'Go' -Ok $false -Hint '未安装,后端无法编译'
|
||||
}
|
||||
|
||||
$nodeCmd = Get-Command node -ErrorAction SilentlyContinue
|
||||
if ($nodeCmd) {
|
||||
$nodeVer = (& node -v).Trim()
|
||||
$major = 0
|
||||
if ($nodeVer -match 'v(\d+)') { $major = [int]$Matches[1] }
|
||||
# 前端 package.json engines 要求 >=22.12,只做提示不拦
|
||||
Check -Name 'Node' -Ok ($major -ge 22) -Value $nodeVer -Hint "当前 $nodeVer,前端要求 >=22.12" -Fatal $false
|
||||
} else {
|
||||
Check -Name 'Node' -Ok $false -Hint '未安装,前端无法启动'
|
||||
}
|
||||
|
||||
$pythonCmd = Get-Command python -ErrorAction SilentlyContinue
|
||||
if ($pythonCmd) {
|
||||
$pyVer = (& python --version 2>&1).ToString().Trim()
|
||||
Check -Name 'Python' -Ok $true -Value $pyVer
|
||||
} else {
|
||||
Check -Name 'Python' -Ok $false -Hint '未安装(只有跑视觉服务才需要)' -Fatal $false
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------- 2. 仓库路径
|
||||
Write-Step '仓库路径'
|
||||
Check -Name "backend $($svc.Backend.Path)" -Ok $svc.Backend.IsValid -Hint "缺标记文件 $($svc.Backend.Marker)"
|
||||
Check -Name "frontend $($svc.Frontend.Path)" -Ok $svc.Frontend.IsValid -Hint "缺标记文件 $($svc.Frontend.Marker)"
|
||||
Check -Name "vision $($svc.Vision.Path)" -Ok $svc.Vision.IsValid -Hint "缺标记文件 $($svc.Vision.Marker),仅影响视觉服务" -Fatal $false
|
||||
|
||||
# ---------------------------------------------------------------- 3. 配置与依赖
|
||||
Write-Step '配置与依赖'
|
||||
if ($svc.Backend.IsValid) {
|
||||
$cfgYml = Join-Path $svc.Backend.Path 'configs\config.yml'
|
||||
$cfgLocal = Join-Path $svc.Backend.Path 'configs\config.local.yml'
|
||||
Check -Name 'backend configs/config.yml' -Ok (Test-Path $cfgYml) -Hint '唯一配置源,缺失则全走默认值'
|
||||
Check -Name 'backend configs/config.local.yml' -Ok (Test-Path $cfgLocal) -Hint '本机密钥(DB 密码 / JWT / S4),缺失会在启动时交互提示' -Fatal $false
|
||||
}
|
||||
|
||||
if ($svc.Frontend.IsValid) {
|
||||
$nm = Join-Path $svc.Frontend.Path 'node_modules'
|
||||
$astro = Join-Path $svc.Frontend.Path 'node_modules\astro\bin\astro.mjs'
|
||||
Check -Name 'frontend node_modules' -Ok (Test-Path $nm) -Hint '缺失时 dev.ps1 会自动 npm ci' -Fatal $false
|
||||
Check -Name 'frontend astro CLI' -Ok (Test-Path $astro) -Hint "缺 $astro(依赖损坏,删掉 node_modules 重装)"
|
||||
|
||||
# 前端 dev 端口的两处来源必须一致,否则手动 npm run dev 会起在别的端口 → CORS 拦截
|
||||
$astroCfg = Join-Path $svc.Frontend.Path 'astro.config.mjs'
|
||||
if (Test-Path $astroCfg) {
|
||||
$raw = Get-Content $astroCfg -Raw
|
||||
if ($raw -match 'port:\s*(\d+)') {
|
||||
$cfgPort = [int]$Matches[1]
|
||||
Check -Name '前端端口一致性' -Ok ($cfgPort -eq $frontendPort) -Value "$cfgPort" `
|
||||
-Hint "astro.config.mjs 的 server.port=$cfgPort,而 FRONTEND_PORT=$frontendPort;dev.ps1 会用 --port 覆盖,但你手动 npm run dev 会起在 $cfgPort 并被 CORS 拦截" `
|
||||
-Fatal $false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($svc.Vision.IsValid) {
|
||||
$uvicorn = Join-Path $svc.Vision.Path '.venv\Scripts\uvicorn.exe'
|
||||
Check -Name 'vision .venv/uvicorn' -Ok (Test-Path $uvicorn) -Hint '缺失则无法启动 CLIP 服务(python -m venv .venv 后装依赖)' -Fatal $false
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------- 4. 数据库连通
|
||||
Write-Step '数据库'
|
||||
if ($svc.Backend.IsValid) {
|
||||
$cfgYml = Join-Path $svc.Backend.Path 'configs\config.yml'
|
||||
$cfgLocal = Join-Path $svc.Backend.Path 'configs\config.local.yml'
|
||||
$dbHost = Get-YamlScalar -Path $cfgYml -Section 'database' -Key 'host'
|
||||
$dbPortRaw = Get-YamlScalar -Path $cfgYml -Section 'database' -Key 'port'
|
||||
$dbName = Get-YamlScalar -Path $cfgYml -Section 'database' -Key 'name'
|
||||
|
||||
# 本机覆盖优先
|
||||
$localHost = Get-YamlScalar -Path $cfgLocal -Section 'database' -Key 'host'
|
||||
if ($localHost -ne '') { $dbHost = $localHost }
|
||||
$localPort = Get-YamlScalar -Path $cfgLocal -Section 'database' -Key 'port'
|
||||
if ($localPort -ne '') { $dbPortRaw = $localPort }
|
||||
|
||||
$dbPort = 5432
|
||||
if ($dbPortRaw -ne '') { $dbPort = [int]$dbPortRaw }
|
||||
|
||||
if ($dbHost -eq '') {
|
||||
Check -Name '数据库配置' -Ok $false -Hint '配置里没读到 database.host' -Fatal $false
|
||||
} else {
|
||||
$reachable = Test-TcpConnect -ComputerName $dbHost -Port $dbPort -TimeoutMs 4000
|
||||
Check -Name "PostgreSQL $dbHost`:$dbPort/$dbName" -Ok $reachable `
|
||||
-Hint 'TCP 连不上:后端启动会直接退出(查网络 / 密码 / 远端是否放行本机 IP)'
|
||||
}
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------- 5. 端口
|
||||
Write-Step '端口'
|
||||
$ports = @(
|
||||
@{ Port = $backendPort; Name = 'backend 公开' },
|
||||
@{ Port = 8091; Name = 'backend SSG' },
|
||||
@{ Port = 8092; Name = 'backend 后台' },
|
||||
@{ Port = $frontendPort; Name = 'frontend dev' },
|
||||
@{ Port = $visionPort; Name = 'vision' }
|
||||
)
|
||||
foreach ($p in $ports) {
|
||||
if (Test-PortInUse $p.Port) {
|
||||
$owner = Get-PortOwnerPid $p.Port
|
||||
Write-Note ($p.Port.ToString() + ' ' + $p.Name + ' 已被 PID ' + $owner + ' 占用(若已在跑属正常)')
|
||||
} else {
|
||||
Write-Note ($p.Port.ToString() + ' ' + $p.Name + ' 空闲')
|
||||
}
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------- 6. 运行时自检
|
||||
Write-Step '运行时自检'
|
||||
if (Test-PortInUse $backendPort) {
|
||||
$healthUrl = "http://127.0.0.1:$backendPort/api/health"
|
||||
Check -Name '后端 /api/health' -Ok (Wait-HttpOk -Url $healthUrl -TimeoutSec 3) -Value $healthUrl -Fatal $false
|
||||
|
||||
$origin = "http://localhost:$frontendPort"
|
||||
$acao = Get-CorsOrigin -Url "http://127.0.0.1:$backendPort/api/v1/public/runway-looks?page=1" -Origin $origin
|
||||
if ($acao -eq $origin) {
|
||||
Check -Name "CORS 放行 $origin" -Ok $true -Value $acao
|
||||
} else {
|
||||
Check -Name "CORS 放行 $origin" -Ok $false -Fatal $false `
|
||||
-Hint "后端回显 '$acao';不在白名单时浏览器会拦掉接口,页面显示「无数据」。改 backend/configs/config.local.yml → cors.allow_origins"
|
||||
}
|
||||
} else {
|
||||
Write-Note '后端未运行,跳过健康与 CORS 检查'
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------- 汇总
|
||||
Write-Host ''
|
||||
if ($script:Fail -gt 0) {
|
||||
Write-Fail ("体检未通过:$($script:Fail) 项必处理,$($script:Warn) 项提醒")
|
||||
exit 1
|
||||
}
|
||||
if ($script:Warn -gt 0) {
|
||||
Write-Warn ("可以开工,但有 $($script:Warn) 项提醒")
|
||||
exit 0
|
||||
}
|
||||
Write-Ok '体检全部通过'
|
||||
Reference in New Issue
Block a user