120 lines
6.3 KiB
PowerShell
120 lines
6.3 KiB
PowerShell
# Igneum relay client (Windows PowerShell 5.1 or later). Driven by send.bat; the agent and the playbooks call it too.
|
|
# send.ps1 "<text>" post a note to everyone
|
|
# send.ps1 <file> post a file (up to 50 MB, straight to Vercel Blob)
|
|
# send.ps1 result "<text>" [-TaskId N] [-File path] post a result (what a task produced; needs this PC's machine secret)
|
|
# send.ps1 inbox print the unread tasks for this machine and mark them read
|
|
# send.ps1 peek print them without marking read
|
|
# send.ps1 get <id> print an item; download its file into the current folder
|
|
# send.ps1 done <id> mark a task done
|
|
# -To PC1 / -Title "..." / -Machine NAME override the defaults (machine = this PC's name on the relay, else its hostname)
|
|
# The token and key travel as headers, never in the URL (X24). The URL, key and token below are written in by
|
|
# make-clients.sh; machine-secret.txt next to this file (or RELAY_MACHINE_SECRET) names this PC on every result (X27).
|
|
# They are the secret; keep this folder off shared drives.
|
|
param(
|
|
[Parameter(Position = 0)] [string] $A,
|
|
[Parameter(Position = 1)] [string] $B,
|
|
[string] $To = 'all',
|
|
[string] $Title = '',
|
|
[string] $Machine = '',
|
|
[long] $TaskId = 0,
|
|
[string] $File = '',
|
|
[string] $Kind = ''
|
|
)
|
|
$ErrorActionPreference = 'Stop'
|
|
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
|
|
$RelayUrl = '__RELAY_URL__'
|
|
$RelayKey = '__RELAY_KEY__'
|
|
$RelayToken = '__RELAY_TOKEN__'
|
|
$Here = Split-Path -Parent $MyInvocation.MyCommand.Path
|
|
$StateDir = Join-Path $env:LOCALAPPDATA 'igneum-relay'
|
|
$MachineSecret = ''
|
|
if ($env:RELAY_MACHINE_SECRET) { $MachineSecret = $env:RELAY_MACHINE_SECRET.Trim() }
|
|
elseif (Test-Path (Join-Path $Here 'machine-secret.txt')) { $MachineSecret = (Get-Content (Join-Path $Here 'machine-secret.txt') -Raw).Trim() }
|
|
$Headers = @{ 'x-relay-token' = $RelayToken; 'x-igneum-key' = $RelayKey }
|
|
if ($MachineSecret) { $Headers['x-machine-secret'] = $MachineSecret }
|
|
|
|
function Api-Url([string] $Fn) { return ($RelayUrl + '/api/relay?fn=' + ($Fn -replace '\?', '&')) }
|
|
function Get-MachineName {
|
|
if ($Machine) { return $Machine }
|
|
if ($env:RELAY_MACHINE) { return $env:RELAY_MACHINE }
|
|
$f = Join-Path $StateDir 'machine.txt'
|
|
if (Test-Path $f) { $n = (Get-Content $f -Raw).Trim(); if ($n) { return $n } }
|
|
return $env:COMPUTERNAME
|
|
}
|
|
function Invoke-Relay([string] $Method, [string] $Fn, $Body) {
|
|
$uri = Api-Url $Fn
|
|
if ($Method -eq 'GET') { return Invoke-RestMethod -Uri $uri -Headers $Headers -TimeoutSec 60 }
|
|
$bytes = [Text.Encoding]::UTF8.GetBytes((ConvertTo-Json $Body -Depth 8 -Compress))
|
|
return Invoke-RestMethod -Method Post -Uri $uri -Headers $Headers -ContentType 'application/json; charset=utf-8' -Body $bytes -TimeoutSec 120
|
|
}
|
|
function Send-RelayFile([string] $Path) {
|
|
$Path = (Resolve-Path $Path).Path
|
|
$name = Split-Path $Path -Leaf
|
|
$size = (Get-Item $Path).Length
|
|
$t = Invoke-Relay 'POST' 'upload' @{ name = $name; size = $size }
|
|
if ($size -gt $t.max) { throw "$name is $size bytes, over the $($t.max) byte cap" }
|
|
$h = @{ 'authorization' = "Bearer $($t.token)"; 'x-api-version' = "$($t.api_version)"; 'x-add-random-suffix' = '1'; 'x-content-type' = 'application/octet-stream' }
|
|
$r = Invoke-RestMethod -Method Put -Uri $t.put_url -Headers $h -ContentType 'application/octet-stream' -InFile $Path -TimeoutSec 600
|
|
if (-not $r.url) { throw "blob upload failed: $($r | ConvertTo-Json -Compress)" }
|
|
return @{ file_name = $name; file_url = $r.url; size = $size }
|
|
}
|
|
function Post-Item([hashtable] $o) {
|
|
$o['from'] = Get-MachineName
|
|
if (-not $o.ContainsKey('to')) { $o['to'] = $To }
|
|
if ($Title -and -not $o['title']) { $o['title'] = $Title }
|
|
if ($TaskId) { $o['task_id'] = $TaskId }
|
|
if ($File) { $f = Send-RelayFile $File; foreach ($k in $f.Keys) { $o[$k] = $f[$k] } }
|
|
$r = Invoke-Relay 'POST' 'drop' $o
|
|
Write-Host ("sent #" + $r.id + " (" + $r.kind + ") from " + $o['from'])
|
|
return $r
|
|
}
|
|
function Show-Item($it) {
|
|
Write-Host ("===== #" + $it.id + " " + $it.kind + " from " + $it.from + " to " + $it.to + " at " + $it.ts + ($(if ($it.title) { " | " + $it.title } else { "" })) + " =====")
|
|
if ($it.body) { Write-Output $it.body }
|
|
if ($it.has_file) { Write-Host ("file: " + $it.file_name + " (" + $it.size + " bytes); send.bat get " + $it.id + " downloads it") }
|
|
}
|
|
|
|
switch -Regex ($A) {
|
|
'^(?i)inbox$' {
|
|
# marking read is a POST (X28: a GET changes nothing)
|
|
$j = Invoke-Relay 'POST' 'inbox' @{ machine = (Get-MachineName); kind = 'task'; ack = $true }
|
|
if (-not $j.items) { Write-Host ("nothing waiting for " + (Get-MachineName)); break }
|
|
foreach ($it in $j.items) { Show-Item $it }
|
|
break
|
|
}
|
|
'^(?i)peek$' {
|
|
$j = Invoke-Relay 'GET' ("inbox?machine=" + [Uri]::EscapeDataString((Get-MachineName)) + "&kind=task")
|
|
if (-not $j.items) { Write-Host ("nothing waiting for " + (Get-MachineName)); break }
|
|
foreach ($it in $j.items) { Show-Item $it }
|
|
break
|
|
}
|
|
'^(?i)get$' {
|
|
if (-not $B) { throw 'get <id>' }
|
|
$it = (Invoke-Relay 'GET' ("item?id=" + $B)).item
|
|
Show-Item $it
|
|
if ($it.has_file) {
|
|
$out = Join-Path (Get-Location) ($it.id.ToString() + '-' + $it.file_name)
|
|
Invoke-WebRequest -Uri (Api-Url ("file?id=" + $it.id)) -Headers $Headers -OutFile $out -UseBasicParsing -TimeoutSec 600
|
|
Write-Host "downloaded: $out"
|
|
}
|
|
break
|
|
}
|
|
'^(?i)done$' { if (-not $B) { throw 'done <id>' }; Invoke-Relay 'POST' 'done' @{ id = [long]$B } | Out-Null; Write-Host "#$B done"; break }
|
|
'^(?i)result$' {
|
|
if (-not $B -and -not $File) { throw 'result "<text>" [-TaskId N] [-File path]' }
|
|
if (-not $MachineSecret) { Write-Host 'note: no machine-secret.txt next to send.ps1; the relay refuses results from a bound machine without it' }
|
|
Post-Item @{ kind = 'result'; body = "$B"; title = $Title } | Out-Null
|
|
break
|
|
}
|
|
default {
|
|
if (-not $A) { throw 'send.bat "<text>" | send.bat <file> | send.bat inbox | send.bat result "<text>" | send.bat get <id> | send.bat done <id>' }
|
|
if (Test-Path -LiteralPath $A -PathType Leaf) {
|
|
$f = Send-RelayFile $A
|
|
$o = @{ kind = 'file'; body = "$B"; title = $(if ($Title) { $Title } else { $f.file_name }) }
|
|
foreach ($k in $f.Keys) { $o[$k] = $f[$k] }
|
|
Post-Item $o | Out-Null
|
|
} else {
|
|
Post-Item @{ kind = $(if ($Kind) { $Kind } else { 'text' }); body = "$A"; title = $Title } | Out-Null
|
|
}
|
|
}
|
|
}
|