Remove legacy OClaw alarm WSS and fix key-alert rules table layout.

Key alerts now deliver only via the DSH hub; Modal Body no longer flex-shrinks the rules rows to empty.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
oliver 2026-09-15 23:10:26 +08:00
parent f937efa0f9
commit 0a4561f385
25 changed files with 36 additions and 729 deletions

View file

@ -1087,15 +1087,6 @@ const en = {
hubColLastSeen: "last seen",
hubEmptyConnections: "No Netx Ops clients are connected to the alarm hub",
hubPath: "path",
legacyOclaw: "Legacy OClaw outbound",
ws: "OClaw WSS",
wsConnected: "connected",
wsDisconnected: "disconnected",
wsDisabled: "disabled",
wsPaused: "paused",
publishedOk: "published ok",
publishedFail: "published fail",
queue: "queue",
matchType: "Match by",
matchNotificationId: "notificationId",
matchKeyword: "description keyword",
@ -1171,10 +1162,8 @@ const en = {
pulling_topology: "Pulling UME topology (nodes/links)…",
topology_sync_busy: "Topology sync already running; skipped",
ume_ws_disabled_no_base_url: "Disabled or UME_BASE_URL not configured",
oclaw_fwd_disabled: "Disabled or NETX_OCLAW_ALARM_WS / token / url not configured",
resumed_sync_soon: "Resumed: skipping debounce wait, sync soon",
resumed_wss_reconnect: "Resumed: reconnecting WSS soon",
resumed_oclaw_wss_reconnect: "Resumed: reconnecting OClaw WSS soon",
resumed: "Resumed",
keepalive_failed: "Token keepalive failed",
},
@ -1191,21 +1180,10 @@ const en = {
subscription_lost: "UME subscription lost",
disabled_no_base_url: "Disabled or UME_BASE_URL not configured",
},
fwdState: {
paused: "Paused",
disabled: "Disabled",
connecting: "Connecting",
connected: "Connected",
connect_refused: "OClaw gateway down or port 8787 refused",
connect_timeout: "Timed out connecting to OClaw gateway",
auth_failed: "OClaw netx-bridge auth failed (check shared token)",
disconnected: "OClaw WSS disconnected",
},
runtimeTask: {
token_keepalive: "UME token keepalive",
alarms_current_auto_sync: "Current alarms auto sync",
alarms_current_ws_consumer: "UME alarm WSS",
oclaw_alarm_forwarder: "OClaw key-alert WSS",
inventory_auto_sync: "Inventory auto sync",
topology_auto_sync: "Topology auto sync",
},

View file

@ -1079,15 +1079,6 @@ const zh = {
hubColLastSeen: "最近心跳",
hubEmptyConnections: "当前没有 Netx Ops 客户端连入告警 hub",
hubPath: "路径",
legacyOclaw: "遗留 OClaw 出站",
ws: "OClaw WSS",
wsConnected: "已连接",
wsDisconnected: "未连接",
wsDisabled: "未启用",
wsPaused: "已暂停",
publishedOk: "发布成功",
publishedFail: "发布失败",
queue: "待发队列",
matchType: "匹配方式",
matchNotificationId: "告警码 (notificationId)",
matchKeyword: "告警描述关键字",
@ -1163,10 +1154,8 @@ const zh = {
pulling_topology: "正在拉取 UME 拓扑(节点/链路)…",
topology_sync_busy: "拓扑同步进行中,已跳过",
ume_ws_disabled_no_base_url: "未启用或未配置 UME_BASE_URL",
oclaw_fwd_disabled: "未启用或未配置 NETX_OCLAW_ALARM_WS / token / url",
resumed_sync_soon: "已恢复:将跳过本轮周期等待并尽快同步",
resumed_wss_reconnect: "已恢复:将尽快重连 WSS",
resumed_oclaw_wss_reconnect: "已恢复:将尽快重连 OClaw WSS",
resumed: "已恢复",
keepalive_failed: "Token 保活失败",
},
@ -1183,21 +1172,10 @@ const zh = {
subscription_lost: "UME 订阅已丢失",
disabled_no_base_url: "未启用或未配置 UME_BASE_URL",
},
fwdState: {
paused: "已暂停",
disabled: "未启用",
connecting: "连接中",
connected: "已连接",
connect_refused: "OClaw 网关未启动或 8787 拒绝连接",
connect_timeout: "连接 OClaw 网关超时",
auth_failed: "OClaw netx-bridge 鉴权失败(检查共享 token)",
disconnected: "OClaw WSS 已断开",
},
runtimeTask: {
token_keepalive: "UME Token 保活",
alarms_current_auto_sync: "当前告警定时同步",
alarms_current_ws_consumer: "UME 告警 WSS",
oclaw_alarm_forwarder: "OClaw 关键告警 WSS",
inventory_auto_sync: "Inventory 定时同步",
topology_auto_sync: "拓扑定时同步",
},

View file

@ -11312,10 +11312,25 @@ option {
max-height: min(78vh, 780px);
}
/*
* Modal.Body often uses flex column; default flex-shrink:1 crushes the rules
* table to thead-only while the pager still shows total > 0.
*/
.app-heroui-modal__dialog [data-slot="modal-body"] > *,
.app-heroui-modal__dialog .modal__body > * {
flex-shrink: 0;
}
.app-heroui-modal--xl .app-heroui-modal__dialog [data-slot="modal-body"],
.app-heroui-modal--xl .app-heroui-modal__dialog .modal__body {
max-height: min(86vh, 900px);
}
.app-heroui-modal .pt-list-table-wrap {
overflow-x: auto;
max-width: 100%;
width: 100%;
min-height: 10rem;
}
.app-heroui-modal .pt-list-table {

View file

@ -437,21 +437,11 @@ export function UmePage() {
const keyAlertHub = keyAlertMonitorQuery.data?.dsh_alarm_hub;
const keyAlertHubConnections = keyAlertHub?.connections || [];
const keyAlertHubSubscribers = Number(keyAlertHub?.subscribers || keyAlertHubConnections.length || 0);
const keyAlertForwarder = keyAlertMonitorQuery.data?.forwarder;
const keyAlertRules = keyAlertMonitorQuery.data?.rules || [];
const keyAlertTotal = Number(keyAlertMonitorQuery.data?.total || keyAlertRules.length);
const keyAlertPages = pageCount(keyAlertTotal, keyAlertPageSize);
const keyAlertForwardOnClear = Boolean(keyAlertMonitorQuery.data?.config?.forward_on_clear);
const hubWsPill = keyAlertHubSubscribers > 0 ? "up" : "down";
const oclawWsPill =
!keyAlertForwarder?.enabled
? "unknown"
: keyAlertForwarder.paused
? "warn"
: keyAlertForwarder.connected
? "up"
: "down";
const showLegacyOclaw = Boolean(keyAlertForwarder?.enabled);
const runtimeTaskLabel = (task: string) => {
const key = `ume.tasks.runtimeTask.${task}`;
@ -878,16 +868,6 @@ export function UmePage() {
<span className="conn-pill">
{t("ume.keyAlert.hubDeliverOk")}: {Number(keyAlertHub?.deliver_ok || 0)}
</span>
{showLegacyOclaw ? (
<span className={`conn-pill conn-pill--${oclawWsPill}`} title={t("ume.keyAlert.legacyOclaw")}>
{t("ume.keyAlert.ws")}:{" "}
{keyAlertForwarder?.paused
? t("ume.keyAlert.wsPaused")
: keyAlertForwarder?.connected
? t("ume.keyAlert.wsConnected")
: t("ume.keyAlert.wsDisconnected")}
</span>
) : null}
</div>
</article>
@ -984,15 +964,6 @@ export function UmePage() {
{` · ${t("ume.keyAlert.hubPublished")} ${Number(keyAlertHub?.published || 0)}`}
{` · ${t("ume.keyAlert.hubDeliverOk")} ${Number(keyAlertHub?.deliver_ok || 0)}`}
{keyAlertHub?.path ? ` · ${t("ume.keyAlert.hubPath")} ${keyAlertHub.path}` : ""}
{showLegacyOclaw
? ` · ${t("ume.keyAlert.legacyOclaw")}: ${
keyAlertForwarder?.paused
? t("ume.keyAlert.wsPaused")
: keyAlertForwarder?.connected
? t("ume.keyAlert.wsConnected")
: t("ume.keyAlert.wsDisconnected")
}`
: ""}
</p>
<div className="ume-modal-form">
<div className="muted ume-modal-form__label">{t("ume.keyAlert.hubConnections")}</div>
@ -1036,18 +1007,6 @@ export function UmePage() {
</table>
</div>
)}
{showLegacyOclaw ? (
<p className="muted ume-modal-form__hint">
{t("ume.keyAlert.legacyOclaw")}:{" "}
{keyAlertForwarder?.paused
? t("ume.keyAlert.wsPaused")
: keyAlertForwarder?.connected
? t("ume.keyAlert.wsConnected")
: t("ume.keyAlert.wsDisconnected")}
{` · ${t("ume.keyAlert.publishedOk")} ${Number(keyAlertForwarder?.published_ok || 0)}`}
{` · ${t("ume.keyAlert.queue")} ${Number(keyAlertForwarder?.queue_size || 0)}`}
</p>
) : null}
</div>
<div className="ume-modal-form">

View file

@ -21,19 +21,6 @@ export type IntegrationStatus = {
netx_api: { status: "up" | "down" | "unknown"; [k: string]: unknown };
db: { status: "up" | "down" | "unknown"; latency_ms?: number; error?: string; [k: string]: unknown };
dsh_alarm_hub?: DshAlarmHubStatus;
oclaw_bridge?: {
status: "up" | "down" | "unknown";
mode?: string;
enabled?: boolean;
connected?: boolean;
queue_size?: number;
published_ok?: number;
published_fail?: number;
latency_ms?: number;
error_kind?: string;
error?: string;
[k: string]: unknown;
};
};
export type UmeKeyAlertRuleItem = {
@ -57,18 +44,6 @@ export type UmeInventoryNeTypeItem = {
ne_count: number;
};
export type UmeKeyAlertForwarderStatus = {
enabled: boolean;
operational?: boolean;
paused?: boolean;
connected: boolean;
queue_size: number;
url: string;
published_ok?: number;
published_fail?: number;
queued_total?: number;
};
export type UmeKeyAlertMonitorResponse = {
ok: boolean;
rules: UmeKeyAlertRuleItem[];
@ -78,10 +53,8 @@ export type UmeKeyAlertMonitorResponse = {
config?: {
forward_on_clear: boolean;
};
/** Primary: NetX hub ← netxops clients (multi-subscriber). */
/** NetX hub ← netxops clients (multi-subscriber). */
dsh_alarm_hub?: DshAlarmHubStatus;
/** Legacy: NetX → OClaw outbound bridge (single link). */
forwarder: UmeKeyAlertForwarderStatus;
};
export type UmeSyncJobItem = {

View file

@ -1,4 +1,4 @@
/** Map backend runtime status codes (rt:* / ws:* / fwd:*) and legacy Chinese to i18n keys. */
/** Map backend runtime status codes (rt:* / ws:*) and legacy Chinese to i18n keys. */
type TFunc = (key: string) => string;
@ -11,10 +11,8 @@ const LEGACY_ZH_RUNTIME_ERROR: Record<string, string> = {
"正在拉取 UME 网元清单…": "ume.tasks.runtimeError.pulling_inventory",
"正在拉取 UME 拓扑(节点/链路)…": "ume.tasks.runtimeError.pulling_topology",
"未启用或未配置 UME_BASE_URL": "ume.tasks.runtimeError.ume_ws_disabled_no_base_url",
"未启用或未配置 NETX_OCLAW_ALARM_WS / token / url": "ume.tasks.runtimeError.oclaw_fwd_disabled",
"已恢复:将跳过本轮周期等待并尽快同步": "ume.tasks.runtimeError.resumed_sync_soon",
"已恢复:将尽快重连 WSS": "ume.tasks.runtimeError.resumed_wss_reconnect",
"已恢复:将尽快重连 OClaw WSS": "ume.tasks.runtimeError.resumed_oclaw_wss_reconnect",
已恢复: "ume.tasks.runtimeError.resumed",
初始化: "ume.tasks.wsState.init",
已连接: "ume.tasks.wsState.connected",
@ -45,8 +43,6 @@ export function runtimeLastError(raw: string | null | undefined, t: TFunc): stri
if (rt) return rt;
const ws = translatePrefixed(s, "ws:", "ume.tasks.wsState", t);
if (ws) return ws;
const fwd = translatePrefixed(s, "fwd:", "ume.tasks.fwdState", t);
if (fwd) return fwd;
if (s === "keepalive_failed") {
const tr = t("ume.tasks.runtimeError.keepalive_failed");