Other campuses set themselves up: one install line, one join line per computer, keys made from what is found
settings/discover.py reads Companion's connections (and their addresses and channel counts), the Dante devices, the network MIDI sessions here and on joined Macs, this Mac's wired network, and what runs on each joined computer. settings/autoconf.py turns that into keys, checks, start and restart fixes and a page layout the way North Attleboro's were made (group keys only when not everything fits; receivers fold first), all through the existing builders and rules. settings/campus.py prepares Companion through its internal API (the panel's custom variables and the blink trigger), picks free pages, applies the first config, serves the program kit, the campus install line and the Mac/PC join lines, and looks every ten minutes to add new gear (discover.auto; never during a service or a recording, never removes, never re-adds what someone took off). Settings > Auto-setup shows what was found and the lines to paste. install.sh keeps the kit and makes the panel's login key. Checked on North Attleboro read-only: discovery finds all 43 connections, 19 Dante devices, four Macs' MIDI sessions and seven computers, and a from-scratch build reproduces NA's layout. The Companion calls were tried with a throwaway variable and a disabled trigger, then removed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -40,6 +40,7 @@ from settings import builders
|
||||
from settings import scripts
|
||||
from settings import outlet as outlets
|
||||
from settings import keyguide, probes
|
||||
from settings import campus, discover
|
||||
|
||||
log = logging.getLogger("status-settings")
|
||||
UI = os.path.join(HERE, "ui")
|
||||
@@ -115,6 +116,7 @@ class Site:
|
||||
self.page_cache = {} # name -> (time, value)
|
||||
self.draft = applying.Draft(data_dir)
|
||||
self.applier = applying.Applier(self)
|
||||
self.discover_poke = threading.Event() # a computer joined: look again soon
|
||||
outlets.DATA_DIR, outlets.FILE = self.data_dir, os.path.join(self.data_dir, "outlets.json")
|
||||
|
||||
def load(self):
|
||||
@@ -684,6 +686,23 @@ class Handler(http.server.BaseHTTPRequestHandler):
|
||||
return self.send(200, keyguide.cheat_sheet(site.load(), self.my_address()), "text/html; charset=utf-8")
|
||||
if path == "/where.html":
|
||||
return self.send(200, keyguide.where_things_are(site.load(), site.data_dir, self.my_address()), "text/html; charset=utf-8")
|
||||
# ---- auto-setup: the install line for a new campus, the join lines for its computers ----
|
||||
if path == "/campus/install":
|
||||
key, _ = scripts.public_key(site.load())
|
||||
return self.send(200, campus.install_script(self.base_url(), q.get("id", ["new"])[0], key), "text/plain; charset=utf-8")
|
||||
if path == "/campus/program.tgz":
|
||||
return self.send(200, campus.program_kit(site.data_dir), "application/gzip")
|
||||
if path == "/join":
|
||||
return self.send(200, campus.join_mac(site.load(), self.base_url()), "text/plain; charset=utf-8")
|
||||
if path == "/join.ps1":
|
||||
return self.send(200, campus.join_windows(site.load(), self.base_url()), "text/plain; charset=utf-8")
|
||||
if path == "/api/discover":
|
||||
cfg = site.load()
|
||||
base = self.base_url()
|
||||
return self.send(200, {"auto": bool((cfg.get("discover") or {}).get("auto")), "last": campus.load_last(site.data_dir),
|
||||
"joined": discover.registered(site.data_dir),
|
||||
"join": {"mac": "curl -fsSL %s/join | sh" % base, "pc": "irm %s/join.ps1 | iex" % base},
|
||||
"base": base})
|
||||
if path == "/api/config":
|
||||
cfg = site.load()
|
||||
return self.send(200, {"config": schema.mask_secrets(cfg), "sha": site.cfg_sha, "mtime": site.cfg_mtime})
|
||||
@@ -845,6 +864,15 @@ class Handler(http.server.BaseHTTPRequestHandler):
|
||||
return self.send(403, {"error": "wrong origin"})
|
||||
site = self.site
|
||||
try:
|
||||
if path == "/api/hello":
|
||||
# A computer ran the join line: keep what it says (checked), and look again soon.
|
||||
try:
|
||||
entry = discover.register(site.data_dir, body, self.client_address[0])
|
||||
except (ValueError, TypeError) as e:
|
||||
return self.send(400, {"error": str(e)})
|
||||
log.info("joined: %s (%s, %s) from %s", entry["name"], entry["os"], entry["user"], self.client_address[0])
|
||||
site.discover_poke.set()
|
||||
return self.send(200, {"ok": True})
|
||||
if path == "/api/editing/check":
|
||||
return self.send(200, site.check_code(self.client_address[0], str(body.get("code", ""))))
|
||||
if path == "/api/machines/probe":
|
||||
@@ -861,6 +889,14 @@ class Handler(http.server.BaseHTTPRequestHandler):
|
||||
ok, who = self.gate()
|
||||
if not ok:
|
||||
return
|
||||
if path == "/api/discover/now":
|
||||
# Look now and add what is new (also when adding by itself is off); never during a service.
|
||||
if getattr(site, "looking", None) and site.looking.is_alive():
|
||||
return self.send(200, {"started": False, "already": True})
|
||||
site.looking = threading.Thread(target=campus.look_and_add, args=(site,), kwargs={"force": True}, daemon=True)
|
||||
site.looking.start()
|
||||
log.info("auto-setup: look now, asked by %s", who)
|
||||
return self.send(200, {"started": True})
|
||||
if path == "/api/draft" and self.command == "PUT":
|
||||
cfg = schema.unmask(body.get("config") or {}, site.load())
|
||||
# A page that loaded the settings before they changed here (an Apply from another tab, a
|
||||
@@ -1289,6 +1325,13 @@ class Handler(http.server.BaseHTTPRequestHandler):
|
||||
except OSError:
|
||||
return self.send(404, {"error": "missing %s" % name})
|
||||
|
||||
def base_url(self):
|
||||
"""This page as the caller reached it (an address, across campuses: names do not cross subnets)."""
|
||||
host = (self.headers.get("Host") or "").strip() or "%s:%d" % (self.server.server_address[0], self.server.server_address[1])
|
||||
if not re.fullmatch(r"[A-Za-z0-9.\-]+(:\d+)?", host):
|
||||
host = "%s:%d" % (self.server.server_address[0], self.server.server_address[1])
|
||||
return "http://" + host
|
||||
|
||||
def my_address(self):
|
||||
cfg = self.site.cfg
|
||||
host = cfg.get("site", {}).get("ingest_hostname") or (self.headers.get("Host") or "").split(":")[0] or "127.0.0.1"
|
||||
@@ -1400,6 +1443,7 @@ def main(argv=None):
|
||||
site, servers = serve(args.data_dir, port, args.bind, args.config)
|
||||
stop = threading.Event()
|
||||
threading.Thread(target=rebind_loop, args=(site, servers, port, stop), daemon=True).start()
|
||||
threading.Thread(target=campus.loop, args=(site, stop, site.discover_poke), name="auto-setup", daemon=True).start()
|
||||
try:
|
||||
while True:
|
||||
time.sleep(3600)
|
||||
|
||||
Reference in New Issue
Block a user