v0.4.3 - auto-logo's legacy-apps + fix lege Docker Images
All checks were successful
Deploy server-up (dev) / deploy (push) Successful in 53s
All checks were successful
Deploy server-up (dev) / deploy (push) Successful in 53s
This commit is contained in:
parent
9b388af0cc
commit
1eab7c07df
4 changed files with 106 additions and 4 deletions
32
CHANGELOG.md
32
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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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 <img> 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():
|
||||
|
|
|
|||
|
|
@ -164,6 +164,8 @@ tailwind.config = {
|
|||
<div class="grid sm:grid-cols-2 lg:grid-cols-3 gap-4" x-show="stacks.length > 0">
|
||||
<template x-for="s in stacks.slice(0, 6)" :key="s.name">
|
||||
<button @click="goto('stacks')" class="card card-hover p-4 flex items-center gap-3 text-left">
|
||||
<template x-if="s.logo_url"><img :src="s.logo_url" class="w-8 h-8 rounded-lg object-contain bg-s2 dark:bg-s2-dark p-0.5 shrink-0" loading="lazy" onerror="this.style.display='none'"></template>
|
||||
<template x-if="!s.logo_url"><span class="w-8 h-8 rounded-lg bg-s2 dark:bg-s2-dark grid place-items-center text-base shrink-0" x-text="s.icon || '📦'"></span></template>
|
||||
<span class="w-2.5 h-2.5 rounded-full shrink-0" :class="s.running ? 'bg-ok shadow-[0_0_8px_#22c55e]' : 'bg-dm dark:bg-dm-dark'"></span>
|
||||
<span class="font-semibold text-wh dark:text-wh-dark text-sm flex-1 truncate" x-text="s.name"></span>
|
||||
<span class="badge" :class="s.running ? 'badge-ok' : 'badge-dim'" x-text="s.running ? t('running') : t('stopped')"></span>
|
||||
|
|
@ -186,6 +188,8 @@ tailwind.config = {
|
|||
<template x-for="s in filteredStacks" :key="s.name">
|
||||
<div class="card card-hover flex flex-col">
|
||||
<div class="px-4 py-3.5 flex items-center gap-2.5 border-b border-bd dark:border-bd-dark">
|
||||
<template x-if="s.logo_url"><img :src="s.logo_url" class="w-8 h-8 rounded-lg object-contain bg-s2 dark:bg-s2-dark p-0.5 shrink-0" loading="lazy" onerror="this.style.display='none'"></template>
|
||||
<template x-if="!s.logo_url"><span class="w-8 h-8 rounded-lg bg-s2 dark:bg-s2-dark grid place-items-center text-base shrink-0" x-text="s.icon || '📦'"></span></template>
|
||||
<span class="w-2.5 h-2.5 rounded-full shrink-0" :class="!s.has_compose ? 'bg-wr' : s.running ? 'bg-ok shadow-[0_0_8px_#22c55e]' : 'bg-dm dark:bg-dm-dark'"></span>
|
||||
<span class="font-semibold text-wh dark:text-wh-dark text-sm flex-1 truncate" x-text="s.name"></span>
|
||||
<span class="badge" :class="s.running ? 'badge-ok' : !s.has_compose ? 'badge-warn' : 'badge-dim'" x-text="!s.has_compose ? '—' : s.running ? t('running') : t('stopped')"></span>
|
||||
|
|
@ -285,7 +289,7 @@ tailwind.config = {
|
|||
<th class="px-4 py-3 font-bold">Size</th><th class="px-4 py-3 font-bold">Age</th><th class="px-4 py-3"></th>
|
||||
</tr></thead>
|
||||
<tbody>
|
||||
<template x-for="img in images" :key="img.id">
|
||||
<template x-for="(img, i) in images" :key="i">
|
||||
<tr class="border-t border-bd dark:border-bd-dark hover:bg-s2/60 dark:hover:bg-s2-dark/60 transition">
|
||||
<td class="px-4 py-2.5 font-mono text-tx dark:text-tx-dark" x-text="img.repo"></td>
|
||||
<td class="px-4 py-2.5 font-mono text-ac dark:text-ac-dark" x-text="img.tag"></td>
|
||||
|
|
@ -724,7 +728,7 @@ tailwind.config = {
|
|||
<script>
|
||||
function app() {
|
||||
return {
|
||||
version: '0.4.1',
|
||||
version: '0.4.3',
|
||||
page: location.hash.slice(1) || 'dashboard',
|
||||
isMobile: window.innerWidth < 768,
|
||||
sidebarOpen: window.innerWidth >= 768 && localStorage.getItem('sidebar') !== 'closed',
|
||||
|
|
|
|||
Loading…
Reference in a new issue