v0.4.7: Protected Stack Detail Pages + Backup Page Caching
This commit is contained in:
@@ -1,274 +1,287 @@
|
||||
# TASK.md — v0.4.6: MariaDB Validation Fix + Dashboard & Protected Stack UX
|
||||
# TASK.md — v0.4.7: Protected Stack Detail Pages + Backup Page Caching
|
||||
|
||||
> Version bump: **v0.4.6**
|
||||
> Scope: Bug fix + 2 UI improvements
|
||||
> Version bump: **v0.4.7**
|
||||
> Scope: UX fix + performance fix
|
||||
|
||||
---
|
||||
|
||||
## Overview
|
||||
## Task 1: Protected stacks — enable detail page + click-through
|
||||
|
||||
Three items:
|
||||
### Problem
|
||||
|
||||
1. **Bugfix**: MariaDB dump validation false positive — header check fails because MariaDB 11.4+ prepends a sandbox comment before the dump header
|
||||
2. **UI**: Dashboard should only show deployed apps (not 47 "Nincs telepítve" entries)
|
||||
3. **UI**: Protected stacks (especially FileBrowser) should show subdomain URL + allow restart
|
||||
Protected stacks (filebrowser, traefik, cloudflared, felhom-controller) are excluded from the app detail page in two ways:
|
||||
|
||||
---
|
||||
1. **No click-through**: `data-href="/apps/{{.Meta.Slug}}"` is gated behind `{{if not .Protected}}` on both `dashboard.html` and `stacks.html`
|
||||
2. **No "Részletek" button**: The protected section in `stacks.html` only shows "Védett rendszerkomponens" badge + restart button — no "Részletek" link
|
||||
3. **Detail page works**: Manually navigating to `/apps/filebrowser` renders fine — so the handler and template already support it
|
||||
|
||||
## Task 1: Fix MariaDB dump validation false positive
|
||||
### Fix — Templates
|
||||
|
||||
### Root cause
|
||||
|
||||
MariaDB 11.4+ prepends a sandbox directive before the header comment:
|
||||
|
||||
```sql
|
||||
/*M!999999\- enable the sandbox mode */
|
||||
-- MariaDB dump 10.19-11.4.10-MariaDB, for debian-linux-gnu (x86_64)
|
||||
```
|
||||
|
||||
The validation code checks only the first line for `-- MariaDB dump` or `-- MySQL dump`, but line 1 is now `/*M!999999...*/`. So the header check fails with "MariaDB dump missing comment header".
|
||||
|
||||
### Fix
|
||||
|
||||
In `ValidateDump()` (file: `internal/backup/dbdump.go`), change the header check to scan the **first 10 lines** (not just line 1) for the expected pattern. Also accept `/*` and `/*!` lines as valid preamble.
|
||||
|
||||
For MariaDB/MySQL dumps, valid header patterns (any of these in the first 10 lines):
|
||||
- `-- MariaDB dump`
|
||||
- `-- MySQL dump`
|
||||
- `-- mysqldump`
|
||||
|
||||
For PostgreSQL dumps, valid header patterns:
|
||||
- `-- PostgreSQL database dump`
|
||||
|
||||
### Implementation
|
||||
|
||||
Find the header validation logic in `ValidateDump()`. Replace the "first line must be" check with a loop over the first 10 lines:
|
||||
|
||||
```go
|
||||
// Check header — scan first 10 lines for expected dump header
|
||||
scanner := bufio.NewScanner(file)
|
||||
headerFound := false
|
||||
linesChecked := 0
|
||||
for scanner.Scan() && linesChecked < 10 {
|
||||
line := scanner.Text()
|
||||
linesChecked++
|
||||
switch dbType {
|
||||
case DBTypeMariaDB:
|
||||
if strings.HasPrefix(line, "-- MariaDB dump") ||
|
||||
strings.HasPrefix(line, "-- MySQL dump") ||
|
||||
strings.HasPrefix(line, "-- mysqldump") {
|
||||
headerFound = true
|
||||
}
|
||||
case DBTypePostgres:
|
||||
if strings.HasPrefix(line, "-- PostgreSQL database dump") {
|
||||
headerFound = true
|
||||
}
|
||||
}
|
||||
if headerFound {
|
||||
break
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Verification
|
||||
|
||||
After deploying, trigger a manual backup ("Mentés most") and check the Adatbázisok table on the backup page. The romm MariaDB entry should show a green validation badge with table count instead of "Hiba".
|
||||
|
||||
---
|
||||
|
||||
## Task 2: Dashboard — show only deployed apps
|
||||
|
||||
### Current behavior
|
||||
|
||||
The "Alkalmazások állapota" section on the Vezérlőpult page shows **all** apps (deployed + not deployed), meaning 47+ "Nincs telepítve / Telepítés" entries clutter the dashboard.
|
||||
|
||||
### New behavior
|
||||
|
||||
Only show **deployed** apps (including protected infra stacks) on the dashboard. Non-deployed apps remain accessible via the Alkalmazások page.
|
||||
|
||||
### Implementation
|
||||
|
||||
In `dashboardHandler()` (`internal/web/handlers.go`), filter the stack list before passing to the template:
|
||||
|
||||
```go
|
||||
// Filter to deployed-only for dashboard
|
||||
stackList := s.stackMgr.GetStacks()
|
||||
var deployedStacks []stacks.Stack
|
||||
for _, st := range stackList {
|
||||
if st.Deployed || st.Protected {
|
||||
deployedStacks = append(deployedStacks, st)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Pass `deployedStacks` as `data["Stacks"]` to the template instead of the full `stackList`.
|
||||
|
||||
**Important**: The `TotalCount` stat card should still show the total count of all apps (deployed + not deployed), not just the filtered list. Keep using `len(stackList)` for that.
|
||||
|
||||
### Template change
|
||||
|
||||
In `dashboard.html`, update the section heading:
|
||||
**stacks.html**: Add `data-href` for protected stacks that have a slug, and add "Részletek" button:
|
||||
|
||||
Change the card opening div from:
|
||||
```html
|
||||
<h3>Telepített alkalmazások</h3>
|
||||
<div class="stack-detail-card ..." {{if not .Protected}} data-href="/apps/{{.Meta.Slug}}"{{end}}>
|
||||
```
|
||||
To:
|
||||
```html
|
||||
<div class="stack-detail-card ..." {{if .Meta.Slug}} data-href="/apps/{{.Meta.Slug}}"{{end}}>
|
||||
```
|
||||
|
||||
("Telepített alkalmazások" = "Installed applications")
|
||||
|
||||
No other template changes needed — the `{{range .Stacks}}` loop just iterates over fewer items.
|
||||
|
||||
### Edge case
|
||||
|
||||
If no user apps are deployed yet (fresh install), the section still shows protected infra stacks (traefik, cloudflared, felhom-controller, filebrowser) — this is reassuring to the customer.
|
||||
|
||||
---
|
||||
|
||||
## Task 3: FileBrowser — show URL + allow restart on protected stacks
|
||||
|
||||
### Current behavior
|
||||
|
||||
Protected stacks show only "Védett rendszerkomponens" badge with no actions or URL link, on both the dashboard compact list and the Alkalmazások detail cards.
|
||||
|
||||
### New behavior
|
||||
|
||||
For protected stacks that are **operational** (running):
|
||||
- Show the subdomain URL link if configured (e.g., `files.demo-felhom.eu ↗`)
|
||||
- Show "Újraindítás" (Restart) button
|
||||
- Keep "Védett" badge
|
||||
- NO stop/start/delete/update buttons (still protected from destructive actions)
|
||||
|
||||
### The subdomain problem
|
||||
|
||||
FileBrowser is deployed by `docker-setup.sh` as infrastructure — it may NOT have a `.felhom.yml` metadata file with the `subdomain` field set. Without it, the controller doesn't know FileBrowser's subdomain.
|
||||
|
||||
**Solution**: Add `.felhom.yml` creation to the `install_filebrowser()` function in `scripts/docker-setup.sh`.
|
||||
|
||||
For the demo node, create it manually after this deploy:
|
||||
|
||||
```bash
|
||||
sudo tee /opt/docker/stacks/filebrowser/.felhom.yml << 'EOF'
|
||||
display_name: Filebrowser
|
||||
slug: filebrowser
|
||||
description: Fájlkezelő a külső merevlemezhez
|
||||
subdomain: files
|
||||
category: storage
|
||||
app_info:
|
||||
tagline: Web-alapú fájlkezelő a külső merevlemezhez
|
||||
use_cases:
|
||||
- Fájlok böngészése és letöltése a külső HDD-ről
|
||||
- Médiafájlok megosztása családtagokkal
|
||||
- Dokumentumok feltöltése és kezelése
|
||||
first_steps:
|
||||
- Nyisd meg a files.DOMAIN címet a böngészőben
|
||||
- Jelentkezz be az admin fiókkal
|
||||
- Tallózd a /srv mappákat
|
||||
prerequisites:
|
||||
- Külső HDD csatlakoztatva és felcsatolva
|
||||
EOF
|
||||
```
|
||||
|
||||
### Template changes — stacks.html (Alkalmazások page)
|
||||
|
||||
Update the protected stack actions section in the detail card:
|
||||
This lets any stack with a slug (including protected ones with `.felhom.yml`) be clickable. Stacks without `.felhom.yml` (no slug) won't have the click handler — which is correct.
|
||||
|
||||
In the protected actions section, add "Részletek" link:
|
||||
```html
|
||||
{{if .Protected}}
|
||||
<span class="badge badge-protected">Védett rendszerkomponens</span>
|
||||
{{if isOperational .State}}
|
||||
<button class="btn btn-warning" onclick="stackAction('{{.Name}}', 'restart')">Újraindítás</button>
|
||||
{{end}}
|
||||
{{else if not .Deployed}}
|
||||
...existing deploy button...
|
||||
```
|
||||
|
||||
The subdomain URL is already shown above the actions for all stacks — it uses `{{if .Meta.Subdomain}}`. If `.felhom.yml` has `subdomain: files`, it should render automatically. Verify this is not gated behind `{{if not .Protected}}` — if it is, remove that guard.
|
||||
|
||||
### Template changes — dashboard.html (Vezérlőpult page)
|
||||
|
||||
In the compact stack list, update the protected stack section similarly:
|
||||
|
||||
```html
|
||||
{{if .Protected}}
|
||||
<span class="badge badge-protected">Védett</span>
|
||||
{{if isOperational .State}}
|
||||
<button class="btn btn-sm btn-warning" onclick="stackAction('{{.Name}}', 'restart')">↻</button>
|
||||
{{if .Meta.Slug}}
|
||||
<a href="/apps/{{.Meta.Slug}}" class="btn btn-outline">Részletek</a>
|
||||
{{end}}
|
||||
{{else if not .Deployed}}
|
||||
```
|
||||
|
||||
### Stack action handler check
|
||||
**dashboard.html**: Same `data-href` fix for the compact card:
|
||||
|
||||
In `internal/stacks/manager.go`, check if the action handler blocks restart on protected stacks. If there's a guard like:
|
||||
|
||||
```go
|
||||
if stack.Protected {
|
||||
return fmt.Errorf("cannot perform action on protected stack")
|
||||
}
|
||||
Change from:
|
||||
```html
|
||||
<div class="stack-card ..." {{if not .Protected}} data-href="/apps/{{.Meta.Slug}}"{{end}}>
|
||||
```
|
||||
To:
|
||||
```html
|
||||
<div class="stack-card ..." {{if .Meta.Slug}} data-href="/apps/{{.Meta.Slug}}"{{end}}>
|
||||
```
|
||||
|
||||
Change it to only block destructive actions:
|
||||
### Fix — FileBrowser .felhom.yml (resources)
|
||||
|
||||
```go
|
||||
if stack.Protected && action != "restart" {
|
||||
return fmt.Errorf("cannot %s protected stack %s", action, name)
|
||||
}
|
||||
The manually created `.felhom.yml` on the demo node is missing `resources`. Update it to include:
|
||||
|
||||
```yaml
|
||||
resources:
|
||||
mem_request: "128M"
|
||||
mem_limit: "256M"
|
||||
pi_compatible: true
|
||||
needs_hdd: true
|
||||
```
|
||||
|
||||
**CRITICAL**: Only `restart` is allowed. `stop`, `start`, `update`, `delete` must remain blocked for protected stacks.
|
||||
|
||||
### docker-setup.sh changes
|
||||
|
||||
In `install_filebrowser()`, add `.felhom.yml` creation after the docker-compose.yml:
|
||||
Also add this to the `.felhom.yml` created by `install_filebrowser()` in `scripts/docker-setup.sh`.
|
||||
|
||||
**Manual fix for demo node** (run after deploy):
|
||||
```bash
|
||||
# Create .felhom.yml metadata
|
||||
cat > "${FILEBROWSER_DIR}/.felhom.yml" << 'METAEOF'
|
||||
display_name: Filebrowser
|
||||
slug: filebrowser
|
||||
description: Fájlkezelő a külső merevlemezhez
|
||||
subdomain: files
|
||||
category: storage
|
||||
app_info:
|
||||
tagline: Web-alapú fájlkezelő a külső merevlemezhez
|
||||
use_cases:
|
||||
- Fájlok böngészése és letöltése a külső HDD-ről
|
||||
- Médiafájlok megosztása családtagokkal
|
||||
- Dokumentumok feltöltése és kezelése
|
||||
first_steps:
|
||||
- Nyisd meg a files.DOMAIN címet a böngészőben
|
||||
- Jelentkezz be az admin fiókkal
|
||||
- Tallózd a /srv mappákat
|
||||
prerequisites:
|
||||
- Külső HDD csatlakoztatva és felcsatolva
|
||||
METAEOF
|
||||
cat >> /opt/docker/stacks/filebrowser/.felhom.yml << 'EOF'
|
||||
resources:
|
||||
mem_request: "128M"
|
||||
mem_limit: "256M"
|
||||
pi_compatible: true
|
||||
needs_hdd: true
|
||||
EOF
|
||||
```
|
||||
|
||||
### Verification
|
||||
|
||||
- Clicking FileBrowser card on Alkalmazások page opens `/apps/filebrowser` detail page
|
||||
- "Részletek" button appears next to "Újraindítás" on FileBrowser
|
||||
- Detail page shows memory badges (~128M, HDD szükséges, Pi kompatibilis)
|
||||
- App info section shows use cases, first steps, prerequisites
|
||||
- Other protected stacks without `.felhom.yml` (traefik, cloudflared) don't show "Részletek" (no slug)
|
||||
|
||||
---
|
||||
|
||||
## Task 2: Backup page — cache expensive data
|
||||
|
||||
### Problem
|
||||
|
||||
`GetFullStatus()` is called synchronously on every page load of `/backups` and runs three blocking subprocess calls:
|
||||
|
||||
1. `m.restic.Stats()` — executes `restic stats --json` + `restic snapshots --json` (~2-3 seconds)
|
||||
2. `ListDumpFiles()` — directory listing (fast, ~1ms)
|
||||
3. `DiscoverDatabases()` — `docker ps` + `docker inspect` per container (~0.5s)
|
||||
|
||||
Total: 3-4 seconds per page load. This is unacceptable for a dashboard page.
|
||||
|
||||
### Solution: Background cache with periodic refresh
|
||||
|
||||
Add a cached `FullBackupStatus` to the Manager that refreshes periodically via the scheduler, instead of computing on every page load.
|
||||
|
||||
#### New fields in Manager:
|
||||
|
||||
```go
|
||||
type Manager struct {
|
||||
// ... existing fields ...
|
||||
|
||||
mu sync.Mutex
|
||||
lastDBDump *DBDumpStatus
|
||||
lastBackup *BackupStatus
|
||||
running bool
|
||||
snapshotHistory []SnapshotRecord
|
||||
|
||||
// Cached status for page rendering
|
||||
cachedStatus *FullBackupStatus
|
||||
cacheTime time.Time
|
||||
}
|
||||
```
|
||||
|
||||
#### New method: `RefreshCache()`
|
||||
|
||||
```go
|
||||
// RefreshCache updates the cached full status. Called by scheduler every 5 minutes
|
||||
// and after each backup run.
|
||||
func (m *Manager) RefreshCache(nextDBDump, nextBackup time.Time) {
|
||||
// Same logic as current GetFullStatus() — run restic stats, list dump files, discover DBs
|
||||
status := &FullBackupStatus{ ... }
|
||||
|
||||
// ... all the expensive calls ...
|
||||
|
||||
m.mu.Lock()
|
||||
m.cachedStatus = status
|
||||
m.cacheTime = time.Now()
|
||||
m.mu.Unlock()
|
||||
|
||||
m.logger.Printf("[INFO] Backup status cache refreshed")
|
||||
}
|
||||
```
|
||||
|
||||
#### Modified `GetFullStatus()`: read from cache
|
||||
|
||||
```go
|
||||
// GetFullStatus returns the cached backup status for page rendering.
|
||||
// Returns instantly — no subprocess calls.
|
||||
func (m *Manager) GetFullStatus(nextDBDump, nextBackup time.Time) *FullBackupStatus {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
|
||||
if m.cachedStatus != nil {
|
||||
// Update dynamic fields that don't need subprocess calls
|
||||
m.cachedStatus.Running = m.running
|
||||
m.cachedStatus.NextDBDump = nextDBDump
|
||||
m.cachedStatus.NextBackup = nextBackup
|
||||
m.cachedStatus.LastDBDump = m.lastDBDump
|
||||
m.cachedStatus.LastBackup = m.lastBackup
|
||||
// Update snapshot history
|
||||
m.cachedStatus.SnapshotHistory = make([]SnapshotRecord, len(m.snapshotHistory))
|
||||
copy(m.cachedStatus.SnapshotHistory, m.snapshotHistory)
|
||||
return m.cachedStatus
|
||||
}
|
||||
|
||||
// No cache yet — return a minimal status (first page load before cache is populated)
|
||||
return &FullBackupStatus{
|
||||
Enabled: m.cfg.Backup.Enabled,
|
||||
Running: m.running,
|
||||
LastDBDump: m.lastDBDump,
|
||||
LastBackup: m.lastBackup,
|
||||
DBDumpSchedule: m.cfg.Backup.DBDumpSchedule,
|
||||
ResticSchedule: m.cfg.Backup.ResticSchedule,
|
||||
PruneSchedule: m.cfg.Backup.PruneSchedule,
|
||||
NextDBDump: nextDBDump,
|
||||
NextBackup: nextBackup,
|
||||
Retention: m.cfg.Backup.Retention,
|
||||
RepoPath: m.cfg.Backup.ResticRepo,
|
||||
LastCheckTime: m.lastCheckTime,
|
||||
LastCheckOK: m.lastCheckOK,
|
||||
BackupPaths: []string{
|
||||
m.cfg.Paths.StacksDir,
|
||||
m.cfg.Paths.DBDumpDir,
|
||||
"/opt/docker/felhom-controller/controller.yaml",
|
||||
},
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Scheduler integration (in `main.go`):
|
||||
|
||||
Register a periodic job that refreshes the backup cache:
|
||||
|
||||
```go
|
||||
if cfg.Backup.Enabled && backupMgr != nil {
|
||||
// ... existing daily jobs ...
|
||||
|
||||
// Cache refresh: every 5 minutes
|
||||
sched.Every("backup-cache", 5*time.Minute, func(ctx context.Context) error {
|
||||
nextDBDump := scheduler.NextDailyRun(cfg.Backup.DBDumpSchedule)
|
||||
nextBackup := scheduler.NextDailyRun(cfg.Backup.ResticSchedule)
|
||||
backupMgr.RefreshCache(nextDBDump, nextBackup)
|
||||
return nil
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
#### Refresh after backup completion:
|
||||
|
||||
At the end of `RunFullBackup()` and `RunBackup()`, call `RefreshCache()` so the page shows updated data immediately after a backup:
|
||||
|
||||
```go
|
||||
func (m *Manager) RunFullBackup(ctx context.Context) error {
|
||||
// ... existing logic ...
|
||||
|
||||
// Refresh cache after backup completes
|
||||
m.RefreshCache(
|
||||
scheduler.NextDailyRun(m.cfg.Backup.DBDumpSchedule),
|
||||
scheduler.NextDailyRun(m.cfg.Backup.ResticSchedule),
|
||||
)
|
||||
return nil // or the backup error
|
||||
}
|
||||
```
|
||||
|
||||
**Note**: `RefreshCache()` needs to import `scheduler.NextDailyRun`. To avoid a circular import (backup → scheduler → backup), either:
|
||||
- Pass the next run times as parameters (already the pattern used)
|
||||
- Make `NextDailyRun` a standalone utility function that both packages can import
|
||||
- Or just call `RefreshCache` from `main.go` via a callback
|
||||
|
||||
Simplest approach: `RefreshCache` takes `nextDBDump, nextBackup time.Time` params (same as `GetFullStatus`). The scheduler job and the post-backup refresh both compute the times before calling.
|
||||
|
||||
#### Initial cache population on startup:
|
||||
|
||||
In `main.go`, after scheduler starts, trigger initial cache refresh in a goroutine (don't block startup):
|
||||
|
||||
```go
|
||||
if cfg.Backup.Enabled && backupMgr != nil {
|
||||
go func() {
|
||||
nextDBDump := scheduler.NextDailyRun(cfg.Backup.DBDumpSchedule)
|
||||
nextBackup := scheduler.NextDailyRun(cfg.Backup.ResticSchedule)
|
||||
backupMgr.RefreshCache(nextDBDump, nextBackup)
|
||||
}()
|
||||
}
|
||||
```
|
||||
|
||||
### Result
|
||||
|
||||
- Page load: instant (reads cached struct)
|
||||
- Cache refresh: every 5 minutes in background (user never waits)
|
||||
- After manual backup: cache refreshes immediately
|
||||
- First page load after startup: may show minimal data for a few seconds until goroutine completes
|
||||
|
||||
### Cache staleness indicator (optional)
|
||||
|
||||
Add `CacheTime time.Time` to `FullBackupStatus`. The template can optionally show "Utolsó frissítés: X perccel ezelőtt" at the bottom of the page in a muted font. Not critical, but helpful for debugging.
|
||||
|
||||
---
|
||||
|
||||
## Implementation order
|
||||
|
||||
### Step 1: MariaDB validation fix
|
||||
1. Fix `ValidateDump()` in `internal/backup/dbdump.go` — scan first 10 lines for header
|
||||
### Step 1: Protected stack detail pages
|
||||
1. Fix `data-href` gating in `stacks.html` and `dashboard.html` — use `{{if .Meta.Slug}}` instead of `{{if not .Protected}}`
|
||||
2. Add "Részletek" button to protected stack section in `stacks.html`
|
||||
3. Update `install_filebrowser()` in `docker-setup.sh` — add `resources` to `.felhom.yml`
|
||||
|
||||
### Step 2: Dashboard deployed-only
|
||||
1. Filter stacks in `dashboardHandler()` in `handlers.go`
|
||||
2. Update heading in `dashboard.html` to "Telepített alkalmazások"
|
||||
### Step 2: Backup page caching
|
||||
1. Add `cachedStatus`, `cacheTime` fields to `Manager`
|
||||
2. Create `RefreshCache()` method
|
||||
3. Modify `GetFullStatus()` to read from cache
|
||||
4. Register `backup-cache` scheduler job in `main.go`
|
||||
5. Call `RefreshCache()` at end of `RunFullBackup()` and `RunBackup()`
|
||||
6. Add initial cache goroutine in `main.go`
|
||||
|
||||
### Step 3: Protected stack UX
|
||||
1. Update `stacks.html` protected section — add restart button, ensure URL link not gated
|
||||
2. Update `dashboard.html` protected section — same (compact form)
|
||||
3. Check `internal/stacks/manager.go` — allow restart on protected stacks
|
||||
4. Add `.felhom.yml` creation to `install_filebrowser()` in `scripts/docker-setup.sh`
|
||||
### Step 3: Build, deploy, verify
|
||||
1. Build v0.4.7
|
||||
2. Deploy to demo node
|
||||
3. Update `/opt/docker/stacks/filebrowser/.felhom.yml` on demo node (add resources)
|
||||
4. Verify FileBrowser card is clickable → detail page with memory badges
|
||||
5. Verify backup page loads instantly
|
||||
6. Trigger manual backup → verify page updates after completion
|
||||
|
||||
### Step 4: Build, deploy, verify
|
||||
1. Build v0.4.6
|
||||
2. Deploy to demo node (sync full docker-compose.yml)
|
||||
3. Manually create `/opt/docker/stacks/filebrowser/.felhom.yml` on demo node
|
||||
4. Trigger backup → verify MariaDB green validation
|
||||
5. Check dashboard shows only deployed apps
|
||||
6. Check FileBrowser shows URL and restart button
|
||||
|
||||
### Step 5: Documentation
|
||||
### Step 4: Documentation
|
||||
1. Update CONTEXT.md, README
|
||||
2. Bump version
|
||||
|
||||
@@ -277,25 +290,25 @@ METAEOF
|
||||
## Files to modify
|
||||
|
||||
```
|
||||
internal/backup/dbdump.go — fix ValidateDump() header check
|
||||
internal/web/handlers.go — filter deployed-only in dashboardHandler()
|
||||
internal/web/templates/dashboard.html — heading + protected stack restart
|
||||
internal/web/templates/stacks.html — protected stack: ensure URL visible + restart button
|
||||
scripts/docker-setup.sh — create .felhom.yml in install_filebrowser()
|
||||
internal/stacks/manager.go — allow restart action on protected stacks
|
||||
internal/backup/backup.go — add cachedStatus, RefreshCache(), modify GetFullStatus()
|
||||
internal/web/templates/stacks.html — fix data-href gating, add Részletek button
|
||||
internal/web/templates/dashboard.html — fix data-href gating
|
||||
scripts/docker-setup.sh — add resources to filebrowser .felhom.yml
|
||||
cmd/controller/main.go — register backup-cache job, initial goroutine
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Verification checklist
|
||||
|
||||
- [ ] MariaDB (romm) shows green validation badge with table count on backup page
|
||||
- [ ] Dashboard heading is "Telepített alkalmazások"
|
||||
- [ ] Dashboard shows only deployed + protected apps (no "Nincs telepítve" entries)
|
||||
- [ ] Dashboard stat cards still show correct total (52 apps)
|
||||
- [ ] FileBrowser shows `files.demo-felhom.eu ↗` link on Alkalmazások page
|
||||
- [ ] FileBrowser shows "Újraindítás" button on both pages
|
||||
- [ ] Restart works on FileBrowser
|
||||
- [ ] Other protected stacks also show restart when operational
|
||||
- [ ] Stop/delete/update still blocked for all protected stacks
|
||||
- [ ] No regressions on backup page, app detail pages, deploy flow
|
||||
- [ ] FileBrowser card on Alkalmazások page is clickable → opens `/apps/filebrowser`
|
||||
- [ ] FileBrowser has "Részletek" button next to "Újraindítás"
|
||||
- [ ] FileBrowser detail page shows ~128M / HDD szükséges / Pi kompatibilis badges
|
||||
- [ ] FileBrowser detail page shows use cases, first steps, prerequisites
|
||||
- [ ] Traefik/cloudflared cards do NOT show "Részletek" (no .felhom.yml/slug)
|
||||
- [ ] Felhom-controller card does NOT show "Részletek" (no .felhom.yml)
|
||||
- [ ] Backup page loads in <500ms (instant, cached)
|
||||
- [ ] Backup page shows correct data after initial cache population
|
||||
- [ ] Manual backup → page shows updated data after completion
|
||||
- [ ] Cache refreshes every 5 minutes (check logs for "[INFO] Backup status cache refreshed")
|
||||
- [ ] No regressions on dashboard, app detail pages, deploy flow
|
||||
Reference in New Issue
Block a user