server-up/CHANGELOG.md
Ramon ae99df2fc7
Some checks failed
Deploy server-up (dev) / deploy (push) Failing after 6s
v0.5.20-beta - complete backups en updates per app
Backups (core/backups.py, core/scheduler.py):
- Terugzetten met een klik: stack stoppen, huidige map opzij, uitpakken,
  starten. Mislukt het uitpakken, dan wordt de oude situatie teruggeplaatst.
- Bewaarbeleid: aantal per stack en/of maximale leeftijd; de nieuwste backup
  van een stack blijft altijd staan.
- Geplande backups (dagelijks/wekelijks) via een eigen planner in de app, geen
  cron. Een gemiste ronde loopt bij de eerstvolgende gelegenheid alsnog.
- Automatisch een backup voor het bijwerken of verwijderen van een stack.
  Mislukt die, dan gaat de actie door - anders kun je een kapotte stack niet
  meer opruimen.
- Uitpakken met tarfile + filter="data": absolute paden en ..-ingangen worden
  geweigerd. Met een kaal `tar xzf` kon een geprepareerd archief buiten de
  stackmap schrijven.
- Twee bugs in de oude implementatie: de returncode van tar werd genegeerd
  (mislukte backup gold als succes) en backups binnen dezelfde seconde
  overschreven elkaar.

Updates per app (core/stackupdates.py, core/registry.py):
- Badge op de stackkaart als er een nieuwer image is; bijwerken doet de
  bestaande update-knop.
- Vergelijking via de Registry API v2 (Docker-Content-Digest) in plaats van
  `docker manifest inspect`: dat laatste geeft per platform een aparte digest
  terwijl RepoDigests de manifest-list-digest bevat, wat bij elk multi-arch
  image permanent "update beschikbaar" zou opleveren.
- Drie statussen: update / current / unknown. Lokaal gebouwd, nog niet gepulld
  of registry onbereikbaar geeft unknown en dus geen badge.
- Dagelijkse achtergrondcheck, resultaten 6 uur gecached.

Verder:
- docs/backups.md, met nadruk op wat er niet in een backup zit: de
  Docker-volumes met de eigenlijke appdata.
- Backup-instellingen onder Instellingen; backup-geschiedenis per stack.
- 197 tests groen (40 nieuwe).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01C7oLCRYzY5ixJ5Sv8Y8EFb
2026-07-26 19:54:51 +02:00

21 KiB

v0.5.20-beta — Vertalingen, SSO-scherm, complete backups en app-updates

🌍 Vertalingen en SSO

  • De schermen uit v0.5.x (login, Beveiliging, Netwerken, Updates, containerpaneel) gebruikten harde Nederlandse teksten terwijl de rest van de interface via t() loopt. Op Engels gaf dat een mengelmoes. Nu volledig vertaald: 208 sleutels in nl en en.
  • t() ondersteunt plaatshouders: t('port_taken', {port: 8080}). Zo blijven zinnen heel in plaats van in losse stukjes geknipt.
  • Nieuw instelscherm voor reverse-proxy-SSO onder Instellingen → Beveiliging: modus lokaal/proxy/beide, identiteitsheader en de vertrouwde proxy-adressen. De backend bestond al maar was alleen met curl te bereiken.
  • Nieuwe test bewaakt dat nl en en dezelfde sleutels houden, dat de plaatshouders in beide talen gelijk zijn, en dat de interface geen sleutels gebruikt die nergens gedefinieerd staan.

💾 Backups compleet

Backup was één tar-commando waarvan de returncode genegeerd werd — een mislukte backup gold als succes, en terugzetten kon alleen met de hand.

  • Terugzetten met één klik: stack stoppen, huidige map opzij, uitpakken, starten. Mislukt het uitpakken, dan komt de oude situatie terug.
  • Bewaarbeleid: aantal per stack en/of maximale leeftijd. De nieuwste backup van een stack blijft altijd staan.
  • Geplande backups, dagelijks of wekelijks, via een eigen planner in de applicatie (geen cron). Een gemiste ronde loopt bij de eerstvolgende gelegenheid alsnog.
  • Automatisch een backup vóór het bijwerken of verwijderen van een stack. Mislukt die, dan gaat de actie door — anders kun je een kapotte stack niet meer opruimen.
  • Uitpakken gebeurt met tarfile en filter="data": absolute paden en ..-ingangen worden geweigerd. Met een kaal tar xzf kon een geprepareerd archief buiten de stackmap schrijven.
  • Backups binnen dezelfde seconde overschreven elkaar; namen krijgen nu een teller.
  • docs/backups.md legt uit wat er wél en niet in zit — de compose- en .env-bestanden, niet de Docker-volumes met de eigenlijke appdata.

📦 Updates per app

  • Server Up controleert nu ook of er nieuwere images zijn voor de geïnstalleerde stacks, met een badge op de stackkaart en een knop om te controleren. Bijwerken doet de bestaande update-knop.
  • De vergelijking loopt via de Registry API v2 (Docker-Content-Digest), niet via docker manifest inspect: dat laatste geeft per platform een aparte digest terug terwijl RepoDigests de digest van de manifest-list bevat, wat bij elk multi-arch image permanent "update beschikbaar" zou opleveren.
  • Drie statussen per service — update, current en unknown. Een lokaal gebouwd image, een nog niet gepulld image of een onbereikbare registry levert unknown op en dus géén badge, zodat er nooit ten onrechte een update wordt gemeld.
  • Dagelijkse achtergrondcheck via dezelfde planner; resultaten 6 uur gecached.

v0.5.10-beta — Update-systeem met kanalen en één-klik bijwerken

🔄 Kanalen

Server Up kent nu twee update-kanalen in plaats van een vinkje "pre-releases meenemen":

  • stable — alleen echte releases (v0.5.10)
  • beta — ook pre-releases (v0.5.10-beta1), die release.yml automatisch als zodanig markeert bij een tag met een streepje

Een release telt hoger dan zijn eigen beta (0.5.10-beta1 < 0.5.10), dus wie op beta zit krijgt de definitieve versie alsnog aangeboden. De oude instelling UPDATE_INCLUDE_PRERELEASE migreert automatisch naar het beta-kanaal.

⬇️ Bijwerken vanuit de interface

Nieuw: Nu bijwerken haalt het image uit je Forgejo container-registry en vervangt de eigen container.

  • Omdat een container zichzelf niet kan hercreëren, doet Server Up alleen het voorwerk (image ophalen, SU_TAG wegschrijven) en laat het de hercreatie over aan een korte helper-container die op het nieuwe image draait — dat bevat de docker- en compose-CLI al.
  • De deploy-map wordt uitgelezen uit de compose-labels van de eigen container, niet geraden. Ontbreken die labels, draait de container niet vanaf een registry-image, of is de docker-socket er niet, dan meldt de interface precies waaróm bijwerken niet kan in plaats van iets te proberen.
  • De vorige tag wordt onthouden, met een terugrolknop in de instellingen.
  • Voortgang van de docker pull loopt via het bestaande job-logvenster; daarna pollt de browser /healthz tot de nieuwe versie leeft.

🔧 Overig

  • Update-check wordt een uur gecached; Controleren forceert een verse check. Eerder deed elke paginalading een netwerkverzoek.
  • Laatst-gecontroleerd-tijdstip zichtbaar in de interface.
  • Registry-inloggegevens instelbaar voor een privé registry. Het token wordt net als de git-tokens nooit teruggegeven door de API (alleen een has_-vlag) en leeg laten betekent "ongewijzigd".
  • docker-compose.yml gebruikt ${SU_IMAGE:-server-up}:${SU_TAG:-latest}, zodat image en tag los instelbaar zijn. Zonder SU_IMAGE blijft alles werken zoals voorheen (lokaal bouwen).
  • deploy-prod.yml bouwt én pusht het image in dezelfde job wanneer SU_IMAGE ingesteld is. Bewust niet als aparte workflow: bij één runner zou de deploy wachten op een build die zelf nog in de wachtrij staat.
  • build.yml is nu alleen handmatig, voor het herbouwen van een specifieke tag.
  • docs/updates.md toegevoegd: hoe het werkt, de complete Forgejo-instelling (registry, tokens, variables, secrets, runners), de release-procedure voor zowel stable als beta, terugrollen en een probleemoplostabel.

v0.5.00-beta — Authenticatie, beveiliging en eigen IP-adressen

Let op bij het bijwerken. Server Up heeft nu een login. Open na het bijwerken meteen de webinterface en maak een beheerdersaccount aan — zolang dat niet gebeurd is, kan iedereen die de pagina bereikt het account claimen. De poort wordt voortaan standaard op 127.0.0.1 gebonden; zet BIND=0.0.0.0 in je .env als je er van buiten de server bij moet (liefst achter een reverse proxy met TLS — zie docs/beveiliging.md).

🔐 Authenticatie

Tot nu toe was elke /api/*-route open. Omdat Server Up de docker-socket als root gebruikt, betekende dat: wie de poort kon bereiken, had root op de host.

  • Lokale accounts met scrypt-gehashte wachtwoorden, sessiecookie (HttpOnly, SameSite=Strict) en lockout na vijf mislukte pogingen.
  • Optionele SSO via een reverse-proxy-header (Authelia/Authentik/Cloudflare Access). De header wordt alleen vertrouwd vanaf een geconfigureerd proxy-IP.
  • Loginscherm en eerste-account-setup in de interface; gebruikersbeheer en wachtwoord wijzigen onder Instellingen → Beveiliging.

🛡️ Beveiligingsfixes

  • CSRF: elke mutatie vereist een X-CSRF-Token-header. Routes die de toestand wijzigen accepteren geen GET meer — /api/docker/restart was eerder met een <img>-tag vanaf een willekeurige website te triggeren.
  • Path traversal: /api/store/install controleerde de instantienaam niet; "instance": "../../…" schreef buiten de library. Alle stack-, instantie- en repo-namen lopen nu door één safe_name()-validatie.
  • Tokenlek: git-tokens werden teruggegeven door /api/repos en /api/settings. Die zijn vervangen door een has_token-vlag; opslaan met een leeg veld wist het bestaande token niet meer.
  • Git-URL's: alleen http(s)://, ssh:// en git@host:pad. Git's ext::-transport voert een shell-commando uit en wordt nu geweigerd.
  • Templates renderen in een Jinja2-sandbox (server-side template injection).
  • SSH: host-keys worden geverifieerd (accept-new + /data/known_hosts); eerder stond StrictHostKeyChecking=no, waarmee elke MITM onzichtbaar was.
  • Modules: repo's worden niet meer automatisch bij elke start gepulld (AUTO_SYNC_ON_BOOT, standaard uit) en kunnen per repo op een commit worden vastgezet.
  • Productie-WSGI-server (waitress) i.p.v. de Flask-ontwikkelserver, limiet op request-grootte, ProxyFix, en CSP/X-Frame-Options/nosniff/ Referrer-Policy-headers.
  • Tailwind, Alpine en htmx worden meegeleverd in plaats van vanaf een CDN geladen; Google Fonts is eruit. De interface werkt nu ook offline.
  • config.json en de sleutel staan op 0600.

🌐 Stacks met een eigen IP-adres

Nieuw: geef een stack een eigen adres in je LAN in plaats van poorten op de host (macvlan/ipvlan). Geen poortconflicten meer, apps op hun eigen standaardpoort, en je kunt per app firewallen.

  • Netwerkbeheer onder Instellingen → Netwerken (driver, host-interface, subnet, gateway, optionele IP-range).
  • Bij het installeren kies je "Poorten op de host" of een netwerk; Server Up stelt het eerstvolgende vrije adres voor en houdt toegekende adressen vast.
  • Het gerenderde compose-bestand wordt automatisch omgezet: poortmappings eruit, netwerk met ipv4_address erin. De templates in apps/ blijven ongewijzigd.
  • Uitleg en valkuilen (waaronder de shim-interface die de host nodig heeft om zijn eigen macvlan-containers te bereiken) staan in docs/netwerken.md.

🧩 Beheer per container

De stackkaart klapt uit naar de losse containers: per container starten, stoppen, herstarten, logs bekijken en live CPU-/geheugengebruik.

📦 Veiliger installeren

  • Poortvelden krijgen een vrij poortnummer voorgesteld — next_free_port() bestond al maar werd nergens gebruikt. Bezette poorten worden gemeld.
  • Velden voor tokens en wachtwoorden krijgen een genereerknop.
  • Compose wordt gevalideerd (docker compose config) vóór het wegschrijven, dus een typefout in de editor maakt een draaiende stack niet meer onstartbaar.

🔧 Overig

  • Testsuite met pytest (112 tests), ook als stap in beide deploy-workflows.
  • Audit-log gebruikt één gedeelde SQLite-verbinding — elke job lekte eerder een file descriptor. Joblogs worden afgekapt op 2000 regels.
  • Het VERSION-bestand is de enige bron voor het versienummer.
  • Lichte /healthz voor de healthcheck in plaats van docker info.
  • fix-config.sh verwijderd: bevatte een hardgecodeerd intern IP en overschreef de configuratie van de gebruiker.

v0.4.60 — Tweecijferig patch-nummer

Versiebeleid vanaf v0.4.60

Het patch-deel (laatste cijfer) gebruikt nu twee cijfers en loopt in tientallen: 0.4.60, 0.4.61, … 0.4.69, 0.4.70. Blijft semver-compatibel (het deel wordt als geheel getal vergeleken, dus 0.4.60 > 0.4.6).


v0.4.6 — Kaal versienummer op alle builds

Wijziging in v0.4.6

De deploy-workflows tonen nu overal het kale versienummer uit het VERSION- bestand (of de tagnaam), zonder -dev/-rc.<sha>-suffix. Eenvoudiger te lezen; bump bij elke wijziging gewoon VERSION.


v0.4.5 — Releases & in-app update-check

Nieuw in v0.4.5

🏷️ Automatische releases bij een tag

Push je een v*-tag, dan maakt de nieuwe release.yml-workflow automatisch een Forgejo-Release aan met notes uit de bijbehorende CHANGELOG.md-sectie plus een commit-overzicht sinds de vorige tag. Pre-release tags (bv. v0.8.4-beta1) worden als pre-release gemarkeerd. Zo krijg je een Releases-pagina met duidelijke changelog per versie, vergelijkbaar met GitHub Releases.

🔔 In-app "update beschikbaar"

Server Up vergelijkt de draaiende versie met de laatste release via de Forgejo/ GitHub Releases-API (semver-vergelijking, pre-releases optioneel). Is er een nieuwere versie, dan verschijnt een melding in de topbar en een blok in Instellingen → Updates met versie, release-notes en een link. Instelbaar via UPDATE_API_URL (of env SU_UPDATE_API) en de optie "pre-releases meenemen".

🔖 Versiebeleid

Bump bij elke wijziging het centrale VERSION-bestand; tags zijn de bron voor release-versies (vX.Y.Z, of -beta/-rc voor pre-releases).


v0.4.4 — Nette versienummers op alle builds

Nieuw in v0.4.4

Centraal VERSION-bestand (semver) is nu de bron voor het versienummer. De deploy-workflows lezen het en bouwen:

  • tag v0.4.40.4.4 (productie, toont v0.4.4)
  • push naar main0.4.4-rc.<sha>
  • push naar dev0.4.4-dev.<sha>

Zo zie je voortaan het échte versienummer in de topbar i.p.v. alleen de git-SHA (vdev-1eab7c07). Bump bij elke wijziging alleen nog VERSION (en voor de zekerheid de fallback in app.py/index.html voor lokale runs).


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

De volledige Tailwind-stylesheet faalde stil omdat .modal via @apply de eigen CSS-animatieklasse anim toepaste. Tailwind's @apply accepteert alleen Tailwind-utilities, geen losse CSS-klassen, waardoor de hele Play-CDN-compilatie afbrak en de pagina ongestyled (kaal HTML) werd geladen. De animatie staat nu als gewone CSS-regel (.anim, .modal { animation: … }) los van @apply.


v0.4.0 — Volledig nieuw UI-ontwerp (Modern SaaS)

Nieuw in v0.4.0

🎨 Compleet herontworpen interface

Volledig nieuw, licht en ruim "Modern SaaS"-ontwerp (Inter-font, indigo accent, zachte schaduwen, ronde 2xl-kaarten). Nieuwe app-shell: verticale sidebar met merk-header, gegroepeerde navigatie met actieve indicator en live status-footer; slanke sticky topbar met dynamische paginatitel en status-chips. Dashboard, stacks, app store, images-/audittabellen, instellingen, modals, wizard, toasts en het log-paneel zijn allemaal opnieuw vormgegeven. Inklapbaar menu en grote touch-targets blijven behouden; volledig mobielvriendelijk.

🌗 Thema volgt systeemvoorkeur

Standaard volgt het thema de OS-voorkeur (licht/donker) en reageert live op wijzigingen. Handmatige override via Auto / Light / Dark in Instellingen.


v0.3.1 — UI-restyle + taalmodule (add-on)

Nieuw in v0.3.1

🎨 Grondige UI-restyle

Grotere, touch-vriendelijke knoppen (min. 44px), ruimere spacing, grotere typografie en kaarten. Layout gecentreerd met max-breedte voor meer lucht op grote schermen. Volledig mobielvriendelijk.

📐 Inklapbaar menu (ook op desktop)

De hamburger klapt de sidebar nu ook op desktop in/uit; voorkeur wordt onthouden (localStorage), hoofdinhoud en log-paneel schuiven mee. Op mobiel een tap-to-close overlay.

🌐 Taalmodule als add-on

Nieuwe sectie in Instellingen → "Taalmodule": talen toevoegen, bewerken, verwijderen en ingebouwde talen (NL/EN) dupliceren als startpunt. Editor toont per sleutel de Engelse referentie met zoekfilter. Backend-endpoints: POST/DELETE /api/i18n, GET /api/i18n/keys, GET /api/i18n/<code>/raw. Toegevoegde talen komen als JSON in translations/ en zijn direct beschikbaar.


v0.3.01 — Boilerplates fixes + git-driven versioning

Fixes bovenop v0.3.0

🔁 Auto-migratie van default-repos voor upgraders

Bestaande installaties (vanaf v0.2.x) hadden de Boilerplates-repo niet zichtbaar in de App Store omdat hun config.json in de su-data volume al bestond en de nieuwe DEFAULTS["APP_REPOS"] daardoor werd overschreven.

core/__init__.py load() doet nu een eenmalige migratie: ontbrekende default-repos worden bij opstart aangevuld op basis van id, en gemarkeerd in MIGRATIONS_DONE: ["v0.3.0_default_repos"]. Wordt direct gepersisteerd. Verwijdert een gebruiker de Boilerplates-repo expliciet, dan komt-ie niet automatisch terug.

🧹 YAML-poetsstap na boilerplate-render

Bij stacks waar de meeste optionele groepen uit staan (Authentik, Nextcloud, etc.) liet de Jinja-render verlaten mapping-sleutels achter (volumes:, networks:, etc. zonder kinderen). Docker-compose faalt daarop met "block sequence entries are not allowed in this context".

core/boilerplates.py _tidy() heeft nu een _drop_empty_mappings() substep die in meerdere passes mapping-sleutels verwijdert die alleen worden gevolgd door whitespace/commentaar of een sibling op gelijke/lagere indent. Werkt in cascade.

🏷️ Versie komt nu uit git

Dockerfile accepteert ARG SU_VERSION=dev en bakt die in ENV SU_VERSION + OCI image-label. Met docker build --build-arg SU_VERSION=$(git describe ...) weet het image zijn eigen versie. De Server Up UI toont automatisch de juiste waarde, ongeacht wat de productie-compose meegeeft.

Forgejo Actions kan een tag-push automatisch verwerken — zie server-up-deploy/README.md.

Migratie vanaf v0.3.0

docker compose build --no-cache
docker compose up -d

v0.3.0 — UI rebuild + Boilerplates support

Hoogtepunten

🎨 Nieuwe UI (Tailwind + Alpine + HTMX)

  • templates/index.html is volledig herschreven. De handgeschreven CSS (--bg/--s1/... variabelen, ad-hoc grid-classes) is vervangen door Tailwind utility-classes met een gematchte donker/licht-palette.
  • Statebeheer via Alpine.js: één app() component bovenop het hele document, geen $=document.getElementById-spaghetti meer.
  • HTMX is geladen voor toekomstige server-rendered partials. Het bestaande fetch-RPC patroon blijft werken; HTMX kan progressief worden ingezet.
  • Modals, toasts, terminal-overlay en first-run wizard zitten allemaal in één Alpine-state — geen losse globale variabelen meer.
  • Mobiele sidebar gedraagt zich nu correct (slide-in i.p.v. layout-flip).

🧩 ChristianLempa Boilerplates ondersteund

Server Up herkent nu twee stack-formaten naast elkaar:

Formaat Detectie Bron
Compose (origineel) compose.yml / docker-compose.yml (+ optioneel stack.json) bes-r/server-up
Boilerplate (nieuw) template.json + files/ ChristianLempa/boilerplates-library

Nieuw bestand app/core/boilerplates.py:

  • is_boilerplate(d) — detectie
  • metadata(d) — converteert template.json["metadata"] (incl. selfhst-icons) naar Server-Up formaat
  • fields(d) — flattened variable-schema voor de install-modal
  • render_to_dir(src, dest, values) — rendert files/*.yaml met de Jinja-achtige << var >> + <%- if expr %> syntax die de boilerplates gebruiken (Jinja2 met custom delimiters). Niet-tekst bestanden worden verbatim gekopieerd.

app/core/git.py_scan_compose_dirs herkent beide formaten en zet format: "boilerplate" in de stack-entry zodat de UI er een badge bij kan tonen.

app/app.py:

  • _find_stack_src accepteert ook boilerplate-mappen.
  • POST /api/store/preview retourneert voor boilerplates het variabelen-schema (fields) plus een gerenderde preview met defaults.
  • POST /api/store/install met body {values: {...}} rendert de templates voor je voordat de stack gestart wordt.

⚙️ Default-repos

Een nieuwe Server Up komt nu uit de doos met twee app-repositories:

  1. bes-r/server-up — eigen stacks, submap apps
  2. ChristianLempa/boilerplates-library — community templates, submap compose

📦 Versie / Dockerfile

  • SU_VERSION = 0.3.0 in Dockerfile en docker-compose.yml
  • requirements.txt: Jinja2 expliciet toegevoegd (was al een Flask-dep)

Migratie vanaf v0.2.29

  • Build opnieuw: docker compose up -d --build --no-cache
  • Bestaande stacks blijven werken (legacy formaat is intact).
  • De Boilerplates-repo wordt automatisch toegevoegd voor verse installs. Bestaande gebruikers kunnen de repo handmatig toevoegen via Instellingen → Git Repositories met submap compose.

Bekende beperkingen

  • Boilerplate-templates met onbekende custom-filters of complexe Ansible-style conditionals kunnen falen — de fout verschijnt in het terminal-paneel.
  • volumes: blokken die conditioneel zijn (<%- if volume_mode == 'local' %>) werken; complexere render-logica (loops over services) is nog niet getest.
  • HTMX is geladen maar de meeste interacties draaien nog op fetch-RPC. Verdere migratie naar server-rendered partials kan stapsgewijs in volgende releases.