llzai/axonhub
0
1param(
2 [Parameter(ValueFromRemainingArguments=$true)]
3 [string[]]$ArgsFromCmd
4)
5
6$ErrorActionPreference = 'Stop'
7[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
8
9function Write-Info([string]$m){ Write-Host "[INFO] $m" -ForegroundColor Cyan }
10function Write-Success([string]$m){ Write-Host "[SUCCESS] $m" -ForegroundColor Green }
11function Write-Warn([string]$m){ Write-Host "[WARNING] $m" -ForegroundColor Yellow }
12function Write-Err([string]$m){ Write-Host "[ERROR] $m" -ForegroundColor Red }
13
14$Repo = 'looplj/axonhub'
15$Api = "https://api.github.com/repos/$Repo"
16
17$IncludeBeta = $false
18$IncludeRC = $false
19$VerboseFlag = $false
20$Version = $env:AXONHUB_VERSION
21
22function Show-Usage {
23 Write-Host @'
24AxonHub Installer (Windows)
25
26Usage:
27 install.bat [options] [version]
28
29Options:
30 -b, --beta Consider beta pre-releases
31 -r, --rc Consider release-candidate pre-releases
32 -v, --verbose Print extra debug logs
33 -h, --help Show this help and exit
34'@
35}
36
37# Parse args
38foreach($a in $ArgsFromCmd){
39 switch -Regex ($a){
40 '^(--beta|-b)$' { $IncludeBeta = $true; continue }
41 '^(--rc|-r)$' { $IncludeRC = $true; continue }
42 '^(--verbose|-v)$' { $VerboseFlag = $true; continue }
43 '^(--help|-h)$' { Show-Usage; exit 0 }
44 default { if(-not $Version){ $Version = $a } else { Write-Warn "Ignoring extra argument: $a" } }
45 }
46}
47
48function Get-Platform {
49 $archEnv = $env:PROCESSOR_ARCHITECTURE
50 switch ($archEnv.ToLower()){
51 'amd64' { return 'windows_amd64' }
52 'arm64' { return 'windows_arm64' }
53 default { Write-Err "Unsupported architecture: $archEnv"; exit 1 }
54 }
55}
56
57function Invoke-GHApi([string]$url){
58 $headers = @{
59 'Accept'='application/vnd.github+json'
60 'X-GitHub-Api-Version'='2022-11-28'
61 'User-Agent'='axonhub-installer'
62 }
63 if($env:GITHUB_TOKEN){ $headers['Authorization'] = "Bearer $($env:GITHUB_TOKEN)" }
64 return Invoke-RestMethod -Method GET -Uri $url -Headers $headers -ErrorAction Stop
65}
66
67function Get-LatestReleaseTag {
68 try {
69 $json = Invoke-GHApi "$Api/releases/latest"
70 return $json.tag_name
71 } catch {
72 # Fallback to HTML redirect (best-effort)
73 Write-Warn "API failed or rate-limited, falling back to HTML redirect..."
74 try {
75 $resp = Invoke-WebRequest -Uri "https://github.com/$Repo/releases/latest" -Headers @{ 'User-Agent'='axonhub-installer' } -MaximumRedirection 0 -ErrorAction Stop
76 } catch { $resp = $_.Exception.Response }
77 if($resp -and $resp.Headers['Location']){
78 $loc = $resp.Headers['Location']
79 if($loc -match '/tag/([^/]+)$'){ return $matches[1] }
80 }
81 Write-Err "Could not determine latest release version"
82 exit 1
83 }
84}
85
86function Get-LatestVersion([bool]$includeBeta,[bool]$includeRC){
87 if(-not $includeBeta -and -not $includeRC){ return Get-LatestReleaseTag }
88 Write-Info "Fetching releases (beta=$includeBeta, rc=$includeRC) ..."
89 try {
90 $rels = Invoke-GHApi "$Api/releases?per_page=100"
91 $filtered = $rels | Where-Object { -not $_.draft }
92 if($includeBeta -and $includeRC){
93 $filtered = $filtered | Where-Object { $_.tag_name -match '-beta' -or $_.tag_name -match '-rc' }
94 } elseif($includeBeta){
95 $filtered = $filtered | Where-Object { $_.tag_name -match '-beta' }
96 } else {
97 $filtered = $filtered | Where-Object { $_.tag_name -match '-rc' }
98 }
99 if(-not $filtered){
100 Write-Warn 'No matching pre-release found; falling back to latest stable.'
101 return Get-LatestReleaseTag
102 }
103 # GitHub API returns releases in reverse chronological order; take the first
104 return ($filtered | Select-Object -First 1).tag_name
105 } catch {
106 Write-Warn 'Failed to fetch releases; falling back to latest stable.'
107 return Get-LatestReleaseTag
108 }
109}
110
111function Get-AssetUrl([string]$version,[string]$platform){
112 Write-Info "Resolving asset for $version ($platform) ..."
113 try {
114 $tag = $version
115 $json = Invoke-GHApi "$Api/releases/tags/$tag"
116 $asset = $json.assets | Where-Object { $_.browser_download_url -match $platform -and $_.browser_download_url -like '*.zip' } | Select-Object -First 1
117 if($asset){ return $asset.browser_download_url }
118 } catch {}
119 # Fallback by pattern
120 $clean = $version.TrimStart('v')
121 $file = "axonhub_${clean}_${platform}.zip"
122 $candidate = "https://github.com/$Repo/releases/download/$version/$file"
123 try {
124 $head = Invoke-WebRequest -Uri $candidate -Method Head -ErrorAction Stop
125 return $candidate
126 } catch {
127 Write-Err "Could not find asset for platform $platform in release $version"
128 exit 1
129 }
130}
131
132function Ensure-Dirs([string]$path){ if(-not (Test-Path $path)){ New-Item -ItemType Directory -Force -Path $path | Out-Null } }
133
134# Main
135Write-Info 'Starting AxonHub installation...'
136
137$Platform = Get-Platform
138Write-Info "Detected platform: $Platform"
139
140if(-not $Version){ $Version = Get-LatestVersion $IncludeBeta $IncludeRC }
141Write-Info "Using version: $Version"
142
143$BaseDir = Join-Path $env:LOCALAPPDATA 'AxonHub'
144$ConfigDir = $BaseDir
145$DataDir = $BaseDir
146$LogDir = $BaseDir
147Ensure-Dirs $BaseDir
148Ensure-Dirs (Join-Path $BaseDir 'logs')
149
150$AssetUrl = Get-AssetUrl $Version $Platform
151$TempDir = New-Item -ItemType Directory -Path (Join-Path ([IO.Path]::GetTempPath()) ([IO.Path]::GetRandomFileName())) -Force
152$ZipPath = Join-Path $TempDir 'axonhub.zip'
153Write-Info "Downloading: $AssetUrl"
154Invoke-WebRequest -Uri $AssetUrl -OutFile $ZipPath -UseBasicParsing
155
156Write-Info 'Extracting archive...'
157Expand-Archive -Path $ZipPath -DestinationPath $TempDir -Force
158
159$BinaryPath = Get-ChildItem -Path $TempDir -Recurse -Filter 'axonhub.exe' -File | Select-Object -First 1 | ForEach-Object { $_.FullName }
160if(-not $BinaryPath){ Write-Err 'axonhub.exe not found in archive'; exit 1 }
161
162$TargetBinary = Join-Path $BaseDir 'axonhub.exe'
163Copy-Item -Path $BinaryPath -Destination $TargetBinary -Force
164
165# Create default config if missing
166$ConfigFile = Join-Path $BaseDir 'config.yml'
167if(-not (Test-Path $ConfigFile)){
168 Write-Info 'Creating default configuration...'
169 $baseForDSN = ($BaseDir -replace '\\','/')
170 @"
171server:
172 port: 8090
173 name: "AxonHub"
174 debug: false
175
176db:
177 dialect: "sqlite3"
178 dsn: "$baseForDSN/axonhub.db?cache=shared&_fk=1&journal_mode=WAL"
179
180cache:
181 mode: "memory"
182 cache:
183 expiration: "5s"
184 cleanup_interval: "5s"
185
186log:
187 level: "info"
188 encoding: "json"
189 output: "file"
190 file:
191 path: "$baseForDSN/logs/axonhub.log"
192 max_size: 100
193 max_age: 30
194 max_backups: 10
195 local_time: true
196"@ | Set-Content -Path $ConfigFile -Encoding UTF8
197}
198
199Write-Success 'AxonHub installation completed!'
200
201# Get configured port for display
202$port = 8090
203if(Test-Path $TargetBinary){
204 try {
205 $configPort = & $TargetBinary config get server.port 2>$null
206 if($configPort -match '^[0-9]+$'){
207 $port = $configPort
208 }
209 } catch {}
210}
211
212Write-Info "Next steps:"
213Write-Host " 1. Edit configuration: $ConfigFile"
214Write-Host " 2. Start AxonHub: start.bat"
215Write-Host " 3. Stop AxonHub: stop.bat"
216Write-Host " 4. View logs: $BaseDir\axonhub.log (or logs\axonhub.log in config)"
217Write-Host " 5. Access web interface: http://localhost:$port"
218 