mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-06-06 21:24:10 +00:00
fix: replace a-descriptions with HTML table and fix ensureDefaultNodeSettings
- Replace a-descriptions/a-descriptions-item with plain HTML table in nodes.html — the components were missing from the antd.min.js bundle due to tree-shaking, causing the worker node view to render empty - Fix ensureDefaultNodeSettings to write defaults to both "node" and "other" groups for backward compatibility (tests were failing)
This commit is contained in:
parent
e4855447cc
commit
4e49f8c072
4 changed files with 92 additions and 25 deletions
|
|
@ -210,28 +210,25 @@ func settingsLayoutMeta() map[string]any {
|
|||
}
|
||||
|
||||
func ensureDefaultNodeSettings(settings map[string]any) {
|
||||
group, ok := settings["node"].(map[string]any)
|
||||
if !ok {
|
||||
group = make(map[string]any)
|
||||
settings["node"] = group
|
||||
}
|
||||
|
||||
defaults := map[string]string{
|
||||
"nodeRole": string(NodeRoleMaster),
|
||||
"nodeId": "",
|
||||
"syncInterval": "30",
|
||||
"trafficFlushInterval": "10",
|
||||
}
|
||||
for key, value := range defaults {
|
||||
if existing, exists := group[key]; !exists || existing == nil {
|
||||
// Also check "other" group for backward compatibility
|
||||
if otherGroup, ok := settings["other"].(map[string]any); ok {
|
||||
if val, ok := otherGroup[key].(string); ok && val != "" {
|
||||
group[key] = val
|
||||
continue
|
||||
}
|
||||
|
||||
// Ensure both "node" and "other" groups have the defaults for backward
|
||||
// compatibility. Old code reads from "other", new code reads from "node".
|
||||
for _, groupName := range []string{"node", "other"} {
|
||||
group, ok := settings[groupName].(map[string]any)
|
||||
if !ok {
|
||||
group = make(map[string]any)
|
||||
settings[groupName] = group
|
||||
}
|
||||
for key, value := range defaults {
|
||||
if existing, exists := group[key]; !exists || existing == nil {
|
||||
group[key] = value
|
||||
}
|
||||
group[key] = value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,6 +25,8 @@ Adding a Node Management sidebar page to the 3x-ui web panel for cluster node vi
|
|||
| 10 | Fix shared MariaDB query for node states | DONE | d5bf2858 |
|
||||
| 11 | Fix node settings not auto-created in x-ui.json | DONE | d733ff2a |
|
||||
| 12 | Fix master heartbeat not visible to workers | DONE | 226bae2b |
|
||||
| 13 | Fix ensureDefaultNodeSettings to write both "node" and "other" groups | DONE | — |
|
||||
| 14 | Replace a-descriptions with HTML table (component missing from antd bundle) | DONE | — |
|
||||
|
||||
## v1.6.3 Fix Details
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,48 @@
|
|||
# 2026-04-25 Fix ensureDefaultNodeSettings and worker node display
|
||||
|
||||
## Problem
|
||||
|
||||
### 1. Test failures in config package
|
||||
Two tests were failing:
|
||||
- `TestWriteSettingToJSONCreatesSettingsFileWhenMissing`
|
||||
- `TestWriteSettingToJSONBackfillsDefaultNodeSettings`
|
||||
|
||||
Both failed with: `expected other group, got <nil>`
|
||||
|
||||
### 2. Worker frontend not showing connected master node
|
||||
The worker's node management page rendered the card structure but didn't display the
|
||||
master node information. The `a-descriptions` and `a-descriptions-item` components were
|
||||
used in the template but were NOT included in the Ant Design Vue bundle (`antd.min.js`).
|
||||
Vue silently skipped the unregistered components, resulting in an empty card body.
|
||||
|
||||
## Root Cause
|
||||
|
||||
### Test failures
|
||||
`ensureDefaultNodeSettings()` only wrote defaults to the `"node"` group. Tests expected
|
||||
the `"other"` group to also have defaults for backward compatibility.
|
||||
|
||||
### Worker node display
|
||||
Ant Design Vue 2.x uses tree-shaking — only components actually imported during the build
|
||||
are included in the bundle. `a-descriptions` and `a-descriptions-item` were not imported
|
||||
in the project's Ant Design Vue build config, so they were missing from `antd.min.js`.
|
||||
When Vue encounters an unregistered component tag, it silently ignores it.
|
||||
|
||||
## Fix
|
||||
|
||||
### Test failures
|
||||
Changed `ensureDefaultNodeSettings()` to iterate over both `"node"` and `"other"` groups,
|
||||
writing defaults to both for backward compatibility.
|
||||
|
||||
### Worker node display
|
||||
Replaced `a-descriptions` / `a-descriptions-item` with a plain HTML `<table>` that
|
||||
replicates the same visual layout (label-value pairs with borders). This doesn't depend
|
||||
on any Ant Design Vue component.
|
||||
|
||||
## Files Changed
|
||||
|
||||
- `config/config.go`: Modified `ensureDefaultNodeSettings()` to write to both groups
|
||||
- `web/html/nodes.html`: Replaced `a-descriptions` with HTML table
|
||||
|
||||
## Verification
|
||||
|
||||
- `go test -race -shuffle=on ./...` — all PASS
|
||||
|
|
@ -49,16 +49,36 @@
|
|||
</a-table>
|
||||
<div v-if="nodeRole === 'worker'">
|
||||
<a-empty v-if="nodes.length === 0" :description="nodeRole === 'master' ? '{{ i18n "pages.nodes.noWorkerNodes" }}' : '{{ i18n "pages.nodes.noMasterNode" }}'" />
|
||||
<a-descriptions v-else bordered size="small" :column="isMobile ? 1 : 2">
|
||||
<a-descriptions-item label='{{ i18n "pages.nodes.nodeId" }}'>[[ nodes[0].nodeId ]]</a-descriptions-item>
|
||||
<a-descriptions-item label='{{ i18n "pages.nodes.status" }}'>
|
||||
<a-badge :status="nodes[0].online ? 'success' : 'error'" :text="nodes[0].online ? '{{ i18n "pages.nodes.online" }}' : '{{ i18n "pages.nodes.offline" }}'" />
|
||||
</a-descriptions-item>
|
||||
<a-descriptions-item label='{{ i18n "pages.nodes.lastHeartbeat" }}'>[[ nodes[0].lastHeartbeatAt ? formatTime(nodes[0].lastHeartbeatAt) : '-' ]]</a-descriptions-item>
|
||||
<a-descriptions-item label='{{ i18n "pages.nodes.lastSync" }}'>[[ nodes[0].lastSyncAt ? formatTime(nodes[0].lastSyncAt) : '-' ]]</a-descriptions-item>
|
||||
<a-descriptions-item label='{{ i18n "pages.nodes.syncVersion" }}'>[[ nodes[0].lastSeenVersion ]]</a-descriptions-item>
|
||||
<a-descriptions-item label='{{ i18n "pages.nodes.error" }}'>[[ nodes[0].lastError || '-' ]]</a-descriptions-item>
|
||||
</a-descriptions>
|
||||
<table v-else class="ant-descriptions-table" style="width:100%;border-collapse:collapse;">
|
||||
<tbody>
|
||||
<tr style="border-bottom:1px solid #e8e8e8;">
|
||||
<td style="padding:8px 12px;font-weight:500;background:#fafafa;width:30%;">{{ i18n "pages.nodes.nodeId" }}</td>
|
||||
<td style="padding:8px 12px;">[[ nodes[0].nodeId ]]</td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e8e8e8;">
|
||||
<td style="padding:8px 12px;font-weight:500;background:#fafafa;">{{ i18n "pages.nodes.status" }}</td>
|
||||
<td style="padding:8px 12px;">
|
||||
<a-badge :status="nodes[0].online ? 'success' : 'error'" :text="nodes[0].online ? '{{ i18n "pages.nodes.online" }}' : '{{ i18n "pages.nodes.offline" }}'" />
|
||||
</td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e8e8e8;">
|
||||
<td style="padding:8px 12px;font-weight:500;background:#fafafa;">{{ i18n "pages.nodes.lastHeartbeat" }}</td>
|
||||
<td style="padding:8px 12px;">[[ nodes[0].lastHeartbeatAt ? formatTime(nodes[0].lastHeartbeatAt) : '-' ]]</td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e8e8e8;">
|
||||
<td style="padding:8px 12px;font-weight:500;background:#fafafa;">{{ i18n "pages.nodes.lastSync" }}</td>
|
||||
<td style="padding:8px 12px;">[[ nodes[0].lastSyncAt ? formatTime(nodes[0].lastSyncAt) : '-' ]]</td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e8e8e8;">
|
||||
<td style="padding:8px 12px;font-weight:500;background:#fafafa;">{{ i18n "pages.nodes.syncVersion" }}</td>
|
||||
<td style="padding:8px 12px;">[[ nodes[0].lastSeenVersion ]]</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="padding:8px 12px;font-weight:500;background:#fafafa;">{{ i18n "pages.nodes.error" }}</td>
|
||||
<td style="padding:8px 12px;">[[ nodes[0].lastError || '-' ]]</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<a-empty v-if="nodeRole === 'master' && nodes.length === 0" description='{{ i18n "pages.nodes.noWorkerNodes" }}' />
|
||||
</a-card>
|
||||
|
|
|
|||
Loading…
Reference in a new issue