feat(nodes): add distinct purple indicator when panel is online but Xray core failed (#5040)

* feat(nodes): add distinct purple indicator when panel is online but Xray core failed

Currently nodes only show binary online/offline based on panel API reachability.

This adds a third state:
- Green: panel reachable + Xray healthy
- Purple pulsing dot + "Online (Xray Error)": panel API works (management actions still available) but the node Xray process is in error or stopped. Tooltip shows the remote xrayError.
- Red: unreachable (unchanged)

Backend now captures xray.state + xray.errorMsg from /panel/api/server/status heartbeats and probes.
New fields on Node + NodeSummary, forwarded for transitive nodes.
Frontend Zod + NodeList rendering + dedicated .xray-error-dot CSS (color #722ED1) + i18n key.

Color chosen purple per feedback after initial implementation.

Refs: worktree xray-failed-in-nodes

* fix: remove invalid JSON comment causing CI failures

* chore: regenerate OpenAPI schemas and types for xray error indicators

* chore: regenerate examples and schemas for xray error indicators

* chore: regenerate missing openapi.json examples

* fix

---------

Co-authored-by: Rqzbeh <rqzbeh@users.noreply.github.com>
Co-authored-by: Sanaei <ho3ein.sanaei@gmail.com>
This commit is contained in:
Rouzbeh†
2026-06-08 20:24:00 +02:00
committed by GitHub
parent 0daedd3db9
commit 1c74b995c3
25 changed files with 239 additions and 49 deletions
+23
View File
@@ -41,3 +41,26 @@
@media (prefers-reduced-motion: reduce) {
.online-dot { animation: none; }
}
/* Purple indicator for nodes that are reachable via the panel API (status=online)
but have Xray core in "error" or "stop" state. This is the new "xray failed on node"
monitoring state. */
.xray-error-dot {
display: inline-block;
width: 7px;
height: 7px;
border-radius: 50%;
margin-inline-end: 5px;
vertical-align: middle;
background: #722ED1;
animation: xray-error-blink 1.1s ease-in-out infinite;
}
@keyframes xray-error-blink {
0%, 100% { opacity: 1; box-shadow: 0 0 0 0 rgba(114, 46, 209, 0.55); }
50% { opacity: 0.35; box-shadow: 0 0 0 4px rgba(114, 46, 209, 0); }
}
@media (prefers-reduced-motion: reduce) {
.xray-error-dot { animation: none; }
}