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:
121
vision/vision.ps1
Normal file
121
vision/vision.ps1
Normal file
@ -0,0 +1,121 @@
|
||||
<#
|
||||
.SYNOPSIS
|
||||
CLIP 向量服务(fashion-siglip)的独立起停脚本。
|
||||
|
||||
.DESCRIPTION
|
||||
该服务把图片编码成归一化向量,供后台「疑似同款」提示使用。
|
||||
- 只监听 127.0.0.1,不对外暴露
|
||||
- --workers 必须为 1(模型常驻进程内,多 worker 会重复加载模型)
|
||||
- 后端需要 VISION_ENABLED=true 才会调用它;服务不可用时后端自动降级,不影响入库
|
||||
|
||||
.EXAMPLE
|
||||
.\vision\vision.ps1 start
|
||||
.\vision\vision.ps1 status
|
||||
.\vision\vision.ps1 logs
|
||||
.\vision\vision.ps1 stop
|
||||
#>
|
||||
param(
|
||||
[Parameter(Position = 0)]
|
||||
[ValidateSet('start', 'stop', 'restart', 'status', 'logs')]
|
||||
[string]$Action = 'start',
|
||||
[switch]$Force
|
||||
)
|
||||
|
||||
$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
|
||||
$port = [int](Get-Setting $envMap 'VISION_PORT' '8900')
|
||||
$healthUrl = "http://127.0.0.1:$port/health"
|
||||
|
||||
function Start-CLIP {
|
||||
if (Test-PortInUse $port) {
|
||||
$owner = Get-PortOwnerPid $port
|
||||
$saved = Get-ServicePid 'vision'
|
||||
if ($saved -gt 0 -and $saved -eq $owner) {
|
||||
Write-Note "视觉服务已在运行(PID $owner)"
|
||||
return $true
|
||||
}
|
||||
if (-not $Force) {
|
||||
Write-Fail "端口 $port 被 PID $owner 占用"
|
||||
Write-Note '加 -Force 强制释放:.\vision\vision.ps1 start -Force'
|
||||
return $false
|
||||
}
|
||||
Stop-PidTree $owner | Out-Null
|
||||
}
|
||||
|
||||
if (-not $svc.Vision.IsValid) {
|
||||
Write-Fail "视觉服务目录无效:$($svc.Vision.Path)"
|
||||
return $false
|
||||
}
|
||||
|
||||
$uvicorn = Join-Path $svc.Vision.Path '.venv\Scripts\uvicorn.exe'
|
||||
if (-not (Test-Path $uvicorn)) {
|
||||
Write-Fail "找不到 $uvicorn"
|
||||
Write-Note "在 $($svc.Vision.Path) 建虚拟环境:python -m venv .venv && .venv\Scripts\pip install -r requirements.txt"
|
||||
return $false
|
||||
}
|
||||
|
||||
Write-Step "启动视觉服务(端口 $port,首次加载模型较慢)"
|
||||
Start-LoggedProcess -Name 'vision' -FilePath $uvicorn `
|
||||
-ArgumentList @('app:app', '--host', '127.0.0.1', '--port', "$port", '--workers', '1') `
|
||||
-WorkingDirectory $svc.Vision.Path | Out-Null
|
||||
|
||||
if (Wait-HttpOk -Url $healthUrl -TimeoutSec 180) {
|
||||
Write-Ok '视觉服务就绪'
|
||||
Show-Health
|
||||
return $true
|
||||
}
|
||||
Write-Fail '视觉服务 180 秒内未就绪'
|
||||
Show-LogTail -Name 'vision' -Lines 25
|
||||
return $false
|
||||
}
|
||||
|
||||
function Show-Health {
|
||||
try {
|
||||
$r = Invoke-WebRequest -Uri $healthUrl -UseBasicParsing -TimeoutSec 5 -ErrorAction Stop
|
||||
Write-Note ("health: " + $r.Content.Trim())
|
||||
} catch { }
|
||||
}
|
||||
|
||||
Write-Host ''
|
||||
Write-Host ' app-cli / CLIP 视觉服务' -ForegroundColor White
|
||||
Write-Host ' ----------------------------------------' -ForegroundColor DarkGray
|
||||
|
||||
switch ($Action) {
|
||||
'start' {
|
||||
if (-not (Start-CLIP)) { exit 1 }
|
||||
}
|
||||
'stop' {
|
||||
if (Stop-NamedService 'vision') { Write-Ok '视觉服务已停止' }
|
||||
else { Write-Note '视觉服务未在本脚本登记(可能没启动过)' }
|
||||
if (Test-PortInUse $port) {
|
||||
$owner = Get-PortOwnerPid $port
|
||||
Write-Warn "端口 $port 仍被 PID $owner 占用"
|
||||
if ($Force) { Stop-PidTree $owner | Out-Null; Write-Ok "已终止 PID $owner" }
|
||||
else { Write-Note '加 -Force 强制终止:.\vision\vision.ps1 stop -Force' }
|
||||
}
|
||||
}
|
||||
'restart' {
|
||||
Stop-NamedService 'vision' | Out-Null
|
||||
Start-Sleep -Milliseconds 500
|
||||
if (-not (Start-CLIP)) { exit 1 }
|
||||
}
|
||||
'status' {
|
||||
$st = Get-ServiceStatus -Name 'vision' -Port $port -HealthUrl $healthUrl
|
||||
if ($st.Healthy) {
|
||||
Write-Ok "运行中(PID $($st.OwnerPid),端口 $port)"
|
||||
Show-Health
|
||||
} elseif ($st.Listening) {
|
||||
Write-Warn "端口 $port 已监听但 /health 未通过(模型可能仍在加载)"
|
||||
Show-LogTail -Name 'vision' -Lines 10
|
||||
} else {
|
||||
Write-Note '未运行'
|
||||
}
|
||||
}
|
||||
'logs' {
|
||||
Show-LogTail -Name 'vision' -Lines 40
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user