From 1575db42c2a6d2737ed0b110edf971c8e3646e02 Mon Sep 17 00:00:00 2001 From: Ramon Date: Tue, 4 Aug 2026 16:49:24 +0200 Subject: [PATCH] v0.8.14-beta - Raspberry Pi compatibiliteit en UniFi Mongo herstellen --- CHANGELOG.md | 31 ++++++++++++ VERSION | 2 +- apps/cal-com/template.json | 6 ++- apps/comfyui/template.json | 6 ++- apps/dawarich/template.json | 6 ++- apps/focalboard/template.json | 6 ++- apps/formbricks/template.json | 6 ++- apps/mattermost/template.json | 6 ++- apps/pinchflat/template.json | 6 ++- apps/pixelfed/template.json | 6 ++- apps/tubearchivist/template.json | 6 ++- apps/unbound/template.json | 6 ++- apps/unifi-network/files/compose.yaml | 22 +++++++-- apps/unifi-network/files/init-mongo.sh | 24 ++++++++++ apps/unifi-network/template.json | 7 ++- apps/wazuh/template.json | 6 ++- docs/apps-maken.md | 14 ++++++ server-up/app.py | 11 ++++- server-up/core/boilerplates.py | 59 ++++++++++++++++++++++- server-up/templates/index.html | 36 +++++++++++++- tests/js/menu.test.js | 12 +++++ tests/test_apps.py | 36 ++++++++++++++ tests/test_boilerplates.py | 42 ++++++++++++++++ tests/test_platform_compat.py | 66 ++++++++++++++++++++++++++ 24 files changed, 408 insertions(+), 20 deletions(-) create mode 100755 apps/unifi-network/files/init-mongo.sh create mode 100644 tests/test_platform_compat.py diff --git a/CHANGELOG.md b/CHANGELOG.md index d39c27d..d06c508 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,34 @@ +# v0.8.14-beta — Raspberry Pi krijgt werkende en eerlijke apps + +**De UniFi-template startte een MongoDB-container, maar maakte de database- +gebruiker en rollen die UniFi nodig heeft niet aan.** Daardoor bleef de +controller zonder werkende database hangen. Tegelijk kon de app-store op een +Raspberry Pi ook images aanbieden die uitsluitend voor AMD64 bestaan; dat werd +pas zichtbaar nadat Docker de pull weigerde. + +- **UniFi initialiseert MongoDB volledig.** Een init-script maakt de UniFi- + gebruiker met de voorgeschreven rollen aan, beide containers gebruiken + `authSource=admin` en UniFi wacht op de database-healthcheck. +- **Geschikt voor een 64-bits Raspberry Pi 4.** MongoDB staat vast op + `4.4.29`: deze tag heeft een ARM64-image en vermijdt de ARMv8.2-eis van + MongoDB 5 en hoger. De hoofdimage ondersteunt AMD64 en ARM64. +- **Platformcontrole vóór installatie.** Templates kunnen hun ondersteunde + Docker-architecturen vastleggen. De catalogus legt een mismatch uit, schakelt + de knop uit en de API weigert dezelfde installatie ook rechtstreeks. +- **Elf AMD64-only apps zijn gemarkeerd.** Alle 318 gebruikte image-manifests + zijn gecontroleerd. Cal.com, ComfyUI, Dawarich, Focalboard, Formbricks, + Mattermost, Pinchflat, Pixelfed, TubeArchivist, Unbound en Wazuh bevatten + momenteel minstens één image zonder ARM64-variant en worden daarom niet meer + als installeerbaar op een Pi getoond. +- **Init-scripts houden hun uitvoerrecht.** Tekstbestanden die tijdens het + renderen worden geschreven nemen voortaan de bestandsmodus van het sjabloon + over. + +Een Mongo-init-script draait alleen bij een lege database. Server Up verwijdert +bestaande data daarom niet automatisch. Verwijder bij een eerdere mislukte +UniFi-installatie eerst bewust het oude Mongo-volume, of installeer onder een +nieuwe instantienaam. + # v0.8.13-beta — De update haalt nu de juiste release op **Bijwerken vanuit de interface vroeg Docker om een image dat niet bestond.** diff --git a/VERSION b/VERSION index 71d2038..f0dce9a 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.8.13-beta +0.8.14-beta diff --git a/apps/cal-com/template.json b/apps/cal-com/template.json index 45fa678..3a8704d 100644 --- a/apps/cal-com/template.json +++ b/apps/cal-com/template.json @@ -18,7 +18,11 @@ "categories": [ "productiviteit", "zakelijk" - ] + ], + "architectures": [ + "amd64" + ], + "architecture_note": "Een of meer containerimages van deze app worden alleen voor AMD64 gepubliceerd. Deze app werkt daardoor niet op een Raspberry Pi." }, "variables": [ { diff --git a/apps/comfyui/template.json b/apps/comfyui/template.json index 7e13510..864a443 100644 --- a/apps/comfyui/template.json +++ b/apps/comfyui/template.json @@ -17,7 +17,11 @@ }, "categories": [ "ai" - ] + ], + "architectures": [ + "amd64" + ], + "architecture_note": "Een of meer containerimages van deze app worden alleen voor AMD64 gepubliceerd. Deze app werkt daardoor niet op een Raspberry Pi." }, "variables": [ { diff --git a/apps/dawarich/template.json b/apps/dawarich/template.json index f0c2937..cca2d41 100644 --- a/apps/dawarich/template.json +++ b/apps/dawarich/template.json @@ -18,7 +18,11 @@ "categories": [ "gezin", "beveiliging" - ] + ], + "architectures": [ + "amd64" + ], + "architecture_note": "Een of meer containerimages van deze app worden alleen voor AMD64 gepubliceerd. Deze app werkt daardoor niet op een Raspberry Pi." }, "variables": [ { diff --git a/apps/focalboard/template.json b/apps/focalboard/template.json index c26c728..aab23d0 100644 --- a/apps/focalboard/template.json +++ b/apps/focalboard/template.json @@ -17,7 +17,11 @@ }, "categories": [ "productiviteit" - ] + ], + "architectures": [ + "amd64" + ], + "architecture_note": "Een of meer containerimages van deze app worden alleen voor AMD64 gepubliceerd. Deze app werkt daardoor niet op een Raspberry Pi." }, "variables": [ { diff --git a/apps/formbricks/template.json b/apps/formbricks/template.json index 57b97a7..2340080 100644 --- a/apps/formbricks/template.json +++ b/apps/formbricks/template.json @@ -18,7 +18,11 @@ "categories": [ "productiviteit", "zakelijk" - ] + ], + "architectures": [ + "amd64" + ], + "architecture_note": "Een of meer containerimages van deze app worden alleen voor AMD64 gepubliceerd. Deze app werkt daardoor niet op een Raspberry Pi." }, "variables": [ { diff --git a/apps/mattermost/template.json b/apps/mattermost/template.json index a86787f..7430750 100644 --- a/apps/mattermost/template.json +++ b/apps/mattermost/template.json @@ -18,7 +18,11 @@ "categories": [ "communicatie", "productiviteit" - ] + ], + "architectures": [ + "amd64" + ], + "architecture_note": "Een of meer containerimages van deze app worden alleen voor AMD64 gepubliceerd. Deze app werkt daardoor niet op een Raspberry Pi." }, "variables": [ { diff --git a/apps/pinchflat/template.json b/apps/pinchflat/template.json index 9f4e393..e3281bf 100644 --- a/apps/pinchflat/template.json +++ b/apps/pinchflat/template.json @@ -18,7 +18,11 @@ "categories": [ "media", "downloaden" - ] + ], + "architectures": [ + "amd64" + ], + "architecture_note": "Een of meer containerimages van deze app worden alleen voor AMD64 gepubliceerd. Deze app werkt daardoor niet op een Raspberry Pi." }, "variables": [ { diff --git a/apps/pixelfed/template.json b/apps/pixelfed/template.json index 5d6a5b3..93970e4 100644 --- a/apps/pixelfed/template.json +++ b/apps/pixelfed/template.json @@ -18,7 +18,11 @@ "categories": [ "fotos", "communicatie" - ] + ], + "architectures": [ + "amd64" + ], + "architecture_note": "Een of meer containerimages van deze app worden alleen voor AMD64 gepubliceerd. Deze app werkt daardoor niet op een Raspberry Pi." }, "variables": [ { diff --git a/apps/tubearchivist/template.json b/apps/tubearchivist/template.json index 47f8f42..24c1169 100644 --- a/apps/tubearchivist/template.json +++ b/apps/tubearchivist/template.json @@ -18,7 +18,11 @@ "categories": [ "media", "downloaden" - ] + ], + "architectures": [ + "amd64" + ], + "architecture_note": "Een of meer containerimages van deze app worden alleen voor AMD64 gepubliceerd. Deze app werkt daardoor niet op een Raspberry Pi." }, "variables": [ { diff --git a/apps/unbound/template.json b/apps/unbound/template.json index 88a517d..34463c6 100644 --- a/apps/unbound/template.json +++ b/apps/unbound/template.json @@ -18,7 +18,11 @@ "categories": [ "netwerk", "beveiliging" - ] + ], + "architectures": [ + "amd64" + ], + "architecture_note": "Een of meer containerimages van deze app worden alleen voor AMD64 gepubliceerd. Deze app werkt daardoor niet op een Raspberry Pi." }, "variables": [ { diff --git a/apps/unifi-network/files/compose.yaml b/apps/unifi-network/files/compose.yaml index 68ec96b..29bdb2b 100644 --- a/apps/unifi-network/files/compose.yaml +++ b/apps/unifi-network/files/compose.yaml @@ -11,6 +11,7 @@ services: - MONGO_HOST=<< service_name >>-mongo - MONGO_PORT=27017 - MONGO_DBNAME=unifi + - MONGO_AUTHSOURCE=admin volumes: - << appdata_dir >>/<< service_name >>/config:/config ports: @@ -19,18 +20,31 @@ services: - "<< port_stun >>:3478/udp" - "<< port_discovery >>:10001/udp" depends_on: - - << service_name >>-mongo + << service_name >>-mongo: + condition: service_healthy restart: unless-stopped << service_name >>-mongo: - image: mongo:4.4 + image: mongo:4.4.29 container_name: << service_name >>-mongo environment: - - MONGO_INITDB_ROOT_USERNAME=unifi + - MONGO_INITDB_ROOT_USERNAME=root - MONGO_INITDB_ROOT_PASSWORD=<< mongo_password >> - - MONGO_INITDB_DATABASE=unifi + - MONGO_USER=unifi + - MONGO_PASS=<< mongo_password >> + - MONGO_DBNAME=unifi + - MONGO_AUTHSOURCE=admin volumes: - << service_name >>_mongodata:/data/db + - ./init-mongo.sh:/docker-entrypoint-initdb.d/init-mongo.sh:ro + healthcheck: + test: + - CMD-SHELL + - 'mongo --quiet --username root --password "$${MONGO_INITDB_ROOT_PASSWORD}" --authenticationDatabase admin --eval "db.adminCommand({ ping: 1 })"' + interval: 10s + timeout: 5s + retries: 12 + start_period: 30s restart: unless-stopped volumes: diff --git a/apps/unifi-network/files/init-mongo.sh b/apps/unifi-network/files/init-mongo.sh new file mode 100755 index 0000000..3785a7d --- /dev/null +++ b/apps/unifi-network/files/init-mongo.sh @@ -0,0 +1,24 @@ +<% raw %> +#!/bin/bash + +if which mongosh > /dev/null 2>&1; then + mongo_init_bin="mongosh" +else + mongo_init_bin="mongo" +fi +"${mongo_init_bin}" < diff --git a/apps/unifi-network/template.json b/apps/unifi-network/template.json index f320d8f..7728d2e 100644 --- a/apps/unifi-network/template.json +++ b/apps/unifi-network/template.json @@ -19,7 +19,12 @@ "categories": [ "netwerk", "beheer" - ] + ], + "architectures": [ + "amd64", + "arm64" + ], + "architecture_note": "Op een Raspberry Pi is een 64-bits besturingssysteem vereist. Voor een Pi 4 is de database bewust vastgezet op MongoDB 4.4.29." }, "variables": [ { diff --git a/apps/wazuh/template.json b/apps/wazuh/template.json index a8c6146..79ebe81 100644 --- a/apps/wazuh/template.json +++ b/apps/wazuh/template.json @@ -18,7 +18,11 @@ "categories": [ "beveiliging", "monitoring" - ] + ], + "architectures": [ + "amd64" + ], + "architecture_note": "Een of meer containerimages van deze app worden alleen voor AMD64 gepubliceerd. Deze app werkt daardoor niet op een Raspberry Pi." }, "variables": [ { diff --git a/docs/apps-maken.md b/docs/apps-maken.md index aa8d7cf..890dc53 100644 --- a/docs/apps-maken.md +++ b/docs/apps-maken.md @@ -112,6 +112,20 @@ De testsuite controleert dit voor elke app. | `categories` | Eén of meer categorie-ids (zie onder). Ontbreken ze, dan worden ze afgeleid uit de tags | | `icon` | `{"provider": "selfhst", "id": "…"}` of `dashboard-icons`. Bestaat het icoon niet, dan valt de interface terug op een emoji | | `version` | Alleen ter informatie in de catalogus | +| `architectures` | Optionele lijst met aantoonbaar ondersteunde Docker-architecturen: `amd64`, `arm64`, `arm/v7` of `arm/v6` | +| `architecture_note` | Uitleg die bij de platformmelding in de catalogus en het installatieformulier staat | + +Laat `architectures` weg als de image-ondersteuning niet is geverifieerd. Een +expliciete mismatch blokkeert de installatie zowel in de interface als in de +API; ontbrekende of onbekende metadata blokkeert bewust niets. Voorbeeld: + +```json +"architectures": ["amd64", "arm64"], +"architecture_note": "Op een Raspberry Pi is een 64-bits OS vereist." +``` + +Controleer alle images uit de compose-stack. Zodra een database- of +hulpcontainer alleen AMD64 aanbiedt, is de hele app alleen geschikt voor AMD64. ### Categorieën diff --git a/server-up/app.py b/server-up/app.py index 534ace6..96bf7e7 100644 --- a/server-up/app.py +++ b/server-up/app.py @@ -1391,7 +1391,10 @@ def api_repos_stacks(rid): repo = next((r for r in cfg.load().get("APP_REPOS", []) if r["id"] == rid), None) if not repo: return jsonify(stacks=[]) - return jsonify(stacks=git.scan_stacks(rid, lib, subdir=repo.get("subdir", ""))) + stacks = git.scan_stacks(rid, lib, subdir=repo.get("subdir", "")) + for stack in stacks: + stack["compatibility"] = boilerplates.compatibility(stack) + return jsonify(stacks=stacks) def _is_stack_dir(p: Path) -> bool: @@ -1655,6 +1658,7 @@ def api_store_preview(): repo_id=rid, format="boilerplate", metadata=meta, + compatibility=boilerplates.compatibility(meta), fields=flds, services=diensten, depends_on=_afhankelijkheden_met_status(meta), @@ -1804,6 +1808,11 @@ def api_store_install(): is_bp = boilerplates.is_boilerplate(src) if not is_bp and not docker.has_compose(src): return jsonify(ok=False, msg=f"'{stack}' heeft geen compose bestand") + if is_bp: + passend = boilerplates.compatibility(boilerplates.metadata(src)) + if not passend["supported"]: + return jsonify(ok=False, msg=passend["reason"], + compatibility=passend), 400 # Verplichte velden vóór het aanmaken van de job: een leeg wachtwoord of # een lege sleutel levert een container op die niet start of die met een diff --git a/server-up/core/boilerplates.py b/server-up/core/boilerplates.py index 3f83098..1232ee0 100644 --- a/server-up/core/boilerplates.py +++ b/server-up/core/boilerplates.py @@ -10,7 +10,7 @@ Renders boilerplate templates into a flat directory that Server Up can manage just like any other stack — i.e. a single compose file (+ optional .env). """ from __future__ import annotations -import json, re, shutil +import json, platform, re, shutil from pathlib import Path from typing import Any @@ -72,9 +72,60 @@ def metadata(d: Path) -> dict: "format": "boilerplate", "draft": bool(md.get("draft")), "depends_on": _afhankelijkheden(md.get("depends_on")), + "architectures": _architecturen(md.get("architectures")), + "architecture_note": str(md.get("architecture_note") or "").strip(), } +_ARCH_ALIASES = { + "x86_64": "amd64", "x86-64": "amd64", "amd64": "amd64", + "aarch64": "arm64", "arm64": "arm64", "arm64v8": "arm64", + "armv7l": "arm/v7", "armv7": "arm/v7", "arm/v7": "arm/v7", + "armv6l": "arm/v6", "armv6": "arm/v6", "arm/v6": "arm/v6", +} + + +def _architectuur(waarde: str) -> str: + return _ARCH_ALIASES.get(str(waarde or "").strip().lower(), "") + + +def _architecturen(waarde) -> list[str]: + "Normaliseer Docker-architecturen en negeer onbekende schrijfwijzen." + if isinstance(waarde, str): + waarde = [waarde] + uit: list[str] = [] + for item in (waarde or []): + arch = _architectuur(item) + if arch and arch not in uit: + uit.append(arch) + return uit + + +def host_architecture(machine: str | None = None) -> str: + "Docker-architectuurnaam van de host waarop Server Up draait." + return _architectuur(machine if machine is not None else platform.machine()) + + +def compatibility(meta: dict, machine: str | None = None) -> dict: + "Beoordeel expliciete template-metadata voor de huidige host." + toegestaan = _architecturen(meta.get("architectures")) + huidig = host_architecture(machine) + bekend = bool(toegestaan and huidig) + ondersteund = not bekend or huidig in toegestaan + reden = "" + if bekend and not ondersteund: + labels = {"amd64": "AMD64", "arm64": "ARM64", + "arm/v7": "ARM 32-bit", "arm/v6": "ARMv6"} + verwacht = ", ".join(labels.get(a, a) for a in toegestaan) + reden = (f"Deze app ondersteunt {labels.get(huidig, huidig)} niet. " + f"Ondersteund: {verwacht}.") + notitie = str(meta.get("architecture_note") or "").strip() + if notitie: + reden += f" {notitie}" + return {"known": bekend, "supported": ondersteund, + "architecture": huidig, "reason": reden} + + def _afhankelijkheden(waarde) -> list[dict]: """Welke andere apps heeft dit sjabloon nodig, en waarom? @@ -437,6 +488,12 @@ def render_to_dir(src: Path, dest: Path, values: dict[str, Any]) -> list[str]: # Drop blocks of pure whitespace left over after conditionals strip rendered = _tidy(rendered) out.write_text(rendered, encoding="utf-8") + # Een tekstscript kan uitvoerbaar zijn. write_text maakt een nieuw + # bestand met standaardrechten; behoud daarom de bronmodus. + try: + shutil.copymode(p, out) + except OSError: + pass else: shutil.copy2(p, out) written.append(str(rel)) diff --git a/server-up/templates/index.html b/server-up/templates/index.html index 1b6b08b..3af25b3 100644 --- a/server-up/templates/index.html +++ b/server-up/templates/index.html @@ -650,6 +650,13 @@ tailwind.config = {
BP + Pi 64-bit + +
+ +
@@ -684,6 +691,8 @@ tailwind.config = {