From 55d7b7d3703dc90248d51dcb610e042c12e44085 Mon Sep 17 00:00:00 2001 From: kisfenyo Date: Sat, 14 Feb 2026 20:27:49 +0100 Subject: [PATCH] updated TASK.md - logo resize, app info missing --- TASK.md | 880 +++++++------------------------------------------------- 1 file changed, 104 insertions(+), 776 deletions(-) diff --git a/TASK.md b/TASK.md index 116ee0d..5f9f0c5 100644 --- a/TASK.md +++ b/TASK.md @@ -1,716 +1,104 @@ -# TASK.md — Current Task: App Detail/Info Pages +# TASK.md — Fix: App Info Page Bugs > Read CLAUDE.md first for project context, workspace layout, and build instructions. -> This file describes the current task to implement. -## Overview +## Problems -Add a dedicated app information page to the felhom-controller dashboard. This page shows detailed -info about each app (use cases, setup guide, prerequisites) and allows configuring **optional -settings** (like API keys for metadata providers) both before and after deployment. +Two bugs on the `/apps/romm` info page: -The first app to get this treatment is **RoMM** (retro game ROM manager), which has optional -metadata provider API keys (IGDB, SteamGridDB, ScreenScraper, MobyGames). +### Bug 1: Logo SVG renders at native size (fills entire viewport) -## Architecture context +The `.app-info-logo` CSS sets `width: 80px; height: 80px` but the SVG logo image ignores these +constraints and renders at its intrinsic size (hundreds of pixels wide/tall), pushing all content +off-screen. -- All HTML/CSS is embedded as Go string constants in `internal/web/templates.go` -- All UI text is in **Hungarian** -- Routes are defined in `internal/web/server.go` `ServeHTTP()` method (manual path matching) -- API routes in `internal/api/router.go` `ServeHTTP()` method -- App metadata lives in `.felhom.yml` files parsed by `internal/stacks/metadata.go` -- Per-app deployed config is saved in `app.yaml` (managed by `internal/stacks/deploy.go`) -- Template functions registered in `server.go` `initTemplates()` +**Root cause:** SVG images inside `` tags can ignore `width`/`height` CSS if the SVG file +itself has explicit `width`/`height` attributes or no `viewBox`. The `object-fit: contain` only +works when the container actually constrains the image. -## Changes needed - -This task touches **4 Go files** in the controller + **2 files** in the app-catalog repo. - ---- - -### 1. `controller/internal/stacks/metadata.go` — Add new structs - -Add these new fields to the existing `Metadata` struct, and add the new supporting structs: - -```go -// Extend the existing Metadata struct with these new fields: -type Metadata struct { - // ... all existing fields stay unchanged ... - DisplayName string `yaml:"display_name" json:"display_name"` - Description string `yaml:"description" json:"description"` - Category string `yaml:"category" json:"category"` - Subdomain string `yaml:"subdomain" json:"subdomain"` - Slug string `yaml:"slug" json:"slug"` - Resources ResourceHints `yaml:"resources" json:"resources"` - DeployFields []DeployField `yaml:"deploy_fields" json:"deploy_fields"` - - // NEW fields — add these: - AppInfo AppInfo `yaml:"app_info" json:"app_info"` - OptionalConfig []OptionalConfigGroup `yaml:"optional_config" json:"optional_config"` -} - -// NEW: Detailed app information for the info page -type AppInfo struct { - Tagline string `yaml:"tagline" json:"tagline"` - UseCases []string `yaml:"use_cases" json:"use_cases"` - FirstSteps []string `yaml:"first_steps" json:"first_steps"` - Prerequisites []string `yaml:"prerequisites" json:"prerequisites"` - DefaultCreds string `yaml:"default_creds" json:"default_creds"` - DocsURL string `yaml:"docs_url" json:"docs_url"` -} - -// NEW: Optional config group (e.g., "Metadata providers") -type OptionalConfigGroup struct { - Group string `yaml:"group" json:"group"` - Description string `yaml:"description" json:"description"` - Fields []OptionalConfigField `yaml:"fields" json:"fields"` -} - -// NEW: Individual optional config field -type OptionalConfigField struct { - EnvVar string `yaml:"env_var" json:"env_var"` - Label string `yaml:"label" json:"label"` - Type string `yaml:"type" json:"type"` // "text" or "secret_input" - HelpURL string `yaml:"help_url" json:"help_url"` - HelpText string `yaml:"help_text" json:"help_text"` -} -``` - -Add helper methods: - -```go -// HasAppInfo returns true if the metadata has any app info content. -func (m *Metadata) HasAppInfo() bool { - return m.AppInfo.Tagline != "" || len(m.AppInfo.UseCases) > 0 || len(m.AppInfo.FirstSteps) > 0 -} - -// HasOptionalConfig returns true if the metadata has any optional config groups. -func (m *Metadata) HasOptionalConfig() bool { - return len(m.OptionalConfig) > 0 -} -``` - -The existing `LoadMetadata()` function uses `yaml.Unmarshal` and will automatically populate the -new fields if they exist in the YAML. No changes needed to `LoadMetadata()`. - ---- - -### 2. `controller/internal/stacks/deploy.go` — Add optional config update method - -Add a new method for updating optional env vars in `app.yaml`. This is safe to call on deployed -apps — it only touches env vars listed in `optional_config`, never locked fields. - -```go -// UpdateOptionalConfig updates optional env vars in app.yaml and restarts the stack if deployed. -// Only updates env vars that are listed in the metadata's optional_config sections. -func (m *Manager) UpdateOptionalConfig(stackName string, values map[string]string) error { - m.mu.Lock() - defer m.mu.Unlock() - - stack, ok := m.stacks[stackName] - if !ok { - return fmt.Errorf("stack not found: %s", stackName) - } - - // Build a set of allowed env vars from optional_config - allowed := make(map[string]bool) - for _, group := range stack.Meta.OptionalConfig { - for _, field := range group.Fields { - allowed[field.EnvVar] = true - } - } - if len(allowed) == 0 { - return fmt.Errorf("no optional config fields defined for %s", stackName) - } - - // Load existing app.yaml (or create empty one) - appCfgPath := filepath.Join(m.cfg.Paths.StacksDir, stackName, "app.yaml") - var appCfg AppConfig - if data, err := os.ReadFile(appCfgPath); err == nil { - _ = yaml.Unmarshal(data, &appCfg) - } - if appCfg.Env == nil { - appCfg.Env = make(map[string]string) - } - - // Update only allowed env vars - changed := false - for key, val := range values { - if !allowed[key] { - m.logger.Printf("[WARN] Ignoring non-optional env var: %s", key) - continue - } - if appCfg.Env[key] != val { - appCfg.Env[key] = val - changed = true - m.logger.Printf("[INFO] Updated optional config %s for %s", key, stackName) - } - } - - if !changed { - return nil - } - - // Save app.yaml - data, err := yaml.Marshal(&appCfg) - if err != nil { - return fmt.Errorf("marshal app.yaml: %w", err) - } - if err := os.WriteFile(appCfgPath, data, 0600); err != nil { - return fmt.Errorf("write app.yaml: %w", err) - } - m.logger.Printf("[INFO] Saved updated app.yaml for %s", stackName) - - // If deployed, write .env and restart to pick up new env vars - if stack.Deployed { - // Write .env file — reuse the same pattern used by DeployStack - // Look at how DeployStack writes the .env file and follow the same approach - if err := m.writeEnvFile(stackName, appCfg.Env); err != nil { - return fmt.Errorf("write .env: %w", err) - } - m.logger.Printf("[INFO] Restarting %s to apply new optional config", stackName) - // Need to call the internal restart logic (without re-acquiring the lock) - // Check if there's an unlocked restart method; if not, create one by factoring - // the restart logic out of RestartStack into a restartStackLocked helper - if err := m.restartStackLocked(stackName); err != nil { - return fmt.Errorf("restart after config update: %w", err) - } - } - - return nil -} -``` - -**IMPORTANT implementation notes:** -- Check how `DeployStack` currently writes `.env` files — there's likely a helper that iterates - `appCfg.Env` and writes `KEY=VALUE` lines. Reuse that exact logic. -- The existing `RestartStack()` acquires the mutex lock. Since `UpdateOptionalConfig` already - holds the lock, you need an internal unlocked version. Either: - - Factor the restart logic out of `RestartStack` into `restartStackLocked` (recommended), or - - Temporarily release and re-acquire the lock (less clean, avoid if possible) -- Look at `manager.go` for the restart implementation to understand what needs to be factored out. - -Also add a public method to load app config (if not already public): - -```go -// LoadAppConfig reads app.yaml from a stack directory. Returns nil if not found. -func (m *Manager) LoadAppConfig(stackName string) (*AppConfig, error) { - appCfgPath := filepath.Join(m.cfg.Paths.StacksDir, stackName, "app.yaml") - data, err := os.ReadFile(appCfgPath) - if err != nil { - return nil, err - } - var cfg AppConfig - if err := yaml.Unmarshal(data, &cfg); err != nil { - return nil, err - } - return &cfg, nil -} -``` - ---- - -### 3. `controller/internal/api/router.go` — Add optional config API endpoint - -Add a new route case in the `ServeHTTP` switch block: - -```go -// POST /api/stacks/{name}/optional-config -case hasSuffix(path, "/optional-config") && req.Method == http.MethodPost: - r.updateOptionalConfig(w, req, extractName(path, "/optional-config")) -``` - -And the handler function: - -```go -func (r *Router) updateOptionalConfig(w http.ResponseWriter, req *http.Request, name string) { - r.logger.Printf("[API] Optional config update requested for stack: %s", name) - - var body struct { - Values map[string]string `json:"values"` - } - if err := json.NewDecoder(req.Body).Decode(&body); err != nil { - writeJSON(w, http.StatusBadRequest, apiResponse{OK: false, Error: "invalid request body"}) - return - } - - if err := r.stackMgr.UpdateOptionalConfig(name, body.Values); err != nil { - r.logger.Printf("[API] Optional config update failed for %s: %v", name, err) - writeJSON(w, http.StatusInternalServerError, apiResponse{OK: false, Error: err.Error()}) - return - } - - writeJSON(w, http.StatusOK, apiResponse{OK: true, Message: "Beállítások frissítve"}) -} -``` - ---- - -### 4. `controller/internal/web/server.go` — Add info page route and handler - -#### 4a. The route already exists — update it - -There's already a case in `ServeHTTP` for `/apps/{slug}`. Currently it redirects to the deploy -page. Replace the `appDetailHandler` method with a real page handler: - -```go -func (s *Server) appDetailHandler(w http.ResponseWriter, r *http.Request, slug string) { - var found *stacks.StackInfo - for _, stack := range s.stackMgr.GetStacks() { - if stack.Meta.Slug == slug { - found = &stack - break - } - } - if found == nil { - http.NotFound(w, r) - return - } - - // Load current optional config values from app.yaml - currentValues := make(map[string]string) - if appCfg, err := s.stackMgr.LoadAppConfig(found.Name); err == nil && appCfg != nil { - for k, v := range appCfg.Env { - currentValues[k] = v - } - } - - data := s.baseData("stacks", found.Meta.DisplayName) - data["Stack"] = found - data["Meta"] = found.Meta - data["AppInfo"] = found.Meta.AppInfo - data["OptionalConfig"] = found.Meta.OptionalConfig - data["CurrentValues"] = currentValues - data["HasAppInfo"] = found.Meta.HasAppInfo() - data["HasOptionalConfig"] = found.Meta.HasOptionalConfig() - - s.render(w, "app_info", data) -} -``` - -#### 4b. Add template functions - -In `initTemplates()`, add to the `funcMap`: - -```go -"screenshotURL": func(slug string, index int) string { - return s.cfg.AppScreenshotURL(slug, index) -}, -"seq": func(n int) []int { - result := make([]int, n) - for i := range result { - result[i] = i + 1 - } - return result -}, -``` - ---- - -### 5. `controller/internal/web/templates.go` — Add info page template + CSS - -#### 5a. Update `allTemplates` const - -```go -const allTemplates = layoutTmpl + dashboardTmpl + stacksTmpl + loginTmpl + logsTmpl + deployTmpl + appInfoTmpl -``` - -#### 5b. Add `appInfoTmpl` const - -Design goals: clean layout matching existing dark theme, Hungarian UI, sections for hero header, -app info cards, screenshots, and optional config form with AJAX save. - -```go -const appInfoTmpl = ` -{{define "app_info"}} -{{template "layout_start" .}} - - - - -
- -
- {{if .AppInfo.Tagline}} -

{{.AppInfo.Tagline}}

- {{else}} -

{{.Meta.Description}}

- {{end}} -
- ~{{.Meta.Resources.MemRequest}} RAM - {{.Meta.Category}} - {{if .Meta.Resources.NeedsHDD}}HDD szükséges{{end}} - {{if .Meta.Resources.PiCompatible}}Pi kompatibilis{{else}}Csak x86{{end}} -
-
-
- - -
- - - -
- -{{if .HasAppInfo}} -
- {{if .AppInfo.UseCases}} -
-

🎯 Mire használható?

-
    - {{range .AppInfo.UseCases}}
  • {{.}}
  • {{end}} -
-
- {{end}} - - {{if .AppInfo.FirstSteps}} -
-

🚀 Első lépések

-
    - {{range .AppInfo.FirstSteps}}
  1. {{.}}
  2. {{end}} -
-
- {{end}} - - {{if .AppInfo.Prerequisites}} -
-

📋 Előfeltételek

-
    - {{range .AppInfo.Prerequisites}}
  • {{.}}
  • {{end}} -
-
- {{end}} - - {{if .AppInfo.DefaultCreds}} -
-

🔑 Alapértelmezett belépés

-

{{.AppInfo.DefaultCreds}}

-

⚠️ Az első bejelentkezés után azonnal változtasd meg!

-
- {{end}} - - {{if .AppInfo.DocsURL}} -
-

📖 Dokumentáció

-

Hivatalos dokumentáció ↗

-
- {{end}} -
-{{end}} - -{{if .HasOptionalConfig}} -
-

⚙️ Opcionális beállítások

- {{range .OptionalConfig}} -
-

{{.Group}}

- {{if .Description}}

{{.Description}}

{{end}} - -
- {{range .Fields}} -
- - {{if .HelpText}}

{{.HelpText}}

{{end}} - {{if .HelpURL}}

Regisztrációs útmutató ↗

{{end}} - -
- {{end}} -
-
- {{end}} - -
- - -
-
- - -{{end}} - -{{template "layout_end" .}} -{{end}} -` -``` - -#### 5c. Add CSS to `cssContent` - -Append these styles at the end of `cssContent` (before the closing backtick): +**Fix:** Make the logo constraint bulletproof. Update the `.app-info-logo` CSS in `templates.go`: ```css -/* --- App Info Page --- */ -.app-info-hero { - display: flex; - align-items: center; - gap: 1.5rem; - padding: 1.5rem; - background: var(--bg-card); - border-radius: var(--radius); - border: 1px solid var(--border-color); - margin-bottom: 1.5rem; -} .app-info-logo { width: 80px; height: 80px; + min-width: 80px; + min-height: 80px; + max-width: 80px; + max-height: 80px; border-radius: 12px; object-fit: contain; background: var(--bg-secondary); padding: 10px; flex-shrink: 0; -} -.app-info-tagline { - font-size: 1.1rem; - color: var(--text-primary); - margin: 0 0 .75rem 0; -} -.app-screenshots { - display: flex; - gap: 1rem; - margin-bottom: 1.5rem; - overflow-x: auto; - padding-bottom: .5rem; -} -.app-screenshot { - max-height: 220px; - border-radius: var(--radius); - border: 1px solid var(--border-color); - object-fit: cover; -} -.app-info-grid { - display: grid; - grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); - gap: 1rem; - margin-bottom: 1.5rem; -} -.app-info-card { - background: var(--bg-card); - border-radius: var(--radius); - padding: 1.25rem; - border: 1px solid var(--border-color); -} -.app-info-card h3 { - margin: 0 0 .75rem 0; - font-size: .95rem; - color: var(--text-primary); -} -.app-info-list { - margin: 0; - padding-left: 1.25rem; - color: var(--text-secondary); - font-size: .9rem; - line-height: 1.6; -} -.app-info-creds { - font-family: monospace; - font-size: 1rem; - color: var(--accent-light); - background: var(--bg-secondary); - padding: .5rem .75rem; - border-radius: 4px; - display: inline-block; - margin: 0 0 .5rem 0; -} -.app-info-creds-warn { - color: var(--orange); - font-size: .85rem; - margin: 0; -} -.app-info-link { - color: var(--accent-light); - text-decoration: none; -} -.app-info-link:hover { text-decoration: underline; } - -/* Optional config section */ -.app-optional-config { - background: var(--bg-card); - border-radius: var(--radius); - padding: 1.5rem; - border: 1px solid var(--border-color); - margin-bottom: 1.5rem; -} -.app-optional-config h3 { - margin: 0 0 1rem 0; - font-size: 1.05rem; -} -.config-group { - margin-bottom: 1.5rem; -} -.config-group h4 { - margin: 0 0 .5rem 0; - font-size: .95rem; - color: var(--text-primary); -} -.config-group-desc { - color: var(--text-secondary); - font-size: .85rem; - margin: 0 0 1rem 0; -} -.config-fields { - display: grid; - grid-template-columns: repeat(auto-fill, minmax(320px, 1fr)); - gap: 1rem; -} -.config-field { - display: flex; - flex-direction: column; - gap: .25rem; -} -.config-field label { - font-size: .85rem; - font-weight: 500; - color: var(--text-primary); -} -.config-field-help { - font-size: .8rem; - color: var(--text-secondary); - margin: 0; - line-height: 1.4; -} -.config-field-help a { - color: var(--accent-light); - text-decoration: none; -} -.config-field-help a:hover { text-decoration: underline; } -.config-input { - background: var(--bg-secondary); - border: 1px solid var(--border-color); - border-radius: 4px; - padding: .5rem .75rem; - color: var(--text-primary); - font-size: .9rem; - font-family: monospace; - width: 100%; - box-sizing: border-box; -} -.config-input:focus { - outline: none; - border-color: var(--accent-blue); -} -.config-actions { - display: flex; - align-items: center; - gap: 1rem; - margin-top: 1rem; -} -.config-save-status { - font-size: .9rem; - transition: opacity .3s; -} -.config-save-ok { color: var(--green); } -.config-save-err { color: var(--red); } -.meta-badge-warn { - background: rgba(255, 152, 0, 0.1) !important; - color: var(--orange) !important; -} -.meta-badge-ok { - background: rgba(76, 175, 80, 0.1) !important; - color: var(--green) !important; + overflow: hidden; } ``` +The key additions are `max-width`, `max-height`, and `overflow: hidden` which will force the +image to stay within bounds regardless of the SVG's intrinsic dimensions. + +### Bug 2: App info sections not rendering (use cases, first steps, optional config all missing) + +The hero section renders (logo + badges) but the `app_info` content (tagline, use cases, first +steps, prerequisites, default creds, docs link) and `optional_config` (metadata provider fields) +are completely absent. The `

` is empty in the DOM. + +This means `HasAppInfo()` returns false and `HasOptionalConfig()` returns false, which means the +`.felhom.yml` file on the demo node does NOT contain the new `app_info` and `optional_config` +sections. + +**Diagnosis steps (run these first):** + +```bash +# 1. Check if the stacks dir has the updated .felhom.yml +ssh kisfenyo@192.168.0.162 "cat /opt/docker/stacks/romm/.felhom.yml" +``` + +If the output does NOT contain `app_info:` and `optional_config:`, the catalog sync hasn't +picked up the changes. Check: + +```bash +# 2. Check if app-catalog repo was actually updated +cd /e/git/app-catalog-felhom.eu +git log --oneline -3 +cat templates/romm/.felhom.yml | grep -c "app_info" +``` + +If the local repo doesn't have `app_info` in the romm `.felhom.yml`, the previous session +didn't update the app-catalog repo. You need to: + +1. Update `E:\git\app-catalog-felhom.eu\templates\romm\.felhom.yml` with the full content + (see "RoMM .felhom.yml content" section below) +2. Update `E:\git\app-catalog-felhom.eu\templates\romm\docker-compose.yml` — add the missing + ScreenScraper + MobyGames env vars (see "RoMM docker-compose.yml changes" section below) +3. Commit and push the app-catalog repo +4. Trigger a catalog sync on the demo node (or wait 15 minutes) + +If the local repo DOES have `app_info` but the demo node doesn't, force a sync: + +```bash +# Trigger sync via API (need to login first to get session cookie) +# Or use the dashboard "Sablonok frissítése" button +``` + +**After the catalog sync, verify the fix:** + +```bash +ssh kisfenyo@192.168.0.162 "grep -c 'app_info' /opt/docker/stacks/romm/.felhom.yml" +# Should return 1 (meaning the app_info section exists) + +ssh kisfenyo@192.168.0.162 "grep -c 'optional_config' /opt/docker/stacks/romm/.felhom.yml" +# Should return 1 +``` + +Then reload `/apps/romm` in the browser — the info cards and optional config form should appear. + --- -### 6. Update navigation links in `templates.go` +## RoMM .felhom.yml content -#### 6a. Stack cards on Alkalmazások page → link to info page - -In `stacksTmpl`, the cards currently have `data-href="/stacks/{{.Name}}/deploy"`. -Change to: - -```html -{{if not .Protected}} data-href="/apps/{{.Meta.Slug}}"{{end}} -``` - -#### 6b. Stack cards on Dashboard → same change - -Check `dashboardTmpl` for any `data-href` attributes pointing to `/stacks/.../deploy` and update -them similarly to link to `/apps/{{.Meta.Slug}}` instead. - -#### 6c. Deploy page → add "Részletek" link - -In `deployTmpl`, add a link back to the info page near the top header area: - -```html -ℹ️ Részletek -``` - ---- - -### 7. RoMM `.felhom.yml` — Update in app-catalog repo - -File: `E:\git\app-catalog-felhom.eu\templates\romm\.felhom.yml` - -Replace the entire file with: +Replace `templates/romm/.felhom.yml` in the **app-catalog-felhom.eu** repo entirely with: ```yaml # ============================================================================= @@ -833,11 +221,10 @@ optional_config: --- -### 8. RoMM docker-compose.yml template — Add missing env vars +## RoMM docker-compose.yml changes -File: `E:\git\app-catalog-felhom.eu\templates\romm\docker-compose.yml` - -In the romm service's `environment:` section, after the existing STEAMGRIDDB line, add: +In `E:\git\app-catalog-felhom.eu\templates\romm\docker-compose.yml`, in the romm service's +`environment:` section, after the existing `STEAMGRIDDB_API_KEY` line, add these three lines: ```yaml - SCREENSCRAPER_USER=${SCREENSCRAPER_USER:-} @@ -849,86 +236,27 @@ In the romm service's `environment:` section, after the existing STEAMGRIDDB lin ## Implementation order -1. `metadata.go` — add structs + helper methods (safe, no side effects) -2. `deploy.go` — add `UpdateOptionalConfig` + `LoadAppConfig` + factor out restart logic -3. `router.go` — add `/optional-config` API route + handler -4. `templates.go` — add `appInfoTmpl`, CSS, update `allTemplates` -5. `server.go` — replace `appDetailHandler`, add template functions -6. `templates.go` — update navigation links (stack cards → `/apps/{slug}`, deploy page → "Részletek") -7. **Commit + push** the controller changes -8. **Build + push + deploy** the new controller image (see CLAUDE.md for exact commands) -9. **Verify** the controller starts correctly and the info page renders -10. Update RoMM `.felhom.yml` in app-catalog repo -11. Update RoMM `docker-compose.yml` in app-catalog repo -12. **Commit + push** the app-catalog changes -13. Trigger catalog sync on demo-felhom (or wait 15m) -14. **Verify** the RoMM info page shows all sections - ---- - -## Testing checklist - -- [ ] `/apps/romm` shows the info page with all sections -- [ ] Optional config fields display with current values (empty for fresh deployment) -- [ ] Saving optional config updates `app.yaml` and restarts the stack (if deployed) -- [ ] Saving optional config on a non-deployed app saves to `app.yaml` without error -- [ ] Screenshots section hidden gracefully if no assets exist -- [ ] Stack cards on Alkalmazások page link to `/apps/{slug}` -- [ ] Deploy page has "Részletek" link back to info page -- [ ] Apps without `app_info` show minimal info page (header + badges) -- [ ] Protected stacks (traefik, cloudflared, felhom-controller) not affected -- [ ] Build succeeds on build server -- [ ] Controller starts and runs without template parse errors -- [ ] New version shows in `docker ps` - ---- - -## Step 15: Update documentation (MANDATORY) - -After all code changes are done and verified, update these three files: - -### 15a. `CONTEXT.md` - -Add a new "What was just completed" section at the top of the history (push down the current -"What was just completed" to "Previously completed"). Include: - -- App info/detail pages feature added -- New `.felhom.yml` fields: `app_info`, `optional_config` -- New route: `GET /apps/{slug}` renders info page (was redirect to deploy) -- New API: `POST /api/stacks/{name}/optional-config` -- RoMM metadata updated with full app_info + 6 metadata provider optional config fields -- RoMM docker-compose.yml updated with ScreenScraper + MobyGames env vars -- Navigation: stack cards now link to info page, deploy page has "Részletek" link -- New controller version: v0.2.11 (or whatever was deployed) - -Update the "What's next" section — remove items that are done, add any new priorities. - -### 15b. `controller/README.md` - -Update to reflect: -- New `app_info` and `optional_config` sections in `.felhom.yml` format -- New info page route (`/apps/{slug}`) -- New API endpoint (`POST /api/stacks/{name}/optional-config`) -- Add to "What works" list: "App detail/info pages with optional config" - -### 15c. `CLAUDE.md` - -Add to the "Key patterns" section: -- App info pages at `/apps/{slug}` — detail view with use cases, setup guide, optional config -- Optional config saves to `app.yaml` and restarts deployed apps -- `optional_config` fields in `.felhom.yml` define post-deploy configurable env vars - -Add to "Important lessons learned" if any new lessons emerge during implementation. - ---- - -## Debugging tips - -- If template fails to parse, the controller crashes on startup with a `template.Must` panic. - The log output will show the exact parse error (usually missing closing brackets or undefined functions). -- If `app.yaml` already has deployed config, `UpdateOptionalConfig` merges new values without - touching locked fields. -- The `onerror` on screenshot images hides them if assets don't exist — graceful degradation. -- `config-input` uses `font-family: monospace` intentionally — API keys are easier to verify. -- The `{{index $.CurrentValues .EnvVar}}` in the template will return empty string for missing - keys (Go template `index` on map returns zero value) — no nil panic. \ No newline at end of file +1. **Diagnose Bug 2 first** — run the SSH commands above to check if `.felhom.yml` on the node + has the `app_info` section. This tells us whether the catalog update was missed or sync failed. +2. **Fix Bug 1** — update `.app-info-logo` CSS in `controller/internal/web/templates.go` +3. **Fix Bug 2** — if the app-catalog wasn't updated: + a. Update `templates/romm/.felhom.yml` in `E:\git\app-catalog-felhom.eu\` + b. Update `templates/romm/docker-compose.yml` in `E:\git\app-catalog-felhom.eu\` + c. Commit + push the app-catalog repo: + ```bash + cd /e/git/app-catalog-felhom.eu + git add -A && git commit -m "romm: add app_info + optional_config metadata, add ScreenScraper + MobyGames env vars" && git push + ``` +4. **Build + deploy** the controller with the CSS fix (follow CLAUDE.md build workflow) +5. **Trigger catalog sync** — either wait 15m or use the dashboard "Sablonok frissítése" button +6. **Verify** — reload `/apps/romm` and confirm: + - [ ] Logo is 80x80, not filling the screen + - [ ] Tagline text visible: "Retró játékgyűjtemény kezelő, böngésző és lejátszó" + - [ ] "Mire használható?" card with 5 use cases + - [ ] "Első lépések" card with 6 steps + - [ ] "Előfeltételek" card with 3 items + - [ ] "Alapértelmezett belépés" card showing "admin / admin" + - [ ] "Dokumentáció" card with link to RomM wiki + - [ ] "Opcionális beállítások" section with 6 metadata provider fields + - [ ] "Mentés" button on optional config form +7. **Update CONTEXT.md** with the fixes applied \ No newline at end of file