sync: auto-sync from GURU-5070 at 2026-06-15 11:54:35

Author: Mike Swanson
Machine: GURU-5070
Timestamp: 2026-06-15 11:54:35
This commit is contained in:
2026-06-15 11:54:50 -07:00
parent 9b9513f69e
commit 55acc7f98a

View File

@@ -0,0 +1,71 @@
#!/usr/bin/env bash
# uos-mongo.sh — one-shot query of the UniFi OS Server (UOS) Network database.
#
# The UOS is the self-hosted UniFi controller VM "Unifi" on Jupiter (172.16.3.20,
# virsh dom id 1); guest IP 172.16.3.29, Rocky Linux 9. The UniFi Network app
# (ace.jar) + its classic MongoDB `ace` (127.0.0.1:27117) run INSIDE a rootless
# podman container `uosserver` (user uosserver, uid 1000). There is no mongo
# client on the host — the shell lives at /usr/bin/mongo *inside* the container.
# Access is by SSH key (our standard key is already authorized as root on .29).
#
# This is the single-shot path: local stdin (Mongo JS) -> ssh root@.29
# -> su - uosserver -> podman exec -i uosserver mongo --port 27117 ace.
#
# Usage:
# echo 'db.device.count()' | bash .claude/scripts/uos-mongo.sh
# bash .claude/scripts/uos-mongo.sh < query.js
# bash .claude/scripts/uos-mongo.sh --find-mac 36:c4 # search device+user by MAC suffix, all sites
# bash .claude/scripts/uos-mongo.sh --sites # list all sites (_id -> name)
#
# Env overrides: UOS_HOST (default 172.16.3.29), UOS_SSH_USER (default root).
#
# Notes:
# - Cloud Site Manager key (vault infrastructure/unifi-site-manager-api) hits
# api.ui.com for ADOPTED devices only; it does NOT auth the local integration
# API (401). The local integration key "Claude" (ace.api_key) is hashed/
# unrecoverable — this Mongo path is the reliable read.
# - Unadopted/pending devices: the controller only persists DISCOVERED devices
# into `device` with adopted:false. If `db.device.count({adopted:false})` is 0,
# there are no pending devices controller-wide (nothing reaching it to adopt).
# - `ace` collections of interest: device (adopted infra), user (clients/stations),
# rogue (neighbor/over-the-air BSSIDs — NOT your gear), site (_id->desc name map).
set -euo pipefail
UOS_HOST="${UOS_HOST:-172.16.3.29}"
UOS_SSH_USER="${UOS_SSH_USER:-root}"
SSH=(ssh -o ConnectTimeout=15 -o StrictHostKeyChecking=accept-new "${UOS_SSH_USER}@${UOS_HOST}")
REMOTE='su - uosserver -c "XDG_RUNTIME_DIR=/run/user/1000 podman exec -i uosserver mongo --quiet --port 27117 ace"'
run_js() { "${SSH[@]}" "$REMOTE"; } # reads Mongo JS from this function's stdin
case "${1:-}" in
--find-mac)
suf="${2:?usage: --find-mac <mac-or-suffix, e.g. 36:c4>}"
run_js <<JS
var sites={}; db.site.find({},{name:1,desc:1}).forEach(function(s){sites[s._id.str]=(s.desc||s.name);});
print("# UOS search: MAC matching /${suf}\$/i across "+Object.keys(sites).length+" sites (device=infra, user=clients)");
["device","user"].forEach(function(c){
var n=0;
db[c].find({mac:/${suf}\$/i}).forEach(function(d){
n++;
print(c+" | "+d.mac+" | model="+(d.model||d.oui||"?")+" | name="+(d.name||d.hostname||"(none)")
+" | adopted="+d.adopted+" | ip="+(d.ip||d.last_ip||"-")+" | site="+(sites[d.site_id]||d.site_id));
});
print("# "+c+" matches: "+n);
});
print("# pending (adopted:false) devices controller-wide: "+db.device.count({adopted:false}));
JS
;;
--sites)
run_js <<'JS'
db.site.find({},{name:1,desc:1}).forEach(function(s){ print(s._id.str+" "+(s.desc||s.name)); });
JS
;;
-h|--help)
sed -n '2,40p' "$0"
;;
*)
# pipe arbitrary Mongo JS from stdin
run_js
;;
esac