feat: expose live enterprise domain presence
This commit is contained in:
parent
406e9d8a7d
commit
ef1734baee
4 changed files with 81 additions and 2 deletions
|
|
@ -4,6 +4,11 @@
|
||||||
|
|
||||||
部署后只监听本机 `127.0.0.1:8031`。公众网站与后续 GLSV 页面通过 Nginx 以单独的受控路由接入;在邮件确认与节点连接器完成前,不开放节点激活或执行。
|
部署后只监听本机 `127.0.0.1:8031`。公众网站与后续 GLSV 页面通过 Nginx 以单独的受控路由接入;在邮件确认与节点连接器完成前,不开放节点激活或执行。
|
||||||
|
|
||||||
|
`hololake-public-status.nginx.conf` 只公开两个精确的只读 GET 路由:
|
||||||
|
`/api/hololake/enterprise/health` 与 `/api/hololake/enterprise/status`。它们用于让
|
||||||
|
HoloLake 回读 `AW-GZ-001` 企业主机和四个本机承载域的实时状态,不转发管理凭据、写入或
|
||||||
|
任意路径。
|
||||||
|
|
||||||
人类管理员与人格体遵守同一条入口链:
|
人类管理员与人格体遵守同一条入口链:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
# Public, read-only enterprise presence receipts for HoloLake.
|
||||||
|
# Include this file inside the TLS server block for guanghu.chat.
|
||||||
|
|
||||||
|
location = /api/hololake/enterprise/health {
|
||||||
|
limit_except GET {
|
||||||
|
deny all;
|
||||||
|
}
|
||||||
|
proxy_pass http://127.0.0.1:8031/health;
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
|
proxy_read_timeout 10s;
|
||||||
|
add_header Cache-Control "no-store" always;
|
||||||
|
}
|
||||||
|
|
||||||
|
location = /api/hololake/enterprise/status {
|
||||||
|
limit_except GET {
|
||||||
|
deny all;
|
||||||
|
}
|
||||||
|
proxy_pass http://127.0.0.1:8031/v1/status;
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
|
proxy_read_timeout 10s;
|
||||||
|
add_header Cache-Control "no-store" always;
|
||||||
|
}
|
||||||
|
|
@ -36,6 +36,12 @@ DOMAINS = {
|
||||||
}
|
}
|
||||||
ENTERPRISE_MANAGED_DOMAINS = {"DOMAIN-ZS", "DOMAIN-MAIN", "DOMAIN-SUB", "DOMAIN-ZERO"}
|
ENTERPRISE_MANAGED_DOMAINS = {"DOMAIN-ZS", "DOMAIN-MAIN", "DOMAIN-SUB", "DOMAIN-ZERO"}
|
||||||
EXTERNAL_FOUNDATION_DOMAINS = {"DOMAIN-FIFTH"}
|
EXTERNAL_FOUNDATION_DOMAINS = {"DOMAIN-FIFTH"}
|
||||||
|
HOSTED_DOMAIN_NODES = {
|
||||||
|
"DOMAIN-MAIN": ("AW-GZ-001-MAIN", "AW-GZ-001 · 光湖主域"),
|
||||||
|
"DOMAIN-SUB": ("AW-GZ-001-SUB", "AW-GZ-001 · 光湖分域"),
|
||||||
|
"DOMAIN-ZERO": ("AW-GZ-001-ZERO", "AW-GZ-001 · 光湖零域"),
|
||||||
|
"DOMAIN-ZS": ("AW-GZ-001-ZS", "AW-GZ-001 · 光湖零感域"),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
def now():
|
def now():
|
||||||
|
|
@ -77,9 +83,28 @@ def connection():
|
||||||
capsule_hash TEXT NOT NULL, created_at INTEGER NOT NULL, expires_at INTEGER NOT NULL
|
capsule_hash TEXT NOT NULL, created_at INTEGER NOT NULL, expires_at INTEGER NOT NULL
|
||||||
);
|
);
|
||||||
""")
|
""")
|
||||||
|
observed_at = now()
|
||||||
for domain_id, name in DOMAINS.items():
|
for domain_id, name in DOMAINS.items():
|
||||||
state = "EXTERNAL_PRIVATE_FOUNDATION" if domain_id in EXTERNAL_FOUNDATION_DOMAINS else "PENDING_ENTRY_NODE"
|
state = "EXTERNAL_PRIVATE_FOUNDATION" if domain_id in EXTERNAL_FOUNDATION_DOMAINS else "PENDING_ENTRY_NODE"
|
||||||
db.execute("INSERT OR IGNORE INTO domains VALUES (?, ?, ?, ?)", (domain_id, name, state, now()))
|
db.execute("INSERT OR IGNORE INTO domains VALUES (?, ?, ?, ?)", (domain_id, name, state, observed_at))
|
||||||
|
for domain_id, (node_id, display_name) in HOSTED_DOMAIN_NODES.items():
|
||||||
|
db.execute(
|
||||||
|
"INSERT OR IGNORE INTO nodes VALUES (?, ?, NULL, 'ACTIVE', ?, '127.0.0.1', ?, ?, ?, ?)",
|
||||||
|
(
|
||||||
|
node_id,
|
||||||
|
domain_id,
|
||||||
|
display_name,
|
||||||
|
json.dumps(["health_check"]),
|
||||||
|
"server-local-lighthouse",
|
||||||
|
observed_at,
|
||||||
|
observed_at,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
db.execute(
|
||||||
|
"UPDATE nodes SET state='ACTIVE', last_heartbeat=? WHERE id=?",
|
||||||
|
(observed_at, node_id),
|
||||||
|
)
|
||||||
|
db.execute("UPDATE domains SET state='HOSTED_ACTIVE' WHERE id=?", (domain_id,))
|
||||||
db.commit()
|
db.commit()
|
||||||
return db
|
return db
|
||||||
|
|
||||||
|
|
@ -250,7 +275,16 @@ class Handler(BaseHTTPRequestHandler):
|
||||||
return self.respond(200, {"ok": True, "service": "guanghu-enterprise-lighthouse", "mode": "map-intent-agent-gate", "map_hash": map_hash, "execution": "disabled"})
|
return self.respond(200, {"ok": True, "service": "guanghu-enterprise-lighthouse", "mode": "map-intent-agent-gate", "map_hash": map_hash, "execution": "disabled"})
|
||||||
if self.path == "/v1/status":
|
if self.path == "/v1/status":
|
||||||
counts = {row["state"]: row["count"] for row in db.execute("SELECT state, COUNT(*) AS count FROM nodes GROUP BY state")}
|
counts = {row["state"]: row["count"] for row in db.execute("SELECT state, COUNT(*) AS count FROM nodes GROUP BY state")}
|
||||||
return self.respond(200, {"ok": True, "domains": [dict(row) for row in db.execute("SELECT id,name,state FROM domains ORDER BY id")], "node_counts": counts, "fixed_actions": sorted(FIXED_ACTIONS), "raw_shell": "rejected"})
|
return self.respond(200, {
|
||||||
|
"ok": True,
|
||||||
|
"node_id": "AW-GZ-001",
|
||||||
|
"host_state": "ONLINE",
|
||||||
|
"observed_at": now(),
|
||||||
|
"domains": [dict(row) for row in db.execute("SELECT id,name,state FROM domains ORDER BY id")],
|
||||||
|
"node_counts": counts,
|
||||||
|
"fixed_actions": sorted(FIXED_ACTIONS),
|
||||||
|
"raw_shell": "rejected",
|
||||||
|
})
|
||||||
if self.path in {"/v1/navigation-map", "/v2/global-navigation-map"}:
|
if self.path in {"/v1/navigation-map", "/v2/global-navigation-map"}:
|
||||||
try:
|
try:
|
||||||
body, map_hash = navigation_map()
|
body, map_hash = navigation_map()
|
||||||
|
|
|
||||||
|
|
@ -66,6 +66,20 @@ with tempfile.TemporaryDirectory() as temp:
|
||||||
else:
|
else:
|
||||||
raise AssertionError("server did not start")
|
raise AssertionError("server did not start")
|
||||||
|
|
||||||
|
status = json.load(urllib.request.urlopen(BASE + "/v1/status"))
|
||||||
|
assert status["node_id"] == "AW-GZ-001"
|
||||||
|
assert status["host_state"] == "ONLINE"
|
||||||
|
assert {domain["state"] for domain in status["domains"] if domain["id"] != "DOMAIN-FIFTH"} == {
|
||||||
|
"HOSTED_ACTIVE"
|
||||||
|
}
|
||||||
|
assert status["node_counts"]["ACTIVE"] == 4
|
||||||
|
|
||||||
|
nodes = json.load(urllib.request.urlopen(BASE + "/v1/nodes"))["nodes"]
|
||||||
|
hosted = {node["domain_id"]: node for node in nodes}
|
||||||
|
assert set(hosted) == {"DOMAIN-MAIN", "DOMAIN-SUB", "DOMAIN-ZERO", "DOMAIN-ZS"}
|
||||||
|
assert all(node["state"] == "ACTIVE" for node in hosted.values())
|
||||||
|
assert all(node["display_name"].startswith("AW-GZ-001") for node in hosted.values())
|
||||||
|
|
||||||
intake = {
|
intake = {
|
||||||
"human_name": "Test",
|
"human_name": "Test",
|
||||||
"email": "test@example.invalid",
|
"email": "test@example.invalid",
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue