Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

7 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

RM Static CMS Kit

RM Static CMS Kit is een lichte static-site-generator met een klein Flask CMS erbovenop.

Het doel is simpel: snelle websites bouwen voor kleine organisaties, zzp'ers, verenigingen en projecten zonder zwaar CMS zoals WordPress.

De publieke website is statisch. Het CMS draait apart en genereert bij wijzigingen opnieuw de statische HTML. Daardoor blijft de publieke website snel, simpel en onderhoudsarm.

Huidige basisversie:

v2.0.0

Wat zit erin?

  • Static-site-generator in Python
  • Flask/Gunicorn CMS
  • Statische publieke HTML-output
  • Markdown-content voor pagina's, blog/publicaties, diensten en projecten
  • JSON-content voor agenda/evenementen
  • Contactpagina en contactformulier
  • Projecten/portfolio-module
  • Agenda/evenementenmodule
  • RSS-feed
  • Sitemap
  • Configureerbare navigatie
  • Configureerbare contenttypes
  • Modulekeuze per site via installer-wizard
  • Per-site kleuren en thema-opties via config/site.json
  • Publiceren naar /var/www/<domein>/public
  • Per-site CMS-wachtwoord via /etc/rmstatic/<domein>.env
  • Per-site systemd-service voor het CMS
  • Installer-scripts voor sites aanmaken en verwijderen
  • Demo-content voor alle standaardmodules

Architectuur

Een RM Static CMS Kit-site bestaat uit twee delen.

Publieke website:

Bezoeker
  ↓
Nginx / reverse proxy
  ↓
/var/www/<domein>/public
  ↓
Statische HTML, CSS, JS en uploads

CMS:

Beheerder
  ↓
cms.<domein>
  ↓
Nginx Proxy Manager / reverse proxy
  ↓
Flask CMS via systemd
  ↓
/opt/sites/<domein>
  ↓
content, config, generator en theme

De publieke website is dus niet afhankelijk van Flask tijdens normaal bezoek. Flask is alleen nodig voor beheer en opnieuw genereren van de statische site.

Belangrijke paden

Centrale kit:

/opt/rm-static-cms-kit

Sites:

/opt/sites/<domein>

Publieke webroot:

/var/www/<domein>/public

CMS-configuratie en wachtwoorden:

/etc/rmstatic/<domein>.env

Systemd-services:

/etc/systemd/system/<site>_cms.service

Voorbeeld:

demo.rmeijer.eu -> demo_rmeijer_eu_cms.service

Nginx-configs:

/etc/nginx/sites-available/<domein>
/etc/nginx/sites-enabled/<domein>

Exports:

/opt/rmstatic-exports

Structuur van de kit

/opt/rm-static-cms-kit/
├── bin/
├── cms/
├── config/
├── content/
├── generator/
├── scripts/
├── theme/
├── requirements.txt
├── VERSION
├── README.md
└── .gitignore

Belangrijke onderdelen:

generator/build.py              Static-site-generator
cms/app.py                      Flask CMS
theme/default/                  Standaardthema
config/site.json                Standaardconfiguratie
content/                        Demo-content
bin/                            Installer- en beheerscripts
scripts/deploy.sh               Build + deploy per site

Beheercommando's

Site aanmaken

Hoofdcommando:

rmstatic-create-site klant.nl "Klantnaam"

Dit start de wizard. De wizard vraagt welke modules moeten worden ingeschakeld:

Blog/publicaties inschakelen?
Dienstenmodule inschakelen?
Agenda/evenementen inschakelen?
Projecten/portfolio inschakelen?
Contactformulier inschakelen?
RSS-feed inschakelen?
Sitemap inschakelen?
Social images/uploads inschakelen?
Demo-content plaatsen/behouden?

Onder water gebruikt dit:

/usr/local/bin/rmstatic-create-site
/usr/local/bin/rmstatic-create-site-wizard
/usr/local/bin/rmstatic-create-site-core

De scripts staan ook in de repository onder:

bin/

Site verwijderen

rmstatic-remove-site klant.nl

De verwijdertool vraagt om expliciete bevestiging door het domein exact in te typen.

De tool verwijdert:

/opt/sites/<domein>
/var/www/<domein>
/etc/rmstatic/<domein>.env
/etc/systemd/system/<site>_cms.service
/etc/nginx/sites-available/<domein>
/etc/nginx/sites-enabled/<domein>

Help:

rmstatic-remove-site --help

Handmatig builden en deployen

Gebruik bij voorkeur het CMS om de website opnieuw te genereren.

Handmatig deployen kan zo:

sudo -u moofz bash -lc 'cd /opt/sites/<domein> && ./scripts/deploy.sh'

Voorbeeld:

sudo -u moofz bash -lc 'cd /opt/sites/demo.rmeijer.eu && ./scripts/deploy.sh'

De scripts build.sh en deploy.sh bevatten een root-guard. Als ze toch als root worden gestart, proberen ze zichzelf opnieuw uit te voeren als eigenaar van de site-directory. Dit voorkomt root-owned bestanden in public/.

Per-site beheer

Ga naar een site-directory:

cd /opt/sites/<domein>

Project controleren:

./scripts/check.sh

Alleen bouwen:

./scripts/build.sh

Bouwen en deployen naar de publieke webroot:

./scripts/deploy.sh

Rechten herstellen:

./scripts/fix-permissions.sh

Let op: fix-permissions.sh slaat .venv over. Dat is bewust, omdat Python virtualenv-bestanden uitvoerbare rechten nodig hebben.

CMS

Het CMS draait per site op een eigen poort.

Voorbeeld:

demo.rmeijer.eu -> 5059

De poort staat in:

/etc/rmstatic/<domein>.env

Controleer de poort:

grep RM_CMS_PORT /etc/rmstatic/<domein>.env

Voor Nginx Proxy Manager:

Publieke website:

<domein> / www.<domein> -> http://<server-ip>:80

CMS:

cms.<domein> -> http://<server-ip>:<CMS_PORT>

Advies: zet het CMS achter een NPM Access List, VPN of LAN-restrictie.

CMS admin-secties

Het CMS is opgesplitst in losse beheerpagina's.

Voorbeelden:

/admin
/admin/pages
/admin/posts
/admin/uploads
/admin/settings
/admin/system

Dashboard:

/admin

Geeft een overzicht van de belangrijkste onderdelen.

Pagina's:

/admin/pages

Beheer vaste pagina's zoals home, contact, over en diensten.

Publicaties/blog:

/admin/posts

Beheer artikelen, nieuwsberichten of externe publicaties.

Uploads:

/admin/uploads

Upload afbeeldingen en PDF-bestanden naar content/uploads. Na build/deploy worden deze publiek beschikbaar onder /uploads/.

Site-instellingen:

/admin/settings

Beheer basisvelden uit config/site.json, zoals sitenaam, site URL, omschrijving, contactmail, navigatie en thema-opties.

Systeembeheer:

/admin/system

Toont technische site-informatie en bevat de knop om de website opnieuw te genereren en publiceren.

Contentstructuur

content/
├── pages/
├── posts/
├── services/
├── events/
├── projects/
└── uploads/

Contenttypen:

Module Pad Type
Pagina's content/pages/*.md Markdown + frontmatter
Blog/publicaties content/posts/*.md Markdown + frontmatter
Diensten content/services/*.md Markdown + frontmatter
Agenda/evenementen content/events/*.json JSON
Projecten/portfolio content/projects/*.md Markdown + frontmatter
Uploads content/uploads/ Afbeeldingen/PDF

Uploads en media

Uploads worden opgeslagen in:

content/uploads/

Na build/deploy komen ze publiek beschikbaar in:

/uploads/

Voorbeeld in Markdown:

![Beschrijving](/uploads/foto.jpg)

Toegestane bestandstypen:

jpg
jpeg
png
gif
webp
pdf

De uploadmanager voorkomt simpele pad-traversal via bestandsnamen en gebruikt veilige bestandsnamen. Als een bestand al bestaat, krijgt de nieuwe upload automatisch een opvolgend nummer.

Configuratie

De belangrijkste configuratie staat in:

config/site.json

Belangrijke velden:

site_name
site_url
description
contact_email
deploy_dir
navigation
features
content_types
theme_options
contact_form

Modules kunnen aan of uit worden gezet via features en content_types.

Voorbeeld:

{
  "features": {
    "posts": true,
    "services": true,
    "events": true,
    "projects": true,
    "contact_form": true,
    "rss": true,
    "sitemap": true
  },
  "content_types": {
    "posts": {
      "enabled": true,
      "label": "Blog",
      "slug": "blog"
    },
    "events": {
      "enabled": true,
      "label": "Agenda",
      "slug": "agenda"
    },
    "projects": {
      "enabled": true,
      "label": "Projecten",
      "slug": "projecten"
    }
  }
}

Standaardmodules

Pagina's

Pad:

content/pages/*.md

Voorbeelden:

content/pages/home.md
content/pages/contact.md

Output:

/
/contact/

Blog/publicaties

Pad:

content/posts/*.md

Voorbeeld:

content/posts/eerste-publicatie.md

Output:

/blog/
/blog/eerste-publicatie/

Diensten

Pad:

content/services/*.md

Voorbeeld:

content/services/nederlandse-les.md

Output:

/diensten/
/diensten/nederlandse-les/

Agenda/evenementen

Pad:

content/events/*.json

Voorbeeld:

{
  "title": "Demo-evenement",
  "slug": "demo-evenement",
  "date": "2026-09-01",
  "dates": ["2026-09-01"],
  "summary": "Een voorbeeld van de agenda/evenementenmodule.",
  "description": "Dit is een demo-evenement om de agenda-module te testen.",
  "location": "Online",
  "published": true
}

Output:

/agenda/
/agenda/demo-evenement/

Projecten/portfolio

Pad:

content/projects/*.md

Voorbeeld:

---
title: Demo project
slug: demo-project
client: Voorbeeldklant
type: Website
year: 2026
status: Live
website_url: https://ontwikkel.rmeijer.eu
image: ""
summary: Een voorbeeldproject voor de portfolio/projectenmodule.
features:
  - Statische website
  - Eigen CMS
  - Contactformulier
  - Responsive ontwerp
tech:
  - Python
  - Flask
  - Static Site Generator
  - Nginx
published: true
featured: true
---

Dit is een voorbeeld van een projectdetailpagina.

Output:

/projecten/
/projecten/demo-project/

Templates:

theme/default/templates/projects.html
theme/default/templates/project.html

Demo-content

De basis-kit bevat demo-content voor alle standaardmodules:

content/pages/home.md
content/pages/contact.md
content/posts/eerste-publicatie.md
content/services/nederlandse-les.md
content/events/demo-evenement.json
content/projects/demo-project.md

Met demo-content kun je na installatie direct alle routes testen.

Smoke tests

Na aanmaken of deployen van een site:

curl -I -H 'Host: demo.rmeijer.eu' http://127.0.0.1/
curl -I -H 'Host: demo.rmeijer.eu' http://127.0.0.1/blog/
curl -I -H 'Host: demo.rmeijer.eu' http://127.0.0.1/diensten/
curl -I -H 'Host: demo.rmeijer.eu' http://127.0.0.1/agenda/
curl -I -H 'Host: demo.rmeijer.eu' http://127.0.0.1/projecten/
curl -I -H 'Host: demo.rmeijer.eu' http://127.0.0.1/contact/
curl -I -H 'Host: demo.rmeijer.eu' http://127.0.0.1/feed.xml
curl -I -H 'Host: demo.rmeijer.eu' http://127.0.0.1/sitemap.xml

Alle ingeschakelde modules moeten 200 OK geven.

CMS-backend testen:

sudo bash -c 'set -a; source /etc/rmstatic/demo.rmeijer.eu.env; curl -I -u "$RM_CMS_USER:$RM_CMS_PASSWORD" http://127.0.0.1:$RM_CMS_PORT/admin'

Installatie vanaf Git

Clone de repository:

cd /opt
git clone git@github.com:RMeijerEU/rm-static-cms-kit.git

Zet rechten:

sudo chown -R moofz:www-data /opt/rm-static-cms-kit
sudo chmod -R g+rwX /opt/rm-static-cms-kit

Installeer de scripts:

sudo cp /opt/rm-static-cms-kit/bin/rmstatic-* /usr/local/bin/
sudo chmod +x /usr/local/bin/rmstatic-*

Maak basispaden:

sudo mkdir -p /opt/sites /var/www /etc/rmstatic /opt/rmstatic-exports
sudo chown -R moofz:www-data /opt/sites /opt/rm-static-cms-kit

Maak een eerste site:

rmstatic-create-site voorbeeld.nl "Voorbeeldsite"

Installatie vanaf export

Pak het archief uit:

tar -xzf rm-static-cms-kit-v2.0.0-all-modules.tar.gz

Kopieer template en scripts:

sudo mkdir -p /opt/rm-static-cms-kit
sudo rsync -a opt/rm-static-cms-kit/ /opt/rm-static-cms-kit/

sudo cp opt/rm-static-cms-kit/bin/rmstatic-* /usr/local/bin/
sudo chmod +x /usr/local/bin/rmstatic-*

Maak basispaden:

sudo mkdir -p /opt/sites /var/www /etc/rmstatic /opt/rmstatic-exports
sudo chown -R moofz:www-data /opt/sites /opt/rm-static-cms-kit

Maak een eerste site:

rmstatic-create-site voorbeeld.nl "Voorbeeldsite"

Export

Maak een schone export van de kit:

cd /opt
tar -czf /opt/rmstatic-exports/rm-static-cms-kit-v2.0.0-all-modules-$(date +%Y%m%d-%H%M%S).tar.gz rm-static-cms-kit

Bestaande export:

/opt/rmstatic-exports/rm-static-cms-kit-v2.0.0-all-modules-20260624-102306.tar.gz

Checksum maken:

cd /opt/rmstatic-exports
sha256sum rm-static-cms-kit-v2.0.0-all-modules-*.tar.gz > SHA256SUMS

De export bevat:

  • template;
  • generator;
  • CMS;
  • thema;
  • scripts;
  • demo-content;
  • installatiebestanden.

De export bevat niet:

  • /opt/sites/*;
  • /etc/rmstatic/*.env;
  • klantdata buiten de kit;
  • bestaande productie-sites.

Git workflow

De centrale kit staat onder Git:

/opt/rm-static-cms-kit

Gebruik Git altijd als moofz, niet als root.

Status:

cd /opt/rm-static-cms-kit
sudo -u moofz git status

Commit:

sudo -u moofz git add .
sudo -u moofz git commit -m "Beschrijving"
sudo -u moofz git push

Tag maken:

sudo -u moofz git tag -a v2.0.0 -m "RM Static CMS Kit v2.0.0"
sudo -u moofz git push origin v2.0.0

Huidige basis:

main
v2.0.0

Backup van oude main:

github-main-before-v2-clean-kit

Rechten en eigenaarschap

Standaard eigenaarschap:

moofz:www-data

Rechten herstellen voor een site:

chown -R moofz:www-data /opt/sites/<domein>
chmod -R g+rwX /opt/sites/<domein>

Voor de centrale kit:

chown -R moofz:www-data /opt/rm-static-cms-kit
chmod -R g+rwX /opt/rm-static-cms-kit

Voorkom root-owned buildbestanden door build/deploy als moofz te draaien.

Herstelstrategie

Er zijn drie lagen:

  1. Proxmox-backup van de LXC.
  2. GitHub-repository en tags.
  3. Losse kit-export op Proxmox-storage.
  4. Per-site content in /opt/sites/<domein>.

Voor een volledige restore van een bestaande site is de Proxmox-backup het makkelijkst.

Voor een nieuwe installatie of migratie gebruik je de Git-repository of kit-export.

Veelvoorkomende problemen

fatal: detected dubious ownership

Oorzaak: Git wordt als root uitgevoerd in een repo die eigendom is van moofz.

Oplossing:

chown -R moofz:www-data /opt/rm-static-cms-kit
sudo -u moofz git config --global --add safe.directory /opt/rm-static-cms-kit

Daarna Git gebruiken als moofz:

sudo -u moofz git status

Module geeft 404

Controleer of de module aan staat:

python3 - <<'PY'
import json
from pathlib import Path
site = json.loads(Path("config/site.json").read_text())
print(site.get("features", {}))
print(site.get("content_types", {}))
PY

Controleer of er output is:

find public /var/www/<domein>/public -path "*/projecten*" -print

Deploy opnieuw:

sudo -u moofz bash -lc 'cd /opt/sites/<domein> && ./scripts/deploy.sh'

CMS werkt lokaal wel, maar niet via domein

Controleer:

  • NPM proxy host;
  • juiste CMS-poort;
  • Access List;
  • firewall;
  • systemd-service.

CMS-poort:

grep RM_CMS_PORT /etc/rmstatic/<domein>.env

Service:

systemctl status <service>.service

nginx -t geeft waarschuwingen over dubbele server names

Voorbeeld:

conflicting server name "hvvliet.nl" on 0.0.0.0:80, ignored

Dit betekent dat er dubbele Nginx server blocks bestaan voor hetzelfde domein. Het is een waarschuwing, maar moet later wel worden opgeschoond om verwarring te voorkomen.

Huidige praktijkcase

Geteste demo-site:

demo.rmeijer.eu

Werkende routes:

/
/blog/
/diensten/
/agenda/
/projecten/
/contact/
/feed.xml
/sitemap.xml

Alle routes geven 200 OK.

Eerste productiecase:

hvvliet.nl

Publiek:

hvvliet.nl
www.hvvliet.nl

CMS:

cms.hvvliet.nl

Versiegeschiedenis

v2.0.0

  • Schone nieuwe basis van de kit
  • Installer-wizard als standaard rmstatic-create-site
  • Modulekeuze per nieuwe site
  • Projecten/portfolio-module toegevoegd
  • Demo-content voor alle modules
  • Agenda-demo toegevoegd
  • RSS en sitemap getest
  • rmstatic-remove-site toegevoegd
  • Installer-scripts opgenomen in bin/
  • GitHub main opnieuw ingericht als schone basis
  • Oude main veiliggesteld als github-main-before-v2-clean-kit

v1.7.x en ouder

Oudere ontwikkelreeks met eerdere modules, beheerfuncties en experimenten.

v1.4.0

  • CMS-editor voor event flyers
  • Verbeterde event/flyer-rendering

v1.3.x

  • Publicatiesysteem verder uitgebreid
  • Publicatie-editor verbeterd
  • Delen-knoppen en metadata uitgebreid

v1.2.x

  • Basis site-instellingen via CMS
  • Automatische backup van config/site.json vóór opslaan

v1.1.x

  • Admin panel opgesplitst in losse secties
  • Dashboard, pagina's, publicaties, instellingen en systeembeheer

v0.9

  • README/documentatie opgeschoond

v0.8

  • Kit exporteerbaar gemaakt
  • Export bevat geen klantdata
  • Export bevat geen CMS-wachtwoorden

v0.7

  • Site-informatie tonen via beheertool

v0.6

  • Per-site credentials via /etc/rmstatic/<domein>.env
  • Geen hardcoded CMS-wachtwoorden meer

v0.5

  • Sitebeheer via commandline uitgebreid

v0.4

  • Site aanmaken via script
  • Automatische vrije CMS-poorten
  • Automatische systemd- en Nginx-configs

v0.3

  • CMS-beheer verbeterd
  • Concept/publiceren
  • Verwijderen via CMS

v0.2

  • Nieuwe pagina's en publicaties aanmaken via CMS

v0.1

  • Eerste werkende static-site-generator met CMS

About

A lightweight Python static-site generator with a Flask CMS, module-based site creation wizard, and static deployment workflow.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages