From 0753adec6a3b9e20a21cfdae066c68500cd595aa Mon Sep 17 00:00:00 2001 From: kisfenyo Date: Sun, 15 Feb 2026 12:12:59 +0100 Subject: [PATCH] =?UTF-8?q?TASK.md=20=E2=80=94=20v0.4.1:=20App=20Filtering?= =?UTF-8?q?=20+=20Bugfixes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TASK.md | 1133 +++++++++++++++---------------------------------------- 1 file changed, 313 insertions(+), 820 deletions(-) diff --git a/TASK.md b/TASK.md index 31fe913..70dd85d 100644 --- a/TASK.md +++ b/TASK.md @@ -1,889 +1,382 @@ -# TASK.md — Phase 2: Monitoring & Health + Phase 3: Backups +# TASK.md — v0.4.1: App Filtering + Bugfixes -> Version bump target: **v0.4.0** -> Priority: Phase 2 first (scheduler + metrics are prerequisites for Phase 3) +> Version bump: **v0.4.1** +> Scope: UI feature + configuration fixes --- ## Overview -Implement two major features in felhom-controller: +Three items: -1. **Phase 2** — Scheduler, CPU/temperature metrics, Healthchecks.io ping integration -2. **Phase 3** — Database dump engine, restic backup snapshots, dashboard status display - -Both phases share the scheduler infrastructure. Implement in order. +1. **Feature**: App filtering on the Alkalmazások (Stacks) page with clickable stat cards on the Vezérlőpult (Dashboard) page +2. **Bugfix**: felhom.demo-felhom.eu returns 404 — docker-compose.yml on demo node needs syncing +3. **Config**: Enable backup on demo node so the backup UI section appears --- -## Phase 2A — Scheduler (`internal/scheduler/`) +## Task 1: App Filtering -### Why first +### Concept -main.go currently has two ad-hoc goroutines (status refresh every 30s, stack scan every 2min). -Phase 2 adds system health pings. Phase 3 adds daily DB dumps and backups. -All need a centralized, logged, observable job runner. Build it once, use everywhere. +**Alkalmazások page** gets filter tabs at the top: -### Design: `internal/scheduler/scheduler.go` - -```go -package scheduler - -type JobFunc func(ctx context.Context) error - -type Job struct { - Name string - Fn JobFunc - Interval time.Duration // for periodic jobs (every N) - Schedule string // for daily jobs ("02:30", "03:00") — mutually exclusive with Interval - LastRun time.Time - LastErr error - Running bool -} - -type Scheduler struct { - jobs []*Job - logger *log.Logger - ctx context.Context - cancel context.CancelFunc - wg sync.WaitGroup -} - -func New(logger *log.Logger) *Scheduler -func (s *Scheduler) Every(name string, interval time.Duration, fn JobFunc) -func (s *Scheduler) Daily(name string, timeStr string, fn JobFunc) // "02:30" format, Europe/Budapest timezone -func (s *Scheduler) Start(ctx context.Context) -func (s *Scheduler) Stop() -func (s *Scheduler) GetJobs() []Job // for dashboard/API display (copy, not pointer) +``` +[ Mind (51) ] [ Futó (4) ] [ Leállítva (0) ] [ Telepíthető (47) ] ``` -### Interval jobs (`Every`) +**Vezérlőpult page** stat cards become clickable — clicking navigates to the Alkalmazások page with the corresponding filter pre-selected: -- Spawns a goroutine with `time.Ticker` -- Logs `[SCHED] Running job: ` at start, `[SCHED] Job completed (took Xs)` or `[SCHED] Job failed: (took Xs)` at end -- Updates `LastRun`, `LastErr`, `Running` fields (mutex-protected) -- Respects ctx.Done() for shutdown -- **Quiet mode for high-frequency jobs:** Jobs that run every <=30 seconds should only log at debug level on success (avoid log spam). Failures always log at WARN/ERROR level. +- Click "4 Futó alkalmazás" → `/stacks?filter=running` +- Click "0 Leállítva" → `/stacks?filter=stopped` +- Click "51 Összes alkalmazás" → `/stacks` (no filter = show all) -### Daily jobs (`Daily`) +### Implementation: 100% client-side -- Parses `timeStr` as "HH:MM" in `Europe/Budapest` timezone -- On start, calculates duration until next occurrence (today if not yet passed, tomorrow if passed) -- After each run, sleeps until the next day's scheduled time -- Uses `time.After()` or `time.Timer`, NOT `time.Ticker` (handles DST transitions correctly) -- Same logging pattern as interval jobs -- Logs `[SCHED] Daily job scheduled for ` on registration +No server/handler changes needed. All filtering is done via JavaScript show/hide on existing DOM elements. + +### Changes to `stacks.html` + +Add filter bar between the page header and the stack grid: + +```html +
+ + + + +
+``` + +Add `data-filter-state` attribute to each stack card in the `{{range .Stacks}}` loop: + +```html +
+``` + +Where `filterCategory` is a new template function that maps states to filter categories: +- `running` → states: running, starting, unhealthy, restarting (has containers, is "up") +- `stopped` → states: stopped, exited (deployed but not running) +- `available` → state: not_deployed (never deployed) + +### New template function: `filterCategory` + +Add to `funcmap.go`: + +```go +"filterCategory": func(state stacks.ContainerState, deployed bool) string { + switch state { + case stacks.StateRunning, stacks.StateStarting, stacks.StateUnhealthy, stacks.StateRestarting: + return "running" + case stacks.StateStopped, stacks.StateExited, stacks.StatePaused: + return "stopped" + default: + if deployed { + return "stopped" // deployed but unknown state → treat as stopped + } + return "available" + } +}, +``` + +### JavaScript for stacks.html + +Add a `