feat: add one-click install scripts for Linux and Windows
- Support multiple install sources: npm, GitHub Release (official/fork) - Auto-detect installed version and support upgrade across sources - Auto-match system architecture when downloading from GitHub Release - Create systemd services (Linux) and scheduled tasks (Windows) - Update README with quick-start one-liner commands via [HAPI](https://hapi.run) Co-Authored-By: HAPI <[email protected]>
This commit is contained in:
@@ -8,6 +8,7 @@
|
||||
|
||||
## 目录
|
||||
|
||||
- [快速开始(一键安装)](#快速开始一键安装)
|
||||
- [服务端部署](#服务端部署)
|
||||
- [客户端配置](#客户端配置)
|
||||
- [安装 CLI](#安装-cli)
|
||||
@@ -20,6 +21,28 @@
|
||||
|
||||
---
|
||||
|
||||
## 快速开始(一键安装)
|
||||
|
||||
一键完成全部环境搭建:Node.js v22 + HAPI CLI(多来源可选)+ Hub Docker + 开机自启。
|
||||
|
||||
### Linux
|
||||
|
||||
```bash
|
||||
curl -fsSL https://raw.githubusercontent.com/arkylin/Hapi-Docker/main/scripts/install.sh | sudo bash
|
||||
```
|
||||
|
||||
### Windows
|
||||
|
||||
以管理员身份打开 PowerShell:
|
||||
|
||||
```powershell
|
||||
irm https://raw.githubusercontent.com/arkylin/Hapi-Docker/main/scripts/install.ps1 | iex
|
||||
```
|
||||
|
||||
脚本支持**多来源安装**(npm / 官方 GitHub Release / 本仓库 GitHub Release)、**版本检测**、**自动升级**。详见 [`scripts/README.md`](scripts/README.md)。
|
||||
|
||||
---
|
||||
|
||||
## 服务端部署
|
||||
|
||||
在服务器上执行以下步骤。
|
||||
|
||||
@@ -0,0 +1,222 @@
|
||||
# Scripts
|
||||
|
||||
HAPI 一键安装脚本,自动完成 Node.js v22、HAPI CLI(多来源可选)、Hub Docker 部署以及 Hub & Runner 开机自启的全套配置。
|
||||
|
||||
**核心特性:**
|
||||
- **多来源安装**:npm registry / 官方 GitHub Release / 本仓库 GitHub Release
|
||||
- **版本检测**:自动检测已安装版本,智能比较远程最新版本
|
||||
- **多途径升级**:支持跨来源切换升级,重复运行脚本即可更新
|
||||
- **架构自适应**:自动检测系统平台与架构,匹配对应二进制
|
||||
|
||||
---
|
||||
|
||||
## 脚本列表
|
||||
|
||||
| 脚本 | 平台 | 说明 |
|
||||
|------|------|------|
|
||||
| `install.sh` | Linux (systemd) | Bash 脚本,支持 Debian/Ubuntu、RHEL/CentOS、Alpine 等 |
|
||||
| `install.ps1` | Windows | PowerShell 脚本,需要管理员权限 |
|
||||
|
||||
---
|
||||
|
||||
## 安装来源对比
|
||||
|
||||
| 来源 | 说明 | 需要 Node.js | 适用场景 |
|
||||
|------|------|-------------|----------|
|
||||
| **npm** | `npm install -g @twsxtd/hapi` | ✅ | 官方推荐,自动处理依赖 |
|
||||
| **github-official** | 从 [tiann/hapi](https://github.com/tiann/hapi/releases) 下载二进制 | ❌ | 独立可执行文件,无需 Node.js 运行时 |
|
||||
| **github-fork** | 从 [arkylin/Hapi-Docker](https://github.com/arkylin/Hapi-Docker/releases) 下载二进制 | ❌ | 本仓库预构建版本 |
|
||||
|
||||
---
|
||||
|
||||
## 使用方法
|
||||
|
||||
### Linux
|
||||
|
||||
```bash
|
||||
# 交互式安装(默认提示选择来源)
|
||||
sudo bash install.sh
|
||||
|
||||
# 指定安装来源
|
||||
sudo bash install.sh --source npm # npm registry
|
||||
sudo bash install.sh --source github-official # 官方 GitHub Release
|
||||
sudo bash install.sh --source github-fork # 本仓库 GitHub Release
|
||||
|
||||
# 仅升级(已是最新则跳过)
|
||||
sudo bash install.sh --upgrade
|
||||
|
||||
# 强制重新安装(即使版本相同)
|
||||
sudo bash install.sh --force
|
||||
|
||||
# 仅安装/升级 CLI,跳过 Hub Docker 配置
|
||||
sudo bash install.sh --skip-hub
|
||||
|
||||
# 显示帮助
|
||||
sudo bash install.sh --help
|
||||
```
|
||||
|
||||
### Windows
|
||||
|
||||
以**管理员身份**打开 PowerShell:
|
||||
|
||||
```powershell
|
||||
# 交互式安装
|
||||
.\install.ps1
|
||||
|
||||
# 指定安装来源
|
||||
.\install.ps1 -Source npm
|
||||
.\install.ps1 -Source github-official
|
||||
.\install.ps1 -Source github-fork
|
||||
|
||||
# 仅升级
|
||||
.\install.ps1 -Upgrade
|
||||
|
||||
# 强制重新安装
|
||||
.\install.ps1 -Force
|
||||
|
||||
# 仅安装/升级 CLI
|
||||
.\install.ps1 -SkipHub
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 脚本流程
|
||||
|
||||
```
|
||||
1. 前置检查 → root/管理员权限、系统架构、必要工具
|
||||
2. 选择安装来源 → 交互式选择或 --source 参数指定
|
||||
3. 获取远程版本 → npm view / GitHub API 查询最新版本
|
||||
4. 检测已安装 → 读取本地版本、来源、安装路径
|
||||
5. 版本比较 → 远程 vs 本地,决定升级/跳过/切换来源
|
||||
6. 安装/升级 CLI → 按来源下载/安装,记录版本和来源
|
||||
7. 安装 Node.js → npm 来源时自动安装 v22(GitHub 来源跳过)
|
||||
8. 检查 Docker → 未安装则自动安装(Linux)/ 提示安装(Windows)
|
||||
9. 配置 Hub → docker-compose.yml + .env,启动容器
|
||||
10. 开机自启 → systemd 服务(Linux)/ 计划任务(Windows)
|
||||
11. 返回结果 → 地址、Token、状态、常用命令
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 版本管理
|
||||
|
||||
脚本会在安装目录下记录元数据:
|
||||
|
||||
| 文件 | 说明 |
|
||||
|------|------|
|
||||
| `.hapi-version` | 已安装的 HAPI CLI 版本号 |
|
||||
| `.hapi-source` | 安装来源(`npm` / `github:tiann/hapi` / `github:arkylin/Hapi-Docker`)|
|
||||
|
||||
重复运行脚本时会自动读取这些信息,判断是否需要升级。
|
||||
|
||||
### 升级场景
|
||||
|
||||
```bash
|
||||
# 场景 1: 保持当前来源升级
|
||||
sudo bash install.sh --upgrade
|
||||
|
||||
# 场景 2: 切换到 GitHub Release 安装
|
||||
sudo bash install.sh --source github-official
|
||||
|
||||
# 场景 3: 已是最新,强制重新安装
|
||||
sudo bash install.sh --force
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 安装后管理
|
||||
|
||||
### Linux (systemd)
|
||||
|
||||
```bash
|
||||
# 查看状态
|
||||
systemctl status hapi-hub
|
||||
systemctl status hapi-runner
|
||||
|
||||
# 启停控制
|
||||
systemctl start|stop|restart hapi-hub
|
||||
systemctl start|stop|restart hapi-runner
|
||||
|
||||
# 禁用开机自启
|
||||
systemctl disable hapi-hub
|
||||
systemctl disable hapi-runner
|
||||
|
||||
# 查看日志
|
||||
journalctl -u hapi-hub -f
|
||||
journalctl -u hapi-runner -f
|
||||
docker compose -f /opt/hapi/docker-compose.yml logs -f
|
||||
```
|
||||
|
||||
### Windows (计划任务)
|
||||
|
||||
```powershell
|
||||
# 查看状态
|
||||
Get-ScheduledTask -TaskName "HAPI Hub"
|
||||
Get-ScheduledTask -TaskName "HAPI Runner"
|
||||
|
||||
# 启停控制
|
||||
Start-ScheduledTask -TaskName "HAPI Hub"
|
||||
Stop-ScheduledTask -TaskName "HAPI Hub"
|
||||
|
||||
# 删除计划任务
|
||||
Unregister-ScheduledTask -TaskName "HAPI Hub" -Confirm:$false
|
||||
Unregister-ScheduledTask -TaskName "HAPI Runner" -Confirm:$false
|
||||
|
||||
# 查看日志
|
||||
docker compose -f C:\hapi\docker-compose.yml logs -f
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 安装目录
|
||||
|
||||
| 平台 | 路径 | 说明 |
|
||||
|------|------|------|
|
||||
| Linux | `/opt/hapi/` | Hub 配置、docker-compose、数据卷 |
|
||||
| Linux | `/opt/hapi/bin/` | GitHub Release 方式安装的 HAPI CLI |
|
||||
| Linux | `/opt/hapi/hapi-data/` | SQLite 数据库、Hub 持久化数据 |
|
||||
| Linux | `/etc/profile.d/hapi.sh` | PATH 环境变量配置 |
|
||||
| Linux | `/tmp/hapi-install.log` | 安装日志 |
|
||||
| Windows | `C:\hapi\` | Hub 配置、docker-compose、数据卷 |
|
||||
| Windows | `C:\hapi\bin\` | GitHub Release 方式安装的 HAPI CLI |
|
||||
| Windows | `C:\hapi\hapi-data\` | SQLite 数据库、Hub 持久化数据 |
|
||||
| Windows | `%TEMP%\hapi-install.log` | 安装日志 |
|
||||
|
||||
---
|
||||
|
||||
## 环境变量说明
|
||||
|
||||
脚本自动生成的 `.env` 文件:
|
||||
|
||||
| 变量 | 必填 | 默认值 | 说明 |
|
||||
|------|------|--------|------|
|
||||
| `CLI_API_TOKEN` | 是 | 自动生成 | Hub 认证令牌 |
|
||||
| `HAPI_PUBLIC_URL` | 否 | 自动检测 | Hub 公网地址 |
|
||||
| `CORS_ORIGINS` | 否 | `*` | CORS 允许的域名 |
|
||||
| `TZ` | 否 | `Asia/Shanghai` | 时区 |
|
||||
|
||||
---
|
||||
|
||||
## 常见问题
|
||||
|
||||
### GitHub API rate limit
|
||||
|
||||
未认证的 GitHub API 请求有每小时 60 次的限制。如遇此错误:
|
||||
|
||||
```bash
|
||||
# Linux: 设置 GITHUB_TOKEN 后重试
|
||||
export GITHUB_TOKEN=your_token_here
|
||||
sudo -E bash install.sh --source github-official
|
||||
```
|
||||
|
||||
### npm 全局路径不在 PATH
|
||||
|
||||
脚本会自动检测并添加。如仍失败,手动添加:
|
||||
|
||||
```bash
|
||||
# Linux
|
||||
export PATH="$(npm config get prefix)/bin:$PATH"
|
||||
|
||||
# Windows (PowerShell)
|
||||
$env:Path += ";$(npm config get prefix)"
|
||||
```
|
||||
@@ -0,0 +1,806 @@
|
||||
#Requires -RunAsAdministrator
|
||||
<#
|
||||
.SYNOPSIS
|
||||
HAPI 一键安装脚本 (Windows)
|
||||
多来源安装 | 版本检测 | 自动升级 | 开机自启
|
||||
|
||||
.DESCRIPTION
|
||||
自动完成以下步骤:
|
||||
1. 检测已安装的 HAPI CLI(版本、来源)
|
||||
2. 选择安装来源(npm / GitHub Release 官方 / GitHub Release 本仓库)
|
||||
3. 检测/安装 Node.js v22(npm 来源时)
|
||||
4. 安装/升级 HAPI CLI(支持多途径)
|
||||
5. 检查 Docker
|
||||
6. 配置并启动 HAPI Hub (Docker)
|
||||
7. 创建 Hub & Runner 开机自启计划任务
|
||||
|
||||
.PARAMETER Source
|
||||
指定 HAPI CLI 安装来源:npm, github-official, github-fork
|
||||
|
||||
.PARAMETER Force
|
||||
强制重新安装(即使版本相同)
|
||||
|
||||
.PARAMETER Upgrade
|
||||
仅升级模式(已是最新则退出)
|
||||
|
||||
.PARAMETER SkipHub
|
||||
跳过 Hub Docker 配置(仅安装/升级 CLI)
|
||||
|
||||
.EXAMPLE
|
||||
.\install.ps1
|
||||
.\install.ps1 -Source npm
|
||||
.\install.ps1 -Source github-official
|
||||
.\install.ps1 -Force
|
||||
.\install.ps1 -Upgrade
|
||||
.\install.ps1 -SkipHub
|
||||
#>
|
||||
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[ValidateSet("npm", "github-official", "github-fork", "")]
|
||||
[string]$Source = "",
|
||||
|
||||
[switch]$Force,
|
||||
[switch]$Upgrade,
|
||||
[switch]$SkipHub
|
||||
)
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
|
||||
# ─────────────────────────────────────────
|
||||
# 配置常量
|
||||
# ─────────────────────────────────────────
|
||||
$INSTALL_DIR = "C:\hapi"
|
||||
$HAPI_BIN_DIR = "$INSTALL_DIR\bin"
|
||||
$HAPI_DATA = "$INSTALL_DIR\hapi-data"
|
||||
$LOG_FILE = "$env:TEMP\hapi-install.log"
|
||||
$VERSION_FILE = "$INSTALL_DIR\.hapi-version"
|
||||
$SOURCE_FILE = "$INSTALL_DIR\.hapi-source"
|
||||
$NODE_MIN_MAJOR = 22
|
||||
|
||||
$NPM_PACKAGE = "@twsxtd/hapi"
|
||||
$GITHUB_OFFICIAL_REPO = "tiann/hapi"
|
||||
$GITHUB_FORK_REPO = "arkylin/Hapi-Docker"
|
||||
|
||||
# ─────────────────────────────────────────
|
||||
# 输出函数
|
||||
# ─────────────────────────────────────────
|
||||
function Write-Step { param($msg) Write-Host "[STEP] $msg" -ForegroundColor Blue; "[STEP] $msg" | Out-File $LOG_FILE -Append }
|
||||
function Write-Info { param($msg) Write-Host "[INFO] $msg" -ForegroundColor Cyan; "[INFO] $msg" | Out-File $LOG_FILE -Append }
|
||||
function Write-Ok { param($msg) Write-Host "[OK] $msg" -ForegroundColor Green; "[OK] $msg" | Out-File $LOG_FILE -Append }
|
||||
function Write-Warn { param($msg) Write-Host "[WARN] $msg" -ForegroundColor Yellow; "[WARN] $msg" | Out-File $LOG_FILE -Append }
|
||||
function Write-Err { param($msg) Write-Host "[ERR] $msg" -ForegroundColor Red; "[ERR] $msg" | Out-File $LOG_FILE -Append }
|
||||
|
||||
# ─────────────────────────────────────────
|
||||
# 刷新环境变量 PATH
|
||||
# ─────────────────────────────────────────
|
||||
function Update-EnvPath {
|
||||
$env:Path = [System.Environment]::GetEnvironmentVariable("Path", "Machine") + ";" +
|
||||
[System.Environment]::GetEnvironmentVariable("Path", "User")
|
||||
}
|
||||
|
||||
# 持久化添加 PATH
|
||||
function Add-ToPath {
|
||||
param([string]$dir)
|
||||
$current = [System.Environment]::GetEnvironmentVariable("Path", "Machine")
|
||||
if ($current -notlike "*$dir*") {
|
||||
[System.Environment]::SetEnvironmentVariable("Path", "$current;$dir", "Machine")
|
||||
$env:Path = "$env:Path;$dir"
|
||||
Write-Ok "已添加 $dir 到系统 PATH"
|
||||
}
|
||||
}
|
||||
|
||||
# ─────────────────────────────────────────
|
||||
# 平台/架构检测
|
||||
# ─────────────────────────────────────────
|
||||
$DETECTED_PLATFORM = "windows"
|
||||
$DETECTED_ARCH = switch ([System.Runtime.InteropServices.RuntimeInformation]::ProcessArchitecture) {
|
||||
X64 { "x64" }
|
||||
Arm64 { "arm64" }
|
||||
X86 { "x86" }
|
||||
Arm { "armv7l" }
|
||||
default { "x64" }
|
||||
}
|
||||
|
||||
# ─────────────────────────────────────────
|
||||
# 版本号比较
|
||||
# 返回: 0(相等), 1(v1>v2), 2(v1<v2)
|
||||
# ─────────────────────────────────────────
|
||||
function Compare-Versions {
|
||||
param([string]$v1, [string]$v2)
|
||||
$a = ($v1 -replace '^v','') -split '\.' | ForEach-Object { ($_ -replace '-.*','') -replace '[^0-9]','' }
|
||||
$b = ($v2 -replace '^v','') -split '\.' | ForEach-Object { ($_ -replace '-.*','') -replace '[^0-9]','' }
|
||||
for ($i = 0; $i -lt [Math]::Max($a.Count, $b.Count); $i++) {
|
||||
$n1 = if ($i -lt $a.Count) { [int]$a[$i] } else { 0 }
|
||||
$n2 = if ($i -lt $b.Count) { [int]$b[$i] } else { 0 }
|
||||
if ($n1 -gt $n2) { return 1 }
|
||||
if ($n1 -lt $n2) { return 2 }
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
# ─────────────────────────────────────────
|
||||
# 检测已安装的 HAPI CLI
|
||||
# ─────────────────────────────────────────
|
||||
$INSTALLED_VERSION = $null
|
||||
$INSTALLED_SOURCE = $null
|
||||
|
||||
function Detect-Installed {
|
||||
$hapiCmd = Get-Command "hapi" -ErrorAction SilentlyContinue
|
||||
if ($hapiCmd) {
|
||||
$script:INSTALLED_VERSION = ((& hapi --version 2>$null) | Select-String -Pattern '\d+\.\d+\.\d+' | ForEach-Object { $_.Matches.Value } | Select-Object -First 1)
|
||||
if (-not $script:INSTALLED_VERSION) { $script:INSTALLED_VERSION = "unknown" }
|
||||
|
||||
$path = $hapiCmd.Source
|
||||
if ($path -like "$script:HAPI_BIN_DIR*") {
|
||||
if (Test-Path $script:SOURCE_FILE) {
|
||||
$script:INSTALLED_SOURCE = (Get-Content $script:SOURCE_FILE -Raw).Trim()
|
||||
} else {
|
||||
$script:INSTALLED_SOURCE = "github-unknown"
|
||||
}
|
||||
} else {
|
||||
$npmPrefix = (& npm config get prefix 2>$null).Trim()
|
||||
if ($path -like "$npmPrefix*") {
|
||||
$script:INSTALLED_SOURCE = "npm"
|
||||
} else {
|
||||
$script:INSTALLED_SOURCE = "unknown"
|
||||
}
|
||||
}
|
||||
|
||||
if ($script:INSTALLED_VERSION -eq "unknown" -and (Test-Path $script:VERSION_FILE)) {
|
||||
$script:INSTALLED_VERSION = (Get-Content $script:VERSION_FILE -Raw).Trim()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# ─────────────────────────────────────────
|
||||
# 获取远程最新版本
|
||||
# ─────────────────────────────────────────
|
||||
function Get-RemoteNpmVersion {
|
||||
try {
|
||||
$ver = (& npm view $script:NPM_PACKAGE version 2>$null).Trim()
|
||||
if ($ver) {
|
||||
Write-Ok "npm registry 最新版本: v$ver"
|
||||
return $ver
|
||||
}
|
||||
} catch {}
|
||||
Write-Warn "无法从 npm registry 获取版本"
|
||||
return $null
|
||||
}
|
||||
|
||||
function Get-RemoteGithubVersion {
|
||||
param([string]$repo)
|
||||
$apiUrl = "https://api.github.com/repos/$repo/releases/latest"
|
||||
try {
|
||||
$response = Invoke-RestMethod -Uri $apiUrl -TimeoutSec 10 -ErrorAction Stop
|
||||
$ver = $response.tag_name -replace '^v',''
|
||||
Write-Ok "GitHub Release 最新版本: v$ver"
|
||||
return $ver
|
||||
} catch {
|
||||
if ($_.Exception.Response.StatusCode -eq 403) {
|
||||
Write-Warn "GitHub API rate limit exceeded,请设置 GITHUB_TOKEN 或稍后重试"
|
||||
} else {
|
||||
Write-Warn "无法连接 GitHub API: $_"
|
||||
}
|
||||
return $null
|
||||
}
|
||||
}
|
||||
|
||||
# ─────────────────────────────────────────
|
||||
# GitHub Release Asset 匹配
|
||||
# ─────────────────────────────────────────
|
||||
function Match-GithubAsset {
|
||||
param([string]$repo)
|
||||
$apiUrl = "https://api.github.com/repos/$repo/releases/latest"
|
||||
try {
|
||||
$release = Invoke-RestMethod -Uri $apiUrl -TimeoutSec 10 -ErrorAction Stop
|
||||
} catch {
|
||||
Write-Err "无法获取 GitHub Release 信息: $_"
|
||||
return $null
|
||||
}
|
||||
|
||||
$platformKw = switch ($script:DETECTED_PLATFORM) {
|
||||
"windows" { @("windows", "win") }
|
||||
default { @("windows", "win") }
|
||||
}
|
||||
$archKw = switch ($script:DETECTED_ARCH) {
|
||||
"x64" { @("x64", "x86_64", "amd64") }
|
||||
"arm64" { @("arm64", "aarch64") }
|
||||
"x86" { @("x86", "i386", "i686", "ia32") }
|
||||
"armv7l" { @("armv7l", "armv7", "arm") }
|
||||
default { @("x64", "x86_64", "amd64") }
|
||||
}
|
||||
$excludeKw = @("source", "src", "sources", "sha256", "checksum", "asc", "sig")
|
||||
|
||||
$bestScore = -1
|
||||
$bestAsset = $null
|
||||
|
||||
foreach ($asset in $release.assets) {
|
||||
$name = $asset.name.ToLower()
|
||||
|
||||
# 排除源码包
|
||||
$isExcluded = $false
|
||||
foreach ($k in $excludeKw) { if ($name -like "*$k*") { $isExcluded = $true; break } }
|
||||
if ($isExcluded) { continue }
|
||||
|
||||
$score = 0
|
||||
|
||||
# 平台匹配
|
||||
foreach ($k in $platformKw) { if ($name -like "*$k*") { $score += 10; break } }
|
||||
|
||||
# 架构匹配
|
||||
foreach ($k in $archKw) { if ($name -like "*$k*") { $score += 10; break } }
|
||||
|
||||
# 不是压缩包加分
|
||||
if (-not ($name -match '\.(tar\.gz|tgz|zip|7z)$')) { $score += 5 }
|
||||
|
||||
# musl 减分
|
||||
if ($name -like "*musl*") { $score -= 3 }
|
||||
|
||||
if ($score -gt $bestScore) {
|
||||
$bestScore = $score
|
||||
$bestAsset = $asset
|
||||
}
|
||||
}
|
||||
|
||||
if (-not $bestAsset) {
|
||||
Write-Err "未找到适合 $($script:DETECTED_PLATFORM)-$($script:DETECTED_ARCH) 的 GitHub Release asset"
|
||||
Write-Info "可用 assets:"
|
||||
$release.assets | ForEach-Object { Write-Info " - $($_.name)" }
|
||||
return $null
|
||||
}
|
||||
|
||||
Write-Ok "匹配到 asset: $($bestAsset.name)"
|
||||
return $bestAsset
|
||||
}
|
||||
|
||||
# ─────────────────────────────────────────
|
||||
# 从 GitHub Release 下载并安装
|
||||
# ─────────────────────────────────────────
|
||||
function Install-FromGithub {
|
||||
param([string]$repo)
|
||||
Write-Info "从 GitHub Release ($repo) 下载 HAPI CLI..."
|
||||
|
||||
$asset = Match-GithubAsset -repo $repo
|
||||
if (-not $asset) { return $false }
|
||||
|
||||
New-Item -ItemType Directory -Force -Path $script:HAPI_BIN_DIR | Out-Null
|
||||
|
||||
$downloadPath = Join-Path $env:TEMP $asset.name
|
||||
Write-Info "下载: $($asset.browser_download_url)"
|
||||
|
||||
try {
|
||||
Invoke-WebRequest -Uri $asset.browser_download_url -OutFile $downloadPath -UseBasicParsing -TimeoutSec 60
|
||||
} catch {
|
||||
Write-Err "下载失败: $_"
|
||||
return $false
|
||||
}
|
||||
Write-Ok "下载完成: $($asset.name)"
|
||||
|
||||
$binPath = Join-Path $script:HAPI_BIN_DIR "hapi.exe"
|
||||
|
||||
# 解压(如果需要)
|
||||
if ($asset.name -match '\.(tar\.gz|tgz)$') {
|
||||
Write-Info "解压 tar.gz..."
|
||||
# PowerShell 7+ 支持 tar
|
||||
if ($PSVersionTable.PSVersion.Major -ge 7) {
|
||||
tar -xzf $downloadPath -C $script:HAPI_BIN_DIR
|
||||
} else {
|
||||
# 使用 7zip 或 expand
|
||||
$tarPath = "$env:TEMP\hapi-temp"
|
||||
New-Item -ItemType Directory -Force -Path $tarPath | Out-Null
|
||||
# 尝试用 tar(Windows 10 1803+ 内置)
|
||||
& tar -xzf $downloadPath -C $tarPath 2>$null
|
||||
$extracted = Get-ChildItem -Path $tarPath -Filter "hapi.exe" -Recurse -ErrorAction SilentlyContinue | Select-Object -First 1
|
||||
if ($extracted) {
|
||||
Move-Item $extracted.FullName $binPath -Force
|
||||
}
|
||||
Remove-Item $tarPath -Recurse -Force -ErrorAction SilentlyContinue
|
||||
}
|
||||
Remove-Item $downloadPath -ErrorAction SilentlyContinue
|
||||
} elseif ($asset.name -match '\.zip$') {
|
||||
Write-Info "解压 zip..."
|
||||
$zipPath = "$env:TEMP\hapi-zip"
|
||||
New-Item -ItemType Directory -Force -Path $zipPath | Out-Null
|
||||
Expand-Archive -Path $downloadPath -DestinationPath $zipPath -Force
|
||||
$extracted = Get-ChildItem -Path $zipPath -Filter "hapi.exe" -Recurse -ErrorAction SilentlyContinue | Select-Object -First 1
|
||||
if ($extracted) {
|
||||
Move-Item $extracted.FullName $binPath -Force
|
||||
}
|
||||
Remove-Item $zipPath -Recurse -Force -ErrorAction SilentlyContinue
|
||||
Remove-Item $downloadPath -ErrorAction SilentlyContinue
|
||||
} else {
|
||||
# 直接可执行文件
|
||||
Move-Item $downloadPath $binPath -Force
|
||||
}
|
||||
|
||||
if (-not (Test-Path $binPath)) {
|
||||
Write-Err "安装后未找到 hapi.exe"
|
||||
return $false
|
||||
}
|
||||
|
||||
# 添加到 PATH
|
||||
Add-ToPath -dir $script:HAPI_BIN_DIR
|
||||
Update-EnvPath
|
||||
|
||||
# 验证
|
||||
$installedVer = ((& $binPath --version 2>$null) | Select-String -Pattern '\d+\.\d+\.\d+' | ForEach-Object { $_.Matches.Value } | Select-Object -First 1)
|
||||
if (-not $installedVer) {
|
||||
Write-Warn "无法验证版本,使用远程版本号"
|
||||
$installedVer = $script:REMOTE_VERSION
|
||||
}
|
||||
|
||||
# 记录版本和来源
|
||||
$installedVer | Set-Content $script:VERSION_FILE -Encoding UTF8
|
||||
"github:$repo" | Set-Content $script:SOURCE_FILE -Encoding UTF8
|
||||
|
||||
Write-Ok "HAPI CLI v$installedVer 安装成功 (来源: github:$repo)"
|
||||
return $true
|
||||
}
|
||||
|
||||
# ─────────────────────────────────────────
|
||||
# 从 npm 安装
|
||||
# ─────────────────────────────────────────
|
||||
function Install-FromNpm {
|
||||
Write-Info "从 npm registry 安装 HAPI CLI..."
|
||||
|
||||
npm install -g $script:NPM_PACKAGE --registry=https://registry.npmjs.org | Out-File $LOG_FILE -Append
|
||||
|
||||
Update-EnvPath
|
||||
$hapi = Get-Command "hapi" -ErrorAction SilentlyContinue
|
||||
if (-not $hapi) {
|
||||
$npmPrefix = (& npm config get prefix).Trim()
|
||||
$npmBin = Join-Path $npmPrefix ""
|
||||
[System.Environment]::SetEnvironmentVariable("Path", "$env:Path;$npmBin", "User")
|
||||
Update-EnvPath
|
||||
$hapi = Get-Command "hapi" -ErrorAction SilentlyContinue
|
||||
}
|
||||
|
||||
if (-not $hapi) {
|
||||
Write-Err "HAPI CLI 安装失败"
|
||||
return $false
|
||||
}
|
||||
|
||||
$installedVer = ((& hapi --version 2>$null) | Select-String -Pattern '\d+\.\d+\.\d+' | ForEach-Object { $_.Matches.Value } | Select-Object -First 1)
|
||||
if (-not $installedVer) { $installedVer = "unknown" }
|
||||
|
||||
$installedVer | Set-Content $script:VERSION_FILE -Encoding UTF8
|
||||
"npm" | Set-Content $script:SOURCE_FILE -Encoding UTF8
|
||||
|
||||
Write-Ok "HAPI CLI v$installedVer 安装成功 (来源: npm, 路径: $($hapi.Source))"
|
||||
return $true
|
||||
}
|
||||
|
||||
# ─────────────────────────────────────────
|
||||
# 选择安装来源
|
||||
# ─────────────────────────────────────────
|
||||
$SELECTED_SOURCE = $null
|
||||
$REMOTE_VERSION = $null
|
||||
|
||||
function Select-InstallSource {
|
||||
# 如果参数指定了来源
|
||||
if ($Source) {
|
||||
$script:SELECTED_SOURCE = switch ($Source) {
|
||||
"npm" { "npm" }
|
||||
"github-official" { "github-official" }
|
||||
"github-fork" { "github-fork" }
|
||||
default { "npm" }
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
# 如果非交互式,默认 npm
|
||||
if (-not [System.Console]::IsInputRedirected -and -not [Environment]::UserInteractive) {
|
||||
Write-Info "非交互式环境,默认使用 npm 来源"
|
||||
$script:SELECTED_SOURCE = "npm"
|
||||
return
|
||||
}
|
||||
|
||||
Write-Host ""
|
||||
Write-Host "请选择 HAPI CLI 安装来源:" -ForegroundColor White
|
||||
Write-Host ""
|
||||
Write-Host " 1) npm registry (官方推荐)"
|
||||
Write-Host " $NPM_PACKAGE"
|
||||
Write-Host ""
|
||||
Write-Host " 2) GitHub Release (官方仓库)"
|
||||
Write-Host " https://github.com/$GITHUB_OFFICIAL_REPO/releases"
|
||||
Write-Host ""
|
||||
Write-Host " 3) GitHub Release (本仓库)"
|
||||
Write-Host " https://github.com/$GITHUB_FORK_REPO/releases"
|
||||
Write-Host ""
|
||||
|
||||
do {
|
||||
$choice = Read-Host "请输入选项 [1-3]"
|
||||
switch ($choice) {
|
||||
"1" { $script:SELECTED_SOURCE = "npm"; break }
|
||||
"2" { $script:SELECTED_SOURCE = "github-official"; break }
|
||||
"3" { $script:SELECTED_SOURCE = "github-fork"; break }
|
||||
default { Write-Warn "无效选项,请重新输入" }
|
||||
}
|
||||
} while (-not $script:SELECTED_SOURCE)
|
||||
}
|
||||
|
||||
# ─────────────────────────────────────────
|
||||
# 处理安装/升级逻辑
|
||||
# ─────────────────────────────────────────
|
||||
function Handle-HapiInstall {
|
||||
Write-Step "处理 HAPI CLI 安装/升级"
|
||||
|
||||
# 1. 选择来源
|
||||
Select-InstallSource
|
||||
Write-Info "安装来源: $SELECTED_SOURCE"
|
||||
|
||||
# 2. 获取远程版本
|
||||
$remoteVer = switch ($SELECTED_SOURCE) {
|
||||
"npm" { Get-RemoteNpmVersion }
|
||||
"github-official" { Get-RemoteGithubVersion -repo $GITHUB_OFFICIAL_REPO }
|
||||
"github-fork" { Get-RemoteGithubVersion -repo $GITHUB_FORK_REPO }
|
||||
}
|
||||
|
||||
if (-not $remoteVer) {
|
||||
Write-Err "无法获取远程版本,安装中止"
|
||||
return $false
|
||||
}
|
||||
$script:REMOTE_VERSION = $remoteVer
|
||||
|
||||
# 3. 检查已安装状态
|
||||
Detect-Installed
|
||||
|
||||
if ($INSTALLED_VERSION -and $INSTALLED_VERSION -ne "unknown") {
|
||||
Write-Host ""
|
||||
Write-Info "检测到已安装 HAPI CLI:"
|
||||
Write-Host " 版本: $INSTALLED_VERSION" -ForegroundColor White
|
||||
Write-Host " 来源: $INSTALLED_SOURCE" -ForegroundColor White
|
||||
Write-Host " 路径: $((Get-Command hapi -ErrorAction SilentlyContinue).Source || 'unknown')" -ForegroundColor White
|
||||
Write-Host " 远程: v$remoteVer" -ForegroundColor White
|
||||
Write-Host ""
|
||||
|
||||
$cmp = Compare-Versions -v1 $INSTALLED_VERSION -v2 $remoteVer
|
||||
|
||||
if ($cmp -eq 0) {
|
||||
Write-Ok "当前已是最新版本 (v$INSTALLED_VERSION)"
|
||||
if ($Force) {
|
||||
Write-Info "-Force 已设置,强制重新安装"
|
||||
} elseif ($Upgrade) {
|
||||
Write-Info "-Upgrade 模式,已是最新,跳过"
|
||||
return $true
|
||||
} else {
|
||||
if ($INSTALLED_SOURCE -ne $SELECTED_SOURCE) {
|
||||
$confirm = Read-Host "是否切换到 $SELECTED_SOURCE 来源重新安装? [y/N]"
|
||||
if ($confirm -notmatch '^[Yy]$') {
|
||||
Write-Info "跳过 HAPI CLI 安装"
|
||||
return $true
|
||||
}
|
||||
} else {
|
||||
return $true
|
||||
}
|
||||
}
|
||||
} elseif ($cmp -eq 1) {
|
||||
Write-Warn "本地版本 (v$INSTALLED_VERSION) 高于远程 (v$remoteVer)"
|
||||
if (-not $Force -and $Upgrade) { return $true }
|
||||
if (-not $Force) {
|
||||
$confirm = Read-Host "是否降级到远程版本 v$remoteVer? [y/N]"
|
||||
if ($confirm -notmatch '^[Yy]$') { Write-Info "跳过"; return $true }
|
||||
}
|
||||
} else {
|
||||
Write-Info "发现新版本: v$remoteVer (当前: v$INSTALLED_VERSION)"
|
||||
if ($Upgrade) { Write-Info "执行升级..." }
|
||||
}
|
||||
} else {
|
||||
Write-Info "未检测到已安装的 HAPI CLI,开始安装 v$remoteVer"
|
||||
}
|
||||
|
||||
# 4. 执行安装
|
||||
switch ($SELECTED_SOURCE) {
|
||||
"npm" { Install-FromNpm }
|
||||
"github-official" { Install-FromGithub -repo $GITHUB_OFFICIAL_REPO }
|
||||
"github-fork" { Install-FromGithub -repo $GITHUB_FORK_REPO }
|
||||
}
|
||||
}
|
||||
|
||||
# ─────────────────────────────────────────
|
||||
# 安装 Node.js v22
|
||||
# ─────────────────────────────────────────
|
||||
function Install-NodeJS {
|
||||
if ($SELECTED_SOURCE -ne "npm") {
|
||||
Write-Info "使用 GitHub Release 来源,跳过 Node.js 检查"
|
||||
return
|
||||
}
|
||||
|
||||
Write-Step "检查 Node.js"
|
||||
Write-Info "检查 Node.js 版本..."
|
||||
|
||||
$node = Get-Command "node" -ErrorAction SilentlyContinue
|
||||
if ($node) {
|
||||
$verStr = (& node -v).Trim()
|
||||
$major = [int]($verStr -replace '^v','' -split '\.' | Select-Object -First 1)
|
||||
if ($major -ge $NODE_MIN_MAJOR) {
|
||||
Write-Ok "Node.js $verStr 已满足要求"
|
||||
return
|
||||
}
|
||||
Write-Warn "当前 Node.js $verStr,需要升级到 v$NODE_MIN_MAJOR+"
|
||||
} else {
|
||||
Write-Info "未检测到 Node.js,开始安装 v$NODE_MIN_MAJOR..."
|
||||
}
|
||||
|
||||
$winget = Get-Command "winget" -ErrorAction SilentlyContinue
|
||||
if ($winget) {
|
||||
Write-Info "通过 winget 安装 Node.js LTS (v22)..."
|
||||
winget install OpenJS.NodeJS.LTS --accept-source-agreements --accept-package-agreements | Out-File $LOG_FILE -Append
|
||||
} else {
|
||||
Write-Info "通过 MSI 安装 Node.js v22..."
|
||||
$msiUrl = "https://nodejs.org/dist/v22.14.0/node-v22.14.0-x64.msi"
|
||||
$msiPath = "$env:TEMP\node-v22.msi"
|
||||
Invoke-WebRequest -Uri $msiUrl -OutFile $msiPath -UseBasicParsing
|
||||
Start-Process msiexec.exe -ArgumentList "/i `"$msiPath`" /qn /norestart" -Wait
|
||||
Remove-Item $msiPath -ErrorAction SilentlyContinue
|
||||
}
|
||||
|
||||
Update-EnvPath
|
||||
$node = Get-Command "node" -ErrorAction SilentlyContinue
|
||||
if (-not $node) {
|
||||
$possiblePaths = @(
|
||||
"C:\Program Files\nodejs\node.exe",
|
||||
"C:\Program Files (x86)\nodejs\node.exe"
|
||||
)
|
||||
foreach ($p in $possiblePaths) {
|
||||
if (Test-Path $p) {
|
||||
$nodeDir = Split-Path $p
|
||||
[System.Environment]::SetEnvironmentVariable("Path", "$env:Path;$nodeDir", "User")
|
||||
Update-EnvPath
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$node = Get-Command "node" -ErrorAction SilentlyContinue
|
||||
if (-not $node) {
|
||||
Write-Err "Node.js 安装失败"
|
||||
return
|
||||
}
|
||||
Write-Ok "Node.js $(node -v) 安装成功"
|
||||
}
|
||||
|
||||
# ─────────────────────────────────────────
|
||||
# 检查 Docker
|
||||
# ─────────────────────────────────────────
|
||||
function Install-Docker {
|
||||
Write-Step "检查 Docker"
|
||||
$docker = Get-Command "docker" -ErrorAction SilentlyContinue
|
||||
if ($docker) {
|
||||
Write-Ok "Docker 已安装"
|
||||
return
|
||||
}
|
||||
Write-Warn "未检测到 Docker"
|
||||
Write-Warn "请手动安装 Docker Desktop: https://docs.docker.com/desktop/install/windows-install/"
|
||||
Write-Warn "安装完成后请重新运行此脚本"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# ─────────────────────────────────────────
|
||||
# 配置 HAPI Hub
|
||||
# ─────────────────────────────────────────
|
||||
function Setup-Hub {
|
||||
if ($SkipHub) { Write-Info "-SkipHub 已设置,跳过 Hub 配置"; return }
|
||||
|
||||
Write-Step "配置 HAPI Hub"
|
||||
New-Item -ItemType Directory -Force -Path $INSTALL_DIR | Out-Null
|
||||
New-Item -ItemType Directory -Force -Path $HAPI_DATA | Out-Null
|
||||
|
||||
$envPath = Join-Path $INSTALL_DIR ".env"
|
||||
if (Test-Path $envPath) {
|
||||
$content = Get-Content $envPath -Raw
|
||||
$tokenMatch = [regex]::Match($content, 'CLI_API_TOKEN=(.+?)\r?\n')
|
||||
if ($tokenMatch.Success) {
|
||||
$script:CLI_TOKEN = $tokenMatch.Groups[1].Value.Trim()
|
||||
}
|
||||
Write-Ok "检测到已有配置文件"
|
||||
} else {
|
||||
$inputToken = Read-Host "请输入 CLI_API_TOKEN(留空自动生成)"
|
||||
if ([string]::IsNullOrWhiteSpace($inputToken)) {
|
||||
$script:CLI_TOKEN = -join ((1..64) | ForEach-Object { '{0:x}' -f (Get-Random -Minimum 0 -Maximum 16) })
|
||||
} else {
|
||||
$script:CLI_TOKEN = $inputToken
|
||||
}
|
||||
|
||||
$publicIp = ""
|
||||
try { $publicIp = Invoke-RestMethod -Uri "https://api.ipify.org" -TimeoutSec 5 }
|
||||
catch { $publicIp = "localhost" }
|
||||
|
||||
$envContent = @"
|
||||
# HAPI Hub 配置
|
||||
# 生成时间: $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')
|
||||
CLI_API_TOKEN=$script:CLI_TOKEN
|
||||
HAPI_PUBLIC_URL=http://${publicIp}:3006
|
||||
CORS_ORIGINS=*
|
||||
TZ=Asia/Shanghai
|
||||
"@
|
||||
$envContent | Set-Content $envPath -Encoding UTF8
|
||||
Write-Ok "已生成配置文件"
|
||||
}
|
||||
|
||||
$composePath = Join-Path $INSTALL_DIR "docker-compose.yml"
|
||||
$composeContent = @"
|
||||
services:
|
||||
hapi-hub:
|
||||
image: ghcr.io/arkylin/hapi-docker:latest
|
||||
container_name: hapi-hub
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- `"3006:3006`"
|
||||
volumes:
|
||||
- ./hapi-data:/root/.hapi
|
||||
env_file:
|
||||
- .env
|
||||
environment:
|
||||
- HAPI_LISTEN_HOST=0.0.0.0
|
||||
- HAPI_LISTEN_PORT=3006
|
||||
"@
|
||||
$composeContent | Set-Content $composePath -Encoding UTF8
|
||||
|
||||
Push-Location $INSTALL_DIR
|
||||
docker compose pull | Out-File $LOG_FILE -Append
|
||||
docker compose up -d | Out-File $LOG_FILE -Append
|
||||
Pop-Location
|
||||
|
||||
Write-Info "等待 Hub 启动..."
|
||||
for ($i = 0; $i -lt 30; $i++) {
|
||||
try {
|
||||
$resp = Invoke-WebRequest -Uri "http://localhost:3006" -TimeoutSec 2 -UseBasicParsing -ErrorAction SilentlyContinue
|
||||
if ($resp.StatusCode -eq 200) { break }
|
||||
} catch { }
|
||||
Start-Sleep -Seconds 1
|
||||
}
|
||||
Write-Ok "HAPI Hub 已启动"
|
||||
}
|
||||
|
||||
# ─────────────────────────────────────────
|
||||
# 创建开机自启计划任务
|
||||
# ─────────────────────────────────────────
|
||||
function Setup-AutoStart {
|
||||
if ($SkipHub) { Write-Info "-SkipHub 已设置,跳过计划任务"; return }
|
||||
|
||||
Write-Step "创建开机自启计划任务"
|
||||
|
||||
# Hub 启动脚本
|
||||
$hubScriptPath = Join-Path $INSTALL_DIR "start-hub.ps1"
|
||||
$hubScript = @"
|
||||
Set-Location "$INSTALL_DIR"
|
||||
docker compose up -d
|
||||
"@
|
||||
$hubScript | Set-Content $hubScriptPath -Encoding UTF8
|
||||
|
||||
# HAPI Hub 计划任务
|
||||
$oldHub = Get-ScheduledTask -TaskName "HAPI Hub" -ErrorAction SilentlyContinue
|
||||
if ($oldHub) { Unregister-ScheduledTask -TaskName "HAPI Hub" -Confirm:$false }
|
||||
|
||||
$trigger = New-ScheduledTaskTrigger -AtLogon
|
||||
$action = New-ScheduledTaskAction -Execute "powershell.exe" `
|
||||
-Argument "-WindowStyle Hidden -ExecutionPolicy Bypass -File `"$hubScriptPath`""
|
||||
$settings = New-ScheduledTaskSettingsSet `
|
||||
-AllowStartIfOnBatteries -DontStopIfGoingOnBatteries `
|
||||
-StartWhenAvailable -ExecutionTimeLimit (New-TimeSpan -Hours 72) -MultipleInstances IgnoreNew
|
||||
$principal = New-ScheduledTaskPrincipal -UserId $env:USERNAME -LogonType Interactive
|
||||
Register-ScheduledTask -TaskName "HAPI Hub" -Trigger $trigger -Action $action `
|
||||
-Principal $principal -Settings $settings -Force | Out-Null
|
||||
Write-Ok "HAPI Hub 计划任务已创建"
|
||||
|
||||
# HAPI Runner 计划任务
|
||||
$oldRunner = Get-ScheduledTask -TaskName "HAPI Runner" -ErrorAction SilentlyContinue
|
||||
if ($oldRunner) { Unregister-ScheduledTask -TaskName "HAPI Runner" -Confirm:$false }
|
||||
|
||||
$hapiHome = Join-Path $env:USERPROFILE ".hapi"
|
||||
New-Item -ItemType Directory -Force -Path $hapiHome | Out-Null
|
||||
$settingsJson = @"
|
||||
{
|
||||
`"apiUrl`": `"http://localhost:3006`",
|
||||
`"cliApiToken`": `"$script:CLI_TOKEN`"
|
||||
}
|
||||
"@
|
||||
$settingsJson | Set-Content (Join-Path $hapiHome "settings.json") -Encoding UTF8
|
||||
|
||||
$trigger2 = New-ScheduledTaskTrigger -AtLogon
|
||||
$action2 = New-ScheduledTaskAction -Execute "powershell.exe" `
|
||||
-Argument '-WindowStyle Hidden -Command "hapi runner start"'
|
||||
$settings2 = New-ScheduledTaskSettingsSet `
|
||||
-AllowStartIfOnBatteries -DontStopIfGoingOnBatteries `
|
||||
-StartWhenAvailable -ExecutionTimeLimit (New-TimeSpan -Hours 72) -MultipleInstances IgnoreNew
|
||||
$principal2 = New-ScheduledTaskPrincipal -UserId $env:USERNAME -LogonType Interactive
|
||||
Register-ScheduledTask -TaskName "HAPI Runner" -Trigger $trigger2 -Action $action2 `
|
||||
-Principal $principal2 -Settings $settings2 -Force | Out-Null
|
||||
Write-Ok "HAPI Runner 计划任务已创建"
|
||||
}
|
||||
|
||||
# ─────────────────────────────────────────
|
||||
# 返回结果
|
||||
# ─────────────────────────────────────────
|
||||
function Print-Result {
|
||||
$publicIp = "localhost"
|
||||
try { $publicIp = Invoke-RestMethod -Uri "https://api.ipify.org" -TimeoutSec 5 }
|
||||
catch {
|
||||
$localIp = Get-NetIPAddress -AddressFamily IPv4 | Where-Object {
|
||||
$_.IPAddress -notlike "127.*" -and $_.IPAddress -notlike "169.254.*"
|
||||
} | Select-Object -First 1 -ExpandProperty IPAddress
|
||||
if ($localIp) { $publicIp = $localIp }
|
||||
}
|
||||
|
||||
$hubTask = Get-ScheduledTask -TaskName "HAPI Hub" -ErrorAction SilentlyContinue
|
||||
$runnerTask = Get-ScheduledTask -TaskName "HAPI Runner" -ErrorAction SilentlyContinue
|
||||
$hubState = if ($hubTask) { $hubTask.State } else { "NotFound" }
|
||||
$runnerState = if ($runnerTask) { $runnerTask.State } else { "NotFound" }
|
||||
|
||||
$hapiPath = (Get-Command hapi -ErrorAction SilentlyContinue).Source
|
||||
if (-not $hapiPath -and (Test-Path (Join-Path $HAPI_BIN_DIR "hapi.exe"))) {
|
||||
$hapiPath = Join-Path $HAPI_BIN_DIR "hapi.exe"
|
||||
}
|
||||
$hapiVer = if ($hapiPath) {
|
||||
$rawVer = & $hapiPath --version 2>$null
|
||||
($rawVer | Select-String -Pattern '\d+\.\d+\.\d+' | ForEach-Object { $_.Matches.Value } | Select-Object -First 1)
|
||||
} else {
|
||||
(Get-Content $VERSION_FILE -Raw).Trim()
|
||||
}
|
||||
$hapiSource = if (Test-Path $SOURCE_FILE) { (Get-Content $SOURCE_FILE -Raw).Trim() } else { "unknown" }
|
||||
|
||||
Write-Host ""
|
||||
Write-Host "╔══════════════════════════════════════════════════════════════════╗" -ForegroundColor Green
|
||||
Write-Host "║ HAPI 安装完成 ║" -ForegroundColor Green
|
||||
Write-Host "╚══════════════════════════════════════════════════════════════════╝" -ForegroundColor Green
|
||||
Write-Host ""
|
||||
Write-Host " HAPI CLI: v$hapiVer (来源: $hapiSource)"
|
||||
Write-Host " CLI 路径: $hapiPath"
|
||||
Write-Host " Hub 地址: http://${publicIp}:3006"
|
||||
Write-Host " Token: $script:CLI_TOKEN"
|
||||
Write-Host ""
|
||||
Write-Host " 计划任务状态:"
|
||||
Write-Host " HAPI Hub: $hubState"
|
||||
Write-Host " HAPI Runner: $runnerState"
|
||||
Write-Host ""
|
||||
Write-Host " 常用命令:"
|
||||
Write-Host " Get-ScheduledTask -TaskName 'HAPI Hub'"
|
||||
Write-Host " Get-ScheduledTask -TaskName 'HAPI Runner'"
|
||||
Write-Host " docker compose -f $composePath logs -f"
|
||||
Write-Host ""
|
||||
Write-Host " 升级命令:"
|
||||
Write-Host " .\install.ps1 -Upgrade"
|
||||
Write-Host " .\install.ps1 -Source github-official -Upgrade"
|
||||
Write-Host ""
|
||||
Write-Host " 日志文件: $LOG_FILE"
|
||||
Write-Host ""
|
||||
}
|
||||
|
||||
# ─────────────────────────────────────────
|
||||
# 前置检查
|
||||
# ─────────────────────────────────────────
|
||||
function Check-Prerequisites {
|
||||
Write-Step "前置检查"
|
||||
Write-Ok "平台: $DETECTED_PLATFORM, 架构: $DETECTED_ARCH"
|
||||
|
||||
# 检查必要工具
|
||||
foreach ($cmd in @("curl")) {
|
||||
if (-not (Get-Command $cmd -ErrorAction SilentlyContinue)) {
|
||||
Write-Err "缺少必要工具: $cmd"
|
||||
exit 1
|
||||
}
|
||||
}
|
||||
Write-Ok "基础工具检查通过"
|
||||
}
|
||||
|
||||
# ─────────────────────────────────────────
|
||||
# 主流程
|
||||
# ─────────────────────────────────────────
|
||||
Write-Host ""
|
||||
Write-Host "╔══════════════════════════════════════════════════════════════════╗" -ForegroundColor White
|
||||
Write-Host "║ HAPI 一键安装脚本 (Windows) ║" -ForegroundColor White
|
||||
Write-Host "║ 多来源安装 | 版本检测 | 自动升级 | 开机自启 ║" -ForegroundColor White
|
||||
Write-Host "╚══════════════════════════════════════════════════════════════════╝" -ForegroundColor White
|
||||
Write-Host ""
|
||||
|
||||
"" | Out-File $LOG_FILE
|
||||
|
||||
Check-Prerequisites
|
||||
Handle-HapiInstall
|
||||
Install-NodeJS
|
||||
Install-Docker
|
||||
Setup-Hub
|
||||
Setup-AutoStart
|
||||
Print-Result
|
||||
Executable
+1040
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user