diff --git a/CHANGELOG.md b/CHANGELOG.md index 32ac5ba..7117a0f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,35 @@ +# v0.4.3 — Auto-logo's legacy-apps + fix lege Docker Images + +## Nieuw / fixes in v0.4.3 + +### 🖼️ Automatische logo's voor legacy-apps +Legacy-apps (eigen `app.json`/`stack.json`-stacks) krijgen nu automatisch een +logo via de dashboard-icons CDN, afgeleid uit de app-naam (bv. "Nextcloud" → +`nextcloud.png`). Een expliciete logo-URL in de metadata wint; bestaat het +geraden icoon niet, dan valt de UI terug op het emoji-icoon. Geldt voor zowel +de App Store als geïnstalleerde stacks. + +### 🐛 Fix: Docker Images-pagina was leeg +De afbeeldingenlijst gebruikte `:key="img.id"`, maar meerdere tags kunnen +dezelfde image-ID delen → dubbele Alpine-keys waardoor de tabel niet rendert +(en de "geen images"-melding ook niet, want er waren wél images). De `x-for` +gebruikt nu de index als key. + +--- + +# v0.4.2 — App-logo's bij stacks + +## Nieuw in v0.4.2 + +Geïnstalleerde stacks tonen nu het app-logo (of een emoji-icoon als fallback), +zowel op het dashboard als op de Stacks-pagina. Bij installatie wordt het +logo/icoon van de bron-app opgeslagen in `.serverup.json` in de stack-map. +Bestaande installaties krijgen hun logo via een naam-match met de App Store, +dus ze hoeven niet opnieuw geïnstalleerd te worden. De store toonde al logo's +voor boilerplate-apps (via de selfhst/dashboard-icons CDN). + +--- + # v0.4.1 — Fix: styling werd niet toegepast ## Fix bovenop v0.4.0 diff --git a/server-up/app.py b/server-up/app.py index 60af414..60c625b 100644 --- a/server-up/app.py +++ b/server-up/app.py @@ -18,7 +18,7 @@ from core.modules import Module, CORE, discover app = Flask(__name__, static_folder="static", template_folder="templates") -VERSION = os.environ.get("SU_VERSION", "0.4.1") +VERSION = os.environ.get("SU_VERSION", "0.4.3") MODULES: list[Module] = [] USER_MOD = APP / "modules" @@ -173,6 +173,37 @@ def api_stacks(): # Bescherm tegen verkeerde LIBRARY_DIR if lib == Path("/app").resolve(): return jsonify([]) + # Logo/icon-lijst uit de store opbouwen (best effort) voor naam-fallback + icon_map = {} + try: + c = cfg.load() + for repo in c.get("APP_REPOS", []): + for st in git.scan_stacks(repo.get("id", ""), lib, subdir=repo.get("subdir", "")): + key = st.get("dir") or st.get("name") + if key and (st.get("logo_url") or st.get("icon")): + icon_map.setdefault(key, {"logo_url": st.get("logo_url", ""), + "icon": st.get("icon", "")}) + except Exception: + pass + + def _stack_logo(d): + # 1) Voorkeur: opgeslagen metadata bij installatie + mf = d / ".serverup.json" + if mf.exists(): + try: + m = json.loads(mf.read_text("utf-8")) + if m.get("logo_url") or m.get("icon"): + return m.get("logo_url", ""), m.get("icon", "") + except Exception: + pass + # 2) Fallback: match op naam met de store (ook voor oudere installaties) + if d.name in icon_map: + h = icon_map[d.name]; return h["logo_url"], h["icon"] + for key, h in icon_map.items(): + if d.name.startswith(key + "-"): + return h["logo_url"], h["icon"] + return "", "" + out = [] for d in sorted(lib.iterdir()): if not d.is_dir() or d.name.startswith("."): @@ -180,10 +211,12 @@ def api_stacks(): if not docker.has_compose(d): continue ct = docker.compose_ps(d, name=d.name) + logo_url, icon = _stack_logo(d) out.append({ "name": d.name, "path": str(d), "has_compose": True, "running": any(c["running"] for c in ct), "containers": ct, + "logo_url": logo_url, "icon": icon, }) return jsonify(out) @@ -637,6 +670,22 @@ def api_store_install(): except Exception: pass + # Bewaar logo/icon-metadata zodat de stack-lijst het kan tonen + try: + meta = boilerplates.metadata(src) if is_bp else git._meta(src) + logo = meta.get("logo_url", "") + if not logo and not is_bp: + logo = git.guess_logo_url(meta.get("name") or stack) + (dest / ".serverup.json").write_text(json.dumps({ + "source": stack, + "repo_id": rid, + "name": meta.get("name", stack), + "logo_url": logo, + "icon": meta.get("icon", ""), + }, ensure_ascii=False, indent=2), encoding="utf-8") + except Exception: + pass + # Start jobs.log(qq, "section", "Starten") rc = docker.compose_up(dest, log_fn=lambda m: jobs.log(qq, "dim", m), name=inst) diff --git a/server-up/core/git.py b/server-up/core/git.py index 3a9984b..7886ace 100644 --- a/server-up/core/git.py +++ b/server-up/core/git.py @@ -1,9 +1,18 @@ """Git operations — clone, pull, scan for stacks and modules.""" -import json, os, subprocess, shutil +import json, os, re, subprocess, shutil from pathlib import Path from core.docker import COMPOSE_NAMES from core import boilerplates as bp + +def guess_logo_url(name: str) -> str: + """Leid een dashboard-icons logo-URL af uit een app-naam. + Bestaat het icoon niet, dan faalt de en valt de UI terug op de emoji.""" + slug = re.sub(r"[^a-z0-9]+", "-", (name or "").lower()).strip("-") + if not slug: + return "" + return f"https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/png/{slug}.png" + CACHE = Path(os.environ.get("SU_GIT_CACHE", "/data/git")) # Fix git "dubious ownership" — container draait als root, cache kan door andere uid zijn @@ -129,6 +138,14 @@ def _scan_compose_dirs(base: Path, lib: Path) -> list[dict]: else: meta = _meta(d) meta.setdefault("format", "compose") + # Auto-logo voor legacy-apps: gebruik expliciete logo_url, anders raden + # op basis van de naam (dashboard-icons). Emoji blijft fallback in de UI. + if not meta.get("logo_url"): + ic = meta.get("icon", "") + if isinstance(ic, str) and (ic.startswith("http://") or ic.startswith("https://")): + meta["logo_url"] = ic + else: + meta["logo_url"] = guess_logo_url(meta.get("name") or d.name) installed = [] if lib.exists(): diff --git a/server-up/templates/index.html b/server-up/templates/index.html index df6b9e2..ec356b7 100644 --- a/server-up/templates/index.html +++ b/server-up/templates/index.html @@ -164,6 +164,8 @@ tailwind.config = {