feat(satellite): der Satelliten-Rechner steht selbst mit in der Geraeteliste
Der Satellit laeuft auf einem ganz normalen Rechner — bei Stefan auf duffy-desktop. Dort laufen typischerweise noch andere Dienste (FreeCAD-MCP, Web-App, Dateiserver). Bisher war ausgerechnet dieser Rechner unsichtbar: man findet sich nicht per mDNS/ARP selbst. Er erscheint jetzt als normales Geraet — ansprechbar wie jedes andere, nur zusaetzlich mit 🛰️ markiert und blau abgesetzt, damit klar ist, dass dort der Satellit traegt. Abschaltbar ueber LIST_SELF=false, falls man den Traeger bewusst heraushalten will. Nebeneffekt, der ein echtes Problem loest: der eigene Rechner gilt damit als "gefunden" und ist ohne Eintrag in TUNNEL_ALLOW tunnelbar. Genau der Fall, um den es ging — der FreeCAD-MCP laeuft auf dem Satelliten-Host selbst. Beim Zusammenfuehren bleibt die Markierung erhalten: findet mDNS denselben Rechner nochmal, haette der Merge isSatellite sonst verschluckt. hostname kommt aus demselben Grund mit in die erhaltenen Felder. Dazu ein ✎-Knopf pro Geraet in der Diagnostic: Namen vergeben, damit ARIA "duffy-desktop" ansprechen kann statt einer IP. Der Name haengt an der MAC und ueberlebt einen DHCP-Wechsel. 8 Pruefungen: Selbst-Eintrag vollstaendig, Markierung ueberlebt den Merge, mDNS-Infos bleiben erhalten, eigener Rechner ohne TUNNEL_ALLOW erlaubt, fremde IP weiterhin blockiert. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
+34
-4
@@ -5378,6 +5378,20 @@
|
|||||||
renderSatellites();
|
renderSatellites();
|
||||||
send({ action: 'sat_discover', satellite: id });
|
send({ action: 'sat_discover', satellite: id });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Geraetenamen vergeben. Danach kann ARIA das Geraet so ansprechen
|
||||||
|
* ("mach mir Port 5000 von duffy-desktop auf") statt ueber eine IP.
|
||||||
|
* Gespeichert wird an der MAC — ein IP-Wechsel per DHCP verliert den
|
||||||
|
* Namen dann nicht. */
|
||||||
|
function renameSatDevice(satId, mac, ip, current) {
|
||||||
|
const name = prompt(
|
||||||
|
'Name für ' + (ip || mac) + '\n\n'
|
||||||
|
+ 'ARIA spricht das Gerät danach so an. Leer lassen = Namen entfernen.',
|
||||||
|
current || '');
|
||||||
|
if (name === null) return; // abgebrochen
|
||||||
|
send({ action: 'sat_name_set', satellite: satId, mac: mac, ip: ip, name: name.trim() });
|
||||||
|
setTimeout(() => scanSatellite(satId), 400);
|
||||||
|
}
|
||||||
function renderSatellites() {
|
function renderSatellites() {
|
||||||
const box = document.getElementById('sat-list');
|
const box = document.getElementById('sat-list');
|
||||||
if (!box) return;
|
if (!box) return;
|
||||||
@@ -5409,6 +5423,12 @@
|
|||||||
if (d.adbCapable) tags.push('ADB');
|
if (d.adbCapable) tags.push('ADB');
|
||||||
if (d.mac) tags.push(escapeHtml(d.mac));
|
if (d.mac) tags.push(escapeHtml(d.mac));
|
||||||
const tagStr = tags.length ? ' <span style="color:#666;">(' + tags.join(' · ') + ')</span>' : '';
|
const tagStr = tags.length ? ' <span style="color:#666;">(' + tags.join(' · ') + ')</span>' : '';
|
||||||
|
// Namen vergeben — danach ist das Geraet fuer ARIA ansprechbar
|
||||||
|
// ("duffy-desktop" statt einer IP).
|
||||||
|
const nameBtn = (d.ip || d.mac) ? '<button class="btn secondary" title="Namen vergeben — ARIA kann das Gerät dann so ansprechen" ' +
|
||||||
|
'onclick="renameSatDevice(\'' + escapeHtml(s.id) + '\',\'' + escapeHtml(d.mac || '') + '\',\'' +
|
||||||
|
escapeHtml(d.ip || '') + '\',\'' + escapeHtml(d.name || '') + '\')" ' +
|
||||||
|
'style="padding:1px 6px;font-size:11px;margin-left:6px;">✎</button>' : '';
|
||||||
const hasCreds = d.ip && (satCreds[s.id] || {})[d.ip] && (satCreds[s.id][d.ip] || []).length;
|
const hasCreds = d.ip && (satCreds[s.id] || {})[d.ip] && (satCreds[s.id][d.ip] || []).length;
|
||||||
const keyBtn = d.ip ? '<button class="btn secondary" title="Zugangsdaten (SNMP / HTTP / FritzBox)" ' +
|
const keyBtn = d.ip ? '<button class="btn secondary" title="Zugangsdaten (SNMP / HTTP / FritzBox)" ' +
|
||||||
'onclick="openDeviceCreds(\'' + escapeHtml(s.id) + '\',\'' + escapeHtml(d.ip) + '\',\'' + escapeHtml(d.name || d.ip) + '\')" ' +
|
'onclick="openDeviceCreds(\'' + escapeHtml(s.id) + '\',\'' + escapeHtml(d.ip) + '\',\'' + escapeHtml(d.name || d.ip) + '\')" ' +
|
||||||
@@ -5418,12 +5438,22 @@
|
|||||||
const apkBtn = canAdb ? '<button class="btn secondary" title="ARIA-Agent (APK) auf dieses Gerät ausrollen" ' +
|
const apkBtn = canAdb ? '<button class="btn secondary" title="ARIA-Agent (APK) auf dieses Gerät ausrollen" ' +
|
||||||
'onclick="openApkDeploy(\'' + escapeHtml(s.id) + '\',\'' + escapeHtml(d.ip) + '\',\'' + escapeHtml(d.name || d.ip) + '\')" ' +
|
'onclick="openApkDeploy(\'' + escapeHtml(s.id) + '\',\'' + escapeHtml(d.ip) + '\',\'' + escapeHtml(d.name || d.ip) + '\')" ' +
|
||||||
'style="padding:1px 6px;font-size:11px;margin-left:6px;">📦</button>' : '';
|
'style="padding:1px 6px;font-size:11px;margin-left:6px;">📦</button>' : '';
|
||||||
return '<div style="padding:3px 6px;background:#0D0D1A;border-radius:4px;display:flex;align-items:center;">' +
|
// Der Rechner, auf dem der Satellit selbst laeuft. Er ist ein
|
||||||
'<div style="flex:1;min-width:0;">' +
|
// Host wie jeder andere (und oft der interessanteste — dort
|
||||||
'<span style="color:#E0E0F0;">' + escapeHtml(d.name || d.id || '?') + '</span> ' +
|
// laufen meist noch weitere Dienste), nur eben zusaetzlich
|
||||||
|
// Traeger des Satelliten. Darum normal auflisten + markieren.
|
||||||
|
const satBadge = d.isSatellite
|
||||||
|
? '<span title="Auf diesem Rechner läuft der Satellit selbst" ' +
|
||||||
|
'style="margin-right:5px;">🛰️</span>'
|
||||||
|
: '';
|
||||||
|
const rowBg = d.isSatellite ? '#111426' : '#0D0D1A';
|
||||||
|
const rowBorder = d.isSatellite ? ';border-left:2px solid #0096FF' : '';
|
||||||
|
return '<div style="padding:3px 6px;background:' + rowBg + ';border-radius:4px;display:flex;align-items:center' + rowBorder + ';">' +
|
||||||
|
'<div style="flex:1;min-width:0;">' + satBadge +
|
||||||
|
'<span style="color:#E0E0F0;">' + escapeHtml(d.name || d.hostname || d.id || '?') + '</span> ' +
|
||||||
'<span style="color:#0096FF;">[' + escapeHtml(d.type || '?') + ']</span> ' +
|
'<span style="color:#0096FF;">[' + escapeHtml(d.type || '?') + ']</span> ' +
|
||||||
'<span style="color:#8888AA;">' + escapeHtml(d.ip || '') + '</span>' + tagStr +
|
'<span style="color:#8888AA;">' + escapeHtml(d.ip || '') + '</span>' + tagStr +
|
||||||
'</div>' + keyBtn + apkBtn +
|
'</div>' + nameBtn + keyBtn + apkBtn +
|
||||||
'</div>';
|
'</div>';
|
||||||
}).join('') + '</div>';
|
}).join('') + '</div>';
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3005,6 +3005,14 @@ wss.on("connection", (ws) => {
|
|||||||
sendToRVS_raw({ type: "sat_discover",
|
sendToRVS_raw({ type: "sat_discover",
|
||||||
payload: { satellite: msg.satellite || "", force: true },
|
payload: { satellite: msg.satellite || "", force: true },
|
||||||
timestamp: Date.now() });
|
timestamp: Date.now() });
|
||||||
|
} else if (msg.action === "sat_name_set") {
|
||||||
|
// Geraetename vergeben/entfernen. Der Satellit haengt ihn an die MAC,
|
||||||
|
// damit ein IP-Wechsel den Namen nicht verliert.
|
||||||
|
sendToRVS_raw({ type: "sat_name_set",
|
||||||
|
payload: { satellite: msg.satellite || "", mac: msg.mac || "",
|
||||||
|
ip: msg.ip || "", name: msg.name || "",
|
||||||
|
requestId: "dn_" + Date.now() },
|
||||||
|
timestamp: Date.now() });
|
||||||
} else if (msg.action === "sat_creds_list") {
|
} else if (msg.action === "sat_creds_list") {
|
||||||
sendToRVS_raw({ type: "sat_creds_list",
|
sendToRVS_raw({ type: "sat_creds_list",
|
||||||
payload: { satellite: msg.satellite || "", requestId: "dc_" + Date.now() },
|
payload: { satellite: msg.satellite || "", requestId: "dc_" + Date.now() },
|
||||||
|
|||||||
@@ -96,6 +96,15 @@ DEVICE_CACHE_TTL_SEC=120 # wie lange ein Inventar als "frisch" gilt
|
|||||||
# Bridge macht daraus einen lokalen Port (aria-bridge:19000...).
|
# Bridge macht daraus einen lokalen Port (aria-bridge:19000...).
|
||||||
#
|
#
|
||||||
# DEFAULT AUS: das ist ein Weg in dein Netz, den schaltet man bewusst ein.
|
# DEFAULT AUS: das ist ein Weg in dein Netz, den schaltet man bewusst ein.
|
||||||
|
# Soll der Rechner, auf dem der Satellit laeuft, SELBST in der Geraeteliste
|
||||||
|
# auftauchen? Er wird per mDNS/ARP nicht gefunden (man findet sich nicht
|
||||||
|
# selbst), ist aber oft der interessanteste Host — dort laufen typischerweise
|
||||||
|
# weitere Dienste (FreeCAD-MCP, Web-App, Dateiserver). Er erscheint dann als
|
||||||
|
# normales Geraet, zusaetzlich mit 🛰️ gekennzeichnet, und laesst sich genauso
|
||||||
|
# ansprechen. Nebeneffekt: er gilt damit als "gefunden" und ist ohne Eintrag in
|
||||||
|
# TUNNEL_ALLOW tunnelbar.
|
||||||
|
LIST_SELF=true
|
||||||
|
|
||||||
TUNNEL_ENABLED=false
|
TUNNEL_ENABLED=false
|
||||||
|
|
||||||
# Welche Ziele sind erlaubt? LEER = nur Geraete, die der Satellit bei der Suche
|
# Welche Ziele sind erlaubt? LEER = nur Geraete, die der Satellit bei der Suche
|
||||||
|
|||||||
+44
-3
@@ -151,6 +151,12 @@ CONTROL_ALLOWLIST = [
|
|||||||
# DEFAULT AUS. Das ist ein Loch in ein fremdes Netz — das schaltet man bewusst
|
# DEFAULT AUS. Das ist ein Loch in ein fremdes Netz — das schaltet man bewusst
|
||||||
# ein, nicht aus Versehen.
|
# ein, nicht aus Versehen.
|
||||||
TUNNEL_ENABLED = _env_bool("TUNNEL_ENABLED", False)
|
TUNNEL_ENABLED = _env_bool("TUNNEL_ENABLED", False)
|
||||||
|
|
||||||
|
# Soll der Rechner, auf dem der Satellit laeuft, selbst in der Geraeteliste
|
||||||
|
# auftauchen? Default JA — meist ist er ein ganz normaler Host mit weiteren
|
||||||
|
# Diensten. Wer den Satelliten-Rechner bewusst heraushalten will, setzt das
|
||||||
|
# auf false.
|
||||||
|
LIST_SELF = _env_bool("LIST_SELF", True)
|
||||||
# Welche Ziele? Leer = nur Geraete, die der Satellit bei der Suche auch
|
# Welche Ziele? Leer = nur Geraete, die der Satellit bei der Suche auch
|
||||||
# wirklich GEFUNDEN hat (beliebiger Port). Damit kann der Tunnel nichts
|
# wirklich GEFUNDEN hat (beliebiger Port). Damit kann der Tunnel nichts
|
||||||
# ansteuern, was nicht ohnehin im LAN sichtbar ist — insbesondere nichts im
|
# ansteuern, was nicht ohnehin im LAN sichtbar ist — insbesondere nichts im
|
||||||
@@ -613,8 +619,11 @@ def _merge_devices(*lists) -> list[dict]:
|
|||||||
merged = {**d, **{k: v for k, v in cur.items() if v}}
|
merged = {**d, **{k: v for k, v in cur.items() if v}}
|
||||||
if order.get(d.get("via"), 0) >= order.get(cur.get("via"), 0):
|
if order.get(d.get("via"), 0) >= order.get(cur.get("via"), 0):
|
||||||
merged.update({k: v for k, v in d.items() if v})
|
merged.update({k: v for k, v in d.items() if v})
|
||||||
# DIAL-URL / mac aus beiden behalten
|
# DIAL-URL / mac aus beiden behalten. isSatellite/satelliteId
|
||||||
for key in ("dialAppUrl", "mac", "model", "manufacturer"):
|
# muessen mit: der Satellit findet sich selbst auch per mDNS,
|
||||||
|
# und dann duerfte die Kennzeichnung nicht verlorengehen.
|
||||||
|
for key in ("dialAppUrl", "mac", "model", "manufacturer",
|
||||||
|
"isSatellite", "satelliteId", "hostname"):
|
||||||
merged[key] = d.get(key) or cur.get(key) or merged.get(key)
|
merged[key] = d.get(key) or cur.get(key) or merged.get(key)
|
||||||
by_ip[ip] = {k: v for k, v in merged.items() if v not in (None, "")}
|
by_ip[ip] = {k: v for k, v in merged.items() if v not in (None, "")}
|
||||||
return list(by_ip.values()) + loose
|
return list(by_ip.values()) + loose
|
||||||
@@ -655,6 +664,36 @@ def _names_save() -> None:
|
|||||||
logger.warning("device_names.json schreiben fehlgeschlagen: %s", exc)
|
logger.warning("device_names.json schreiben fehlgeschlagen: %s", exc)
|
||||||
|
|
||||||
|
|
||||||
|
def _self_device() -> dict:
|
||||||
|
"""Der Rechner, auf dem der Satellit SELBST laeuft, als normales Geraet.
|
||||||
|
|
||||||
|
Er taucht in der Suche nicht auf (man findet sich nicht per mDNS/ARP
|
||||||
|
selbst), ist aber oft der interessanteste Host im Netz: hier laufen
|
||||||
|
typischerweise noch andere Dienste — ein FreeCAD-MCP, eine Web-App, ein
|
||||||
|
Dateiserver. Ohne diesen Eintrag waere ausgerechnet der Rechner unsichtbar,
|
||||||
|
auf dem ARIA schon eine Verbindung hat.
|
||||||
|
|
||||||
|
`isSatellite` kennzeichnet ihn; ansonsten ist er ein Geraet wie jedes
|
||||||
|
andere und laesst sich genauso ansprechen.
|
||||||
|
"""
|
||||||
|
ip = (NET or {}).get("primary_ip") or ""
|
||||||
|
if not ip:
|
||||||
|
return {}
|
||||||
|
d = {
|
||||||
|
"ip": ip,
|
||||||
|
"mac": (NET or {}).get("mac") or "",
|
||||||
|
"via": "self",
|
||||||
|
"isSatellite": True,
|
||||||
|
"satelliteId": SATELLITE_ID,
|
||||||
|
"type": "host",
|
||||||
|
}
|
||||||
|
try:
|
||||||
|
d["hostname"] = socket.gethostname()
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
return {k: v for k, v in d.items() if v not in (None, "")}
|
||||||
|
|
||||||
|
|
||||||
def _name_key(dev: dict) -> str:
|
def _name_key(dev: dict) -> str:
|
||||||
mac = (dev.get("mac") or "").strip().lower()
|
mac = (dev.get("mac") or "").strip().lower()
|
||||||
return mac if mac else (dev.get("ip") or "").strip()
|
return mac if mac else (dev.get("ip") or "").strip()
|
||||||
@@ -1778,7 +1817,9 @@ class Satellite:
|
|||||||
mdns = await loop.run_in_executor(None, _discover_mdns, DISCOVER_TIMEOUT_SEC)
|
mdns = await loop.run_in_executor(None, _discover_mdns, DISCOVER_TIMEOUT_SEC)
|
||||||
ssdp = await loop.run_in_executor(None, _discover_ssdp, DISCOVER_TIMEOUT_SEC)
|
ssdp = await loop.run_in_executor(None, _discover_ssdp, DISCOVER_TIMEOUT_SEC)
|
||||||
arp = await loop.run_in_executor(None, _discover_arp)
|
arp = await loop.run_in_executor(None, _discover_arp)
|
||||||
self._devices = _apply_names(_merge_devices(mdns, ssdp, arp))
|
_self = [_self_device()] if LIST_SELF else []
|
||||||
|
_self = [d for d in _self if d]
|
||||||
|
self._devices = _apply_names(_merge_devices(mdns, ssdp, arp, _self))
|
||||||
if SNMP_DISCOVERY:
|
if SNMP_DISCOVERY:
|
||||||
await self._enrich_snmp(self._devices)
|
await self._enrich_snmp(self._devices)
|
||||||
await self._enrich_adb(self._devices)
|
await self._enrich_adb(self._devices)
|
||||||
|
|||||||
Reference in New Issue
Block a user