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 以单独的受控路由接入;在邮件确认与节点连接器完成前,不开放节点激活或执行。
|
||||
|
||||
`hololake-public-status.nginx.conf` 只公开两个精确的只读 GET 路由:
|
||||
`/api/hololake/enterprise/health` 与 `/api/hololake/enterprise/status`。它们用于让
|
||||
HoloLake 回读 `AW-GZ-001` 企业主机和四个本机承载域的实时状态,不转发管理凭据、写入或
|
||||
任意路径。
|
||||
|
||||
人类管理员与人格体遵守同一条入口链:
|
||||
|
||||
```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"}
|
||||
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():
|
||||
|
|
@ -77,9 +83,28 @@ def connection():
|
|||
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():
|
||||
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()
|
||||
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"})
|
||||
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")}
|
||||
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"}:
|
||||
try:
|
||||
body, map_hash = navigation_map()
|
||||
|
|
|
|||
|
|
@ -66,6 +66,20 @@ with tempfile.TemporaryDirectory() as temp:
|
|||
else:
|
||||
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 = {
|
||||
"human_name": "Test",
|
||||
"email": "test@example.invalid",
|
||||
|
|
|
|||
Loading…
Reference in a new issue