bookworm-boot/admin-authcode-gui.ps1
bookworm d87b92319f chore: 清理过期文件 (-2731 行) + AuthGen 历史记录功能
删除:
- 5 个旧版 .bat 安装器 (已被 auto-setup.ps1 EXE 替代)
- 4 个旧版 HTML 指南 (已被 guide-unified.html 替代)
- encrypt-secrets.ps1 (已被 gen-authcode.js 替代)
- lessons-learned.md (开发备忘, 非功能文件)
- 6 个未提交测试 .enc + 备份文件 (本地清理)

新增:
- AuthGen GUI: 生成成功后自动追加 authcode-history.log
- AuthGen GUI: "推送到 Gitea" 按钮 (git add + commit + push 一键完成)
- AuthGen GUI: $args→$nodeArgs (PowerShell 保留变量冲突)
- AuthGen GUI: UTF-8 Process 编码 (修复 PS2EXE 中文乱码)
- .gitignore: +authcode-history.log +Bookworm-AuthGen.exe

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 03:20:39 +08:00

483 lines
21 KiB
PowerShell

<#
.SYNOPSIS
Bookworm 授权码生成器 (管理员 GUI 工具)
.DESCRIPTION
内部调用 node gen-authcode.js, 提供可视化界面生成多用户授权码。
打包命令: 见 build.ps1 -Admin
#>
# 全局错误捕获 (PS2EXE -NoOutput 会吞掉所有错误, 这里确保弹窗显示)
try {
$ErrorActionPreference = "Stop"
Add-Type -AssemblyName System.Windows.Forms
Add-Type -AssemblyName System.Drawing
[System.Windows.Forms.Application]::EnableVisualStyles()
# ─── 路径 ─────────────────────────────────────────────
$ScriptDir = if ($PSScriptRoot) { $PSScriptRoot }
elseif ([System.Diagnostics.Process]::GetCurrentProcess().MainModule.FileName -match '\.exe$') {
Split-Path -Parent ([System.Diagnostics.Process]::GetCurrentProcess().MainModule.FileName)
} elseif ($MyInvocation.MyCommand.Path) {
Split-Path -Parent $MyInvocation.MyCommand.Path
} else { $PWD.Path }
# gen-authcode.js / secrets.txt 查找: 当前目录 → 父目录 (dist/ 内运行时)
$GenScript = Join-Path $ScriptDir "gen-authcode.js"
if (-not (Test-Path $GenScript)) {
$parentDir = Split-Path $ScriptDir -Parent
$GenScript = Join-Path $parentDir "gen-authcode.js"
if (Test-Path $GenScript) { $ScriptDir = $parentDir } # 切到父目录
}
$SecretsTxt = Join-Path $ScriptDir "secrets.txt"
# ─── 品牌色 ───────────────────────────────────────────
$brandBlue = [System.Drawing.Color]::FromArgb(88, 101, 242)
$brandDark = [System.Drawing.Color]::FromArgb(24, 25, 38)
$brandLight = [System.Drawing.Color]::FromArgb(245, 246, 250)
$cardBg = [System.Drawing.Color]::FromArgb(248, 249, 253)
$successGreen = [System.Drawing.Color]::FromArgb(35, 134, 54)
$warningOrange = [System.Drawing.Color]::FromArgb(227, 137, 29)
$textPrimary = [System.Drawing.Color]::FromArgb(36, 41, 47)
$textSecondary = [System.Drawing.Color]::FromArgb(110, 119, 129)
$inputBorder = [System.Drawing.Color]::FromArgb(208, 215, 222)
# ─── 主窗口 ───────────────────────────────────────────
$form = New-Object System.Windows.Forms.Form
$form.Text = "Bookworm 授权码生成器"
$form.ClientSize = New-Object System.Drawing.Size(540, 594)
$form.StartPosition = "CenterScreen"
$form.FormBorderStyle = "FixedSingle"
$form.MaximizeBox = $false
$form.BackColor = [System.Drawing.Color]::White
$form.Font = New-Object System.Drawing.Font("Segoe UI", 9)
# ─── 标题栏 (深色渐变 + 副标题) ──────────────────────
$header = New-Object System.Windows.Forms.Panel
$header.Dock = "Top"
$header.Size = New-Object System.Drawing.Size(540, 70)
$header.BackColor = $brandDark
$form.Controls.Add($header)
$titleLabel = New-Object System.Windows.Forms.Label
$titleLabel.Location = New-Object System.Drawing.Point(24, 12)
$titleLabel.Size = New-Object System.Drawing.Size(500, 28)
$titleLabel.Text = "Bookworm 授权码生成器"
$titleLabel.Font = New-Object System.Drawing.Font("Segoe UI", 15, [System.Drawing.FontStyle]::Bold)
$titleLabel.ForeColor = [System.Drawing.Color]::White
$header.Controls.Add($titleLabel)
$subtitleLabel = New-Object System.Windows.Forms.Label
$subtitleLabel.Location = New-Object System.Drawing.Point(24, 42)
$subtitleLabel.Size = New-Object System.Drawing.Size(500, 18)
$subtitleLabel.Text = "管理员专用 · 为每位用户生成独立授权码与加密凭证"
$subtitleLabel.Font = New-Object System.Drawing.Font("Segoe UI", 8.5)
$subtitleLabel.ForeColor = [System.Drawing.Color]::FromArgb(160, 170, 200)
$header.Controls.Add($subtitleLabel)
# ═══ 输入卡片 (Panel 模拟卡片) ═══════════════════════
$inputCard = New-Object System.Windows.Forms.Panel
$inputCard.Location = New-Object System.Drawing.Point(20, 84)
$inputCard.Size = New-Object System.Drawing.Size(500, 186)
$inputCard.BackColor = $cardBg
$form.Controls.Add($inputCard)
# ── 卡片标题
$inputTitle = New-Object System.Windows.Forms.Label
$inputTitle.Location = New-Object System.Drawing.Point(16, 10)
$inputTitle.Size = New-Object System.Drawing.Size(200, 20)
$inputTitle.Text = "用户信息"
$inputTitle.Font = New-Object System.Drawing.Font("Segoe UI", 9, [System.Drawing.FontStyle]::Bold)
$inputTitle.ForeColor = $brandBlue
$inputCard.Controls.Add($inputTitle)
# ── 用户名
$lblUser = New-Object System.Windows.Forms.Label
$lblUser.Location = New-Object System.Drawing.Point(16, 40)
$lblUser.Size = New-Object System.Drawing.Size(90, 22)
$lblUser.Text = "用户标识"
$lblUser.ForeColor = $textPrimary
$lblUser.TextAlign = [System.Drawing.ContentAlignment]::MiddleRight
$inputCard.Controls.Add($lblUser)
$txtUser = New-Object System.Windows.Forms.TextBox
$txtUser.Location = New-Object System.Drawing.Point(114, 38)
$txtUser.Size = New-Object System.Drawing.Size(370, 26)
$txtUser.Font = New-Object System.Drawing.Font("Segoe UI", 10)
$txtUser.BorderStyle = "FixedSingle"
$inputCard.Controls.Add($txtUser)
# ── Relay Key
$lblKey = New-Object System.Windows.Forms.Label
$lblKey.Location = New-Object System.Drawing.Point(16, 74)
$lblKey.Size = New-Object System.Drawing.Size(90, 22)
$lblKey.Text = "Relay Key"
$lblKey.ForeColor = $textPrimary
$lblKey.TextAlign = [System.Drawing.ContentAlignment]::MiddleRight
$inputCard.Controls.Add($lblKey)
$txtKey = New-Object System.Windows.Forms.TextBox
$txtKey.Location = New-Object System.Drawing.Point(114, 72)
$txtKey.Size = New-Object System.Drawing.Size(370, 26)
$txtKey.Font = New-Object System.Drawing.Font("Segoe UI", 10)
$txtKey.PasswordChar = '*'
$txtKey.BorderStyle = "FixedSingle"
$inputCard.Controls.Add($txtKey)
$chkShowKey = New-Object System.Windows.Forms.CheckBox
$chkShowKey.Location = New-Object System.Drawing.Point(114, 102)
$chkShowKey.Size = New-Object System.Drawing.Size(100, 20)
$chkShowKey.Text = "显示 Key"
$chkShowKey.ForeColor = $textSecondary
$chkShowKey.Font = New-Object System.Drawing.Font("Segoe UI", 8)
$chkShowKey.Add_CheckedChanged({ $txtKey.PasswordChar = if ($chkShowKey.Checked) { [char]0 } else { '*' } })
$inputCard.Controls.Add($chkShowKey)
# ── 有效期
$lblDays = New-Object System.Windows.Forms.Label
$lblDays.Location = New-Object System.Drawing.Point(16, 132)
$lblDays.Size = New-Object System.Drawing.Size(90, 22)
$lblDays.Text = "有效期"
$lblDays.ForeColor = $textPrimary
$lblDays.TextAlign = [System.Drawing.ContentAlignment]::MiddleRight
$inputCard.Controls.Add($lblDays)
$cmbDays = New-Object System.Windows.Forms.ComboBox
$cmbDays.Location = New-Object System.Drawing.Point(114, 130)
$cmbDays.Size = New-Object System.Drawing.Size(80, 26)
$cmbDays.DropDownStyle = "DropDownList"
$cmbDays.Font = New-Object System.Drawing.Font("Segoe UI", 10)
$null = $cmbDays.Items.AddRange(@(7, 14, 30, 60, 90, 180, 365))
$cmbDays.SelectedIndex = 2
$inputCard.Controls.Add($cmbDays)
$lblDaysUnit = New-Object System.Windows.Forms.Label
$lblDaysUnit.Location = New-Object System.Drawing.Point(198, 132)
$lblDaysUnit.Size = New-Object System.Drawing.Size(30, 22)
$lblDaysUnit.Text = ""
$lblDaysUnit.ForeColor = $textSecondary
$inputCard.Controls.Add($lblDaysUnit)
$lblDaysHint = New-Object System.Windows.Forms.Label
$lblDaysHint.Location = New-Object System.Drawing.Point(240, 132)
$lblDaysHint.Size = New-Object System.Drawing.Size(240, 22)
$lblDaysHint.Text = ""
$lblDaysHint.ForeColor = $textSecondary
$lblDaysHint.Font = New-Object System.Drawing.Font("Segoe UI", 8.5)
$inputCard.Controls.Add($lblDaysHint)
$cmbDays.Add_SelectedIndexChanged({
$d = [int]$cmbDays.SelectedItem
$lblDaysHint.Text = "-> 到期: " + (Get-Date).AddDays($d).ToString("yyyy-MM-dd")
})
$cmbDays.SelectedIndex = 2
# ── 环境状态行 ──
$nodeOK = [bool](Get-Command node -ErrorAction SilentlyContinue)
$secretsOK = Test-Path $SecretsTxt
$lblStatus1 = New-Object System.Windows.Forms.Label
$lblStatus1.Location = New-Object System.Drawing.Point(16, 160)
$lblStatus1.Size = New-Object System.Drawing.Size(230, 18)
$lblStatus1.Font = New-Object System.Drawing.Font("Segoe UI", 8)
if ($secretsOK) {
$lineCount = (Get-Content $SecretsTxt -ErrorAction SilentlyContinue | Where-Object { $_ -match '=' }).Count
$lblStatus1.Text = "secrets.txt: $lineCount 个凭证"
$lblStatus1.ForeColor = $successGreen
} else {
$lblStatus1.Text = "secrets.txt: 未找到"
$lblStatus1.ForeColor = [System.Drawing.Color]::Red
}
$inputCard.Controls.Add($lblStatus1)
$lblStatus2 = New-Object System.Windows.Forms.Label
$lblStatus2.Location = New-Object System.Drawing.Point(250, 160)
$lblStatus2.Size = New-Object System.Drawing.Size(230, 18)
$lblStatus2.Font = New-Object System.Drawing.Font("Segoe UI", 8)
if ($nodeOK) {
$nodeVer = try { (& node --version 2>$null) } catch { "" }
$lblStatus2.Text = "Node.js: $nodeVer"
$lblStatus2.ForeColor = $successGreen
} else {
$lblStatus2.Text = "Node.js: 未安装"
$lblStatus2.ForeColor = [System.Drawing.Color]::Red
}
$inputCard.Controls.Add($lblStatus2)
# ═══ 操作按钮 ═════════════════════════════════════════
$btnGenerate = New-Object System.Windows.Forms.Button
$btnGenerate.Location = New-Object System.Drawing.Point(138, 282)
$btnGenerate.Size = New-Object System.Drawing.Size(180, 42)
$btnGenerate.Text = " 生成授权码"
$btnGenerate.Font = New-Object System.Drawing.Font("Segoe UI", 11, [System.Drawing.FontStyle]::Bold)
$btnGenerate.FlatStyle = "Flat"
$btnGenerate.BackColor = $brandBlue
$btnGenerate.ForeColor = [System.Drawing.Color]::White
$btnGenerate.FlatAppearance.BorderSize = 0
$btnGenerate.Cursor = [System.Windows.Forms.Cursors]::Hand
$btnGenerate.TextAlign = [System.Drawing.ContentAlignment]::MiddleCenter
$form.Controls.Add($btnGenerate)
$btnClear = New-Object System.Windows.Forms.Button
$btnClear.Location = New-Object System.Drawing.Point(330, 282)
$btnClear.Size = New-Object System.Drawing.Size(72, 42)
$btnClear.Text = "清空"
$btnClear.Font = New-Object System.Drawing.Font("Segoe UI", 9)
$btnClear.FlatStyle = "Flat"
$btnClear.BackColor = [System.Drawing.Color]::White
$btnClear.ForeColor = $textSecondary
$btnClear.FlatAppearance.BorderColor = $inputBorder
$btnClear.FlatAppearance.BorderSize = 1
$form.Controls.Add($btnClear)
# ═══ 结果卡片 ═════════════════════════════════════════
$resultCard = New-Object System.Windows.Forms.Panel
$resultCard.Location = New-Object System.Drawing.Point(20, 338)
$resultCard.Size = New-Object System.Drawing.Size(500, 220)
$resultCard.BackColor = $cardBg
$form.Controls.Add($resultCard)
$lblResultTitle = New-Object System.Windows.Forms.Label
$lblResultTitle.Location = New-Object System.Drawing.Point(16, 10)
$lblResultTitle.Size = New-Object System.Drawing.Size(200, 20)
$lblResultTitle.Text = "生成结果"
$lblResultTitle.Font = New-Object System.Drawing.Font("Segoe UI", 9, [System.Drawing.FontStyle]::Bold)
$lblResultTitle.ForeColor = $brandBlue
$resultCard.Controls.Add($lblResultTitle)
$txtAuthCode = New-Object System.Windows.Forms.TextBox
$txtAuthCode.Location = New-Object System.Drawing.Point(16, 38)
$txtAuthCode.Size = New-Object System.Drawing.Size(380, 34)
$txtAuthCode.Font = New-Object System.Drawing.Font("Consolas", 13, [System.Drawing.FontStyle]::Bold)
$txtAuthCode.ReadOnly = $true
$txtAuthCode.BackColor = [System.Drawing.Color]::White
$txtAuthCode.ForeColor = $brandDark
$txtAuthCode.BorderStyle = "FixedSingle"
$txtAuthCode.Text = ""
$resultCard.Controls.Add($txtAuthCode)
$btnCopy = New-Object System.Windows.Forms.Button
$btnCopy.Location = New-Object System.Drawing.Point(404, 38)
$btnCopy.Size = New-Object System.Drawing.Size(80, 34)
$btnCopy.Text = "复制"
$btnCopy.Font = New-Object System.Drawing.Font("Segoe UI", 9)
$btnCopy.FlatStyle = "Flat"
$btnCopy.BackColor = $brandBlue
$btnCopy.ForeColor = [System.Drawing.Color]::White
$btnCopy.FlatAppearance.BorderSize = 0
$btnCopy.Cursor = [System.Windows.Forms.Cursors]::Hand
$btnCopy.Enabled = $false
$resultCard.Controls.Add($btnCopy)
$lblDetails = New-Object System.Windows.Forms.Label
$lblDetails.Location = New-Object System.Drawing.Point(16, 82)
$lblDetails.Size = New-Object System.Drawing.Size(468, 66)
$lblDetails.Text = "点击「生成授权码」后,结果将显示在此处。"
$lblDetails.ForeColor = $textSecondary
$lblDetails.Font = New-Object System.Drawing.Font("Segoe UI", 8.5)
$resultCard.Controls.Add($lblDetails)
# 推送到 Gitea 按钮
$btnPush = New-Object System.Windows.Forms.Button
$btnPush.Location = New-Object System.Drawing.Point(16, 156)
$btnPush.Size = New-Object System.Drawing.Size(468, 38)
$btnPush.Text = "推送到 Gitea (git add + commit + push)"
$btnPush.Font = New-Object System.Drawing.Font("Segoe UI", 10)
$btnPush.FlatStyle = "Flat"
$btnPush.BackColor = $successGreen
$btnPush.ForeColor = [System.Drawing.Color]::White
$btnPush.FlatAppearance.BorderSize = 0
$btnPush.Cursor = [System.Windows.Forms.Cursors]::Hand
$btnPush.Enabled = $false
$resultCard.Controls.Add($btnPush)
# ═══ 状态栏 ═══════════════════════════════════════════
$statusBar = New-Object System.Windows.Forms.Label
$statusBar.Location = New-Object System.Drawing.Point(0, 568)
$statusBar.Size = New-Object System.Drawing.Size(540, 26)
$statusBar.BackColor = $brandDark
$statusBar.ForeColor = [System.Drawing.Color]::FromArgb(160, 170, 200)
$statusBar.Text = " 就绪 | $ScriptDir"
$statusBar.Font = New-Object System.Drawing.Font("Segoe UI", 8)
$statusBar.TextAlign = [System.Drawing.ContentAlignment]::MiddleLeft
$form.Controls.Add($statusBar)
# ─── 事件处理 ─────────────────────────────────────────
$btnCopy.Add_Click({
if ($txtAuthCode.Text) {
[System.Windows.Forms.Clipboard]::SetText($txtAuthCode.Text)
$statusBar.Text = " 已复制到剪贴板"
$statusBar.ForeColor = $successGreen
}
})
$btnPush.Add_Click({
$btnPush.Enabled = $false
$btnPush.Text = "推送中..."
$statusBar.Text = " git add + commit + push ..."
$statusBar.ForeColor = $warningOrange
[System.Windows.Forms.Application]::DoEvents()
try {
$gitExe = (Get-Command git -ErrorAction Stop).Source
$userName = if ($global:lastGenUser) { $global:lastGenUser } else { "user" }
# git add secrets-*.enc
$psi = New-Object System.Diagnostics.ProcessStartInfo
$psi.FileName = $gitExe
$psi.Arguments = "add secrets-*.enc"
$psi.UseShellExecute = $false
$psi.RedirectStandardOutput = $true
$psi.RedirectStandardError = $true
$psi.StandardOutputEncoding = [System.Text.Encoding]::UTF8
$psi.StandardErrorEncoding = [System.Text.Encoding]::UTF8
$psi.CreateNoWindow = $true
$psi.WorkingDirectory = $ScriptDir
$p = [System.Diagnostics.Process]::Start($psi)
$p.WaitForExit(15000)
# git commit
$psi.Arguments = "commit -m `"add user $userName`""
$p = [System.Diagnostics.Process]::Start($psi)
$p.StandardOutput.ReadToEnd() | Out-Null
$p.WaitForExit(15000)
# git push
$statusBar.Text = " git push 中 (可能需要几秒)..."
[System.Windows.Forms.Application]::DoEvents()
$psi.Arguments = "push"
$p = [System.Diagnostics.Process]::Start($psi)
$pushOut = $p.StandardError.ReadToEnd() # git push 输出在 stderr
$p.WaitForExit(30000)
if ($p.ExitCode -eq 0) {
$btnPush.Text = "已推送"
$btnPush.BackColor = [System.Drawing.Color]::FromArgb(200, 220, 200)
$btnPush.ForeColor = $successGreen
$statusBar.Text = " 推送成功 — 现在可以把授权码发给 $userName"
$statusBar.ForeColor = $successGreen
} else {
throw "git push 失败: $pushOut"
}
} catch {
$btnPush.Text = "推送失败 (点击重试)"
$btnPush.BackColor = [System.Drawing.Color]::FromArgb(220, 200, 200)
$btnPush.ForeColor = [System.Drawing.Color]::Red
$btnPush.Enabled = $true
$statusBar.Text = " 推送失败: $_"
$statusBar.ForeColor = [System.Drawing.Color]::Red
[System.Windows.Forms.MessageBox]::Show("推送失败:`n$_`n`n请检查 Git 配置和网络。", "Git 错误", "OK", "Error")
}
})
$btnClear.Add_Click({
$txtUser.Text = ""
$txtKey.Text = ""
$txtAuthCode.Text = ""
$lblDetails.Text = ""
$btnCopy.Enabled = $false
$statusBar.Text = " 已清空"
$statusBar.ForeColor = [System.Drawing.Color]::Gray
})
$btnGenerate.Add_Click({
# 校验
$user = $txtUser.Text.Trim()
$key = $txtKey.Text.Trim()
$days = $cmbDays.SelectedItem.ToString()
if (-not $user) {
[System.Windows.Forms.MessageBox]::Show("请输入用户标识", "缺少用户名", "OK", "Warning")
$txtUser.Focus(); return
}
if (-not $key) {
[System.Windows.Forms.MessageBox]::Show("请输入 Relay Sub-Key`n(从中转站后台为用户创建)", "缺少 Sub-Key", "OK", "Warning")
$txtKey.Focus(); return
}
if (-not (Test-Path $SecretsTxt)) {
[System.Windows.Forms.MessageBox]::Show("secrets.txt 不存在:`n$SecretsTxt`n`n请先创建 secrets.txt (每行 KEY=VALUE)", "缺少凭证文件", "OK", "Error")
return
}
if (-not $nodeOK) {
[System.Windows.Forms.MessageBox]::Show("Node.js 未安装。`n请先安装: https://nodejs.org", "缺少 Node.js", "OK", "Error")
return
}
$statusBar.Text = " 生成中..."
$statusBar.ForeColor = $warningOrange
$btnGenerate.Enabled = $false
[System.Windows.Forms.Application]::DoEvents()
try {
# 用 Process + UTF8 编码读取 (PS2EXE 默认 GBK 会乱码)
$psi = New-Object System.Diagnostics.ProcessStartInfo
$psi.FileName = (Get-Command node -ErrorAction Stop).Source
$psi.Arguments = "`"$GenScript`" $days -k `"$key`" -u `"$user`""
$psi.UseShellExecute = $false
$psi.RedirectStandardOutput = $true
$psi.RedirectStandardError = $true
$psi.StandardOutputEncoding = [System.Text.Encoding]::UTF8
$psi.StandardErrorEncoding = [System.Text.Encoding]::UTF8
$psi.CreateNoWindow = $true
$psi.WorkingDirectory = $ScriptDir
$proc = [System.Diagnostics.Process]::Start($psi)
$stdout = $proc.StandardOutput.ReadToEnd()
$stderr = $proc.StandardError.ReadToEnd()
$proc.WaitForExit()
if ($proc.ExitCode -ne 0) {
throw "gen-authcode.js 退出码 $($proc.ExitCode)`n$stderr"
}
# 解析输出 (编码无关: PS2EXE 下中文可能乱码, 只匹配 ASCII 格式)
$authCode = if ($stdout -match '(BW-\d{8}-[A-F0-9]{24})') { $Matches[1] } else { "" }
$fileId = if ($stdout -match '(secrets-[a-f0-9]{8}\.enc)') { $Matches[1] } else { "" }
$expiry = if ($stdout -match '(\d{4}-\d{2}-\d{2})') { $Matches[1] } else { "" }
if ($authCode) {
$txtAuthCode.Text = $authCode
$lblDetails.Text = "用户: $user`n加密文件: $fileId`n有效期: $days 天 (至 $expiry)`n路径: $ScriptDir\$fileId"
$btnCopy.Enabled = $true
$btnPush.Enabled = $true
$global:lastGenUser = $user
$global:lastGenFile = $fileId
$statusBar.Text = " 生成成功 — 点击「推送到 Gitea」完成部署"
$statusBar.ForeColor = $successGreen
# 追加历史记录 (仅本机, 已在 .gitignore)
$historyFile = Join-Path $ScriptDir "authcode-history.log"
$logLine = "$(Get-Date -Format 'yyyy-MM-dd HH:mm') $($user.PadRight(12)) $fileId $($days)天 至$expiry $authCode"
try { Add-Content -Path $historyFile -Value $logLine -Encoding utf8 } catch {}
} else {
throw "无法解析授权码输出:`n$stdout"
}
} catch {
$txtAuthCode.Text = ""
$lblDetails.Text = "错误: $_"
$btnCopy.Enabled = $false
$statusBar.Text = " 生成失败"
$statusBar.ForeColor = [System.Drawing.Color]::Red
[System.Windows.Forms.MessageBox]::Show("生成失败:`n$_", "错误", "OK", "Error")
} finally {
$btnGenerate.Enabled = $true
}
})
# ─── 启动 ─────────────────────────────────────────────
$form.Add_Shown({ $txtUser.Focus() })
[System.Windows.Forms.Application]::Run($form)
} catch {
# 全局错误弹窗 (PS2EXE 下唯一的错误可见方式)
$errMsg = "Bookworm AuthGen 启动失败:`n`n" +
"错误: $($_.Exception.Message)`n" +
"行号: $($_.InvocationInfo.ScriptLineNumber)`n" +
"代码: $($_.InvocationInfo.Line.Trim())`n`n" +
"ScriptDir: $ScriptDir`n" +
"GenScript: $GenScript`n" +
"SecretsTxt: $SecretsTxt"
try {
[System.Windows.Forms.MessageBox]::Show($errMsg, "AuthGen 错误", "OK", "Error")
} catch {
# 如果连 MsgBox 都失败 (WinForms 未加载), 写文件
$errMsg | Out-File "$env:TEMP\bookworm-authgen-error.txt" -Encoding utf8
}
}