#!/bin/sh # hub/ingest.sh — Fleet hub telemetry ingest + config backup receiver. # Runs on the busfleet hub (167.172.237.162). # # Routes: # POST /api/telemetry Receive telemetry JSON, normalise, store # POST /api/backup Receive config backup archive # # Normalises both GL (openwrt) and Synology telemetry into a single schema. set -e HUB_DIR="${HUB_DIR:-/opt/busfleet-hub}" TELEM_DIR="${TELEM_DIR:-${HUB_DIR}/telemetry}" BACKUP_DIR="${BACKUP_DIR:-${HUB_DIR}/backups}" mkdir -p "$TELEM_DIR" "$BACKUP_DIR" # ── Normalise telemetry ──────────────────────────────────────────── # Both platforms POST to /api/telemetry. # Canonical format uses modem_0001/wan keys (matching GL standard). # Handles three variants: # - GL (no platform field): add platform="openwrt", pass through # - GL (platform="openwrt"): already canonical, pass through # - Old Synology (platform="synology" + wan1/wan2): remap to modem_0001/wan # - New unified Synology (platform="synology" + modem_0001/wan): pass through normalise_telemetry() { json="$1" platform="" platform=$(echo "$json" | grep -o '"platform":"[^"]*"' | cut -d'"' -f4) if [ -z "$platform" ]; then # Pre-unified GL telemetry — add platform field, keys already canonical platform="openwrt" json=$(echo "$json" | sed \ -e 's/^{/{"platform":"'"$platform"'",/') fi # Remap old Synology wan1/wan2 keys to canonical modem_0001/wan if echo "$json" | grep -q '"wan1"'; then json=$(echo "$json" | sed \ -e 's/"wan1"/"modem_0001"/g' \ -e 's/"wan2"/"wan"/g') fi echo "$json" } # ── Store telemetry ──────────────────────────────────────────────── store_telemetry() { local device_id="$1" local json="$2" local ts="$3" local date_dir="" today="" today=$(date -d "@$ts" +%Y-%m-%d 2>/dev/null || date +%Y-%m-%d) date_dir="${TELEM_DIR}/${device_id}/${today}" mkdir -p "$date_dir" # Store as individual JSON lines (one per POST) for easy querying echo "$json" >> "${date_dir}/telemetry.jsonl" # Also update latest for quick dashboard reads echo "$json" > "${date_dir}/latest.json" echo "$json" > "${TELEM_DIR}/${device_id}/latest.json" } # ── Store config backup ──────────────────────────────────────────── store_backup() { local device_id="$1" local platform="$2" local timestamp="$3" local tmpfile="$4" local dir="${BACKUP_DIR}/${device_id}" mkdir -p "$dir" # Store the backup file cp "$tmpfile" "${dir}/${timestamp}-${platform}.tar.gz" # Keep last 10 backups per device, delete older ones ls -t "${dir}"/*.tar.gz 2>/dev/null | tail -n +11 | xargs rm -f 2>/dev/null || true # Log echo "$(date -Iseconds) backup: ${device_id} ${platform} ${timestamp}" >> "${BACKUP_DIR}/backup.log" } # ── CGI/HTTP Handler (called by socat or netcat listener) ───────── handle_request() { local method="$1" local path="$2" local body="$3" local content_type="$4" case "$path" in /api/telemetry) # Expect JSON body local device_id="" ts="" normalised="" device_id=$(echo "$body" | grep -o '"device_id":"[^"]*"' | cut -d'"' -f4) ts=$(echo "$body" | grep -o '"timestamp":[0-9]*' | grep -o '[0-9]*') [ -z "$device_id" ] && { echo '{"error":"missing device_id"}'; return 1; } normalised=$(normalise_telemetry "$body") store_telemetry "$device_id" "$normalised" "${ts:-0}" echo '{"status":"ok"}' ;; /api/backup) # Multipart form data — device_id, platform, timestamp, file # For simplicity: socat passes raw body; caller uses curl -F # The CGI wrapper extracts multipart parts echo '{"status":"ok","note":"backup received"}' ;; /api/status) # Fleet status summary echo '{"status":"ok","devices":[]}' ;; *) echo '{"error":"not found"}' return 1 ;; esac } # ── CLI mode ────────────────────────────────────────────────────── case "${1:-}" in --ingest) # Pipe JSON stdin → normalise → store body=$(cat) handle_request "POST" "/api/telemetry" "$body" "application/json" ;; --backup) device_id="$2" platform="$3" timestamp="$4" file="$5" store_backup "$device_id" "$platform" "$timestamp" "$file" ;; --status) echo "=== Fleet Hub Status ===" echo "Telemetry dir: $TELEM_DIR" echo "Backup dir: $BACKUP_DIR" echo "" echo "Devices reporting:" ls "$TELEM_DIR" 2>/dev/null || echo " (none)" echo "" echo "Backups:" find "$BACKUP_DIR" -name "*.tar.gz" 2>/dev/null | sort || echo " (none)" ;; *) echo "Usage: ingest.sh --ingest | --backup | --status" ;; esac