Initial commit: RHINE LAB · ANALYSIS OS:三维界面与动效实验(本仓库不含个人归档索引数据)

This commit is contained in:
WpyQwq
2026-09-19 13:04:56 +08:00
commit 3f422218a5
1098 changed files with 39410 additions and 0 deletions
+189
View File
@@ -0,0 +1,189 @@
<#
Rhine Lab UI · 一键启动(桌面那个脚本调的就是它)
做三件事:
1. 确保本地归档后端(「数据库」,127.0.0.1:43117)在跑;没装开机自启就顺手装上。
2. 在单独窗口里启动界面(npm run dev),窗口关掉即停止界面。
3. 等界面真的响应了,再用默认浏览器打开它。
顺序很重要:先等后端首轮扫描结束再起界面。因为 back 端启动时会重建快照,
而 npm run dev 的 predev 也会重建一次,两边同时写同一批文件会互相踩。
用法:
-Mode start 一键启动(默认)
-Mode stop 停掉界面与后端
-Mode status 只看状态
#>
[CmdletBinding()]
param(
[ValidateSet('start', 'stop', 'status')]
[string]$Mode = 'start'
)
$ErrorActionPreference = 'Stop'
$ProjectRoot = Split-Path -Parent $PSScriptRoot
$ServiceScript = Join-Path $PSScriptRoot 'archive-service.ps1'
$ConfigPath = Join-Path $ProjectRoot 'archive.config.json'
$LogDir = Join-Path $ProjectRoot 'logs'
$UiPidFile = Join-Path $LogDir 'ui.pid'
$UiLog = Join-Path $LogDir 'ui.log'
$UiErrLog = Join-Path $LogDir 'ui.err.log'
$UiPortFirst = 5173
$UiPortLast = 5185
$UiMarker = 'ANALYSIS OS'
function Write-Head($text) { Write-Host ''; Write-Host " $text" -ForegroundColor Cyan }
function Write-Ok($text) { Write-Host " $text" -ForegroundColor Green }
function Write-Info($text) { Write-Host " $text" -ForegroundColor Gray }
function Write-Warn2($text) { Write-Host " $text" -ForegroundColor Yellow }
function Get-ArchiveConfig {
if (-not (Test-Path -LiteralPath $ConfigPath)) { throw "缺少配置文件:$ConfigPath" }
return (Get-Content -LiteralPath $ConfigPath -Raw -Encoding UTF8 | ConvertFrom-Json)
}
function Find-UiPort {
foreach ($p in $UiPortFirst..$UiPortLast) {
try {
$r = Invoke-WebRequest -Uri "http://127.0.0.1:$p/" -TimeoutSec 2 -UseBasicParsing
if ($r.StatusCode -eq 200 -and ([string]$r.Content) -like "*$UiMarker*") { return $p }
} catch { }
}
return 0
}
function Wait-UiPort {
param([int]$TimeoutSec = 240)
$deadline = (Get-Date).AddSeconds($TimeoutSec)
while ((Get-Date) -lt $deadline) {
$p = Find-UiPort
if ($p -gt 0) { return $p }
Start-Sleep -Milliseconds 1000
}
return 0
}
function Get-RecordedUiPid {
if (-not (Test-Path -LiteralPath $UiPidFile)) { return 0 }
$raw = (Get-Content -LiteralPath $UiPidFile -Raw).Trim()
$value = 0
if ([int]::TryParse($raw, [ref]$value)) { return $value }
return 0
}
function Stop-Ui {
$stopped = $false
$recorded = Get-RecordedUiPid
if ($recorded -gt 0 -and (Get-Process -Id $recorded -ErrorAction SilentlyContinue)) {
& taskkill.exe /PID $recorded /T /F 2>&1 | Out-Null
Write-Ok "已关闭界面窗口(进程树 $recorded)"
$stopped = $true
}
# 兜底:按命令行找这个项目的 vite 进程(例如用户自己敲的 npm run dev)
$extra = @()
foreach ($p in (Get-CimInstance Win32_Process -ErrorAction SilentlyContinue)) {
$cl = [string]$p.CommandLine
if ($cl -and $cl -like "*$ProjectRoot*vite*") { $extra += [int]$p.ProcessId }
}
foreach ($t in ($extra | Sort-Object -Unique)) {
Stop-Process -Id $t -Force -ErrorAction SilentlyContinue
$stopped = $true
}
if (-not $stopped) { Write-Warn2 '界面本来就没在运行。' }
Remove-Item -LiteralPath $UiPidFile -Force -ErrorAction SilentlyContinue
}
function Show-Status {
Write-Head '界面'
$port = Find-UiPort
if ($port -gt 0) {
$recorded = Get-RecordedUiPid
Write-Ok "在运行:http://127.0.0.1:$port/ (记录的 PID $recorded)"
} else {
Write-Info '未运行'
}
Write-Head '归档后端'
& $ServiceScript -Action status
}
switch ($Mode) {
'status' { Show-Status }
'stop' {
Write-Head '停止档案库'
Stop-Ui
& "$env:SystemRoot\System32\WindowsPowerShell\v1.0\powershell.exe" `
-NoProfile -ExecutionPolicy Bypass -File $ServiceScript -Action stop
Write-Host ''
}
'start' {
Write-Head '启动档案库'
if (-not (Test-Path -LiteralPath $ServiceScript)) {
throw "找不到服务脚本:$ServiceScript"
}
# 1) 后端(「数据库」)
# 放进子进程调用:这样它的 exit 码语义明确,也不会把本脚本一起 exit 掉。
# (直接在同一个会话里 & 调用时 $LASTEXITCODE 可能是 $null,而 $null -ne 0 为真,
# 会把「后端其实好好的」误报成失败。)
Write-Info '检查归档后端……'
& "$env:SystemRoot\System32\WindowsPowerShell\v1.0\powershell.exe" `
-NoProfile -ExecutionPolicy Bypass -File $ServiceScript -Action ensure
$backendOk = ($LASTEXITCODE -eq 0)
if (-not $backendOk) {
Write-Warn2 '归档后端没能就绪 —— 界面照常启动,但 LOCAL ARCHIVE 面板与「文件位置」会不可用。'
}
$cfg = Get-ArchiveConfig
# 2) 界面
$port = Find-UiPort
if ($port -gt 0) {
Write-Ok "界面已经在跑:http://127.0.0.1:$port/"
} else {
if (-not (Test-Path -LiteralPath $LogDir)) { New-Item -ItemType Directory -Path $LogDir -Force | Out-Null }
$npm = (Get-Command npm.cmd -ErrorAction SilentlyContinue)
if (-not $npm) { throw '找不到 npm.cmd,请确认 Node.js 已正确安装。' }
Write-Info '启动界面(后台静默运行 npm run dev,日志写进 logs\ui.log)……'
# 隐藏窗口 + 重定向输出:一是干净,二是万一起不来,日志就在手边可查。
# (反过来,不重定向时 Start-Process 在非交互宿主里可能连窗口都建不出来,
# 表现就是「什么都没发生」,排查时毫无线索。)
$proc = Start-Process -FilePath $npm.Source -ArgumentList @('run', 'dev') `
-WorkingDirectory $ProjectRoot -PassThru -WindowStyle Hidden `
-RedirectStandardOutput $UiLog -RedirectStandardError $UiErrLog
Set-Content -LiteralPath $UiPidFile -Value $proc.Id -Encoding ASCII
Write-Info '等待界面就绪(predev 会先重建一次索引,约十几秒)……'
$port = Wait-UiPort -TimeoutSec 300
if ($port -le 0) {
Write-Warn2 '界面在超时前没有响应。下面是它自己的输出:'
Write-Host ''
if (Test-Path -LiteralPath $UiLog) { Get-Content -LiteralPath $UiLog -Tail 20 -Encoding UTF8 | ForEach-Object { Write-Host " $_" } }
if ((Test-Path -LiteralPath $UiErrLog) -and (Get-Item -LiteralPath $UiErrLog).Length -gt 0) {
Write-Host ''
Write-Host ' --- stderr ---' -ForegroundColor DarkGray
Get-Content -LiteralPath $UiErrLog -Tail 20 -Encoding UTF8 | ForEach-Object { Write-Host " $_" }
}
Write-Host ''
exit 1
}
Write-Ok "界面已就绪:http://127.0.0.1:$port/"
}
# 3) 开浏览器
Start-Process ("http://127.0.0.1:$port/") | Out-Null
Write-Ok '已用默认浏览器打开。'
Write-Host ''
Write-Info "归档根:$($cfg.root)"
Write-Info '要停止:运行「启动档案库.cmd stop」(界面与后端一起停)'
Write-Info "界面日志:$UiLog"
Write-Host ''
}
}