2026-04-10 01:59:27 +08:00
|
|
|
<#
|
|
|
|
|
.SYNOPSIS
|
|
|
|
|
Bookworm 授权码生成器 (管理员 GUI 工具)
|
|
|
|
|
.DESCRIPTION
|
|
|
|
|
内部调用 node gen-authcode.js, 提供可视化界面生成多用户授权码。
|
|
|
|
|
打包命令: 见 build.ps1 -Admin
|
|
|
|
|
#>
|
2026-04-10 03:20:39 +08:00
|
|
|
|
|
|
|
|
# 全局错误捕获 (PS2EXE -NoOutput 会吞掉所有错误, 这里确保弹窗显示)
|
|
|
|
|
try {
|
|
|
|
|
|
2026-04-10 01:59:27 +08:00
|
|
|
$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 }
|
|
|
|
|
|
2026-04-10 02:10:15 +08:00
|
|
|
# gen-authcode.js / secrets.txt 查找: 当前目录 → 父目录 (dist/ 内运行时)
|
2026-04-10 01:59:27 +08:00
|
|
|
$GenScript = Join-Path $ScriptDir "gen-authcode.js"
|
2026-04-10 02:10:15 +08:00
|
|
|
if (-not (Test-Path $GenScript)) {
|
|
|
|
|
$parentDir = Split-Path $ScriptDir -Parent
|
|
|
|
|
$GenScript = Join-Path $parentDir "gen-authcode.js"
|
|
|
|
|
if (Test-Path $GenScript) { $ScriptDir = $parentDir } # 切到父目录
|
|
|
|
|
}
|
2026-04-10 01:59:27 +08:00
|
|
|
$SecretsTxt = Join-Path $ScriptDir "secrets.txt"
|
|
|
|
|
|
|
|
|
|
# ─── 品牌色 ───────────────────────────────────────────
|
2026-04-10 03:20:39 +08:00
|
|
|
$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)
|
2026-04-10 01:59:27 +08:00
|
|
|
$warningOrange = [System.Drawing.Color]::FromArgb(227, 137, 29)
|
2026-04-10 03:20:39 +08:00
|
|
|
$textPrimary = [System.Drawing.Color]::FromArgb(36, 41, 47)
|
|
|
|
|
$textSecondary = [System.Drawing.Color]::FromArgb(110, 119, 129)
|
|
|
|
|
$inputBorder = [System.Drawing.Color]::FromArgb(208, 215, 222)
|
2026-04-10 01:59:27 +08:00
|
|
|
|
|
|
|
|
# ─── 主窗口 ───────────────────────────────────────────
|
|
|
|
|
$form = New-Object System.Windows.Forms.Form
|
2026-04-10 03:20:39 +08:00
|
|
|
$form.Text = "Bookworm 授权码生成器"
|
|
|
|
|
$form.ClientSize = New-Object System.Drawing.Size(540, 594)
|
2026-04-10 01:59:27 +08:00
|
|
|
$form.StartPosition = "CenterScreen"
|
2026-04-10 03:20:39 +08:00
|
|
|
$form.FormBorderStyle = "FixedSingle"
|
2026-04-10 01:59:27 +08:00
|
|
|
$form.MaximizeBox = $false
|
|
|
|
|
$form.BackColor = [System.Drawing.Color]::White
|
|
|
|
|
$form.Font = New-Object System.Drawing.Font("Segoe UI", 9)
|
|
|
|
|
|
2026-04-10 03:20:39 +08:00
|
|
|
# ─── 标题栏 (深色渐变 + 副标题) ──────────────────────
|
2026-04-10 01:59:27 +08:00
|
|
|
$header = New-Object System.Windows.Forms.Panel
|
2026-04-10 03:20:39 +08:00
|
|
|
$header.Dock = "Top"
|
|
|
|
|
$header.Size = New-Object System.Drawing.Size(540, 70)
|
2026-04-10 01:59:27 +08:00
|
|
|
$header.BackColor = $brandDark
|
|
|
|
|
$form.Controls.Add($header)
|
|
|
|
|
|
|
|
|
|
$titleLabel = New-Object System.Windows.Forms.Label
|
2026-04-10 03:20:39 +08:00
|
|
|
$titleLabel.Location = New-Object System.Drawing.Point(24, 12)
|
|
|
|
|
$titleLabel.Size = New-Object System.Drawing.Size(500, 28)
|
2026-04-10 01:59:27 +08:00
|
|
|
$titleLabel.Text = "Bookworm 授权码生成器"
|
2026-04-10 03:20:39 +08:00
|
|
|
$titleLabel.Font = New-Object System.Drawing.Font("Segoe UI", 15, [System.Drawing.FontStyle]::Bold)
|
2026-04-10 01:59:27 +08:00
|
|
|
$titleLabel.ForeColor = [System.Drawing.Color]::White
|
|
|
|
|
$header.Controls.Add($titleLabel)
|
|
|
|
|
|
2026-04-10 03:20:39 +08:00
|
|
|
$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)
|
|
|
|
|
|
|
|
|
|
# ── 用户名
|
2026-04-10 01:59:27 +08:00
|
|
|
$lblUser = New-Object System.Windows.Forms.Label
|
2026-04-10 03:20:39 +08:00
|
|
|
$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)
|
2026-04-10 01:59:27 +08:00
|
|
|
$txtUser = New-Object System.Windows.Forms.TextBox
|
2026-04-10 03:20:39 +08:00
|
|
|
$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
|
2026-04-10 01:59:27 +08:00
|
|
|
$lblKey = New-Object System.Windows.Forms.Label
|
2026-04-10 03:20:39 +08:00
|
|
|
$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)
|
2026-04-10 01:59:27 +08:00
|
|
|
$txtKey = New-Object System.Windows.Forms.TextBox
|
2026-04-10 03:20:39 +08:00
|
|
|
$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)
|
2026-04-10 01:59:27 +08:00
|
|
|
$txtKey.PasswordChar = '*'
|
2026-04-10 03:20:39 +08:00
|
|
|
$txtKey.BorderStyle = "FixedSingle"
|
|
|
|
|
$inputCard.Controls.Add($txtKey)
|
2026-04-10 01:59:27 +08:00
|
|
|
|
|
|
|
|
$chkShowKey = New-Object System.Windows.Forms.CheckBox
|
2026-04-10 03:20:39 +08:00
|
|
|
$chkShowKey.Location = New-Object System.Drawing.Point(114, 102)
|
|
|
|
|
$chkShowKey.Size = New-Object System.Drawing.Size(100, 20)
|
2026-04-10 01:59:27 +08:00
|
|
|
$chkShowKey.Text = "显示 Key"
|
2026-04-10 03:20:39 +08:00
|
|
|
$chkShowKey.ForeColor = $textSecondary
|
|
|
|
|
$chkShowKey.Font = New-Object System.Drawing.Font("Segoe UI", 8)
|
2026-04-10 01:59:27 +08:00
|
|
|
$chkShowKey.Add_CheckedChanged({ $txtKey.PasswordChar = if ($chkShowKey.Checked) { [char]0 } else { '*' } })
|
2026-04-10 03:20:39 +08:00
|
|
|
$inputCard.Controls.Add($chkShowKey)
|
2026-04-10 01:59:27 +08:00
|
|
|
|
2026-04-10 03:20:39 +08:00
|
|
|
# ── 有效期
|
2026-04-10 01:59:27 +08:00
|
|
|
$lblDays = New-Object System.Windows.Forms.Label
|
2026-04-10 03:20:39 +08:00
|
|
|
$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)
|
2026-04-10 01:59:27 +08:00
|
|
|
$cmbDays = New-Object System.Windows.Forms.ComboBox
|
2026-04-10 03:20:39 +08:00
|
|
|
$cmbDays.Location = New-Object System.Drawing.Point(114, 130)
|
|
|
|
|
$cmbDays.Size = New-Object System.Drawing.Size(80, 26)
|
2026-04-10 01:59:27 +08:00
|
|
|
$cmbDays.DropDownStyle = "DropDownList"
|
2026-04-10 03:20:39 +08:00
|
|
|
$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)
|
2026-04-10 01:59:27 +08:00
|
|
|
|
|
|
|
|
$lblDaysHint = New-Object System.Windows.Forms.Label
|
2026-04-10 03:20:39 +08:00
|
|
|
$lblDaysHint.Location = New-Object System.Drawing.Point(240, 132)
|
|
|
|
|
$lblDaysHint.Size = New-Object System.Drawing.Size(240, 22)
|
2026-04-10 01:59:27 +08:00
|
|
|
$lblDaysHint.Text = ""
|
2026-04-10 03:20:39 +08:00
|
|
|
$lblDaysHint.ForeColor = $textSecondary
|
|
|
|
|
$lblDaysHint.Font = New-Object System.Drawing.Font("Segoe UI", 8.5)
|
|
|
|
|
$inputCard.Controls.Add($lblDaysHint)
|
2026-04-10 01:59:27 +08:00
|
|
|
$cmbDays.Add_SelectedIndexChanged({
|
|
|
|
|
$d = [int]$cmbDays.SelectedItem
|
2026-04-10 03:20:39 +08:00
|
|
|
$lblDaysHint.Text = "-> 到期: " + (Get-Date).AddDays($d).ToString("yyyy-MM-dd")
|
2026-04-10 01:59:27 +08:00
|
|
|
})
|
|
|
|
|
$cmbDays.SelectedIndex = 2
|
|
|
|
|
|
2026-04-10 03:20:39 +08:00
|
|
|
# ── 环境状态行 ──
|
|
|
|
|
$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) {
|
2026-04-10 01:59:27 +08:00
|
|
|
$lineCount = (Get-Content $SecretsTxt -ErrorAction SilentlyContinue | Where-Object { $_ -match '=' }).Count
|
2026-04-10 03:20:39 +08:00
|
|
|
$lblStatus1.Text = "secrets.txt: $lineCount 个凭证"
|
|
|
|
|
$lblStatus1.ForeColor = $successGreen
|
2026-04-10 01:59:27 +08:00
|
|
|
} else {
|
2026-04-10 03:20:39 +08:00
|
|
|
$lblStatus1.Text = "secrets.txt: 未找到"
|
|
|
|
|
$lblStatus1.ForeColor = [System.Drawing.Color]::Red
|
2026-04-10 01:59:27 +08:00
|
|
|
}
|
2026-04-10 03:20:39 +08:00
|
|
|
$inputCard.Controls.Add($lblStatus1)
|
2026-04-10 01:59:27 +08:00
|
|
|
|
2026-04-10 03:20:39 +08:00
|
|
|
$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)
|
2026-04-10 01:59:27 +08:00
|
|
|
if ($nodeOK) {
|
|
|
|
|
$nodeVer = try { (& node --version 2>$null) } catch { "" }
|
2026-04-10 03:20:39 +08:00
|
|
|
$lblStatus2.Text = "Node.js: $nodeVer"
|
|
|
|
|
$lblStatus2.ForeColor = $successGreen
|
2026-04-10 01:59:27 +08:00
|
|
|
} else {
|
2026-04-10 03:20:39 +08:00
|
|
|
$lblStatus2.Text = "Node.js: 未安装"
|
|
|
|
|
$lblStatus2.ForeColor = [System.Drawing.Color]::Red
|
2026-04-10 01:59:27 +08:00
|
|
|
}
|
2026-04-10 03:20:39 +08:00
|
|
|
$inputCard.Controls.Add($lblStatus2)
|
2026-04-10 01:59:27 +08:00
|
|
|
|
2026-04-10 03:20:39 +08:00
|
|
|
# ═══ 操作按钮 ═════════════════════════════════════════
|
2026-04-10 01:59:27 +08:00
|
|
|
$btnGenerate = New-Object System.Windows.Forms.Button
|
2026-04-10 03:20:39 +08:00
|
|
|
$btnGenerate.Location = New-Object System.Drawing.Point(138, 282)
|
|
|
|
|
$btnGenerate.Size = New-Object System.Drawing.Size(180, 42)
|
|
|
|
|
$btnGenerate.Text = " 生成授权码"
|
2026-04-10 01:59:27 +08:00
|
|
|
$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
|
2026-04-10 03:20:39 +08:00
|
|
|
$btnGenerate.TextAlign = [System.Drawing.ContentAlignment]::MiddleCenter
|
2026-04-10 01:59:27 +08:00
|
|
|
$form.Controls.Add($btnGenerate)
|
|
|
|
|
|
|
|
|
|
$btnClear = New-Object System.Windows.Forms.Button
|
2026-04-10 03:20:39 +08:00
|
|
|
$btnClear.Location = New-Object System.Drawing.Point(330, 282)
|
|
|
|
|
$btnClear.Size = New-Object System.Drawing.Size(72, 42)
|
2026-04-10 01:59:27 +08:00
|
|
|
$btnClear.Text = "清空"
|
2026-04-10 03:20:39 +08:00
|
|
|
$btnClear.Font = New-Object System.Drawing.Font("Segoe UI", 9)
|
2026-04-10 01:59:27 +08:00
|
|
|
$btnClear.FlatStyle = "Flat"
|
2026-04-10 03:20:39 +08:00
|
|
|
$btnClear.BackColor = [System.Drawing.Color]::White
|
|
|
|
|
$btnClear.ForeColor = $textSecondary
|
|
|
|
|
$btnClear.FlatAppearance.BorderColor = $inputBorder
|
|
|
|
|
$btnClear.FlatAppearance.BorderSize = 1
|
2026-04-10 01:59:27 +08:00
|
|
|
$form.Controls.Add($btnClear)
|
|
|
|
|
|
2026-04-10 03:20:39 +08:00
|
|
|
# ═══ 结果卡片 ═════════════════════════════════════════
|
|
|
|
|
$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)
|
2026-04-10 01:59:27 +08:00
|
|
|
|
|
|
|
|
$lblResultTitle = New-Object System.Windows.Forms.Label
|
2026-04-10 03:20:39 +08:00
|
|
|
$lblResultTitle.Location = New-Object System.Drawing.Point(16, 10)
|
|
|
|
|
$lblResultTitle.Size = New-Object System.Drawing.Size(200, 20)
|
2026-04-10 01:59:27 +08:00
|
|
|
$lblResultTitle.Text = "生成结果"
|
2026-04-10 03:20:39 +08:00
|
|
|
$lblResultTitle.Font = New-Object System.Drawing.Font("Segoe UI", 9, [System.Drawing.FontStyle]::Bold)
|
|
|
|
|
$lblResultTitle.ForeColor = $brandBlue
|
|
|
|
|
$resultCard.Controls.Add($lblResultTitle)
|
2026-04-10 01:59:27 +08:00
|
|
|
|
|
|
|
|
$txtAuthCode = New-Object System.Windows.Forms.TextBox
|
2026-04-10 03:20:39 +08:00
|
|
|
$txtAuthCode.Location = New-Object System.Drawing.Point(16, 38)
|
|
|
|
|
$txtAuthCode.Size = New-Object System.Drawing.Size(380, 34)
|
2026-04-10 01:59:27 +08:00
|
|
|
$txtAuthCode.Font = New-Object System.Drawing.Font("Consolas", 13, [System.Drawing.FontStyle]::Bold)
|
|
|
|
|
$txtAuthCode.ReadOnly = $true
|
2026-04-10 03:20:39 +08:00
|
|
|
$txtAuthCode.BackColor = [System.Drawing.Color]::White
|
2026-04-10 01:59:27 +08:00
|
|
|
$txtAuthCode.ForeColor = $brandDark
|
2026-04-10 03:20:39 +08:00
|
|
|
$txtAuthCode.BorderStyle = "FixedSingle"
|
2026-04-10 01:59:27 +08:00
|
|
|
$txtAuthCode.Text = ""
|
2026-04-10 03:20:39 +08:00
|
|
|
$resultCard.Controls.Add($txtAuthCode)
|
2026-04-10 01:59:27 +08:00
|
|
|
|
|
|
|
|
$btnCopy = New-Object System.Windows.Forms.Button
|
2026-04-10 03:20:39 +08:00
|
|
|
$btnCopy.Location = New-Object System.Drawing.Point(404, 38)
|
|
|
|
|
$btnCopy.Size = New-Object System.Drawing.Size(80, 34)
|
2026-04-10 01:59:27 +08:00
|
|
|
$btnCopy.Text = "复制"
|
2026-04-10 03:20:39 +08:00
|
|
|
$btnCopy.Font = New-Object System.Drawing.Font("Segoe UI", 9)
|
2026-04-10 01:59:27 +08:00
|
|
|
$btnCopy.FlatStyle = "Flat"
|
2026-04-10 03:20:39 +08:00
|
|
|
$btnCopy.BackColor = $brandBlue
|
|
|
|
|
$btnCopy.ForeColor = [System.Drawing.Color]::White
|
|
|
|
|
$btnCopy.FlatAppearance.BorderSize = 0
|
|
|
|
|
$btnCopy.Cursor = [System.Windows.Forms.Cursors]::Hand
|
2026-04-10 01:59:27 +08:00
|
|
|
$btnCopy.Enabled = $false
|
2026-04-10 03:20:39 +08:00
|
|
|
$resultCard.Controls.Add($btnCopy)
|
2026-04-10 01:59:27 +08:00
|
|
|
|
|
|
|
|
$lblDetails = New-Object System.Windows.Forms.Label
|
2026-04-10 03:20:39 +08:00
|
|
|
$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)
|
|
|
|
|
|
|
|
|
|
# ═══ 状态栏 ═══════════════════════════════════════════
|
2026-04-10 01:59:27 +08:00
|
|
|
$statusBar = New-Object System.Windows.Forms.Label
|
2026-04-10 03:20:39 +08:00
|
|
|
$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"
|
2026-04-10 01:59:27 +08:00
|
|
|
$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
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
|
2026-04-10 03:20:39 +08:00
|
|
|
$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")
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
|
2026-04-10 01:59:27 +08:00
|
|
|
$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 {
|
2026-04-10 03:20:39 +08:00
|
|
|
# 用 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()
|
2026-04-10 01:59:27 +08:00
|
|
|
|
|
|
|
|
if ($proc.ExitCode -ne 0) {
|
|
|
|
|
throw "gen-authcode.js 退出码 $($proc.ExitCode)`n$stderr"
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-10 03:20:39 +08:00
|
|
|
# 解析输出 (编码无关: 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 { "" }
|
2026-04-10 01:59:27 +08:00
|
|
|
|
|
|
|
|
if ($authCode) {
|
|
|
|
|
$txtAuthCode.Text = $authCode
|
|
|
|
|
$lblDetails.Text = "用户: $user`n加密文件: $fileId`n有效期: $days 天 (至 $expiry)`n路径: $ScriptDir\$fileId"
|
|
|
|
|
$btnCopy.Enabled = $true
|
2026-04-10 03:20:39 +08:00
|
|
|
$btnPush.Enabled = $true
|
|
|
|
|
$global:lastGenUser = $user
|
|
|
|
|
$global:lastGenFile = $fileId
|
|
|
|
|
$statusBar.Text = " 生成成功 — 点击「推送到 Gitea」完成部署"
|
2026-04-10 01:59:27 +08:00
|
|
|
$statusBar.ForeColor = $successGreen
|
2026-04-10 03:20:39 +08:00
|
|
|
|
|
|
|
|
# 追加历史记录 (仅本机, 已在 .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 {}
|
2026-04-10 01:59:27 +08:00
|
|
|
} 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)
|
2026-04-10 03:20:39 +08:00
|
|
|
|
|
|
|
|
} 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
|
|
|
|
|
}
|
|
|
|
|
}
|