55 lines
5.2 KiB
Bash
Executable file
55 lines
5.2 KiB
Bash
Executable file
#!/usr/bin/env bash
|
|
# Igneum relay client for Mac/Linux/WSL. Needs curl and jq (python3 fallback for jq).
|
|
# send.sh "<text>" note to everyone send.sh <file> file (up to 50 MB)
|
|
# send.sh inbox unread tasks for this machine, marked read send.sh peek same, not marked
|
|
# send.sh get <id> print an item, download its file here send.sh done <id>
|
|
# send.sh result "<text>" [--task-id N] [--file path] (needs this machine's secret)
|
|
# RELAY_TO=PC1 RELAY_TITLE="..." RELAY_MACHINE=Mac override the defaults.
|
|
# The token, key and machine secret go to curl through a header file (-K), never on the command line or in the URL
|
|
# (X24, X29). The URL, key and token are written in by make-clients.sh (the repo copy holds placeholders);
|
|
# machine-secret.txt next to this file (or RELAY_MACHINE_SECRET) names this machine on every result (X27).
|
|
set -euo pipefail
|
|
HERE="$(cd "$(dirname "$0")" && pwd)"
|
|
RELAY_URL='__RELAY_URL__'; RELAY_KEY='__RELAY_KEY__'; RELAY_TOKEN='__RELAY_TOKEN__'
|
|
API="$RELAY_URL/api/relay?fn="
|
|
STATE="${XDG_STATE_HOME:-$HOME/.local/state}/igneum-relay"; mkdir -p "$STATE"; chmod 700 "$STATE"
|
|
SECRET="${RELAY_MACHINE_SECRET:-}"; [ -n "$SECRET" ] || { [ -f "$HERE/machine-secret.txt" ] && SECRET="$(tr -d '[:space:]' < "$HERE/machine-secret.txt")" || SECRET=""; }
|
|
HDR="$STATE/headers.cfg"
|
|
( umask 077; { printf 'header = "x-relay-token: %s"\nheader = "x-igneum-key: %s"\n' "$RELAY_TOKEN" "$RELAY_KEY"; [ -n "$SECRET" ] && printf 'header = "x-machine-secret: %s"\n' "$SECRET"; } > "$HDR" )
|
|
machine() { if [ -n "${RELAY_MACHINE:-}" ]; then echo "$RELAY_MACHINE"; elif [ -f "$STATE/machine.txt" ]; then cat "$STATE/machine.txt"; else hostname -s; fi; }
|
|
jqq() { if command -v jq >/dev/null; then jq "$@"; else python3 -c 'import json,sys; q=sys.argv[1]; d=json.load(sys.stdin); print(d[q.strip(".")])' "$@"; fi; }
|
|
post() { curl -sS --max-time 120 -K "$HDR" -X POST "$API$1" -H 'Content-Type: application/json' --data-binary "$2"; }
|
|
get() { curl -sS --max-time 60 -K "$HDR" "$API${1/\?/&}"; }
|
|
jstr() { python3 -c 'import json,sys; print(json.dumps(sys.stdin.read()))' ; }
|
|
upload() {
|
|
local path="$1" name size tok
|
|
name="$(basename "$path")"; size=$(stat -f%z "$path" 2>/dev/null || stat -c%s "$path")
|
|
tok="$(post upload "{\"name\":$(printf '%s' "$name" | jstr),\"size\":$size}")"
|
|
local put url token ver
|
|
put="$(printf '%s' "$tok" | jqq -r .put_url)"; token="$(printf '%s' "$tok" | jqq -r .token)"; ver="$(printf '%s' "$tok" | jqq -r .api_version)"
|
|
url="$(curl -sS --max-time 600 -X PUT "$put" -H "authorization: Bearer $token" -H "x-api-version: $ver" -H 'x-add-random-suffix: 1' -H 'x-content-type: application/octet-stream' --data-binary "@$path" | jqq -r .url)"
|
|
[ -n "$url" ] && [ "$url" != "null" ] || { echo "blob upload failed" >&2; return 1; }
|
|
printf '"file_name":%s,"file_url":"%s","size":%s' "$(printf '%s' "$name" | jstr)" "$url" "$size"
|
|
}
|
|
show() { python3 -c '
|
|
import json,sys; it=json.load(sys.stdin)
|
|
for x in (it if isinstance(it,list) else [it]):
|
|
print("===== #%s %s from %s to %s at %s%s =====" % (x["id"],x["kind"],x["from"],x["to"],x["ts"], (" | "+x["title"]) if x.get("title") else ""))
|
|
if x.get("body"): print(x["body"])
|
|
if x.get("has_file"): print("file: %s (%s bytes); send.sh get %s downloads it" % (x["file_name"],x["size"],x["id"]))'; }
|
|
cmd="${1:-}"; M="$(machine)"; TO="${RELAY_TO:-all}"; TITLE="${RELAY_TITLE:-}"
|
|
case "$cmd" in
|
|
inbox) post inbox "{\"machine\":$(printf '%s' "$M" | jstr),\"kind\":\"task\",\"ack\":true}" | jqq .items | show ;; # marking read is a POST (X28)
|
|
peek) get "inbox?machine=$M&kind=task" | jqq .items | show ;;
|
|
get) [ -n "${2:-}" ] || { echo 'get <id>' >&2; exit 1; }; j="$(get "item?id=$2")"; printf '%s' "$j" | jqq .item | show
|
|
if [ "$(printf '%s' "$j" | jqq -r .item.has_file)" = "true" ]; then out="$2-$(printf '%s' "$j" | jqq -r .item.file_name)"; curl -sSL --max-time 600 -K "$HDR" "${API}file&id=$2" -o "$out"; echo "downloaded: $out"; fi ;;
|
|
done) [ -n "${2:-}" ] || { echo "done <id>" >&2; exit 1; }; post done "{\"id\":$2}" >/dev/null; echo "#$2 done" ;;
|
|
result) shift; text="${1:-}"; shift || true; task=0; file=""
|
|
while [ $# -gt 0 ]; do case "$1" in --task-id) task="$2"; shift 2;; --file) file="$2"; shift 2;; *) shift;; esac; done
|
|
[ -n "$SECRET" ] || echo "note: no machine-secret.txt next to send.sh; the relay refuses results from a bound machine without it" >&2
|
|
extra=""; [ -n "$file" ] && extra=",$(upload "$file")"
|
|
post drop "{\"from\":$(printf '%s' "$M" | jstr),\"to\":\"$TO\",\"kind\":\"result\",\"title\":$(printf '%s' "$TITLE" | jstr),\"body\":$(printf '%s' "$text" | jstr),\"task_id\":$task$extra}"; echo ;;
|
|
"") echo 'send.sh "<text>" | send.sh <file> | send.sh inbox | send.sh result "<text>" | send.sh get <id> | send.sh done <id>' >&2; exit 1 ;;
|
|
*) if [ -f "$cmd" ]; then post drop "{\"from\":$(printf '%s' "$M" | jstr),\"to\":\"$TO\",\"kind\":\"file\",\"title\":$(printf '%s' "${TITLE:-$(basename "$cmd")}" | jstr),\"body\":$(printf '%s' "${2:-}" | jstr),$(upload "$cmd")}"
|
|
else post drop "{\"from\":$(printf '%s' "$M" | jstr),\"to\":\"$TO\",\"kind\":\"text\",\"title\":$(printf '%s' "$TITLE" | jstr),\"body\":$(printf '%s' "$cmd" | jstr)}"; fi; echo ;;
|
|
esac
|