Fix hub report (v0.15.4)
This commit is contained in:
@@ -174,6 +174,20 @@ $SSH kisfenyo@192.168.0.162 "docker logs felhom-controller --tail 20"
|
||||
| 3. Deploy | `$SSH kisfenyo@192.168.0.162 "... docker compose up -d"` | Demo node |
|
||||
| 4. Verify | `$SSH kisfenyo@192.168.0.162 "docker ps ..."` | Demo node |
|
||||
|
||||
### Build & deploy workflow — Hub (felhom-hub)
|
||||
|
||||
The central hub (`hub.felhom.eu`) is a separate Go app in the `E:\git\felhom.eu\hub\` repo.
|
||||
The controller pushes periodic reports to it (when `hub.enabled: true` in `controller.yaml`).
|
||||
|
||||
| Step | Command | Where |
|
||||
|------|---------|-------|
|
||||
| 1. Commit + push | `cd /e/git/felhom.eu && git add -A && git commit && git push` | Local |
|
||||
| 2. Build + push image | `$SSH kisfenyo@192.168.0.180 "cd ~/build/felhom-hub && ./build.sh <VER> --push"` | Build server |
|
||||
| 3. Deploy to k3s | `$SSH kisfenyo@192.168.0.180 "sudo kubectl set image -n felhom-system deploy/hub hub=gitea.dooplex.hu/admin/felhom-hub:<VER>"` | Build server |
|
||||
| 4. Verify | `$SSH kisfenyo@192.168.0.180 "sudo kubectl get pods -n felhom-system -l app=hub && sudo kubectl logs -n felhom-system -l app=hub --tail 10"` | Build server |
|
||||
|
||||
See `E:\git\felhom.eu\CLAUDE.md` for full hub details.
|
||||
|
||||
**IMPORTANT:** If you make changes to the app-catalog-felhom.eu repo, commit and push those too:
|
||||
```bash
|
||||
cd /e/git/app-catalog-felhom.eu
|
||||
|
||||
@@ -1,291 +1,392 @@
|
||||
# TASK: Show all storage paths on dashboard + fix hub report (v0.15.3)
|
||||
# TASK: Fix hub report (v0.15.4)
|
||||
|
||||
Two issues after introducing partitioned system drives (OS and user data on separate partitions):
|
||||
Three areas of work:
|
||||
|
||||
1. **Dashboard & Monitoring pages** only show "SSD tárhely" (root `/`) and "Külső HDD" (one default storage path). Missing: the data partition on the system drive (`/mnt/sys_drive`). All registered storage paths should appear as separate bars.
|
||||
2. **Hub storage report** only sends root `/` and one HDD entry (with wrong mount path — uses deprecated `cfg.Paths.HDDPath` which is `""`). This causes the hub to display incorrect/stale storage info and a spurious "nem külön meghajtón van" warning.
|
||||
|
||||
## Current state (demo-felhom)
|
||||
|
||||
```
|
||||
sda1 ext4 → /mnt/sys_drive (user data partition, 320G)
|
||||
sda3 ext4 → / (OS partition, 139G)
|
||||
sdb1 ext4 → /mnt/hdd_1 (external USB HDD, 916G)
|
||||
```
|
||||
|
||||
Registered storage paths in `settings.json`:
|
||||
```json
|
||||
"storage_paths": [
|
||||
{"path": "/mnt/hdd_1", "label": "USB HDD 1TB", "is_default": true},
|
||||
{"path": "/mnt/sys_drive", "label": "SYS Storage 350G", "schedulable": true}
|
||||
]
|
||||
```
|
||||
|
||||
Config `controller.yaml`:
|
||||
- `paths.system_data_path: "/mnt/sys_drive"`
|
||||
- `paths.hdd_path: ""` (deprecated, empty)
|
||||
|
||||
Health check already works correctly: `IsMountPoint("/mnt/sys_drive")` returns `true` inside container (Dev 2049 ≠ parent Dev 2051). No code changes needed in `healthcheck.go`.
|
||||
1. **Hub storage report** — already fixed in `builder.go`/`types.go` (applied before this task).
|
||||
2. **Hub reporting lifecycle** — when hub reporting is disabled on a controller, the hub should know about it. Currently the hub just shows "DOWN" after reports stop arriving, with no distinction between "node crashed" vs "reporting turned off". The controller should send a one-time "disabled" notification so the hub can display the correct status. Also: hub report history should show dates (not just times), and hub should use storage labels.
|
||||
|
||||
---
|
||||
|
||||
## Fix 1: Dashboard & Monitoring — show all storage paths
|
||||
## Fix 1: Hub storage report — include all storage paths (ALREADY DONE)
|
||||
|
||||
### Root cause
|
||||
The following changes have ALREADY been applied:
|
||||
|
||||
`SystemInfo` struct only has fields for ONE SSD (root `/`) and ONE HDD. `GetInfo(hddPath)` takes a single HDD path. Templates hardcode "SSD tárhely" and "Külső HDD" labels.
|
||||
- **`controller/internal/report/types.go`** — `Label` field added to `StorageReport`
|
||||
- **`controller/internal/report/builder.go`** — Storage section now iterates over all `storagePaths` using `system.GetDiskUsage()`
|
||||
|
||||
### Fix approach
|
||||
These were applied before this task. No further changes needed in these files.
|
||||
|
||||
Pass a separate `StorageBars` list to templates alongside `SystemInfo` (which keeps SSD/root info). Each storage bar has label, usage, and color.
|
||||
---
|
||||
|
||||
### Implementation steps
|
||||
## Fix 2: Controller sends "disabled" notification when hub reporting is off
|
||||
|
||||
#### Step 1: Add `StorageBarInfo` type
|
||||
### Problem
|
||||
|
||||
In `controller/internal/web/handlers.go` (or a new small type near the top), add:
|
||||
When `hub.enabled: false` but `hub.url` and `hub.api_key` ARE configured, the controller sends nothing to the hub. The hub has no way to know whether the node is dead or reporting was intentionally turned off. It just shows "DOWN" after the stale threshold (30min+).
|
||||
|
||||
### Design
|
||||
|
||||
The intent behind the config values:
|
||||
- `hub.url` + `hub.api_key` configured → a relationship with the hub exists
|
||||
- `hub.enabled: false` → periodic reporting is turned off (but the hub relationship still exists)
|
||||
|
||||
So: when `hub.enabled == false` but URL + API key are present, the controller should send **one** "reporting disabled" notification on startup.
|
||||
|
||||
### Controller changes
|
||||
|
||||
#### Step 1: Add `ReportingDisabled` field to `Report`
|
||||
|
||||
**File:** `controller/internal/report/types.go`
|
||||
|
||||
Add a new field to the `Report` struct:
|
||||
|
||||
```go
|
||||
// StorageBarInfo holds data for rendering a storage usage bar on dashboard/monitoring.
|
||||
type StorageBarInfo struct {
|
||||
Label string // e.g., "USB HDD 1TB", "SYS Storage 350G"
|
||||
Path string // e.g., "/mnt/hdd_1"
|
||||
TotalGB float64
|
||||
UsedGB float64
|
||||
Percent float64
|
||||
type Report struct {
|
||||
Version int `json:"version"`
|
||||
CustomerID string `json:"customer_id"`
|
||||
CustomerName string `json:"customer_name"`
|
||||
ControllerVersion string `json:"controller_version"`
|
||||
Timestamp time.Time `json:"timestamp"`
|
||||
ReportingDisabled bool `json:"reporting_disabled,omitempty"`
|
||||
System SystemReport `json:"system"`
|
||||
Storage []StorageReport `json:"storage"`
|
||||
Containers ContainerReport `json:"containers"`
|
||||
Backup BackupReport `json:"backup"`
|
||||
Health HealthReport `json:"health"`
|
||||
Stacks StacksReport `json:"stacks"`
|
||||
}
|
||||
```
|
||||
|
||||
#### Step 2: Build storage bars in handlers
|
||||
#### Step 2: Add `PushOnce()` method to Pusher
|
||||
|
||||
Create a helper method on `Server`:
|
||||
**File:** `controller/internal/report/pusher.go`
|
||||
|
||||
The existing `Push()` method checks `p.enabled` and silently returns if false. We need a method that pushes regardless of the `enabled` flag, for the one-time disabled notification.
|
||||
|
||||
Add after `Push()`:
|
||||
|
||||
```go
|
||||
func (s *Server) buildStorageBars() []StorageBarInfo {
|
||||
var bars []StorageBarInfo
|
||||
for _, sp := range s.settings.GetStoragePaths() {
|
||||
di := system.GetDiskUsage(sp.Path)
|
||||
if di == nil {
|
||||
continue
|
||||
// PushOnce sends a single report regardless of the enabled flag.
|
||||
// Used for one-time notifications (e.g., reporting-disabled on startup).
|
||||
func (p *Pusher) PushOnce(report *Report) error {
|
||||
if p.hubURL == "" || p.apiKey == "" {
|
||||
return nil
|
||||
}
|
||||
bars = append(bars, StorageBarInfo{
|
||||
Label: sp.Label,
|
||||
Path: sp.Path,
|
||||
TotalGB: di.TotalGB,
|
||||
UsedGB: di.UsedGB,
|
||||
Percent: di.UsedPercent,
|
||||
|
||||
data, err := json.Marshal(report)
|
||||
if err != nil {
|
||||
p.logger.Printf("[WARN] Hub report marshal failed: %v", err)
|
||||
return nil
|
||||
}
|
||||
|
||||
url := p.hubURL + "/api/v1/report"
|
||||
|
||||
req, err := http.NewRequest(http.MethodPost, url, bytes.NewReader(data))
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
req.Header.Set("Authorization", "Bearer "+p.apiKey)
|
||||
|
||||
resp, err := p.httpClient.Do(req)
|
||||
if err != nil {
|
||||
p.logger.Printf("[WARN] Hub disabled-notification failed: %v", err)
|
||||
return nil
|
||||
}
|
||||
io.Copy(io.Discard, resp.Body)
|
||||
resp.Body.Close()
|
||||
|
||||
if resp.StatusCode >= 200 && resp.StatusCode < 300 {
|
||||
p.logger.Printf("[INFO] Hub disabled-notification sent (%d bytes)", len(data))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
```
|
||||
|
||||
#### Step 3: Send disabled notification on startup
|
||||
|
||||
**File:** `controller/cmd/controller/main.go` (~lines 248-261, 285-293)
|
||||
|
||||
Change the hub reporting section. Currently:
|
||||
|
||||
```go
|
||||
// --- Central hub reporting ---
|
||||
var hubPusher *report.Pusher
|
||||
if cfg.Hub.Enabled && cfg.Hub.URL != "" {
|
||||
// ... create pusher, register scheduler ...
|
||||
}
|
||||
```
|
||||
|
||||
Replace with:
|
||||
|
||||
```go
|
||||
// --- Central hub reporting ---
|
||||
var hubPusher *report.Pusher
|
||||
if cfg.Hub.URL != "" && cfg.Hub.APIKey != "" {
|
||||
hubPusher = report.NewPusher(&cfg.Hub, logger)
|
||||
if cfg.Hub.Enabled {
|
||||
pushInterval, err := time.ParseDuration(cfg.Hub.PushInterval)
|
||||
if err != nil {
|
||||
pushInterval = 15 * time.Minute
|
||||
}
|
||||
sched.Every("hub-report", pushInterval, func(ctx context.Context) error {
|
||||
r := report.BuildReport(cfg, stackMgr, backupMgr, cpuCollector, metricsStore, Version, sett.GetStoragePaths())
|
||||
return hubPusher.Push(r)
|
||||
})
|
||||
logger.Printf("[INFO] Hub reporting enabled (every %s to %s)", pushInterval, cfg.Hub.URL)
|
||||
} else {
|
||||
logger.Printf("[INFO] Hub reporting disabled — will send disabled notification to %s", cfg.Hub.URL)
|
||||
}
|
||||
return bars
|
||||
}
|
||||
```
|
||||
|
||||
In `dashboardHandler()` (~line 84-91), ADD after the SystemInfo line:
|
||||
Then in the startup goroutine (~line 285-293), change the hub report block:
|
||||
|
||||
```go
|
||||
data["StorageBars"] = s.buildStorageBars()
|
||||
// Hub report
|
||||
if hubPusher != nil {
|
||||
if cfg.Hub.Enabled {
|
||||
r := report.BuildReport(cfg, stackMgr, backupMgr, cpuCollector, metricsStore, Version, sett.GetStoragePaths())
|
||||
if err := hubPusher.Push(r); err != nil {
|
||||
logger.Printf("[WARN] Startup hub report failed: %v", err)
|
||||
} else {
|
||||
logger.Println("[INFO] Startup hub report sent")
|
||||
}
|
||||
} else {
|
||||
// Send a minimal "disabled" notification so hub knows we're alive but not reporting
|
||||
r := &report.Report{
|
||||
Version: 1,
|
||||
CustomerID: cfg.Customer.ID,
|
||||
CustomerName: cfg.Customer.Name,
|
||||
ControllerVersion: Version,
|
||||
Timestamp: time.Now().UTC(),
|
||||
ReportingDisabled: true,
|
||||
Health: report.HealthReport{Status: "disabled", Issues: []string{}, Warnings: []string{}},
|
||||
Stacks: report.StacksReport{Deployed: []string{}, Available: []string{}},
|
||||
Containers: report.ContainerReport{List: []report.ContainerDetailReport{}},
|
||||
}
|
||||
hubPusher.PushOnce(r)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
In `monitoringHandler()` (~line 365), ADD:
|
||||
**Note:** The `Report`, `HealthReport`, `StacksReport`, `ContainerReport`, and `ContainerDetailReport` types are all in `controller/internal/report/types.go`. Import the `report` package (already imported as it's used on the line above). The minimal report includes empty slices for all list fields so JSON serialization produces `[]` not `null`.
|
||||
|
||||
**Note:** `NewPusher` sets `enabled: cfg.Enabled`. When `cfg.Hub.Enabled == false`, `Push()` will silently return nil. The startup code uses `PushOnce()` instead for the disabled notification, and the scheduler is never registered, so the `enabled` flag doesn't matter. No change needed to `NewPusher`.
|
||||
|
||||
---
|
||||
|
||||
## Fix 4: Hub — handle "disabled" status + show dates in history
|
||||
|
||||
### Repo: `E:\git\felhom.eu` (hub code at `hub/`)
|
||||
|
||||
### 4a: Hub status logic — handle "disabled"
|
||||
|
||||
**File:** `hub/internal/web/server.go`
|
||||
|
||||
The `handleDashboard()` function (~line 142-151) determines `OverallStatus`:
|
||||
|
||||
```go
|
||||
data["StorageBars"] = s.buildStorageBars()
|
||||
if c.TimeSinceReport > time.Hour {
|
||||
dc.OverallStatus = "down"
|
||||
} else if c.TimeSinceReport > 30*time.Minute || c.HealthStatus == "warn" {
|
||||
dc.OverallStatus = "warn"
|
||||
} else if c.HealthStatus == "fail" {
|
||||
dc.OverallStatus = "down"
|
||||
} else {
|
||||
dc.OverallStatus = "ok"
|
||||
}
|
||||
```
|
||||
|
||||
#### Step 3: Update dashboard template
|
||||
**Replace with:**
|
||||
|
||||
**File:** `controller/internal/web/templates/dashboard.html` (~lines 58-91)
|
||||
```go
|
||||
if c.HealthStatus == "disabled" {
|
||||
dc.OverallStatus = "disabled"
|
||||
} else if c.TimeSinceReport > time.Hour {
|
||||
dc.OverallStatus = "down"
|
||||
} else if c.TimeSinceReport > 30*time.Minute || c.HealthStatus == "warn" {
|
||||
dc.OverallStatus = "warn"
|
||||
} else if c.HealthStatus == "fail" {
|
||||
dc.OverallStatus = "down"
|
||||
} else {
|
||||
dc.OverallStatus = "ok"
|
||||
}
|
||||
```
|
||||
|
||||
Replace the storage section. Keep the SSD bar as-is. Replace the single `{{if .SystemInfo.HDDConfigured}}` HDD block with a range over `StorageBars`:
|
||||
Same change in `handleCustomerDetail()` (~line 200-208):
|
||||
|
||||
**Current code** (lines 68-78):
|
||||
```go
|
||||
overallStatus := "ok"
|
||||
if customer.HealthStatus == "disabled" {
|
||||
overallStatus = "disabled"
|
||||
} else if customer.TimeSinceReport > time.Hour {
|
||||
// ...existing logic...
|
||||
```
|
||||
|
||||
Add "disabled" status color and icon to the existing functions:
|
||||
|
||||
**In `statusColor()`:**
|
||||
```go
|
||||
case "disabled":
|
||||
return "#94a3b8" // gray (same as default)
|
||||
```
|
||||
|
||||
### 4b: Dashboard template — show "PAUSED" badge
|
||||
|
||||
**File:** `hub/internal/web/templates/dashboard.html` (line 46)
|
||||
|
||||
**Current:**
|
||||
```html
|
||||
{{if .SystemInfo.HDDConfigured}}
|
||||
<div class="system-info-item">
|
||||
<div class="system-info-header">
|
||||
<span class="system-info-label">Külső HDD</span>
|
||||
<span class="system-info-value">{{fmtGB .SystemInfo.HDDUsedGB}} / {{fmtGB .SystemInfo.HDDTotalGB}} ({{printf "%.0f" .SystemInfo.HDDPercent}}%)</span>
|
||||
</div>
|
||||
<div class="system-bar">
|
||||
<div class="system-bar-fill system-bar-{{usageColor .SystemInfo.HDDPercent}}" style="width:{{printf "%.0f" .SystemInfo.HDDPercent}}%"></div>
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
{{if eq .OverallStatus "ok"}}OK{{else if eq .OverallStatus "warn"}}WARN{{else}}DOWN{{end}}
|
||||
```
|
||||
|
||||
**Replace with:**
|
||||
```html
|
||||
{{range .StorageBars}}
|
||||
<div class="system-info-item">
|
||||
<div class="system-info-header">
|
||||
<span class="system-info-label">{{.Label}}</span>
|
||||
<span class="system-info-value">{{fmtGB .UsedGB}} / {{fmtGB .TotalGB}} ({{printf "%.0f" .Percent}}%)</span>
|
||||
</div>
|
||||
<div class="system-bar">
|
||||
<div class="system-bar-fill system-bar-{{usageColor .Percent}}" style="width:{{printf "%.0f" .Percent}}%"></div>
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
{{if eq .OverallStatus "ok"}}OK{{else if eq .OverallStatus "warn"}}WARN{{else if eq .OverallStatus "disabled"}}PAUSED{{else}}DOWN{{end}}
|
||||
```
|
||||
|
||||
#### Step 4: Update monitoring template
|
||||
### 4c: Customer detail — show "Reporting disabled" message
|
||||
|
||||
**File:** `controller/internal/web/templates/monitoring.html` (~lines 53-63)
|
||||
**File:** `hub/internal/web/templates/customer.html`
|
||||
|
||||
Same pattern — replace the `{{if .HDDConfigured}}` block:
|
||||
In the Health section (~line 130-155), add a check for disabled status BEFORE the existing `{{with .Report.health}}` block:
|
||||
|
||||
After line 132 (`<h2>Health</h2>`), add:
|
||||
|
||||
**Current code** (lines 53-63):
|
||||
```html
|
||||
{{if .HDDConfigured}}
|
||||
<div class="storage-item">
|
||||
<div class="storage-header">
|
||||
<span class="storage-label">Külső HDD</span>
|
||||
<span class="storage-value">{{fmtGB .HDDUsedGB}} / {{fmtGB .HDDTotalGB}} ({{printf "%.0f" .HDDPercent}}%)</span>
|
||||
</div>
|
||||
<div class="system-bar">
|
||||
<div class="system-bar-fill {{usageColor .HDDPercent | printf "system-bar-%s"}}" style="width:{{printf "%.1f" .HDDPercent}}%"></div>
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
{{if eq .OverallStatus "disabled"}}
|
||||
<p class="health-status health-status-disabled">Reporting has been disabled on this node</p>
|
||||
<p class="hint">Enable it in the controller's <code>controller.yaml</code>: <code>hub.enabled: true</code></p>
|
||||
{{else}}
|
||||
```
|
||||
|
||||
**Replace with** (note: this block is inside `{{with .SystemInfo}}` so we need to access root data with `$`):
|
||||
And after the existing `{{end}}` that closes `{{with .Report.health}}` (line 155), add:
|
||||
|
||||
```html
|
||||
{{range $.StorageBars}}
|
||||
<div class="storage-item">
|
||||
<div class="storage-header">
|
||||
<span class="storage-label">{{.Label}}</span>
|
||||
<span class="storage-value">{{fmtGB .UsedGB}} / {{fmtGB .TotalGB}} ({{printf "%.0f" .Percent}}%)</span>
|
||||
</div>
|
||||
<div class="system-bar">
|
||||
<div class="system-bar-fill {{usageColor .Percent | printf "system-bar-%s"}}" style="width:{{printf "%.1f" .Percent}}%"></div>
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
```
|
||||
|
||||
**IMPORTANT:** The monitoring template uses `{{with .SystemInfo}}` context (line 43), so `$.StorageBars` is needed to access the root template data. Check the exact template context before editing.
|
||||
So the full Health section becomes:
|
||||
```html
|
||||
<section class="card">
|
||||
<h2>Health</h2>
|
||||
{{if eq .OverallStatus "disabled"}}
|
||||
<p class="health-status health-status-disabled">Reporting has been disabled on this node</p>
|
||||
<p class="hint">Enable it in the controller's <code>controller.yaml</code>: <code>hub.enabled: true</code></p>
|
||||
{{else}}
|
||||
{{with .Report.health}}
|
||||
... existing content ...
|
||||
{{end}}
|
||||
{{end}}
|
||||
</section>
|
||||
```
|
||||
|
||||
#### Step 5: Template function access
|
||||
### 4d: Storage labels — use `label` with fallback to `mount`
|
||||
|
||||
The `fmtGB` and `usageColor` template functions are already registered in `funcmap.go`. The `StorageBarInfo` fields (`TotalGB`, `UsedGB`, `Percent`) are `float64` — same types used by the existing SSD/HDD fields. No funcmap changes needed.
|
||||
**File:** `hub/internal/web/templates/customer.html` (line 65)
|
||||
|
||||
**Current:**
|
||||
```html
|
||||
<span class="metric-label">{{index . "mount"}}</span>
|
||||
```
|
||||
|
||||
**Replace with:**
|
||||
```html
|
||||
<span class="metric-label">{{with index . "label"}}{{.}}{{else}}{{index . "mount"}}{{end}}</span>
|
||||
```
|
||||
|
||||
### 4e: Report history — show date + time
|
||||
|
||||
**File:** `hub/internal/web/templates/customer.html` (line 216)
|
||||
|
||||
**Current:**
|
||||
```html
|
||||
<td>{{.ReceivedAt.Format "15:04:05"}}</td>
|
||||
```
|
||||
|
||||
**Replace with:**
|
||||
```html
|
||||
<td>{{.ReceivedAt.Format "Jan 02 15:04"}}</td>
|
||||
```
|
||||
|
||||
This shows "Feb 18 17:10" instead of just "17:10:54", making it clear when reports are from different days.
|
||||
|
||||
### 4f: CSS for disabled status badge
|
||||
|
||||
**File:** `hub/internal/web/templates/style.css`
|
||||
|
||||
Search for `.status-badge-` CSS rules. Add a rule for `disabled`:
|
||||
|
||||
```css
|
||||
.status-badge-disabled {
|
||||
background: #475569;
|
||||
color: #e2e8f0;
|
||||
}
|
||||
```
|
||||
|
||||
Use a neutral gray — not red (DOWN) or yellow (WARN). This visually signals "intentionally paused", not "something is wrong".
|
||||
|
||||
---
|
||||
|
||||
## Fix 2: Hub storage report — include all storage paths
|
||||
## Summary of all changes
|
||||
|
||||
### Root cause
|
||||
|
||||
In `controller/internal/report/builder.go` lines 62-72:
|
||||
|
||||
```go
|
||||
r.Storage = []StorageReport{
|
||||
{Mount: "/", TotalGB: sysInfo.DiskTotalGB, UsedGB: sysInfo.DiskUsedGB, Percent: sysInfo.DiskPercent},
|
||||
}
|
||||
if sysInfo.HDDConfigured {
|
||||
r.Storage = append(r.Storage, StorageReport{
|
||||
Mount: cfg.Paths.HDDPath, // BUG: empty string "", not the actual path
|
||||
TotalGB: sysInfo.HDDTotalGB,
|
||||
...
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
Two bugs:
|
||||
1. `Mount: cfg.Paths.HDDPath` is `""` (deprecated field), so the hub receives an empty mount path
|
||||
2. Only ONE extra storage entry is sent — misses additional storage paths like `/mnt/sys_drive`
|
||||
|
||||
### Fix
|
||||
|
||||
**File:** `controller/internal/report/builder.go`
|
||||
|
||||
#### Step 1: Add `Label` field to `StorageReport`
|
||||
|
||||
**File:** `controller/internal/report/types.go` (~line 39-44)
|
||||
|
||||
Add a `Label` field:
|
||||
|
||||
```go
|
||||
type StorageReport struct {
|
||||
Mount string `json:"mount"`
|
||||
Label string `json:"label,omitempty"`
|
||||
TotalGB float64 `json:"total_gb"`
|
||||
UsedGB float64 `json:"used_gb"`
|
||||
Percent float64 `json:"percent"`
|
||||
}
|
||||
```
|
||||
|
||||
#### Step 2: Replace storage section in BuildReport
|
||||
|
||||
**File:** `controller/internal/report/builder.go` (~lines 62-72)
|
||||
|
||||
Replace:
|
||||
```go
|
||||
// Storage
|
||||
r.Storage = []StorageReport{
|
||||
{Mount: "/", TotalGB: sysInfo.DiskTotalGB, UsedGB: sysInfo.DiskUsedGB, Percent: sysInfo.DiskPercent},
|
||||
}
|
||||
if sysInfo.HDDConfigured {
|
||||
r.Storage = append(r.Storage, StorageReport{
|
||||
Mount: cfg.Paths.HDDPath,
|
||||
TotalGB: sysInfo.HDDTotalGB,
|
||||
UsedGB: sysInfo.HDDUsedGB,
|
||||
Percent: sysInfo.HDDPercent,
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
With:
|
||||
```go
|
||||
// Storage — root filesystem + all registered storage paths
|
||||
r.Storage = []StorageReport{
|
||||
{Mount: "/", Label: "SSD", TotalGB: sysInfo.DiskTotalGB, UsedGB: sysInfo.DiskUsedGB, Percent: sysInfo.DiskPercent},
|
||||
}
|
||||
for _, sp := range storagePaths {
|
||||
di := system.GetDiskUsage(sp.Path)
|
||||
if di == nil {
|
||||
continue
|
||||
}
|
||||
r.Storage = append(r.Storage, StorageReport{
|
||||
Mount: sp.Path,
|
||||
Label: sp.Label,
|
||||
TotalGB: di.TotalGB,
|
||||
UsedGB: di.UsedGB,
|
||||
Percent: di.UsedPercent,
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
**Note:** This requires adding `"gitea.dooplex.hu/admin/felhom-controller/internal/system"` to the imports in `builder.go` (it may already be imported — check first).
|
||||
|
||||
---
|
||||
|
||||
## Summary of files to modify
|
||||
### Controller repo (`deploy-felhom-compose`)
|
||||
|
||||
| File | Change |
|
||||
|------|--------|
|
||||
| `controller/internal/web/handlers.go` | Add `StorageBarInfo` type, `buildStorageBars()` method, pass `StorageBars` to dashboard/monitoring handlers |
|
||||
| `controller/internal/web/templates/dashboard.html` | Replace hardcoded HDD bar with `{{range .StorageBars}}` |
|
||||
| `controller/internal/web/templates/monitoring.html` | Replace hardcoded HDD bar with `{{range $.StorageBars}}` |
|
||||
| `controller/internal/report/builder.go` | Replace storage section to iterate over all `storagePaths` with `system.GetDiskUsage()` |
|
||||
| `controller/internal/report/types.go` | Add `Label` field to `StorageReport` |
|
||||
| `controller/internal/report/types.go` | Add `ReportingDisabled bool` field to `Report` struct (Label already applied) |
|
||||
| `controller/internal/report/pusher.go` | Add `PushOnce()` method |
|
||||
| `controller/cmd/controller/main.go` | Always create Pusher when URL+key present; send disabled notification when `hub.enabled == false` |
|
||||
|
||||
### Hub repo (`felhom.eu`)
|
||||
|
||||
| File | Change |
|
||||
|------|--------|
|
||||
| `hub/internal/web/server.go` | Handle `"disabled"` in status logic (dashboard + detail handlers), add disabled color |
|
||||
| `hub/internal/web/templates/dashboard.html` | Show "PAUSED" badge for disabled status |
|
||||
| `hub/internal/web/templates/customer.html` | Disabled health message, storage labels, date+time in history |
|
||||
| `hub/internal/web/templates/style.css` | Add `.status-badge-disabled` CSS |
|
||||
|
||||
### Runtime config (demo node)
|
||||
|
||||
| File | Change |
|
||||
|------|--------|
|
||||
| `/opt/docker/felhom-controller/controller.yaml` | Set `hub.enabled: true` |
|
||||
|
||||
---
|
||||
|
||||
## Build & Deploy
|
||||
|
||||
Version: **v0.15.3**
|
||||
### Part A: Controller (v0.15.4)
|
||||
|
||||
### Build workflow
|
||||
```bash
|
||||
SSH=/c/Windows/System32/OpenSSH/ssh.exe
|
||||
# 1. Commit & push
|
||||
cd e:/git/deploy-felhom-compose
|
||||
git add -A && git commit -m "v0.15.3: Show all storage paths on dashboard/monitoring + fix hub report" && git push
|
||||
git add -A && git commit -m "v0.15.4: Show all storage paths on dashboard/monitoring, hub disabled notification" && git push
|
||||
# 2. Build
|
||||
$SSH kisfenyo@192.168.0.180 "cd ~/build/felhom-controller && git -C ~/git/deploy-felhom-compose pull && ./build.sh v0.15.3 --push"
|
||||
# 3. Deploy
|
||||
$SSH kisfenyo@192.168.0.162 "cd /opt/docker/felhom-controller && sudo docker pull gitea.dooplex.hu/admin/felhom-controller:v0.15.3 && sudo sed -i 's|image: gitea.dooplex.hu/admin/felhom-controller:.*|image: gitea.dooplex.hu/admin/felhom-controller:v0.15.3|' docker-compose.yml && sudo docker compose up -d"
|
||||
$SSH kisfenyo@192.168.0.180 "cd ~/build/felhom-controller && git -C ~/git/deploy-felhom-compose pull && ./build.sh v0.15.4 --push"
|
||||
# 3. Enable hub reporting + deploy
|
||||
$SSH kisfenyo@192.168.0.162 "cd /opt/docker/felhom-controller && sudo sed -i 's/enabled: false/enabled: true/' controller.yaml && sudo docker pull gitea.dooplex.hu/admin/felhom-controller:v0.15.4 && sudo sed -i 's|image: gitea.dooplex.hu/admin/felhom-controller:.*|image: gitea.dooplex.hu/admin/felhom-controller:v0.15.4|' docker-compose.yml && sudo docker compose up -d"
|
||||
# 4. Verify
|
||||
$SSH kisfenyo@192.168.0.162 "docker ps --filter name=felhom-controller --format '{{.Image}} {{.Status}}'"
|
||||
# 5. Check hub reporting is active
|
||||
$SSH kisfenyo@192.168.0.162 "docker logs felhom-controller --tail 5 2>&1 | grep -i hub"
|
||||
```
|
||||
|
||||
### Part B: Hub (v0.1.6)
|
||||
|
||||
```bash
|
||||
# 1. Commit & push
|
||||
cd e:/git/felhom.eu
|
||||
git add -A && git commit -m "hub v0.1.6: Handle disabled reporting status, storage labels, date in history" && git push
|
||||
# 2. Build + push
|
||||
$SSH kisfenyo@192.168.0.180 "cd ~/build/felhom-hub && ./build.sh 0.1.6 --push"
|
||||
# 3. Deploy to k3s
|
||||
$SSH kisfenyo@192.168.0.180 "sudo kubectl set image -n felhom-system deploy/hub hub=gitea.dooplex.hu/admin/felhom-hub:0.1.6"
|
||||
# 4. Verify
|
||||
$SSH kisfenyo@192.168.0.180 "sudo kubectl get pods -n felhom-system -l app=hub && echo '---' && sudo kubectl logs -n felhom-system -l app=hub --tail 10"
|
||||
```
|
||||
|
||||
### Compile check
|
||||
@@ -297,21 +398,25 @@ Add a CHANGELOG.md entry at the top (under `## Changelog`). Read the first 30 li
|
||||
|
||||
```markdown
|
||||
### vX.X.X (2026-02-19 session XX)
|
||||
- **v0.15.3 — Show all storage paths on dashboard + fix hub report:**
|
||||
- **v0.15.4 — Show all storage paths on dashboard + hub reporting improvements:**
|
||||
|
||||
Dashboard ("Vezérlőpult") and monitoring ("Rendszermonitor") pages now show usage bars for ALL registered storage paths instead of just one hardcoded "Külső HDD" bar. Each bar displays the storage label and usage from settings.
|
||||
|
||||
Hub storage report now correctly includes all registered storage paths with proper mount paths and labels. Previously it sent only root `/` and one HDD entry with an empty mount path (used deprecated `cfg.Paths.HDDPath`).
|
||||
|
||||
**Files modified:** `internal/web/handlers.go`, `internal/web/templates/dashboard.html`, `internal/web/templates/monitoring.html`, `internal/report/builder.go`, `internal/report/types.go`
|
||||
When hub reporting is disabled (`hub.enabled: false`) but hub URL/key are configured, the controller now sends a one-time "disabled" notification so the hub shows "PAUSED" instead of "DOWN".
|
||||
|
||||
**Files modified:** `internal/web/handlers.go`, `internal/web/templates/dashboard.html`, `internal/web/templates/monitoring.html`, `internal/report/types.go`, `internal/report/pusher.go`, `cmd/controller/main.go`
|
||||
**Hub files:** `hub/internal/web/server.go`, `hub/internal/web/templates/dashboard.html`, `hub/internal/web/templates/customer.html`, `hub/internal/web/templates/style.css`
|
||||
```
|
||||
|
||||
Update version in `C:\Users\User\.claude\projects\e--git\memory\MEMORY.md` to `v0.15.3`.
|
||||
Update version in `C:\Users\User\.claude\projects\e--git\memory\MEMORY.md` to `v0.15.4`.
|
||||
|
||||
## Verification
|
||||
|
||||
After deploying v0.15.3:
|
||||
1. Navigate to `/` (Vezérlőpult) — should show three bars: "SSD tárhely" + "USB HDD 1TB" + "SYS Storage 350G"
|
||||
2. Navigate to `/monitoring` (Rendszermonitor) — same three bars in Tárhely section
|
||||
3. Check hub at `hub.felhom.eu/customers/demo-felhom` — Health section should show no storage warnings; Storage should list all three mounts
|
||||
4. No health warning about "nem külön meghajtón van" should appear (IsMountPoint already returns true for `/mnt/sys_drive`)
|
||||
After deploying v0.15.4 (controller) and v0.1.6 (hub):
|
||||
1. Wait ~30s for startup hub report, then check `hub.felhom.eu/customers/demo-felhom`:
|
||||
- Health section should show **STATUS: OK** with no warnings (since hub.enabled is now true)
|
||||
- Storage section should show three bars with labels: "SSD", "USB HDD 1TB", "SYS Storage 350G"
|
||||
- Report history should show dates (e.g., "Feb 19 09:46")
|
||||
2. To test PAUSED state: set `hub.enabled: false` on demo node, restart controller. Hub should show "PAUSED" (gray badge), not "DOWN" (red)
|
||||
|
||||
Reference in New Issue
Block a user