v0.8.01-beta - "Nog 106 tonen" onder een leeg scherm
Some checks failed
Deploy server-up (dev) / deploy (push) Failing after 16m45s

De app store toonde geen kaarten met daaronder een knop die 106 apps beloofde.
Het raster liep over de repolijst, de tellers over de opgehaalde apps: liepen
die uit elkaar, dan stond er een getal onder een leeg scherm zonder dat er
ergens een fout te zien was.

- storeResultaat en storeVerborgen tellen nu over dezelfde lijst die ook de
  kaarten oplevert
- een lege store zegt wat er aan de hand is (geen repository, of geen apps die
  aan het filter voldoen) in plaats van leeg te blijven
- GET /api/repos gaf een 500 op een repo zonder id, waardoor de repolijst in de
  interface leeg werd en dus de hele store; zulke regels worden overgeslagen

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Q9eqpADJSRs49SoGGr4NAy
This commit is contained in:
Ramon 2026-08-03 21:12:26 +02:00
parent 2d85668021
commit 34d0116efd
7 changed files with 78 additions and 9 deletions

View file

@ -1,3 +1,19 @@
# v0.8.01-beta — "Nog 106 tonen" onder een leeg scherm
De app store toonde geen enkele kaart, met daaronder een knop die beloofde dat
er nog 106 apps wachtten. Twee bronnen die uit elkaar konden lopen: het raster
liep over de **repolijst**, de teller over de **opgehaalde apps**. Waren die
twee het oneens, dan stond er een getal onder een leeg scherm — zonder dat er
ergens een foutmelding te zien was.
- Teller en raster komen nu uit dezelfde lijst. De knop kan niets meer beloven
wat er niet staat.
- Een lege store zegt nu wát er aan de hand is: geen repository toegevoegd, of
geen apps die aan het filter voldoen. Eerder bleef het scherm gewoon leeg.
- `GET /api/repos` gaf een 500 op een repo zonder `id`. Eén rare regel in de
configuratie maakte daarmee de hele store leeg; zulke regels worden nu
overgeslagen.
# v0.8.00-beta — Een kapotte cache herstelt zichzelf
`fatal: shallow file has changed since we read it` liet een repo achter die zich

View file

@ -1 +1 @@
0.8.00-beta
0.8.01-beta

View file

@ -1228,7 +1228,14 @@ _BAD_URL_MSG = ("Ongeldige repo-URL. Toegestaan zijn http(s)://, ssh:// en "
def api_repos():
# Nooit het token zelf teruggeven — alleen of er één ingesteld is.
repos = cfg.redact(cfg.load()).get("APP_REPOS", [])
result = [{**r, "cloned": git.cache_dir(r["id"]).exists()} for r in repos]
# Een repo zonder id gaf hier een KeyError, dus een 500, dus een lege
# repolijst in de interface — en daarmee een app store zonder enkele kaart.
# Eén rare regel in de configuratie hoort de rest niet mee te nemen.
result = []
for r in repos:
if not isinstance(r, dict) or not r.get("id"):
continue
result.append({**r, "cloned": git.cache_dir(r["id"]).exists()})
return jsonify(result)

View file

@ -683,6 +683,15 @@ tailwind.config = {
</div>
</template>
<div x-show="!repos.length" class="empty">
<span class="mdi mdi-source-branch"></span>
<p x-text="t('no_repos')">Nog geen repository toegevoegd</p>
</div>
<div x-show="repos.length && !storeResultaat" class="empty">
<span class="mdi mdi-package-variant"></span>
<p x-text="t('no_apps_found')">Geen apps gevonden</p>
</div>
<!-- Niet alles tegelijk: 258 kaarten in de DOM is traag en niemand
scrolt eroverheen. -->
<div x-show="storeVerborgen > 0" class="flex justify-center pt-1">
@ -2927,7 +2936,7 @@ function app() {
},
// Hoeveel er nog wachten achter de knop, over alle repo's heen.
get storeVerborgen() {
return Math.max(0, this.storeResultaat - Object.keys(this.storeStacks || {})
return Math.max(0, this.storeResultaat - this.storeRepoIds
.reduce((n, id) => n + this.zichtbareStore(id).length, 0));
},
// Alle labels uit alle repo's met hun aantal, op frequentie gesorteerd
@ -2967,9 +2976,15 @@ function app() {
return (this.storeCat ? 1 : 0) + (this.storeTag ? 1 : 0);
},
// Hoeveel apps er over blijven met de huidige keuze, over alle repo's heen.
// Tellen over dezelfde lijst die ook de kaarten oplevert. Eerder telde dit
// over `storeStacks` terwijl het raster over `repos` liep: liepen die twee
// uit elkaar, dan beloofde de knop "Nog 106 tonen" apps die nergens op het
// scherm stonden, zonder dat er ergens iets van een fout te zien was.
get storeRepoIds() {
return (this.repos || []).map(r => r.id).filter(Boolean);
},
get storeResultaat() {
return Object.keys(this.storeStacks || {})
.reduce((n, id) => n + this.filteredStore(id).length, 0);
return this.storeRepoIds.reduce((n, id) => n + this.filteredStore(id).length, 0);
},
// Wat er op een telefoon onder de meer-knop komt te staan. Met namen
// erbij: twaalf icoontjes zonder tekst zeggen niets.

View file

@ -49,7 +49,7 @@
"docker": "Docker",
"no_stacks": "No apps found",
"no_images": "No images found",
"no_repos": "No repositories",
"no_repos": "No repository added yet — add one to see apps.",
"add_repo": "Add repository",
"repo_name": "Name",
"repo_url": "Git URL",
@ -465,5 +465,6 @@
"move_data_confirm": "Move {n} folder(s)? Apps are stopped, data is copied and verified, and only then is the old cleared out.",
"manual_started": "Started manually",
"manual_hint": "These apps run on this host but were not created by Server Up — started manually, or from before you began using Server Up. You can start, stop and view their logs here.",
"containers_n": "{n} containers"
"containers_n": "{n} containers",
"no_apps_found": "No apps found"
}

View file

@ -49,7 +49,7 @@
"docker": "Docker",
"no_stacks": "Geen apps gevonden",
"no_images": "Geen images gevonden",
"no_repos": "Geen repositories",
"no_repos": "Nog geen repository toegevoegd — voeg er een toe om apps te zien.",
"add_repo": "Repository toevoegen",
"repo_name": "Naam",
"repo_url": "Git URL",
@ -465,5 +465,6 @@
"move_data_confirm": "{n} map(pen) verplaatsen? De apps worden gestopt, de data wordt gekopieerd en gecontroleerd, en pas daarna wordt het oude opgeruimd.",
"manual_started": "Handmatig gestart",
"manual_hint": "Deze apps draaien op deze host maar zijn niet door Server Up aangemaakt — handmatig gestart, of van vóór je Server Up ging gebruiken. Je kunt ze hier starten, stoppen en hun logs bekijken.",
"containers_n": "{n} containers"
"containers_n": "{n} containers",
"no_apps_found": "Geen apps gevonden"
}

View file

@ -163,6 +163,8 @@ check('doorklikken opent de stackspagina', c.page === 'stacks');
// ── Filtermenu van de app store ─────────────────────────────────────────────
// De labelrij was afgekapt op veertien, waardoor je de rest niet kon kiezen. Het
// menu zoekt nu in de volledige lijst.
// De teller volgt de repolijst, want die bepaalt ook welke kaarten er komen.
c.repos = [{id:'r', name:'Proefrepo'}];
c.storeStacks = { r: [
{dir:'sonarr', name:'Sonarr', categories:['media'], tags:['media','series']},
{dir:'radarr', name:'Radarr', categories:['media'], tags:['media','films']},
@ -395,6 +397,33 @@ check('balk staat halverwege', c.job.progress.pct === 50);
await c.verplaatsPaden();
check('afbreken bij de bevestiging start niets', verstuurd === null);
// ── De store telt wat hij ook laat zien ───────────────────────────────────
// Het raster liep over `repos`, de teller over `storeStacks`. Liepen die twee
// uit elkaar, dan stond er "Nog 106 tonen" onder een leeg scherm — zonder dat
// er ergens een fout te zien was.
const nepApps = n => Array.from({length: n},
(_, i) => ({dir: 'a' + i, name: 'App' + i, categories: [], tags: []}));
const kaarten = () => c.repos.reduce((n, r) => n + c.zichtbareStore(r.id).length, 0);
c._storeCache = {sleutel: null, lijsten: {}};
c.storeQuery = ''; c.storeCat = ''; c.storeTag = ''; c.storeLimiet = 60;
c.repos = [{id: 'server-up', name: 'Server Up'}];
c.storeStacks = {'server-up': nepApps(106)};
check('de eerste zestig staan in beeld', kaarten() === 60);
check('en de rest wacht achter de knop', c.storeVerborgen === 46);
c.meerTonen();
c._storeCache = {sleutel: null, lijsten: {}};
check('meer tonen laat de rest zien', kaarten() === 106);
check('en de knop verdwijnt', c.storeVerborgen === 0);
// Zonder repo's is er niets te tonen — en dus ook niets te beloven.
c._storeCache = {sleutel: null, lijsten: {}};
c.storeLimiet = 60;
c.repos = [];
check('geen repo\'s: geen kaarten', kaarten() === 0);
check('en geen knop die apps belooft', c.storeVerborgen === 0);
check('de teller is ook nul', c.storeResultaat === 0);
c.rpc = poortRpc;
// ── Eén lijst voor beheerde en handmatig gestarte apps ────────────────────