Compare commits

...
Author SHA1 Message Date
stefankoelle 9c736532e1 Fix timecapsule build path (../../ was wrong, should be ../) 2026-09-11 12:50:44 +02:00
stefankoelle 401868cde5 Fix stefankoelle build: temporarily hide .eleventyignore
build-pdf.sh now temporarily renames .eleventyignore during the
stefankoelle Eleventy build, restored via trap on EXIT. This allows
the standalone stefankoelle build to work while the main build
still ignores stefankoelle/ via .eleventyignore.
2026-09-11 12:47:03 +02:00
stefankoelle dbb791b1a6 Centralize robots.txt and sitemap
- One robots.txt at root referencing two sitemaps
- One central sitemap.xml with all pages (no per-site sitemaps)
- Deleted: 4 per-site sitemap templates, 4 per-site robots.txt files
- Simplified sitemap template (uses collections.all)
2026-09-11 12:36:18 +02:00
stefankoelle c77ad91e0e Consolidate all moonweb sites into single Eleventy config
- Single eleventy.config.js at root with computed pathPrefix per section
- Accent colors inlined in base.njk (no per-site theme.css needed)
- Hub pages output at root via explicit permalinks
- Per-section sitemaps with tag-based collections
- .eleventyignore excludes stefankoelle, timecapsule, markdown files
- Deleted: 5 per-site configs, merge script
- Simplified: package.json (single build/dev commands)
- Simplified: GitHub Actions workflow (no matrix, no merge job)
- Fixed: parent links for sub-site detail pages
2026-09-11 12:31:47 +02:00
stefankoelle 10b05736c8 Revert sitemap fix - page.url does not include pathPrefix 2026-09-11 12:11:47 +02:00
stefankoelle 84cd3f8562 Fix sitemap doubled pathPrefix and CSS paths for sub-sites
- sitemap.njk: remove site.pathPrefix (page.url already includes it in Eleventy v3)
- base.njk: prefix CSS paths with site.pathPrefix so sub-sites load their own theme
2026-09-11 12:10:18 +02:00
stefankoelle 9cda842250 Clean up build artifacts and update TODO.md
- Remove timecapsule/_site/ from tracking (was in .gitignore but tracked)
- Add .tmp/ to .gitignore
- Update TODO.md: Phase 5 now automated via GitHub Action
2026-09-11 11:31:49 +02:00
stefankoelle 37f7d8f1fb Add Cloudflare redirect rules script and GitHub Action
- scripts/cloudflare/setup-redirects.sh: CLI tool to manage redirects
- scripts/cloudflare/redirect-rules.json: rule definitions
- .github/workflows/cloudflare-redirects.yml: manual trigger workflow
- Supports: apply, status, dry-run, delete
- Requires secrets: CLOUDFLARE_API_TOKEN, CLOUDFLARE_ZONE_ID
2026-09-11 11:29:45 +02:00
stefankoelle c76de3ec36 Fix sitemaps: add pathPrefix for subdirectory sites
- Add site.pathPrefix to infra, smarthome, code, retro configs
- Update shared sitemap template to use pathPrefix
- Hub has no prefix, subdirectories get /infra/, /smarthome/, etc.
2026-09-11 11:22:44 +02:00
stefankoelle 7524210768 Fix timecapsule sitemap: remove doubled timecapsule path in URLs
site.domain was 'https://www.moonweb.org/timecapsule' but page.url
already includes '/timecapsule/', causing doubled paths like
'/timecapsule/timecapsule/index.html'.
2026-09-11 11:19:28 +02:00
stefankoelle 41a9236ca7 Fix robots.txt: list all sitemaps and use www.moonweb.org URLs
- Hub robots.txt now lists all 6 moonweb sitemaps
- infra/smarthome/code/retro robots.txt point to correct www.moonweb.org URLs
- Timecapsule robots.txt was already correct
2026-09-11 11:17:08 +02:00
stefankoelle 7c3e29d282 Add old www redirects and fix impressum hosting sections
- Create redirect HTML files for all old www.moonweb.org URLs
- Root shortcuts (/start.html, /index2.html, etc.) redirect to /timecapsule/
- Content subdirectories (/partners/, /projects/, etc.) redirect to /timecapsule/
- Use .htm extension to prevent Eleventy template processing
- Fix impressum: buildbroken + 28k8 are on Cloudflare Pages, not IONOS
2026-09-11 11:13:04 +02:00
stefankoelle 2b997c11d0 Add timecapsule build output to .gitignore 2026-09-11 10:53:54 +02:00
stefankoelle 466df3bd04 Consolidate all moonweb.org sites under www.moonweb.org
- Migrate timecapsule (old www.moonweb.org) into monorepo as Eleventy 2.x site
- Change all site URLs from subdomains to subdirectories under www.moonweb.org
- Replace Cloudflare Pages deployment with IONOS SFTP
- Update site-switcher, footer, and all internal links
- Add merge script for combining all sites into single deployment
- Update CI/CD workflow for IONOS SFTP deployment
- Update stefankoelle.de references to use new URLs
- Update AGENTS.md and README.md documentation
2026-09-11 10:53:22 +02:00
stefankoelle a97c3d3a89 fix impressum link 2026-09-11 00:11:47 +02:00
stefankoelle d213154b79 kids rfid player to media 2026-09-11 00:02:31 +02:00
stefankoelle 13fdffce07 link new dosmenu page 2026-09-10 23:57:01 +02:00
stefankoelle b1639bbece kids rfid player 2026-09-10 23:53:19 +02:00
186 changed files with 5186 additions and 603 deletions
+17
View File
@@ -0,0 +1,17 @@
stefankoelle/
timecapsule/
scripts/
.tmp/
# Root markdown files (not templates)
/*.md
# Hub static redirect files (handled by passthrough copy)
hub/hardware.html
hub/users.html
hub/index2.html
hub/index3.html
hub/new.html
hub/links.html
hub/infos.html
hub/start.html
+118
View File
@@ -0,0 +1,118 @@
name: Build & Deploy moonweb.org
on:
push:
branches: [main]
paths-ignore:
- 'stefankoelle/**'
- '.github/workflows/deploy-stefankoelle.yml'
pull_request:
branches: [main]
paths-ignore:
- 'stefankoelle/**'
- '.github/workflows/deploy-stefankoelle.yml'
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
env:
NODE_VERSION: "22"
jobs:
build:
name: Build moonweb sites
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- uses: actions/setup-node@v7
with:
node-version: ${{ env.NODE_VERSION }}
cache: "npm"
- name: Install dependencies
run: npm ci
- name: Build all moonweb sites
run: npm run build
- name: Build timecapsule
run: npm run build:timecapsule
- name: Validate build output
run: |
echo "Build output:"
find dist -maxdepth 2 -type f | sort | head -40
echo "..."
echo "Total files:"
find dist -type f | wc -l
# Verify key directories exist
for dir in "" infra smarthome code retro timecapsule; do
if [ -n "$dir" ] && [ ! -d "dist/$dir" ]; then
echo "Error: dist/$dir not found"
exit 1
fi
done
- name: Upload build artifact
uses: actions/upload-artifact@v7
with:
name: dist-moonweb
path: dist/
retention-days: 7
deploy:
name: Deploy to IONOS
needs: build
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
steps:
- name: Download build artifact
uses: actions/download-artifact@v8
with:
name: dist-moonweb
path: dist/
- name: Deploy via SFTP
uses: wlixcc/SFTP-Deploy-Action@v1.2.6
with:
local_path: './dist/*'
server: ${{ secrets.IONOS_SFTP_HOST }}
username: ${{ secrets.IONOS_SFTP_USER }}
password: ${{ secrets.IONOS_SFTP_PASSWORD }}
remote_path: '/websites/moonweb/'
port: 22
sftp_only: true
summary:
name: Deploy Summary
needs: deploy
runs-on: ubuntu-latest
if: always()
steps:
- name: Print deployment summary
run: |
echo "## Deployment Summary" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
if [ "${{ needs.deploy.result }}" = "success" ]; then
echo "moonweb.org deployed successfully!" >> $GITHUB_STEP_SUMMARY
else
echo "Deployment failed. Check the deploy jobs for details." >> $GITHUB_STEP_SUMMARY
fi
echo "" >> $GITHUB_STEP_SUMMARY
echo "### Sites" >> $GITHUB_STEP_SUMMARY
echo "| Site | URL |" >> $GITHUB_STEP_SUMMARY
echo "|------|-----|" >> $GITHUB_STEP_SUMMARY
echo "| hub | https://www.moonweb.org/ |" >> $GITHUB_STEP_SUMMARY
echo "| infra | https://www.moonweb.org/infra/ |" >> $GITHUB_STEP_SUMMARY
echo "| smarthome | https://www.moonweb.org/smarthome/ |" >> $GITHUB_STEP_SUMMARY
echo "| code | https://www.moonweb.org/code/ |" >> $GITHUB_STEP_SUMMARY
echo "| retro | https://www.moonweb.org/retro/ |" >> $GITHUB_STEP_SUMMARY
echo "| timecapsule | https://www.moonweb.org/timecapsule/ |" >> $GITHUB_STEP_SUMMARY
# Required repo secrets:
# IONOS_SFTP_HOST — IONOS SFTP server hostname
# IONOS_SFTP_USER — IONOS SFTP username
# IONOS_SFTP_PASSWORD — IONOS SFTP password
-139
View File
@@ -1,139 +0,0 @@
name: Build & Deploy moonweb-site
on:
push:
branches: [main]
paths-ignore:
- 'stefankoelle/**'
- '.github/workflows/deploy-stefankoelle.yml'
pull_request:
branches: [main]
paths-ignore:
- 'stefankoelle/**'
- '.github/workflows/deploy-stefankoelle.yml'
# Cancel in-progress runs for the same branch
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
env:
NODE_VERSION: "22"
jobs:
build:
name: Build ${{ matrix.site }}
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
site: [hub, infra, smarthome, code, retro]
steps:
- uses: actions/checkout@v7
- uses: actions/setup-node@v7
with:
node-version: ${{ env.NODE_VERSION }}
cache: "npm"
- name: Install dependencies
run: npm ci
- name: Build ${{ matrix.site }}
run: npm run build:${{ matrix.site }}
- name: Validate build output
run: |
if [ ! -d "dist/${{ matrix.site }}" ]; then
echo "Error: dist/${{ matrix.site }} not found"
exit 1
fi
if [ -z "$(ls -A dist/${{ matrix.site }} 2>/dev/null)" ]; then
echo "Error: dist/${{ matrix.site }} is empty"
exit 1
fi
echo "Build output:"
find dist/${{ matrix.site }} -type f | head -20
- name: Upload build artifact
uses: actions/upload-artifact@v7
with:
name: dist-${{ matrix.site }}
path: dist/${{ matrix.site }}
retention-days: 7
deploy:
name: Deploy ${{ matrix.site }}
needs: build
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
strategy:
fail-fast: false
matrix:
include:
- site: hub
cf_project: moonweb-hub
- site: infra
cf_project: moonweb-infra
- site: smarthome
cf_project: moonweb-smarthome
- site: code
cf_project: moonweb-code
- site: retro
cf_project: moonweb-retro
steps:
- name: Download build artifact
uses: actions/download-artifact@v8
with:
name: dist-${{ matrix.site }}
path: dist/${{ matrix.site }}
- name: Create wrangler.toml for Worker
run: |
echo 'name = "${{ matrix.cf_project }}"' > wrangler.toml
echo 'compatibility_date = "2024-01-01"' >> wrangler.toml
echo '' >> wrangler.toml
echo '[assets]' >> wrangler.toml
echo 'directory = "./dist/${{ matrix.site }}"' >> wrangler.toml
echo 'not_found_handling = "404-page"' >> wrangler.toml
- name: Deploy to Cloudflare Workers
uses: cloudflare/wrangler-action@v4
with:
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
command: deploy
summary:
name: Deploy Summary
needs: deploy
runs-on: ubuntu-latest
if: always()
steps:
- name: Print deployment summary
run: |
echo "## Deployment Summary" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
if [ "${{ needs.deploy.result }}" = "success" ]; then
echo "✅ All sites deployed successfully!" >> $GITHUB_STEP_SUMMARY
else
echo "❌ Deployment failed. Check the deploy jobs for details." >> $GITHUB_STEP_SUMMARY
fi
echo "" >> $GITHUB_STEP_SUMMARY
echo "### Sites" >> $GITHUB_STEP_SUMMARY
echo "| Site | URL |" >> $GITHUB_STEP_SUMMARY
echo "|------|-----|" >> $GITHUB_STEP_SUMMARY
echo "| hub | https://hub.moonweb.org |" >> $GITHUB_STEP_SUMMARY
echo "| infra | https://infra.moonweb.org |" >> $GITHUB_STEP_SUMMARY
echo "| smarthome | https://smarthome.moonweb.org |" >> $GITHUB_STEP_SUMMARY
echo "| code | https://code.moonweb.org |" >> $GITHUB_STEP_SUMMARY
echo "| retro | https://retro.moonweb.org |" >> $GITHUB_STEP_SUMMARY
# Required repo secrets:
# CLOUDFLARE_API_TOKEN — Cloudflare API token with Workers:Edit permission
# CLOUDFLARE_ACCOUNT_ID — Cloudflare account ID
#
# Required one-time setup (see PLAN.md Phase 2):
# Create Cloudflare Workers and bind custom domains.
# DNS is already on Cloudflare (SPEC.md §4.6).
#
@@ -0,0 +1,58 @@
name: Setup Cloudflare Redirects
on:
workflow_dispatch:
inputs:
action:
description: "Action to perform"
required: true
default: "apply"
type: choice
options:
- apply
- status
- dry-run
- delete
env:
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
CLOUDFLARE_ZONE_ID: ${{ secrets.CLOUDFLARE_ZONE_ID }}
jobs:
redirects:
name: Cloudflare Redirects
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- name: Validate secrets
run: |
if [ -z "$CLOUDFLARE_API_TOKEN" ]; then
echo "Error: CLOUDFLARE_API_TOKEN secret is not set"
exit 1
fi
if [ -z "$CLOUDFLARE_ZONE_ID" ]; then
echo "Error: CLOUDFLARE_ZONE_ID secret is not set"
exit 1
fi
- name: Run ${{ inputs.action }}
run: |
case "${{ inputs.action }}" in
status)
./scripts/cloudflare/setup-redirects.sh --status
;;
dry-run)
./scripts/cloudflare/setup-redirects.sh --dry-run
;;
delete)
./scripts/cloudflare/setup-redirects.sh --delete
;;
*)
./scripts/cloudflare/setup-redirects.sh
;;
esac
# Required repo secrets:
# CLOUDFLARE_API_TOKEN — API token with Dynamic Redirects Write permission
# CLOUDFLARE_ZONE_ID — Zone ID for moonweb.org (found in Cloudflare dashboard)
+3
View File
@@ -15,3 +15,6 @@ dist/
.DS_Store
*.swp
*.swo
timecapsule/_site/
timecapsule/node_modules/
.tmp/
+73 -41
View File
@@ -1,18 +1,19 @@
# AGENTS.md — moonweb-site
## Projektübersicht
## Projektuebersicht
Monorepo für 5 statische Websites unter moonweb.org + stefankoelle.de, basierend auf Eleventy (11ty).
Monorepo fuer 6 statische Websites unter www.moonweb.org + stefankoelle.de, basierend auf Eleventy (11ty).
### Websites (Cloudflare Pages)
### Websites (IONOS SFTP)
| Site | Domain | Zweck | Status |
|------|--------|-------|--------|
| hub | hub.moonweb.org | Zentrale Indexseite, verlinkt alles | Fertig |
| infra | infra.moonweb.org | Infrastruktur-Übersicht (Proxmox, Synology, Netzwerk) | Übersicht + 3 Detailseiten |
| smarthome | smarthome.moonweb.org | Smart Home Projekte und Dashboards | Übersicht + 6 Detailseiten |
| code | code.moonweb.org | GitHub-Projekte (aggregiert via .moonweb.yml) | Fertig (14 Repos) |
| retro | retro.moonweb.org | Physische Retro-Hardware | Nur Übersicht (WIP) |
| Site | URL | Zweck | Status |
|------|-----|-------|--------|
| hub | www.moonweb.org/ | Zentrale Indexseite, verlinkt alles | Fertig |
| infra | www.moonweb.org/infra/ | Infrastruktur-Uebersicht (Proxmox, Synology, Netzwerk) | Uebersicht + 8 Detailseiten |
| smarthome | www.moonweb.org/smarthome/ | Smart Home Projekte und Dashboards | Uebersicht + 9 Detailseiten |
| code | www.moonweb.org/code/ | GitHub-Projekte (aggregiert via .moonweb.yml) | Fertig |
| retro | www.moonweb.org/retro/ | Physische Retro-Hardware | Uebersicht + 13 Detailseiten |
| timecapsule | www.moonweb.org/timecapsule/ | 2000er Internet-Zeitkapsel (altes Design) | Fertig |
### Externe Sites (SFTP-Deployment)
@@ -22,19 +23,23 @@ Monorepo für 5 statische Websites unter moonweb.org + stefankoelle.de, basieren
### Andere Sites (nicht im Monorepo)
- www.moonweb.org — 2000er Internet-Zeitkapsel
- 28k8.moonweb.org — 90er BBS/Scene-Archiv
- buildbroken.moonweb.org — .NET Open Space Blog Archiv
## Dateistruktur
```
moonweb-site/
├── hub/ # Eleventy-Config + index.njk
├── infra/ # Eleventy-Config + index.njk + 3 Subseiten
├── smarthome/ # Eleventy-Config + index.njk + 6 Subseiten
├── infra/ # Eleventy-Config + index.njk + 8 Subseiten
├── smarthome/ # Eleventy-Config + index.njk + 9 Subseiten
├── code/ # Eleventy-Config + index.njk
│ └── _data/repos.json
├── retro/ # Eleventy-Config + index.njk
├── retro/ # Eleventy-Config + index.njk + 13 Subseiten
├── timecapsule/ # Eleventy 2.x (eigene Config)
│ ├── eleventy.config.js
│ ├── package.json
│ └── src/ # 2001er Retro-Content
├── stefankoelle/ # Eleventy-Config + Onepager
│ ├── eleventy.config.js
│ ├── _includes/
@@ -52,10 +57,11 @@ moonweb-site/
│ ├── base.css
│ └── theme-*.css
├── scripts/
── github-aggregator/
── github-aggregator/
│ └── merge-moonweb.sh # Merge-Skript fuer Deployment
├── .github/workflows/
│ ├── build-deploy.yml # CI/CD: Cloudflare Pages (5 Sites)
│ └── deploy-stefankoelle.yml # CI/CD: IONOS SFTP (stefankoelle.de)
│ ├── build-deploy-moonweb.yml # CI/CD: IONOS SFTP (www.moonweb.org)
│ └── deploy-stefankoelle.yml # CI/CD: IONOS SFTP (stefankoelle.de)
├── DESIGN.md
├── SPEC.md
├── PLAN.md
@@ -66,10 +72,11 @@ moonweb-site/
## Technischer Stack
- **SSG:** Eleventy (11ty) v3.1.6
- **SSG:** Eleventy (11ty) v3.1.6 (hub, infra, smarthome, code, retro)
- **SSG:** Eleventy v2.0.1 (timecapsule - retro 2001 Design)
- **Templates:** Nunjucks (.njk)
- **CSS:** Variables-basiert mit Accent-Farben pro Site
- **Deploy (moonweb):** Cloudflare Pages (5 separate Projects)
- **Deploy (moonweb):** IONOS SFTP
- **Deploy (stefankoelle):** IONOS SFTP
- **CI/CD:** GitHub Actions (2 Workflows)
@@ -77,32 +84,54 @@ moonweb-site/
```bash
npm install
npm run prebuild # Pre-Build Tasks (CV PDF generieren)
npm run dev:hub # localhost:8081
npm run dev:infra # localhost:8082
npm run dev:smarthome # localhost:8083
npm run dev:code # localhost:8084
npm run dev:retro # localhost:8085
npm run dev:stefankoelle # localhost:8086
npm run build # Alle Sites bauen
cd timecapsule && npm install # Timecapsule Dependencies
npm run prebuild # Pre-Build Tasks (CV PDF generieren)
npm run dev:hub # localhost:8081
npm run dev:infra # localhost:8082
npm run dev:smarthome # localhost:8083
npm run dev:code # localhost:8084
npm run dev:retro # localhost:8085
npm run dev:stefankoelle # localhost:8086
npm run dev:timecapsule # localhost:8087
npm run build # Alle Sites bauen
npm run build:moonweb # Nur moonweb Sites (ohne stefankoelle)
npm run merge:moonweb # Sites mergen fuer Deployment
```
## Design-Prinzipien
1. **Header konsistent** — Identischer Site-Switcher auf allen Home-Sites
2. **Content flexibel** — Detailseiten dürfen eigenes Layout haben
2. **Content flexibel** — Detailseiten duerfen eigenes Layout haben
3. **Accent-Farben:** hub=#3b6ea5, infra=#99333A, smarthome=#1f8a8a, code=#3E5098, retro=#8a6d3b
4. **Englisch** — Alle Sites komplett auf Englisch
5. **Keine Analytics** — Keine Tracking-Tools
6. **Sensible Daten** — Infra-Content wird manuell redigiert (keine IPs, Keys, Passwörter)
6. **Sensible Daten** — Infra-Content wird manuell redigiert (keine IPs, Keys, Passwoerter)
## URL-Struktur
Alle Sites sind unter `www.moonweb.org` als Subverzeichnisse erreichbar:
- `www.moonweb.org/` — Hub (Root)
- `www.moonweb.org/infra/` — Infra
- `www.moonweb.org/smarthome/` — Smarthome
- `www.moonweb.org/code/` — Code
- `www.moonweb.org/retro/` — Retro
- `www.moonweb.org/timecapsule/` — Timecapsule (2001 Design)
- `www.moonweb.org/impressum/` — Impressum
Cloudflare Redirects leiten alte Subdomains weiter:
- `hub.moonweb.org/*``www.moonweb.org/*`
- `infra.moonweb.org/*``www.moonweb.org/infra/*`
- `smarthome.moonweb.org/*``www.moonweb.org/smarthome/*`
- `code.moonweb.org/*``www.moonweb.org/code/*`
- `retro.moonweb.org/*``www.moonweb.org/retro/*`
## stefankoelle.de
- Eigene Eleventy-Config (nicht shared base.njk)
- Eigenes CSS-Design (nicht moonweb design system)
- Onepager mit Anchor-Links (bleibt so)
- Deploy via SFTP auf IONOS `/deploy/stefankoelle/`
- CV-Partial zentral pflegbar (einmal aendern Web + PDF aktualisieren)
- Deploy via SFTP auf IONOS `/websites/stefankoelle/`
- CV-Partial zentral pflegbar (einmal aendern -> Web + PDF aktualisieren)
- LED Matrix als eigene Seite unter stefankoelle.de/ledmatrix/
### CV PDF Generierung
@@ -116,7 +145,7 @@ npm run prebuild # Alle Pre-Build Tasks (inkl. CV PDF)
Dateien:
- `stefankoelle/pdf/cv-style.css` — WeasyPrint-Stylesheet (A4, Typografie)
- `stefankoelle/pdf/build-pdf.sh` — Shell-Skript für PDF-Generierung
- `stefankoelle/pdf/build-pdf.sh` — Shell-Skript fuer PDF-Generierung
- `stefankoelle/cv-print.njk` — Standalone HTML-Template (nur CV-Content)
- `stefankoelle/pdf/cv.pdf` — Generiertes PDF (Output)
@@ -124,21 +153,22 @@ Wichtig: Das PDF wird via Eleventy-Passthrough ins Build-Output kopiert (`dist/s
### CSS Cache-Busting
CSS-Dateien werden als Passthrough kopiert (kein Hash im Dateinamen). Bei CSS-Änderungen muss der Query-String in der `?v=N` Inkludierung erhöht werden:
CSS-Dateien werden als Passthrough kopiert (kein Hash im Dateinamen). Bei CSS-Aenderungen muss der Query-String in der `?v=N` Inkludierung erhoeht werden:
- `stefankoelle/index.njk`: `<link rel="stylesheet" href="/assets/style.css?v=N">`
- `stefankoelle/ledmatrix/index.njk`: `<link rel="stylesheet" href="assets/style.css?v=N">`
Bei jeder CSS-Anpassung `?v=N` um 1 erhöhen, sonst cached der Browser die alte Datei.
Bei jeder CSS-Anpassung `?v=N` um 1 erhoehen, sonst cached der Browser die alte Datei.
## CI/CD
### Cloudflare Pages (moonweb)
`.github/workflows/build-deploy.yml` baut hub, infra, smarthome, code, retro.
### IONOS SFTP (www.moonweb.org)
`.github/workflows/build-deploy-moonweb.yml` baut alle moonweb Sites (hub, infra, smarthome, code, retro, timecapsule) und deployed per SFTP.
Benötigte Secrets:
- `CLOUDFLARE_API_TOKEN`
- `CLOUDFLARE_ACCOUNT_ID`
- `IONOS_SFTP_HOST`
- `IONOS_SFTP_USER`
- `IONOS_SFTP_PASSWORD`
### IONOS SFTP (stefankoelle.de)
`.github/workflows/deploy-stefankoelle.yml` baut stefankoelle.de und deployed per SFTP.
@@ -150,9 +180,11 @@ Benötigte Secrets:
## Offene Punkte
- retro/ ist bewusst rudimentär gehalten
- retro/ ist bewusst rudimentaer gehalten
- Querverlinkungen stefankoelle.de <-> smarthome (zukuenftig)
- Ledmatrix ggf. nach smarthome verschieben (when ready)
- Cloudflare Redirects einrichten (nach Deploy)
- Google Search Console: Neue Property www.moonweb.org
## GitHub Aggregator
@@ -191,10 +223,10 @@ python3 -m venv .venv
## Text / Stil
- Keine Em-Dashes (—) verwenden, stattdessen umformulieren (Komma, Satzzeichen, neu formulieren)
- Keine Em-Dashes verwenden, stattdessen umformulieren (Komma, Satzzeichen, neu formulieren)
## Python / pip
- Niemals `pip install` direkt ausführen
- Niemals `pip install` direkt ausfuehren
- Immer ein virtuelles Umfeld (`.venv`) anlegen und darin arbeiten
- `python3 -m venv .venv` im Projektverzeichnis
+136 -121
View File
@@ -1,88 +1,67 @@
# 🌙 moonweb-site
# moonweb-site
Monorepo for the **moonweb.org** homelab — five static sites built with [Eleventy](https://www.11ty.dev/), deployed to [Cloudflare Pages](https://pages.cloudflare.com/).
Monorepo for the **moonweb.org** homelab — six static sites built with [Eleventy](https://www.11ty.dev/), deployed to [IONOS SFTP](https://www.ionos.de/).
```
hub.moonweb.org → 🏠 Central index & gateway
infra.moonweb.org → 🏗️ Infrastructure overview (Proxmox, Synology, Docker)
smarthome.moonweb.org → 🏡 Smart home projects & dashboards
code.moonweb.org → 💻 Curated GitHub project catalog
retro.moonweb.org → 🕹️ Physical retro hardware collection
stefankoelle.de → 👤 CV, career, personal site (LED Matrix docs)
www.moonweb.org/ -> Central index & gateway
www.moonweb.org/infra/ -> Infrastructure overview (Proxmox, Synology, Docker)
www.moonweb.org/smarthome/ -> Smart home projects & dashboards
www.moonweb.org/code/ -> Curated GitHub project catalog
www.moonweb.org/retro/ -> Physical retro hardware collection
www.moonweb.org/timecapsule/ -> 2000s internet time capsule (retro design)
stefankoelle.de -> CV, career, personal site (LED Matrix docs)
```
> **Other sites** (not in this monorepo): [www.moonweb.org](https://www.moonweb.org) (2000s time capsule), [28k8.moonweb.org](https://28k8.moonweb.org) (90s BBS archive).
> **Other sites** (not in this monorepo): [28k8.moonweb.org](https://28k8.moonweb.org) (90s BBS archive), [buildbroken.moonweb.org](https://buildbroken.moonweb.org) (.NET Open Space blog).
---
## 📐 Architecture
## Architecture
```
┌─────────────────────────────────────────────────────────────┐
GitHub Actions CI
│ ┌───────────┐ ┌────────────┐ ┌─────────────┐ ┌─────────┐ │
build:hub │ │build:infra │ │ build:smart │ │ build:… │ │
│ └─────┬─────┘ └─────┬──────┘ └──────┬──────┘ └────┬────┘ │
│ │ │ │ │ │
│ ▼ ▼ ▼ ▼ │
dist/hub/ dist/infra/ dist/smarthome/ dist//
└────────┬───────────────────────────────────────────┬────────┘
│ │
▼ ▼
┌──────────────────┐ ┌─────────────────┐
│ Cloudflare Pages │ IONOS SFTP
│ (5 sites) │ (stefankoelle) │
└────────┬─────────┘ └────────┬────────┘
▼ ▼
hub / infra / ... stefankoelle.de
+-------------------------------------------------------------------+
| GitHub Actions CI |
| +-----------+ +------------+ +-------------+ +---------+ +------+ |
| | build:hub | |build:infra | | build:smart | | build:... | | build:timecapsule | |
| +-----+-----+ +-----+------+ +------+------+ +----+----+ +----+----+ +--------+ |
| | | | | | | |
| v v v v v v |
| dist/hub/ dist/infra/ dist/smarthome/ dist/.../ dist/timecapsule/ |
+--------+---------------------------------------------------+--------------------+
| |
v v
+------------------+ +------------------+
| IONOS SFTP | | IONOS SFTP |
| (www.moonweb.org)| | (stefankoelle.de)|
+--------+---------+ +--------+---------+
v v
www.moonweb.org/* stefankoelle.de
```
---
## 🛠️ Tech Stack
## Tech Stack
| Layer | Technology | Why |
|-------|-----------|-----|
| **SSG** | [Eleventy 3.1.6](https://www.11ty.dev/) | Markdown/YAML-first, minimal JS, `_data` folders map directly to aggregator output, low maintenance for 5 sites |
| **SSG** | [Eleventy 3.1.6](https://www.11ty.dev/) | Markdown/YAML-first, minimal JS, `_data` folders map directly to aggregator output |
| **SSG (timecapsule)** | [Eleventy 2.0.1](https://www.11ty.dev/) | Legacy 2001 design, CommonJS config |
| **Templates** | [Nunjucks](https://mozilla.github.io/nunjucks/) | Shared `base.njk` layout with site-switcher header, `card-grid.njk` for index pages |
| **Styling** | Custom CSS (variables-based) | `base.css` for shared layout, `theme-*.css` per domain accent color, no build step needed |
| **Styling** | Custom CSS (variables-based) | `base.css` for shared layout, `theme-*.css` per domain accent color |
| **Fonts** | [Lobster](https://fonts.google.com/specimen/Lobster) (Google Fonts) | Distinctive heading font across all sites |
| **CI/CD** | [GitHub Actions](https://github.com/features/actions) | Matrix build for all 5 sites, artifact upload, parallel deploy |
| **Deploy** | [Cloudflare Workers](https://workers.cloudflare.com/) | Static asset hosting via `wrangler pages deploy`, one worker per site |
| **DNS** | Cloudflare | Already managing DNS — zero additional setup for Pages custom domains |
| **CI/CD** | [GitHub Actions](https://github.com/features/actions) | Matrix build for all sites, artifact upload, merge step, parallel deploy |
| **Deploy** | IONOS SFTP | Static hosting via SFTP upload |
| **DNS** | Cloudflare | DNS management + redirects from old subdomains |
| **GitHub Catalog** | Python aggregator | Reads `.moonweb.yml` from each repo, outputs `repos.json` |
| **Runtime** | Fully static | No server-side code, no containers, no database — pure HTML/CSS/JS |
| **Runtime** | Fully static | No server-side code, no containers, no database |
---
## 🎨 Design System
### Header-consistent, content-flexible
- **Header is identical** across all home-section sites: site-switcher (hub · infra · smarthome · code · retro · cv), domain accent color, Lobster title font.
- **Index pages** use a shared card-grid layout with grouped sections.
- **Detail pages** keep the same header but use a freer layout below it (e.g., pin tables, API docs, photos in free arrangement).
### Accent colors
| Domain | Color | Hex |
|--------|-------|-----|
| hub | Neutral blue | `#3b6ea5` |
| infra | Grey-blue | `#99333A` |
| smarthome | Teal | `#1f8a8a` |
| code | Violet | `#3E5098` |
| retro | Warm brown | `#8a6d3b` |
### Emojis
Each card on index pages has an emoji for visual navigation — consistent across hub, infra, smarthome, code, and retro.
---
## 🚀 Local Development
## Local Development
```bash
npm install # install Eleventy + deps
cd timecapsule && npm install # timecapsule has own deps (Eleventy 2.x)
npm run dev:hub # http://localhost:8081
npm run dev:infra # http://localhost:8082
@@ -90,8 +69,9 @@ npm run dev:smarthome # http://localhost:8083
npm run dev:code # http://localhost:8084
npm run dev:retro # http://localhost:8085
npm run dev:stefankoelle # http://localhost:8086
npm run dev:timecapsule # http://localhost:8087
npm run dev # all 6 in parallel
npm run dev # all 7 in parallel
```
Each site has its own minimal Eleventy config (`<site>/eleventy.config.js`). Live reload is built in.
@@ -100,83 +80,96 @@ Each site has its own minimal Eleventy config (`<site>/eleventy.config.js`). Liv
```bash
npm run prebuild # pre-build tasks (CV PDF)
npm run build # builds all 5 → dist/<site>/
npm run build # builds all sites
npm run build:moonweb # builds moonweb sites only (excludes stefankoelle)
npm run build:hub # build single site
npm run merge:moonweb # merge all moonweb sites into dist/ for deployment
```
---
## 📦 Project Structure
## Project Structure
```
moonweb-site/
├── hub/ # 🏠 Central index & gateway
├── infra/ # 🏗️ Infra overview + 3 detail pages
├── hub/ # Central index & gateway
├── infra/ # Infra overview + 8 detail pages
│ ├── backup-strategy/
│ ├── monitoring/
── dev-environment/
├── smarthome/ # 🏡 Smart home overview + 6 detail pages
── dev-environment/
│ └── ...
├── smarthome/ # Smart home overview + 9 detail pages
│ ├── homematic-mqtt/
│ ├── tasmota-energy/
── balkonpi/
│ ├── airplay-audio/
│ ├── octoprint/
│ └── tubearchivist/
├── code/ # 💻 GitHub catalog
── ...
├── code/ # GitHub catalog
│ └── _data/repos.json # populated by the aggregator
├── retro/ # 🕹️ Retro hardware (WIP)
├── stefankoelle/ # 👤 CV, career, personal site
├── retro/ # Retro hardware + 13 detail pages
├── timecapsule/ # 2000s retro design (Eleventy 2.x)
│ ├── eleventy.config.js
│ ├── package.json
│ └── src/
├── stefankoelle/ # CV, career, personal site
│ ├── eleventy.config.js
│ ├── index.njk # Onepager (CV, Projects, Languages)
│ ├── cv-print.njk # CV-only for PDF generation
│ ├── ledmatrix/ # LED Matrix WebServer documentation
│ └── assets/ # CSS, JS, images, favicons
├── shared/ # 🔧 Shared components
├── shared/ # Shared components
│ ├── _includes/
│ │ ├── base.njk # base layout (header, site-switcher, footer)
│ │ └── card-grid.njk # card-grid template with emoji support
│ ├── base.css # shared CSS (layout, cards, typography)
│ └── theme-*.css # accent colors per domain
├── scripts/
── github-aggregator/ # 🐍 Python: reads .moonweb.yml repos.json
├── aggregate.py
│ ├── example.moonweb.yml
│ └── README.md
── github-aggregator/ # Python: reads .moonweb.yml -> repos.json
└── merge-moonweb.sh # Merge script for deployment
├── .github/workflows/
── build-deploy.yml # ⚙️ CI/CD: build + deploy to Cloudflare
├── DESIGN.md # 📋 Initial concept (German)
├── SPEC.md # 📋 Full specification (English, 159 lines)
├── PLAN.md # 📋 Implementation plan
├── TODO.md # 📋 Open items & workflow
── build-deploy-moonweb.yml # CI/CD: build + deploy to IONOS SFTP
│ └── deploy-stefankoelle.yml # CI/CD: stefankoelle.de to IONOS SFTP
├── DESIGN.md # Initial concept
├── SPEC.md # Full specification
├── PLAN.md # Implementation plan
├── TODO.md # Open items & workflow
└── package.json # npm scripts for dev/build
```
---
## 🔄 CI/CD Pipeline
## CI/CD Pipeline
### Cloudflare Pages (moonweb sites)
### IONOS SFTP (www.moonweb.org)
Defined in `.github/workflows/build-deploy.yml`:
Defined in `.github/workflows/build-deploy-moonweb.yml`:
```
push to main
├── Build (matrix: hub, infra, smarthome, code, retro)
├── checkout setup-node (22) npm ci
├── npm run build:<site>
├── validate dist/<site>/ exists & non-empty
└── upload artifact (7-day retention)
└── Deploy (matrix: 5 Cloudflare Workers)
├── download artifact
├── generate wrangler.toml
└── wrangler pages deploy
|
+-- Build (matrix: hub, infra, smarthome, code, retro)
| +-- checkout -> setup-node (22) -> npm ci
| +-- npm run build:<site>
| +-- validate dist/<site>/ exists & non-empty
| +-- upload artifact (7-day retention)
|
+-- Build timecapsule
| +-- cd timecapsule && npm ci
| +-- npm run build:timecapsule
| +-- upload artifact
|
+-- Merge
| +-- download all artifacts
| +-- merge into dist/ (hub=root, others=subdirs)
| +-- upload merged artifact
|
+-- Deploy
+-- download merged artifact
+-- SFTP upload to IONOS /websites/moonweb/
```
**Required secrets:**
- `CLOUDFLARE_API_TOKEN` — Workers:Edit permission
- `CLOUDFLARE_ACCOUNT_ID` — Cloudflare account ID
- `IONOS_SFTP_HOST`
- `IONOS_SFTP_USER`
- `IONOS_SFTP_PASSWORD`
### IONOS SFTP (stefankoelle.de)
@@ -184,12 +177,12 @@ Defined in `.github/workflows/deploy-stefankoelle.yml`:
```
push to main (paths: stefankoelle/**)
├── Build stefankoelle
└── npm run build:stefankoelle
└── Deploy via SFTP
└── lftp mirror → IONOS /deploy/stefankoelle/
|
+-- Build stefankoelle
| +-- npm run build:stefankoelle
|
+-- Deploy via SFTP
+-- SFTP upload to IONOS /websites/stefankoelle/
```
**Required secrets:**
@@ -199,7 +192,30 @@ push to main (paths: stefankoelle/**)
---
## 🐍 GitHub Aggregator (code.moonweb.org)
## URL Structure
All moonweb.org sites are accessible under `www.moonweb.org` as subdirectories:
| URL | Content |
|-----|---------|
| `www.moonweb.org/` | Hub (root) |
| `www.moonweb.org/infra/` | Infrastructure |
| `www.moonweb.org/smarthome/` | Smart Home |
| `www.moonweb.org/code/` | Code catalog |
| `www.moonweb.org/retro/` | Retro hardware |
| `www.moonweb.org/timecapsule/` | 2000s time capsule |
| `www.moonweb.org/impressum/` | Legal notice |
Cloudflare redirects forward old subdomains:
- `hub.moonweb.org/*` -> `www.moonweb.org/*`
- `infra.moonweb.org/*` -> `www.moonweb.org/infra/*`
- `smarthome.moonweb.org/*` -> `www.moonweb.org/smarthome/*`
- `code.moonweb.org/*` -> `www.moonweb.org/code/*`
- `retro.moonweb.org/*` -> `www.moonweb.org/retro/*`
---
## GitHub Aggregator (www.moonweb.org/code/)
`scripts/github-aggregator/aggregate.py` automatically builds the project catalog:
@@ -214,7 +230,7 @@ push to main (paths: stefankoelle/**)
```yaml
title: "MVG Departures"
category: code # code | smarthome | infra
subcategory: "Web Apps" # drives grouping on code.moonweb.org
subcategory: "Web Apps" # drives grouping on www.moonweb.org/code/
status: active
stack: [Python, FastAPI]
hosted_on: "Docker Host Debian (PVE)"
@@ -229,42 +245,41 @@ export GITHUB_TOKEN=ghp_xxx
python scripts/github-aggregator/aggregate.py
```
> **Deliberately manual** — no scheduled CI job. The catalog is refreshed on demand, not on every push.
---
## 📏 Content Rules
## Content Rules
| Site | Detail pages? | Rule |
|------|--------------|------|
| smarthome | Yes | When enough content exists no placeholder cards |
| infra | ⚠️ Rarely | Deliberately shallow sensitive data (IPs, keys, passwords) stripped |
| code | Never | Overview cards + GitHub links only no README duplication |
| retro | 🔨 Minimal | Still WIP — honest minimal overview, no over-investment |
| smarthome | Yes | When enough content exists - no placeholder cards |
| infra | Rarely | Deliberately shallow - sensitive data (IPs, keys, passwords) stripped |
| code | Never | Overview cards + GitHub links only - no README duplication |
| retro | Minimal | Honest minimal overview, no over-investment |
| timecapsule | Static | 1:1 migration of original 2001 design, no changes |
**Infra redaction rule:** Architecture-level only (Proxmox, Synology, Docker, VLAN concept). No concrete IPs, WireGuard keys, passwords, internal hostnames.
---
## 🌍 Language
## Language
All five sites are written **entirely in English**. German source documents are translated once during migration (AI-assisted). New content is authored in English from the start.
All moonweb sites are written **entirely in English**. The timecapsule uses the original 2001 English content.
---
## 📚 Documentation
## Documentation
| File | Purpose |
|------|---------|
| `DESIGN.md` | Initial concept and design decisions (German) |
| `SPEC.md` | Complete specification what gets built (English) |
| `DESIGN.md` | Initial concept and design decisions |
| `SPEC.md` | Complete specification - what gets built |
| `PLAN.md` | Phased implementation plan |
| `TODO.md` | Open items, workflow, and current status |
| `README.md` | This file project overview for GitHub |
| `README.md` | This file - project overview for GitHub |
---
## 📄 License
## License
[![CC BY-NC-SA 4.0](https://img.shields.io/badge/License-CC%20BY--NC--SA%204.0-lightgrey.svg)](https://creativecommons.org/licenses/by-nc-sa/4.0/)
+71 -60
View File
@@ -1,76 +1,87 @@
# TODO — moonweb-site Offene Items
# TODO: Consolidation zu www.moonweb.org
Stand: 2026-08-14
## Uebersicht
## Aktueller Status
Alle moonweb.org Sites (hub, infra, smarthome, code, retro, timecapsule) unter `www.moonweb.org` als Subverzeichnisse vereinen. Deployment von Cloudflare Pages zu IONOS SFTP migrieren.
| Phase | Status | Bemerkung |
|-------|--------|-----------|
| Phase 0 — Repo & Tooling | ✅ fertig | Eleventy 3.1.6, CI/CD, .gitignore erweitert |
| Phase 1 — Content | ✅ fertig | hub, code, infra, smarthome komplett, retro bewusst rudimentär |
| Phase 2 — CI/CD | ✅ fertig | build-deploy.yml mit Caching + Validation + Summary |
| Phase 3 — Launch | ✅ fertig | Cloudflare Pages live, Smoke-Test bestanden |
| Phase 4 — Deferred | ⏸️ zurückgestellt | 28k8, Analytics |
| Phase 5 — stefankoelle.de | ✅ fertig | Ins Monorepo migriert, Eleventy-Build, SFTP-Deployment |
**URL-Struktur nach Migration:**
| URL | Inhalt |
|-----|--------|
| `www.moonweb.org/` | Hub (neue Root) |
| `www.moonweb.org/infra/` | Infra |
| `www.moonweb.org/smarthome/` | Smarthome |
| `www.moonweb.org/code/` | Code |
| `www.moonweb.org/retro/` | Retro |
| `www.moonweb.org/timecapsule/` | Altes www (2001-Retro) |
| `stefankoelle.de/` | CV (bleibt separat) |
## Offene Items
---
### P4 — Deferred (nicht dringend)
## Phase 1: Timecapsule in Monorepo integrieren
- [ ] 28k8.moonweb.org Zukunft klären
- [ ] Perplexity-Rückkanal
- [ ] Apex-Domain Redirect (moonweb.org → hub.moonweb.org)
- [ ] PDF-Build fuer CV (WeasyPrint, lokal testen)
- [ ] Querverlinkungen stefankoelle.de <-> smarthome
- [ ] Ledmatrix ggf. nach smarthome verschieben
- [ ] **Dotnet Home-Automation-Projekt beschreiben** — Interne .NET Core Software für Kommunikation auf buttons.html und InfluxDB. Code muss erst aufgeraeumt und nach GitHub publiziert werden, dann Beschreibung auf stefankoelle.de und ggf. smarthome ergänzen.
- [x] 1.1 Dateien aus ../moonweb-www/src/ nach timecapsule/src/ kopieren
- [x] 1.2 timecapsule/eleventy.config.js erstellen (Eleventy 2.x)
- [x] 1.3 timecapsule/package.json erstellen (eigene Dependencies)
- [x] 1.4 timecapsule/src/_data/site.json anpassen (Domain mit /timecapsule/)
- [x] 1.5 Alle internen Pfade mit /timecapsule/ prefixieren
- [x] 1.6 Passthrough Copy in eleventy.config.js anpassen
- [x] 1.7 Build-Output pruefen (dist/timecapsule/)
### Offene Entscheidungen
## Phase 2: Eleventy-Configs aller Sites anpassen
- [ ] **stefankoelle.de SFTP: `delete_remote_files`?** — Derzeit werden beim Deploy nur Dateien hochgeloaden, alte bleiben liegen. Mit `delete_remote_files: true` waere jeder Deploy ein sauberes Image (alles weg + neu). Kurzer Ausfall moeglich. Entscheidung offen.
- [x] 2.1 hub/eleventy.config.js: site.url auf www.moonweb.org
- [x] 2.2 infra/eleventy.config.js: site.url auf www.moonweb.org
- [x] 2.3 smarthome/eleventy.config.js: site.url auf www.moonweb.org
- [x] 2.4 code/eleventy.config.js: site.url auf www.moonweb.org
- [x] 2.5 retro/eleventy.config.js: site.url auf www.moonweb.org
- [x] 2.6 shared/_includes/base.njk anpassen
- [x] 2.7 hub/index.njk: Card-Hrefs relativieren
- [x] 2.8 hub/impressum.njk: Domain-Liste aktualisieren
## Schon erledigt (Stand 2026-08-14)
## Phase 3: Build-System anpassen
### stefankoelle.de Migration (2026-08-14)
- [x] Eleventy-Config + package.json erweitert
- [x] Onepager-Layout mit Anchor-Links beibehalten
- [x] CV-Partial (zentral fuer Web + PDF)
- [x] CSS ausgelagert + modernisiert (Custom Properties, box-sizing)
- [x] Assets in assets/ verschoben
- [x] Ledmatrix als eigene Seite integriert
- [x] Alte Dateien geloescht (index.html, browserconfig.xml, sitemap.txt)
- [x] Deployment-Workflow fuer IONOS SFTP
- [x] Cloudflare-Workflow um stefankoelle erweitert (paths-ignore)
- [x] 3.1 package.json: Neue Scripts fuer timecapsule + merge
- [x] 3.2 scripts/merge-moonweb.sh erstellen
- [x] 3.3 Lokal Build testen (alle Sites)
### Vorherige Erledigungen (Stand 2026-08-13)
- [x] Impressum angelegt + Footer-Link auf allen Sites
- [x] Favicons (SVG mit Emojis) pro Subdomain — `shared/favicon/*.svg`, per Passthrough Copy
- [x] Meta descriptions auf allen 33 Seiten
- [x] CSS @import-Kette bereinigt (kein Render-Blocking mehr)
- [x] .gitignore erweitert (.env*, Zertifikate, Swap-Files)
- [x] Placeholder-Cards `"#"` bereinigt — keine mehr vorhanden
- [x] README.md überarbeitet (Emojis, Architektur, Tech-Stack, CI/CD)
- [x] Lizenz hinzugefügt (CC BY-NC-SA 4.0)
- [x] Content-Review — alle Seiten geprüft, alles passt
- [x] Cloudflare Pages Projects erstellt (moonweb-hub, -infra, -smarthome, -code, -retro)
- [x] DNS & Custom Domains gebunden
- [x] Secrets konfiguriert (CLOUDFLARE_API_TOKEN, CLOUDFLARE_ACCOUNT_ID)
- [x] Smoke-Test Cross-Linking — alles funktioniert auf live
## Phase 4: Deployment umstellen
## Workflow für neue Contexts
- [x] 4.1 Neuen Workflow .github/workflows/build-deploy-moonweb.yml erstellen
- [x] 4.2 Alten Workflow .github/workflows/build-deploy.yml entfernen
- [ ] 4.3 Alten deploy-moonweb.yml in moonweb-www deaktivieren
Wenn du einen neuen Context öffnest, lies diese Datei und prüfe:
1. Welche P4-Items sind relevant? → Nur wenn angefordert
## Phase 5: Cloudflare Redirects
## Definition of done
- [x] 5.1 Redirect-Regeln einrichten (via GitHub Action)
Alle 5 Sites live auf Cloudflare Pages:
- ✅ hub.moonweb.org — verlinkt alles (+ stefankoelle.de extern)
- ✅ code.moonweb.org — GitHub-Katalog via Aggregator
- ✅ smarthome.moonweb.org — Smart Home Übersicht + Detailseiten
- ✅ infra.moonweb.org — Infra-Übersicht (redigiert)
- ✅ retro.moonweb.org — Ehrliche minimale Übersicht (WIP)
| Quell-Domain | Ziel-URL | Type |
|-------------|----------|------|
| `hub.moonweb.org/*` | `https://www.moonweb.org/$1` | 301 |
| `infra.moonweb.org/*` | `https://www.moonweb.org/infra/$1` | 301 |
| `smarthome.moonweb.org/*` | `https://www.moonweb.org/smarthome/$1` | 301 |
| `code.moonweb.org/*` | `https://www.moonweb.org/code/$1` | 301 |
| `retro.moonweb.org/*` | `https://www.moonweb.org/retro/$1` | 301 |
stefankoelle.de live auf IONOS (SFTP-Deployment):
- ✅ stefankoelle.de — Onepager mit CV, Projects, Languages, Impressum
- ✅ stefankoelle.de/ledmatrix/ — LED Matrix WebServer Dokumentation
**Umsetzung:** `scripts/cloudflare/setup-redirects.sh` + GitHub Action `cloudflare-redirects.yml`
**Benötigte Secrets:** `CLOUDFLARE_API_TOKEN`, `CLOUDFLARE_ZONE_ID`
## Phase 6: SEO
- [ ] 6.1 Google Search Console: Neue Property www.moonweb.org
- [ ] 6.2 Sitemap submiten
## Phase 7: Cleanup
- [ ] 7.1 moonweb-www Repository archivieren
- [ ] 7.2 Cloudflare Pages Projects loeschen (nach Redirect-Test)
---
## Abgeschlossen
Alle Code-Aenderungen sind fertig. Nächste Schritte:
1. Commit auf feature/consolidate-www Branch
2. Push und PR erstellen
3. Deployen
4. Cloudflare Redirects einrichten
5. Google Search Console aktualisieren
+2 -1
View File
@@ -1,9 +1,10 @@
---
title: "Page Not Found"
section: "code"
tags: "code"
description: "The page you are looking for does not exist."
layout: base.njk
permalink: /404.html
permalink: /code/404.html
---
<div class="error-page">
<h1>404</h1>
-20
View File
@@ -1,20 +0,0 @@
module.exports = function (eleventyConfig) {
eleventyConfig.addGlobalData("site", { url: "https://code.moonweb.org" });
eleventyConfig.addFilter("date", (d) => d.toISOString());
eleventyConfig.addPassthroughCopy({ "shared/theme-code.css": "theme.css" });
eleventyConfig.addPassthroughCopy({ "shared/base.css": "shared-base.css" });
eleventyConfig.addPassthroughCopy({ "shared/favicon/code.svg": "favicon.svg" });
eleventyConfig.addPassthroughCopy({ "code/robots.txt": "robots.txt" });
return {
dir: {
input: "code",
includes: "../shared/_includes",
output: "dist/code"
},
serverOptions: {
host: "0.0.0.0",
port: 8084
}
};
};
+1
View File
@@ -1,6 +1,7 @@
---
title: "Open Source Projects — GitHub Repos & Code | moonweb"
section: "code"
tags: "code"
description: "Curated catalog of my public GitHub projects — automation, dev tools, web apps, and firmware."
layout: base.njk
subcategories:
-4
View File
@@ -1,4 +0,0 @@
User-agent: *
Allow: /
Sitemap: https://code.moonweb.org/sitemap.xml
-5
View File
@@ -1,5 +0,0 @@
---
permalink: /sitemap.xml
eleventyExcludeFromCollections: true
---
{% include "sitemap.njk" %}
+91
View File
@@ -0,0 +1,91 @@
const SITES = {
hub: { pathPrefix: "" },
infra: { pathPrefix: "/infra" },
smarthome: { pathPrefix: "/smarthome" },
code: { pathPrefix: "/code" },
retro: { pathPrefix: "/retro" },
};
module.exports = function (eleventyConfig) {
eleventyConfig.addGlobalData("site", { url: "https://www.moonweb.org" });
eleventyConfig.addFilter("date", (d) => d.toISOString());
// Computed pathPrefix based on page's section front matter
eleventyConfig.addGlobalData("eleventyComputed", {
pathPrefix: (data) => SITES[data.section]?.pathPrefix ?? "",
});
// Per-section collections (for sitemaps)
for (const section of Object.keys(SITES)) {
eleventyConfig.addCollection(section, (collectionsApi) =>
collectionsApi.getFilteredByTag(section)
);
}
// --- Passthrough copies ---
// Root-level shared assets
eleventyConfig.addPassthroughCopy({ "shared/base.css": "shared-base.css" });
eleventyConfig.addPassthroughCopy({ "shared/favicon/hub.svg": "favicon.svg" });
eleventyConfig.addPassthroughCopy({ "hub/robots.txt": "robots.txt" });
// Per-section assets: favicon
for (const [section, cfg] of Object.entries(SITES)) {
const prefix = cfg.pathPrefix || ".";
eleventyConfig.addPassthroughCopy({
[`shared/favicon/${section}.svg`]: `${prefix}/favicon.svg`,
});
}
// Hub: profile photo
eleventyConfig.addPassthroughCopy({
"hub/stefan-koelle-foto.jpg": "stefan-koelle-foto.jpg",
});
// Hub: old www.moonweb.org redirects (.htm → .html)
const htmRedirects = [
"start",
"index2",
"index3",
"new",
"links",
"infos",
"hardware",
"users",
];
for (const name of htmRedirects) {
eleventyConfig.addPassthroughCopy({
[`hub/${name}.htm`]: `${name}.html`,
});
}
const htmSubdirs = [
"beginning",
"projects",
"products",
"acoustics",
"location",
"background",
"partners",
"contact",
"network",
"stefan",
"legal-notice",
];
for (const dir of htmSubdirs) {
eleventyConfig.addPassthroughCopy({
[`hub/${dir}/index.htm`]: `${dir}/index.html`,
});
}
return {
dir: {
input: ".",
includes: "shared/_includes",
output: "dist",
},
serverOptions: {
host: "0.0.0.0",
port: 8081,
},
};
};
+1
View File
@@ -1,6 +1,7 @@
---
title: "Page Not Found"
section: "hub"
tags: "hub"
description: "The page you are looking for does not exist."
layout: base.njk
permalink: /404.html
+11
View File
@@ -0,0 +1,11 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="refresh" content="0;url=/timecapsule/acoustics/">
<title>Redirecting...</title>
</head>
<body>
<p>Redirecting to <a href="/timecapsule/acoustics/">/timecapsule/acoustics/</a>...</p>
</body>
</html>
+11
View File
@@ -0,0 +1,11 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="refresh" content="0;url=/timecapsule/background/">
<title>Redirecting...</title>
</head>
<body>
<p>Redirecting to <a href="/timecapsule/background/">/timecapsule/background/</a>...</p>
</body>
</html>
+11
View File
@@ -0,0 +1,11 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="refresh" content="0;url=/timecapsule/beginning/">
<title>Redirecting...</title>
</head>
<body>
<p>Redirecting to <a href="/timecapsule/beginning/">/timecapsule/beginning/</a>...</p>
</body>
</html>
+11
View File
@@ -0,0 +1,11 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="refresh" content="0;url=/timecapsule/contact/">
<title>Redirecting...</title>
</head>
<body>
<p>Redirecting to <a href="/timecapsule/contact/">/timecapsule/contact/</a>...</p>
</body>
</html>
-21
View File
@@ -1,21 +0,0 @@
module.exports = function (eleventyConfig) {
eleventyConfig.addGlobalData("site", { url: "https://hub.moonweb.org" });
eleventyConfig.addFilter("date", (d) => d.toISOString());
eleventyConfig.addPassthroughCopy({ "shared/theme-hub.css": "theme.css" });
eleventyConfig.addPassthroughCopy({ "shared/base.css": "shared-base.css" });
eleventyConfig.addPassthroughCopy({ "shared/favicon/hub.svg": "favicon.svg" });
eleventyConfig.addPassthroughCopy({ "hub/robots.txt": "robots.txt" });
eleventyConfig.addPassthroughCopy({ "hub/stefan-koelle-foto.jpg": "stefan-koelle-foto.jpg" });
return {
dir: {
input: "hub",
includes: "../shared/_includes",
output: "dist/hub"
},
serverOptions: {
host: "0.0.0.0",
port: 8081
}
};
};
+11
View File
@@ -0,0 +1,11 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="refresh" content="0;url=/timecapsule/">
<title>Redirecting...</title>
</head>
<body>
<p>Redirecting to <a href="/timecapsule/">/timecapsule/</a>...</p>
</body>
</html>
+16 -9
View File
@@ -1,6 +1,8 @@
---
title: "Legal Notice & Privacy Policy"
section: "hub"
tags: "hub"
permalink: /impressum/index.html
description: "Legal notice (Impressum) and privacy policy for moonweb.org."
layout: base.njk
---
@@ -19,7 +21,7 @@ layout: base.njk
</p>
<p>
Scope of this legal notice: This legal notice and the privacy policy below apply to the following websites, all operated by Stefan Kölle: hub.moonweb.org, smarthome.moonweb.org, infra.moonweb.org, code.moonweb.org, retro.moonweb.org, buildbroken.moonweb.org, 28k8.moonweb.org, www.moonweb.org, and stefankoelle.de. All of these sites are part of the same personal project network and are covered jointly by this single document.
Scope of this legal notice: This legal notice and the privacy policy below apply to the following websites, all operated by Stefan Kölle: www.moonweb.org (including /infra/, /smarthome/, /code/, /retro/, /timecapsule/), buildbroken.moonweb.org, 28k8.moonweb.org, and stefankoelle.de. All of these sites are part of the same personal project network and are covered jointly by this single document.
</p>
<p>
@@ -41,7 +43,7 @@ layout: base.njk
<h3>2. Purpose of these Websites</h3>
<p>
The moonweb.org subdomains (hub, smarthome, infra, code, retro) document my private home-lab, home-automation and retro-computing hobby projects. buildbroken.moonweb.org is an archive of a blog I created with friends in our spare time. 28k8.moonweb.org is a homage to the 90s era. www.moonweb.org is an archived version of my original website from 2001. stefankoelle.de presents my professional CV and background as the same person. All are part of one personal project network and share this legal notice and privacy policy.
The moonweb.org websites (hub, smarthome, infra, code, retro, timecapsule) document my private home-lab, home-automation and retro-computing hobby projects. buildbroken.moonweb.org is an archive of a blog I created with friends in our spare time. 28k8.moonweb.org is a homage to the 90s era. stefankoelle.de presents my professional CV and background as the same person. All are part of one personal project network and share this legal notice and privacy policy.
</p>
<h3>3. Hosting</h3>
@@ -49,18 +51,23 @@ layout: base.njk
Two different hosting providers are used within this network:
</p>
<h4>a) moonweb.org subdomains (hub, smarthome, infra, code, retro, buildbroken) — Cloudflare Pages</h4>
<p>
Hosted via Cloudflare Pages, operated by Cloudflare, Inc., 101 Townsend St, San Francisco, CA 94107, USA. Cloudflare processes connection-level data (e.g. your IP address, request headers) as part of its content delivery and DDoS/security systems. This is governed by Cloudflare's Data Processing Addendum, which incorporates the EU Standard Contractual Clauses (2021) as a safeguard for the transfer of data to the USA. See <a href="https://www.cloudflare.com/privacypolicy/">cloudflare.com/privacypolicy</a>.
</p>
<h4>b) www.moonweb.org and stefankoelle.de — IONOS SE</h4>
<h4>a) www.moonweb.org (hub, infra, smarthome, code, retro, timecapsule) &mdash; IONOS SE</h4>
<p>
Hosted on web space provided by IONOS SE, Elgendorfer Str. 57, 56410 Montabaur, Germany, acting as a processor under Art. 28 GDPR (Data Processing Agreement in place).
</p>
<h4>b) stefankoelle.de &mdash; IONOS SE</h4>
<p>
Legal basis for both: Art. 6(1)(f) GDPR (legitimate interest in reliable, secure delivery of the websites).
Hosted on web space provided by IONOS SE, Elgendorfer Str. 57, 56410 Montabaur, Germany, acting as a processor under Art. 28 GDPR (Data Processing Agreement in place).
</p>
<h4>c) buildbroken.moonweb.org, 28k8.moonweb.org &mdash; Cloudflare Pages</h4>
<p>
Hosted via Cloudflare Pages, operated by Cloudflare, Inc., 101 Townsend St, San Francisco, CA 94107, USA. Cloudflare processes connection-level data (e.g. your IP address, request headers) as part of its content delivery and DDoS/security systems. This is governed by Cloudflare&rsquo;s Data Processing Addendum, which incorporates the EU Standard Contractual Clauses (2021) as a safeguard for the transfer of data to the USA. See <a href="https://www.cloudflare.com/privacypolicy/">cloudflare.com/privacypolicy</a>.
</p>
<p>
Legal basis: Art. 6(1)(f) GDPR (legitimate interest in reliable, secure delivery of the websites).
</p>
<h3>4. No Tracking, No Cookies, No Analytics (current state)</h3>
+8 -6
View File
@@ -1,6 +1,8 @@
---
title: "Stefan Koelle — Homelab, Smart Home, Code & Retro Hardware | moonweb"
section: "hub"
tags: "hub"
permalink: /index.html
description: "Stefan Koelle — Senior Software Developer & Architect based in Munich. Homelab projects, smart home automation, open-source code, and retro hardware."
layout: base.njk
ogImage: "/stefan-koelle-foto.jpg"
@@ -9,19 +11,19 @@ sections:
cards:
- title: "infra"
summary: "Stack overview: Proxmox, Synology, network, backups, and how I work."
href: "https://infra.moonweb.org"
href: "/infra/"
emoji: "🏗️"
- title: "smarthome"
summary: "What's actually running on the homelab, and why."
href: "https://smarthome.moonweb.org"
href: "/smarthome/"
emoji: "🏠"
- title: "code"
summary: "Curated catalog of my GitHub projects."
href: "https://code.moonweb.org"
href: "/code/"
emoji: "💻"
- title: "retro"
summary: "Physical retro hardware collection."
href: "https://retro.moonweb.org"
href: "/retro/"
emoji: "🕹️"
- heading: "About me"
cards:
@@ -37,7 +39,7 @@ sections:
emoji: "💾"
- title: "www.moonweb.org"
summary: "The 2000s internet projects, networking and engineering."
href: "https://www.moonweb.org"
href: "/timecapsule/"
emoji: "🌐"
- title: "inseco.de"
summary: "Internet services & consulting for gastronomy and clubs, Peak 20032008. Custom PHP/CMS."
@@ -63,7 +65,7 @@ sections:
<section class="about-expanded">
<h2>What this site is about</h2>
<p>moonweb is a collection of personal projects that have grown over the past 25 years. What started as a single 486 running a BBS in the mid-90s has turned into a full homelab with Proxmox, Docker, smart home automation, and a growing collection of retro hardware. The name "moonweb" comes from the original domain moonweb.org, which has been online since 2000.</p>
<p>Each section of this site documents a different part of the setup. The <a href="https://infra.moonweb.org">infrastructure section</a> covers the Proxmox cluster, Synology NAS, Docker containers, networking, and backup strategies. The <a href="https://smarthome.moonweb.org">smart home section</a> shows what automation projects are running, from Tasmota energy monitoring to MQTT sensors and AirPlay audio. The <a href="https://code.moonweb.org">code section</a> lists the public GitHub projects that power these setups, and the <a href="https://retro.moonweb.org">retro section</a> documents the hardware collection.</p>
<p>Each section of this site documents a different part of the setup. The <a href="/infra/">infrastructure section</a> covers the Proxmox cluster, Synology NAS, Docker containers, networking, and backup strategies. The <a href="/smarthome/">smart home section</a> shows what automation projects are running, from Tasmota energy monitoring to MQTT sensors and AirPlay audio. The <a href="/code/">code section</a> lists the public GitHub projects that power these setups, and the <a href="/retro/">retro section</a> documents the hardware collection.</p>
<h2>What I do professionally</h2>
<p>I work as a Senior Software Developer &amp; Architect at TENHIL GmbH (formerly stellenanzeigen.de), where I build and maintain microservices on .NET Core and Kubernetes. My day job involves designing cloud-native architectures, building APIs, and migrating legacy applications to modern stacks. I've been working with .NET since the early 2000s and have used everything from ASP Classic to the latest .NET 8 features.</p>
+11
View File
@@ -0,0 +1,11 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="refresh" content="0;url=/timecapsule/">
<title>Redirecting...</title>
</head>
<body>
<p>Redirecting to <a href="/timecapsule/">/timecapsule/</a>...</p>
</body>
</html>
+11
View File
@@ -0,0 +1,11 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="refresh" content="0;url=/timecapsule/">
<title>Redirecting...</title>
</head>
<body>
<p>Redirecting to <a href="/timecapsule/">/timecapsule/</a>...</p>
</body>
</html>
+11
View File
@@ -0,0 +1,11 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="refresh" content="0;url=/timecapsule/">
<title>Redirecting...</title>
</head>
<body>
<p>Redirecting to <a href="/timecapsule/">/timecapsule/</a>...</p>
</body>
</html>
+11
View File
@@ -0,0 +1,11 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="refresh" content="0;url=/timecapsule/legal-notice/">
<title>Redirecting...</title>
</head>
<body>
<p>Redirecting to <a href="/timecapsule/legal-notice/">/timecapsule/legal-notice/</a>...</p>
</body>
</html>
+11
View File
@@ -0,0 +1,11 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="refresh" content="0;url=/timecapsule/">
<title>Redirecting...</title>
</head>
<body>
<p>Redirecting to <a href="/timecapsule/">/timecapsule/</a>...</p>
</body>
</html>
+11
View File
@@ -0,0 +1,11 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="refresh" content="0;url=/timecapsule/location/">
<title>Redirecting...</title>
</head>
<body>
<p>Redirecting to <a href="/timecapsule/location/">/timecapsule/location/</a>...</p>
</body>
</html>
+11
View File
@@ -0,0 +1,11 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="refresh" content="0;url=/timecapsule/network/">
<title>Redirecting...</title>
</head>
<body>
<p>Redirecting to <a href="/timecapsule/network/">/timecapsule/network/</a>...</p>
</body>
</html>
+11
View File
@@ -0,0 +1,11 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="refresh" content="0;url=/timecapsule/">
<title>Redirecting...</title>
</head>
<body>
<p>Redirecting to <a href="/timecapsule/">/timecapsule/</a>...</p>
</body>
</html>
+11
View File
@@ -0,0 +1,11 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="refresh" content="0;url=/timecapsule/partners/">
<title>Redirecting...</title>
</head>
<body>
<p>Redirecting to <a href="/timecapsule/partners/">/timecapsule/partners/</a>...</p>
</body>
</html>
+11
View File
@@ -0,0 +1,11 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="refresh" content="0;url=/timecapsule/products/">
<title>Redirecting...</title>
</head>
<body>
<p>Redirecting to <a href="/timecapsule/products/">/timecapsule/products/</a>...</p>
</body>
</html>
+11
View File
@@ -0,0 +1,11 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="refresh" content="0;url=/timecapsule/projects/">
<title>Redirecting...</title>
</head>
<body>
<p>Redirecting to <a href="/timecapsule/projects/">/timecapsule/projects/</a>...</p>
</body>
</html>
+2 -1
View File
@@ -1,4 +1,5 @@
User-agent: *
Allow: /
Sitemap: https://hub.moonweb.org/sitemap.xml
Sitemap: https://www.moonweb.org/sitemap.xml
Sitemap: https://www.moonweb.org/timecapsule/sitemap.xml
+11
View File
@@ -0,0 +1,11 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="refresh" content="0;url=/timecapsule/">
<title>Redirecting...</title>
</head>
<body>
<p>Redirecting to <a href="/timecapsule/">/timecapsule/</a>...</p>
</body>
</html>
+11
View File
@@ -0,0 +1,11 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="refresh" content="0;url=/timecapsule/stefan/">
<title>Redirecting...</title>
</head>
<body>
<p>Redirecting to <a href="/timecapsule/stefan/">/timecapsule/stefan/</a>...</p>
</body>
</html>
+11
View File
@@ -0,0 +1,11 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="refresh" content="0;url=/timecapsule/">
<title>Redirecting...</title>
</head>
<body>
<p>Redirecting to <a href="/timecapsule/">/timecapsule/</a>...</p>
</body>
</html>
+2 -1
View File
@@ -1,9 +1,10 @@
---
title: "Page Not Found"
section: "infra"
tags: "infra"
description: "The page you are looking for does not exist."
layout: base.njk
permalink: /404.html
permalink: /infra/404.html
---
<div class="error-page">
<h1>404</h1>
+2 -1
View File
@@ -1,7 +1,8 @@
---
title: "Backup Strategy"
section: "infra"
parent: "/"
tags: "infra"
parent: "/infra/"
description: "Layered 3-2-1 backup strategy covering NAS snapshots, offsite copies, and cloud storage."
layout: base.njk
---
+2 -1
View File
@@ -1,7 +1,8 @@
---
title: "Xubuntu Dev VM"
section: "infra"
parent: "/"
tags: "infra"
parent: "/infra/"
description: "A disposable, daily-backed-up Xubuntu VM for AI-assisted, remote development via SSH and Chrome Remote Desktop."
layout: base.njk
---
+2 -1
View File
@@ -1,7 +1,8 @@
---
title: "Docker Container Strategy"
section: "infra"
parent: "/"
tags: "infra"
parent: "/infra/"
description: "How self-hosted services are organized across three Docker hosts on Proxmox and Synology, using Docker Compose as the single source of truth."
layout: base.njk
---
-20
View File
@@ -1,20 +0,0 @@
module.exports = function (eleventyConfig) {
eleventyConfig.addGlobalData("site", { url: "https://infra.moonweb.org" });
eleventyConfig.addFilter("date", (d) => d.toISOString());
eleventyConfig.addPassthroughCopy({ "shared/theme-infra.css": "theme.css" });
eleventyConfig.addPassthroughCopy({ "shared/base.css": "shared-base.css" });
eleventyConfig.addPassthroughCopy({ "shared/favicon/infra.svg": "favicon.svg" });
eleventyConfig.addPassthroughCopy({ "infra/robots.txt": "robots.txt" });
return {
dir: {
input: "infra",
includes: "../shared/_includes",
output: "dist/infra"
},
serverOptions: {
host: "0.0.0.0",
port: 8082
}
};
};
+1
View File
@@ -1,6 +1,7 @@
---
title: "Homelab Infrastructure — Proxmox, Docker & Networking | moonweb"
section: "infra"
tags: "infra"
description: "Homelab infrastructure overview: Proxmox hosts, Synology NAS, Docker VMs and Setup-Guides."
layout: base.njk
sections:
+2 -1
View File
@@ -1,7 +1,8 @@
---
title: "LXC Container Strategy"
section: "infra"
parent: "/"
tags: "infra"
parent: "/infra/"
description: "Overview of the lightweight LXC containers running on the main Proxmox host, and why they are LXC instead of Docker."
layout: base.njk
---
+2 -1
View File
@@ -1,7 +1,8 @@
---
title: "Monitoring"
section: "infra"
parent: "/"
tags: "infra"
parent: "/infra/"
description: "Prometheus and Grafana monitoring stack for hosts, containers, and hardware metrics."
layout: base.njk
---
+2 -1
View File
@@ -1,7 +1,8 @@
---
title: "OpenWrt Multi-WAN Router"
section: "infra"
parent: "/"
tags: "infra"
parent: "/infra/"
description: "Multi-WAN OpenWrt router in the study with WiFi and Powerline failover, plus VLAN 189 for IoT isolation."
layout: base.njk
---
+2 -1
View File
@@ -1,7 +1,8 @@
---
title: "Proxmox Virtualization"
section: "infra"
parent: "/"
tags: "infra"
parent: "/infra/"
description: "What Proxmox VE is, the hardware it runs on in my homelab, its key advantages, and a short setup walkthrough."
layout: base.njk
---
-4
View File
@@ -1,4 +0,0 @@
User-agent: *
Allow: /
Sitemap: https://infra.moonweb.org/sitemap.xml
-5
View File
@@ -1,5 +0,0 @@
---
permalink: /sitemap.xml
eleventyExcludeFromCollections: true
---
{% include "sitemap.njk" %}
+2 -1
View File
@@ -1,7 +1,8 @@
---
title: "Synology NAS"
section: "infra"
parent: "/"
tags: "infra"
parent: "/infra/"
description: "Model, storage setup and current role of the Synology NAS in the homelab after the late-2025 cleanup."
layout: base.njk
---
+7 -14
View File
@@ -1,23 +1,16 @@
{
"name": "moonweb-site",
"version": "0.2.0",
"version": "0.3.0",
"private": true,
"description": "Monorepo for hub/infra/smarthome/code/retro (Eleventy, static, deployed to Cloudflare Pages)",
"description": "Monorepo for hub/infra/smarthome/code/retro/timecapsule (Eleventy, static, deployed to IONOS SFTP)",
"scripts": {
"dev:hub": "npx @11ty/eleventy --config=hub/eleventy.config.js --serve --port=8081",
"dev:infra": "npx @11ty/eleventy --config=infra/eleventy.config.js --serve --port=8082",
"dev:smarthome": "npx @11ty/eleventy --config=smarthome/eleventy.config.js --serve --port=8083",
"dev:code": "npx @11ty/eleventy --config=code/eleventy.config.js --serve --port=8084",
"dev:retro": "npx @11ty/eleventy --config=retro/eleventy.config.js --serve --port=8085",
"dev": "npx @11ty/eleventy --serve --port=8081",
"dev:stefankoelle": "npx @11ty/eleventy --config=stefankoelle/eleventy.config.js --serve --port=8086",
"dev": "npm run dev:hub & npm run dev:infra & npm run dev:smarthome & npm run dev:code & npm run dev:retro & npm run dev:stefankoelle",
"build:hub": "npx @11ty/eleventy --config=hub/eleventy.config.js",
"build:infra": "npx @11ty/eleventy --config=infra/eleventy.config.js",
"build:smarthome": "npx @11ty/eleventy --config=smarthome/eleventy.config.js",
"build:code": "npx @11ty/eleventy --config=code/eleventy.config.js",
"build:retro": "npx @11ty/eleventy --config=retro/eleventy.config.js",
"dev:timecapsule": "cd timecapsule && npx @11ty/eleventy --serve --port=8087",
"build": "npx @11ty/eleventy",
"build:stefankoelle": "npx @11ty/eleventy --config=stefankoelle/eleventy.config.js",
"build": "npm run build:hub && npm run build:infra && npm run build:smarthome && npm run build:code && npm run build:retro && npm run build:stefankoelle",
"build:timecapsule": "cd timecapsule && npx @11ty/eleventy && mkdir -p ../dist/timecapsule && cp -r _site/timecapsule/* ../dist/timecapsule/",
"build:moonweb": "npm run build && npm run build:timecapsule",
"pdf:cv": "bash stefankoelle/pdf/build-pdf.sh",
"prebuild": "bash prebuild.sh"
},
+2 -1
View File
@@ -1,9 +1,10 @@
---
title: "Page Not Found"
section: "retro"
tags: "retro"
description: "The page you are looking for does not exist."
layout: base.njk
permalink: /404.html
permalink: /retro/404.html
---
<div class="error-page">
<h1>404</h1>
+2 -1
View File
@@ -1,9 +1,10 @@
---
title: "Running AmigaOS in 2026: MiSTer FPGA vs Amiberry on Pi"
section: "retro"
tags: "retro"
description: "Two very different ways to get a fully working AmigaOS 3.2.3 desktop running today."
layout: base.njk
parent: "/"
parent: "/retro/"
---
<h1>Running AmigaOS in 2026: MiSTer FPGA vs Amiberry on Pi</h1>
<div class="detail-content">
+2 -1
View File
@@ -1,9 +1,10 @@
---
title: "Vampire V4, A600GS or MiSTer: Amiga Hardware Isn't a Simple Speed Race"
section: "retro"
tags: "retro"
description: "Why raw benchmark numbers are actively misleading when comparing modern Amiga hardware options."
layout: base.njk
parent: "/"
parent: "/retro/"
---
<h1>Vampire V4, A600GS or MiSTer: Amiga Hardware Isn't a Simple Speed Race</h1>
<div class="detail-content">
+2 -1
View File
@@ -1,9 +1,10 @@
---
title: "The Atari Mega ST Fleet: Three Machines, Three Jobs"
section: "retro"
tags: "retro"
description: "How three Atari Mega ST computers ended up with completely different roles in a modern retro setup."
layout: base.njk
parent: "/"
parent: "/retro/"
---
<h1>The Atari Mega ST Fleet: Three Machines, Three Jobs</h1>
<div class="detail-content">
+2 -1
View File
@@ -1,9 +1,10 @@
---
title: "The 15kHz Problem: Finding a Monitor That Speaks Atari"
section: "retro"
tags: "retro"
description: "Why most modern monitors refuse to display Atari ST colour modes, and which ones actually work."
layout: base.njk
parent: "/"
parent: "/retro/"
---
<h1>The 15kHz Problem: Finding a Monitor That Speaks Atari</h1>
<div class="detail-content">
+2 -1
View File
@@ -1,9 +1,10 @@
---
title: "Getting a 40-Year-Old Computer Online: Atari ST Networking and BBS Access"
section: "retro"
tags: "retro"
description: "Connecting an Atari ST to the modern internet and to dial-up-era bulletin board systems, warts and all."
layout: base.njk
parent: "/"
parent: "/retro/"
---
<h1>Getting a 40-Year-Old Computer Online: Atari ST Networking and BBS Access</h1>
<div class="detail-content">
+2 -1
View File
@@ -1,9 +1,10 @@
---
title: "Batocera in the Living Room: One Box, Every Retro System"
section: "retro"
tags: "retro"
description: "Why the living room emulation station takes a completely different approach than the FPGA rigs upstairs."
layout: base.njk
parent: "/"
parent: "/retro/"
---
<h1>Batocera in the Living Room: One Box, Every Retro System</h1>
<div class="detail-content">
+4 -3
View File
@@ -1,9 +1,10 @@
---
title: "The 486 DX2-66: Rebuilding a DOS Era on MiSTer FPGA"
section: "retro"
tags: "retro"
description: "Recreating a Vobis-Highscreen 486 DX2-66 setup on MiSTer FPGA, down to the original DOS configuration."
layout: base.njk
parent: "/"
parent: "/retro/"
---
<h1>The 486 DX2-66: Rebuilding a DOS Era on MiSTer FPGA</h1>
<div class="detail-content">
@@ -17,7 +18,7 @@ parent: "/"
<p>The AO486 core on MiSTer FPGA reproduces the original hardware with near-perfect accuracy. The key was not just getting the hardware emulation right, but rebuilding the exact software environment: the same MS-DOS version, the same memory managers (HIMEM.SYS, EMM386), the same Sound Blaster drivers, and the same AUTOEXEC.BAT and CONFIG.SYS tuning that made the original machine run smoothly. Even the directory structure and file names match the original setup.</p>
<h2>Context: DOSMENU</h2>
<p>A key part of the preserved environment is <strong>DOSMENU</strong>, a boot configuration utility written in Turbo Pascal 6.0 in 1994 — originally developed specifically for this Colani 486 machine. DOSMENU let you choose at boot time which AUTOEXEC.BAT and CONFIG.SYS commands to load — a critical feature in the DOS era, where memory management was manual and games often required different driver configurations. Features included saveable presets, a built-in MOD player, PCX picture display, and a DoubleSpace memory unloader. The original source: ~50k of Turbo Pascal, roughly 1900 lines plus some inline assembler.</p>
<p>A key part of the preserved environment is <strong><a href="https://28k8.moonweb.org/bbs/pc/dosmenu/">DOSMENU</a></strong>, a boot configuration utility written in Turbo Pascal 6.0 in 1994 — originally developed specifically for this Colani 486 machine. DOSMENU let you choose at boot time which AUTOEXEC.BAT and CONFIG.SYS commands to load — a critical feature in the DOS era, where memory management was manual and games often required different driver configurations. Features included saveable presets, a built-in MOD player, PCX picture display, and a DoubleSpace memory unloader. The original source: ~50k of Turbo Pascal, roughly 1900 lines plus some inline assembler.</p>
<h2>Why a 1:1 recreation</h2>
<p>The point isn't just to play DOS games — it's to preserve the exact computing experience from that era. The way DOS memory management worked, the way sound drivers had to be configured manually, the way batch files chained together to create a usable environment. This level of detail is lost when you just launch a DOS game from a modern frontend. The MiSTer setup recreates the entire boot process, from BIOS POST to the C:\> prompt, just as it was in the mid-90s.</p>
@@ -27,7 +28,7 @@ parent: "/"
<li><strong>DOS boot environment</strong> — the exact AUTOEXEC.BAT and CONFIG.SYS from the original machine, including memory manager configuration and Sound Blaster IRQ settings.</li>
<li><strong>Game installations</strong> — original game installations with their setup utilities, configuration files, and saved games, all in the same directory paths as the original.</li>
<li><strong>System utilities</strong> — Norton Commander, PKZIP, the usual toolkit of the era, all configured the way they were used daily.</li>
<li><strong>DOSMENU</strong> — a custom boot configuration utility written specifically for this Colani 486 in 1994. Built in Turbo Pascal, it let you choose at boot time which AUTOEXEC.BAT and CONFIG.SYS commands to load — essential for managing DOS memory across different games and applications.</li>
<li><strong><a href="https://28k8.moonweb.org/bbs/pc/dosmenu/">DOSMENU</a></strong> — a custom boot configuration utility written specifically for this Colani 486 in 1994. Built in Turbo Pascal, it let you choose at boot time which AUTOEXEC.BAT and CONFIG.SYS commands to load — essential for managing DOS memory across different games and applications.</li>
<li><strong>The quirks</strong> — the specific workarounds and tweaks that were needed to get everything running together, the kind of institutional knowledge that only exists on a well-used personal machine.</li>
</ul>
-20
View File
@@ -1,20 +0,0 @@
module.exports = function (eleventyConfig) {
eleventyConfig.addGlobalData("site", { url: "https://retro.moonweb.org" });
eleventyConfig.addFilter("date", (d) => d.toISOString());
eleventyConfig.addPassthroughCopy({ "shared/theme-retro.css": "theme.css" });
eleventyConfig.addPassthroughCopy({ "shared/base.css": "shared-base.css" });
eleventyConfig.addPassthroughCopy({ "shared/favicon/retro.svg": "favicon.svg" });
eleventyConfig.addPassthroughCopy({ "retro/robots.txt": "robots.txt" });
return {
dir: {
input: "retro",
includes: "../shared/_includes",
output: "dist/retro"
},
serverOptions: {
host: "0.0.0.0",
port: 8085
}
};
};
+2 -1
View File
@@ -1,9 +1,10 @@
---
title: "Why an ET4000 Graphics Card and a Mega ST Don't Mix (Yet)"
section: "retro"
tags: "retro"
description: "A cautionary tale about pushing 1980s power supplies past their limits with a modern-ish graphics card."
layout: base.njk
parent: "/"
parent: "/retro/"
---
<h1>Why an ET4000 Graphics Card and a Mega ST Don't Mix (Yet)</h1>
<div class="detail-content">
+1
View File
@@ -1,6 +1,7 @@
---
title: "Retro Computing — Vintage Hardware & FPGA | moonweb"
section: "retro"
tags: "retro"
description: "Physical retro hardware collection — Raspberry Pi units, Pi 400, and vintage computing setups."
layout: base.njk
sections:
+2 -1
View File
@@ -1,9 +1,10 @@
---
title: "Mini Arcades and Handhelds: Sorting the Real Deal from the Bootlegs"
section: "retro"
tags: "retro"
description: "A buyer's guide to compact retro gaming devices, and why the cheapest option is almost never the right one."
layout: base.njk
parent: "/"
parent: "/retro/"
---
<h1>Mini Arcades and Handhelds: Sorting the Real Deal from the Bootlegs</h1>
<div class="detail-content">
+2 -1
View File
@@ -1,9 +1,10 @@
---
title: "MiST and MiSTer: Two FPGA Boxes, Two Very Different Personalities"
section: "retro"
tags: "retro"
description: "Why the newer, more powerful MiSTer isn't automatically the better choice for every retro system."
layout: base.njk
parent: "/"
parent: "/retro/"
---
<h1>MiST and MiSTer: Two FPGA Boxes, Two Very Different Personalities</h1>
<div class="detail-content">
+2 -1
View File
@@ -1,9 +1,10 @@
---
title: "PiMiga 3 to 5: Chasing the Perfect Amiga-on-a-Pi Distro"
section: "retro"
tags: "retro"
description: "Three generations of PiMiga later, here's what actually changed — and why newer isn't always better."
layout: base.njk
parent: "/"
parent: "/retro/"
---
<h1>PiMiga 3 to 5: Chasing the Perfect Amiga-on-a-Pi Distro</h1>
<div class="detail-content">
+2 -1
View File
@@ -1,9 +1,10 @@
---
title: "The Retro Corner Today: A Snapshot of the Whole Setup"
section: "retro"
tags: "retro"
description: "A tour through every retro system currently active across the home, room by room."
layout: base.njk
parent: "/"
parent: "/retro/"
---
<h1>The Retro Corner Today: A Snapshot of the Whole Setup</h1>
<div class="detail-content">
-4
View File
@@ -1,4 +0,0 @@
User-agent: *
Allow: /
Sitemap: https://retro.moonweb.org/sitemap.xml
-5
View File
@@ -1,5 +0,0 @@
---
permalink: /sitemap.xml
eleventyExcludeFromCollections: true
---
{% include "sitemap.njk" %}
+2 -1
View File
@@ -1,9 +1,10 @@
---
title: "The A1200: What We Know Before It Ships"
section: "retro"
tags: "retro"
description: "A close look at Retro Games Ltd.'s upcoming Amiga 1200 console, based on everything public so far."
layout: base.njk
parent: "/"
parent: "/retro/"
---
<h1>The A1200: What We Know Before It Ships</h1>
<div class="detail-content">
+72
View File
@@ -0,0 +1,72 @@
[
{
"expression": "http.host eq \"hub.moonweb.org\"",
"description": "hub.moonweb.org -> www.moonweb.org",
"action": "redirect",
"action_parameters": {
"from_value": {
"target_url": {
"expression": "concat(\"https://www.moonweb.org\", http.request.uri.path)"
},
"status_code": 301,
"preserve_query_string": true
}
}
},
{
"expression": "http.host eq \"infra.moonweb.org\"",
"description": "infra.moonweb.org -> www.moonweb.org/infra/",
"action": "redirect",
"action_parameters": {
"from_value": {
"target_url": {
"expression": "concat(\"https://www.moonweb.org/infra\", http.request.uri.path)"
},
"status_code": 301,
"preserve_query_string": true
}
}
},
{
"expression": "http.host eq \"smarthome.moonweb.org\"",
"description": "smarthome.moonweb.org -> www.moonweb.org/smarthome/",
"action": "redirect",
"action_parameters": {
"from_value": {
"target_url": {
"expression": "concat(\"https://www.moonweb.org/smarthome\", http.request.uri.path)"
},
"status_code": 301,
"preserve_query_string": true
}
}
},
{
"expression": "http.host eq \"code.moonweb.org\"",
"description": "code.moonweb.org -> www.moonweb.org/code/",
"action": "redirect",
"action_parameters": {
"from_value": {
"target_url": {
"expression": "concat(\"https://www.moonweb.org/code\", http.request.uri.path)"
},
"status_code": 301,
"preserve_query_string": true
}
}
},
{
"expression": "http.host eq \"retro.moonweb.org\"",
"description": "retro.moonweb.org -> www.moonweb.org/retro/",
"action": "redirect",
"action_parameters": {
"from_value": {
"target_url": {
"expression": "concat(\"https://www.moonweb.org/retro\", http.request.uri.path)"
},
"status_code": 301,
"preserve_query_string": true
}
}
}
]
+172
View File
@@ -0,0 +1,172 @@
#!/bin/bash
# Setup Cloudflare redirect rules for moonweb.org
# Redirects old subdomains to www.moonweb.org subdirectories
#
# Required env vars:
# CLOUDFLARE_API_TOKEN - API token with Dynamic Redirects Write permission
# CLOUDFLARE_ZONE_ID - Zone ID for moonweb.org
#
# Usage:
# ./setup-redirects.sh # Apply redirects
# ./setup-redirects.sh --dry-run # Show what would be created
# ./setup-redirects.sh --status # Show current redirect rules
# ./setup-redirects.sh --delete # Delete all redirect rules
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
RULES_FILE="$SCRIPT_DIR/redirect-rules.json"
API="https://api.cloudflare.com/client/v4"
PHASE="http_request_dynamic_redirect"
RULESET_NAME="moonweb-subdomain-redirects"
DRY_RUN=false
STATUS=false
DELETE=false
for arg in "$@"; do
case $arg in
--dry-run) DRY_RUN=true ;;
--status) STATUS=true ;;
--delete) DELETE=true ;;
esac
done
if [ -z "${CLOUDFLARE_API_TOKEN:-}" ] || [ -z "${CLOUDFLARE_ZONE_ID:-}" ]; then
echo "Error: CLOUDFLARE_API_TOKEN and CLOUDFLARE_ZONE_ID must be set"
exit 1
fi
cf_api() {
local method=$1
local url=$2
shift 2
curl -s -X "$method" "$url" \
-H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \
-H "Content-Type: application/json" \
"$@"
}
# Get existing ruleset ID for this phase
get_ruleset_id() {
cf_api GET "$API/zones/$CLOUDFLARE_ZONE_ID/rulesets" | \
python3 -c "
import sys, json
data = json.load(sys.stdin)
for r in data.get('result', []):
if r.get('phase') == '$PHASE':
print(r['id'])
sys.exit(0)
print('')
" 2>/dev/null
}
# Get rules in a ruleset
get_rules() {
local ruleset_id=$1
cf_api GET "$API/zones/$CLOUDFLARE_ZONE_ID/rulesets/$ruleset_id" | \
python3 -c "
import sys, json
data = json.load(sys.stdin)
for r in data.get('result', {}).get('rules', []):
desc = r.get('description', 'unnamed')
expr = r.get('expression', '')
print(f' {desc}')
print(f' expression: {expr}')
" 2>/dev/null
}
# Show current status
if [ "$STATUS" = true ]; then
echo "=== Current redirect rules ==="
ruleset_id=$(get_ruleset_id)
if [ -z "$ruleset_id" ]; then
echo "No redirect ruleset found for phase $PHASE"
exit 0
fi
echo "Ruleset ID: $ruleset_id"
get_rules "$ruleset_id"
exit 0
fi
# Delete all redirect rules
if [ "$DELETE" = true ]; then
echo "=== Deleting redirect rules ==="
ruleset_id=$(get_ruleset_id)
if [ -z "$ruleset_id" ]; then
echo "No redirect ruleset found, nothing to delete"
exit 0
fi
echo "Deleting ruleset $ruleset_id..."
RESULT=$(cf_api DELETE "$API/zones/$CLOUDFLARE_ZONE_ID/rulesets/$ruleset_id")
echo "$RESULT" | python3 -c "
import sys, json
data = json.load(sys.stdin)
if data.get('success'):
print('Deleted successfully')
else:
for e in data.get('errors', []):
print(f\"Error: {e.get('message', '')}\")
" 2>/dev/null
exit 0
fi
# Build payload from JSON file
PAYLOAD=$(python3 -c "
import json
with open('$RULES_FILE') as f:
rules = json.load(f)
payload = {
'name': '$RULESET_NAME',
'kind': 'zone',
'phase': '$PHASE',
'rules': rules
}
print(json.dumps(payload))
")
RULE_COUNT=$(echo "$PAYLOAD" | python3 -c "import sys, json; print(len(json.load(sys.stdin)['rules']))" 2>/dev/null)
if [ "$DRY_RUN" = true ]; then
echo "=== DRY RUN ==="
echo "Would create/update ruleset '$RULESET_NAME' with $RULE_COUNT rules:"
python3 -c "
import json
with open('$RULES_FILE') as f:
rules = json.load(f)
for r in rules:
print(f\" - {r['description']}\")
" 2>/dev/null
exit 0
fi
# Check if ruleset already exists
echo "=== Checking existing ruleset ==="
EXISTING_ID=$(get_ruleset_id)
if [ -n "$EXISTING_ID" ]; then
echo "Updating existing ruleset $EXISTING_ID..."
RESULT=$(cf_api PUT "$API/zones/$CLOUDFLARE_ZONE_ID/rulesets/$EXISTING_ID" \
-d "$PAYLOAD")
else
echo "Creating new ruleset..."
RESULT=$(cf_api POST "$API/zones/$CLOUDFLARE_ZONE_ID/rulesets" \
-d "$PAYLOAD")
fi
# Check result
echo "$RESULT" | python3 -c "
import sys, json
data = json.load(sys.stdin)
if data.get('success'):
ruleset = data['result']
print(f\"Success! Ruleset: {ruleset['name']} (ID: {ruleset['id']})\")
print(f\"Rules: {len(ruleset.get('rules', []))}\")
for r in ruleset.get('rules', []):
print(f\" - {r.get('description', 'unnamed')}\")
else:
print('Error:')
for e in data.get('errors', []):
print(f\" {e.get('code', '')}: {e.get('message', '')}\")
sys.exit(1)
" 2>/dev/null
+10 -11
View File
@@ -17,12 +17,11 @@
<meta name="twitter:title" content="{{ title }}{% if 'moonweb' not in title and 'stefankoelle' not in title %} · moonweb.org{% endif %}">
<meta name="twitter:description" content="{{ description }}">
{% if ogImage %}<meta name="twitter:image" content="{{ site.url }}{{ ogImage }}">{% endif %}
<link rel="icon" href="/favicon.svg" type="image/svg+xml">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Lobster&display=swap" rel="stylesheet">
<link rel="stylesheet" href="/shared-base.css">
<link rel="stylesheet" href="/theme.css">
<style>:root { --accent: {% if section == 'infra' %}#99333A{% elif section == 'smarthome' %}#1f8a8a{% elif section == 'code' %}#3E5098{% elif section == 'retro' %}#8a6d3b{% else %}#3b6ea5{% endif %}; }</style>
<script type="application/ld+json">
{
"@context": "https://schema.org",
@@ -45,8 +44,8 @@
"@context": "https://schema.org",
"@type": "Person",
"name": "Stefan Koelle",
"url": "https://hub.moonweb.org",
"image": "https://hub.moonweb.org/stefan-koelle-foto.jpg",
"url": "https://www.moonweb.org",
"image": "https://www.moonweb.org/stefan-koelle-foto.jpg",
"jobTitle": "Senior Software Developer & Architect",
"address": {
"@type": "PostalAddress",
@@ -69,11 +68,11 @@
<span class="site-title">{{ section }}.moonweb.org</span>
</div>
<nav class="site-switcher">
<a href="https://hub.moonweb.org">hub</a>
<a href="https://infra.moonweb.org">infra</a>
<a href="https://smarthome.moonweb.org">smarthome</a>
<a href="https://code.moonweb.org">code</a>
<a href="https://retro.moonweb.org">retro</a>
<a href="/">hub</a>
<a href="/infra/">infra</a>
<a href="/smarthome/">smarthome</a>
<a href="/code/">code</a>
<a href="/retro/">retro</a>
<a href="https://stefankoelle.de">cv</a>
</nav>
</header>
@@ -84,8 +83,8 @@
</main>
<footer class="site-footer">
<p>Part of the <a href="https://hub.moonweb.org">moonweb.org</a> homelab.</p>
<p><a href="https://hub.moonweb.org/impressum/">Legal Notice &amp; Privacy Policy</a></p>
<p>Part of the <a href="/">moonweb.org</a> homelab.</p>
<p><a href="/impressum/">Legal Notice &amp; Privacy Policy</a></p>
</footer>
</body>
</html>
+2 -1
View File
@@ -1,9 +1,10 @@
---
title: "Page Not Found"
section: "smarthome"
tags: "smarthome"
description: "The page you are looking for does not exist."
layout: base.njk
permalink: /404.html
permalink: /smarthome/404.html
---
<div class="error-page">
<h1>404</h1>
+2 -1
View File
@@ -1,7 +1,8 @@
---
title: "Air Quality Dashboard"
section: "smarthome"
parent: "/"
tags: "smarthome"
parent: "/smarthome/"
description: "Real-time temperature and humidity monitoring across all rooms, powered by InfluxDB and Chart.js."
layout: base.njk
---
+2 -1
View File
@@ -1,7 +1,8 @@
---
title: "AirPlay Audio"
section: "smarthome"
parent: "/"
tags: "smarthome"
parent: "/smarthome/"
description: "Multi-room AirPlay receivers with software volume boost on Raspberry Pi devices."
layout: base.njk
---
+2 -1
View File
@@ -1,7 +1,8 @@
---
title: "Balkon Pi"
section: "smarthome"
parent: "/"
tags: "smarthome"
parent: "/smarthome/"
description: "Balcony sensor, lighting, and audio automation running on a Raspberry Pi Zero W."
layout: base.njk
---
-20
View File
@@ -1,20 +0,0 @@
module.exports = function (eleventyConfig) {
eleventyConfig.addGlobalData("site", { url: "https://smarthome.moonweb.org" });
eleventyConfig.addFilter("date", (d) => d.toISOString());
eleventyConfig.addPassthroughCopy({ "shared/theme-smarthome.css": "theme.css" });
eleventyConfig.addPassthroughCopy({ "shared/base.css": "shared-base.css" });
eleventyConfig.addPassthroughCopy({ "shared/favicon/smarthome.svg": "favicon.svg" });
eleventyConfig.addPassthroughCopy({ "smarthome/robots.txt": "robots.txt" });
return {
dir: {
input: "smarthome",
includes: "../shared/_includes",
output: "dist/smarthome"
},
serverOptions: {
host: "0.0.0.0",
port: 8083
}
};
};
+2 -1
View File
@@ -1,9 +1,10 @@
---
title: "Home Control Buttons"
section: "smarthome"
tags: "smarthome"
description: "Overview of the central buttons dashboard used to control lighting, smart plugs, music, network devices and monitoring shortcuts around the apartment."
layout: base.njk
parent: "/"
parent: "/smarthome/"
---
<h1>Home Control Buttons</h1>
<div class="detail-content">
+2 -1
View File
@@ -1,7 +1,8 @@
---
title: "HomematicIP + MQTT"
section: "smarthome"
parent: "/"
tags: "smarthome"
parent: "/smarthome/"
description: "How HomematicIP room sensors are bridged into MQTT and stored in InfluxDB for dashboards."
layout: base.njk
---
+5
View File
@@ -1,6 +1,7 @@
---
title: "Smart Home Projects — Home Assistant, MQTT & IoT | moonweb"
section: "smarthome"
tags: "smarthome"
description: "Smart home projects: Home Assistant, MQTT sensors, AirPlay audio, OctoPrint, and energy monitoring."
layout: base.njk
sections:
@@ -72,6 +73,10 @@ sections:
summary: "TubeArchivist and TubeSync for self-hosted YouTube downloading on the Synology."
href: "/tubearchivist/"
emoji: "📺"
- title: "Kids RFID MP3 Player"
summary: "Arduino-based audio player for kids using RFID cards, built as a custom alternative to Hoerbert."
href: "/kids-rfid-player/"
emoji: "🎵"
---
<p class="site-intro">The heart of the homelab. Home Assistant, MQTT sensors, dashboards, and automation projects that make everyday life a little more interesting.</p>
+53
View File
@@ -0,0 +1,53 @@
---
title: "Kids RFID MP3 Player"
section: "smarthome"
tags: "smarthome"
parent: "/smarthome/"
description: "A custom Arduino-based RFID audio player for kids, inspired by the Hoerbert and Tonuino projects."
layout: base.njk
---
<h1>Kids RFID MP3 Player</h1>
<div class="detail-content">
<p>An Arduino-based MP3 player where kids select audio content by holding RFID cards against the device. No screen, no menus, no WiFi — just tap a card and music plays. Built together with my son in 2018 as a cheaper, more flexible alternative to commercial products like the Hoerbert or Tonuino.</p>
<h2>Why this exists</h2>
<p>Commercial kids' audio players have trade-offs. The Hoerbert only offers 9 direct-select buttons with color-coded cards that aren't very intuitive for very young children. The Tonuino uses expensive RFID figures and relies on WiFi streaming, which introduces latency and dependency on network availability. This project aimed for the best of both worlds: RFID-based content selection with local-only playback, analog volume control, and minimal buttons.</p>
<h2>Hardware</h2>
<ul>
<li><strong>Arduino</strong> — the microcontroller running the custom firmware.</li>
<li><strong>RFID module</strong> — reads MIFARE cards to trigger audio playback.</li>
<li><strong>DFPlayer Mini</strong> — compact MP3 player module with built-in amplifier, handling audio decoding and playback directly from an SD card.</li>
<li><strong>Pololu power switch</strong> — handles auto-shutdown to prevent battery drain, replacing a manual toggle switch that a child would forget to turn off.</li>
<li><strong>Analog volume knob</strong> — a rotary potentiometer for tactile volume control, deliberately chosen over digital buttons.</li>
<li><strong>3 LEDs</strong> — green (playing), yellow (paused), red (stopped/at end).</li>
</ul>
<h2>How it works</h2>
<p>Each RFID card is mapped to a specific audio file on the SD card. Hold a card against the reader and the corresponding story or song starts playing. Hold the RFID keychain against the reader to pause; hold it again to resume. No other buttons exist on the device — the design intentionally minimizes interaction points so even a toddler can use it independently.</p>
<h2>Auto-shutdown behavior</h2>
<p>The power management distinguishes between two states to avoid unnecessary battery drain:</p>
<ul>
<li><strong>Stopped</strong> (playback reached the end) — the device shuts down after 5 minutes.</li>
<li><strong>Paused</strong> (user paused via RFID) — the device stays on for 1 hour before shutting down, allowing easy resumption.</li>
</ul>
<h2>The case</h2>
<p>The enclosure is a custom-built retro-style wooden box, painted by hand. The name "Rocky Box" came from my son, who painted a Paw Patrol character on the front. The design is deliberately quirky and homemade rather than polished — it has character.</p>
<h2>What's missing (by design)</h2>
<ul>
<li>No resume after power-off — turning the device off mid-playback means starting the story over from the beginning.</li>
<li>No skip forward/backward within a track — stories play start to finish.</li>
</ul>
<p>Both omissions are intentional. Young children don't need seeking functionality, and keeping the interface to just "tap card, listen" reduces confusion.</p>
<h2>Setup</h2>
<p>All 100 RFID cards were pre-programmed once and labeled with numbers. No card learning or reprogramming is needed after initial setup — just hand a child a numbered card and they know which story it triggers.</p>
<h2>Code</h2>
<p>The Arduino firmware is not yet published on GitHub. It's a custom implementation (not Tonuino), written in C++ for the Arduino platform. The code may be uploaded in the future.</p>
</div>
+2 -1
View File
@@ -1,7 +1,8 @@
---
title: "OctoPrint"
section: "smarthome"
parent: "/"
tags: "smarthome"
parent: "/smarthome/"
description: "Remote 3D printer control and camera streaming with OctoPrint on Raspberry Pi."
layout: base.njk
---
-4
View File
@@ -1,4 +0,0 @@
User-agent: *
Allow: /
Sitemap: https://smarthome.moonweb.org/sitemap.xml
-5
View File
@@ -1,5 +0,0 @@
---
permalink: /sitemap.xml
eleventyExcludeFromCollections: true
---
{% include "sitemap.njk" %}
+2 -1
View File
@@ -1,7 +1,8 @@
---
title: "Tasmota & Energy Monitoring"
section: "smarthome"
parent: "/"
tags: "smarthome"
parent: "/smarthome/"
description: "Smart plugs and power monitoring across the flat with daily usage statistics."
layout: base.njk
---
+2 -1
View File
@@ -1,7 +1,8 @@
---
title: "YouTube Archiving: TubeArchivist & TubeSync"
section: "smarthome"
parent: "/"
tags: "smarthome"
parent: "/smarthome/"
description: "Self-hosted YouTube archiving and media management running on the Synology NAS, comparing TubeArchivist and TubeSync."
layout: base.njk
---
+7 -7
View File
@@ -26,11 +26,11 @@
<span class="site-title">{{ siteTitle | default(section + ".moonweb.org") }}</span>
</div>
<nav class="site-switcher">
<a href="https://hub.moonweb.org">hub</a>
<a href="https://infra.moonweb.org">infra</a>
<a href="https://smarthome.moonweb.org">smarthome</a>
<a href="https://code.moonweb.org">code</a>
<a href="https://retro.moonweb.org">retro</a>
<a href="https://www.moonweb.org/">hub</a>
<a href="https://www.moonweb.org/infra/">infra</a>
<a href="https://www.moonweb.org/smarthome/">smarthome</a>
<a href="https://www.moonweb.org/code/">code</a>
<a href="https://www.moonweb.org/retro/">retro</a>
<a href="https://stefankoelle.de">cv</a>
</nav>
</header>
@@ -41,8 +41,8 @@
</main>
<footer class="site-footer">
<p>Part of the <a href="https://hub.moonweb.org">moonweb.org</a> homelab.</p>
<p><a href="https://hub.moonweb.org/impressum/">Legal Notice &amp; Privacy Policy</a></p>
<p>Part of the <a href="https://www.moonweb.org/">moonweb.org</a> homelab.</p>
<p><a href="https://www.moonweb.org/impressum/">Legal Notice &amp; Privacy Policy</a></p>
</footer>
</body>
</html>
+1 -1
View File
@@ -43,7 +43,7 @@
"url": "{{ site.url }}",
"jobTitle": "Software Developer",
"sameAs": [
"https://hub.moonweb.org",
"https://www.moonweb.org/",
"https://www.linkedin.com/in/stefankoelle/",
"https://github.com/skoelle",
"https://www.xing.com/profile/Stefan_Koelle"
+18 -18
View File
@@ -25,30 +25,30 @@ layout: stefankoelle.njk
<h3>moonweb.org (since 2020)</h3>
<p>My personal corner on the internet, documenting the homelab, smart home setups, infrastructure experiments, retro hardware, and the code that ties it all together. Everything runs on a homelab that has been evolving since the mid-90s.</p>
<ul>
<li><b><a href="https://hub.moonweb.org">hub.moonweb.org</a></b>: Overview of all sites and projects.</li>
<li><b><a href="https://infra.moonweb.org">infra.moonweb.org</a></b>: Proxmox cluster, Synology NAS, Docker containers, networking, and backup strategies.</li>
<li><b><a href="https://smarthome.moonweb.org">smarthome.moonweb.org</a></b>: Home Assistant, MQTT sensors, AirPlay audio, energy monitoring, and ESP32 dashboards.</li>
<li><b><a href="https://code.moonweb.org">code.moonweb.org</a></b>: Curated catalog of public GitHub projects, sorted by topic.</li>
<li><b><a href="https://retro.moonweb.org">retro.moonweb.org</a></b>: Atari ST, Amiga, DOS, MiSTer FPGA, and the retro computing corner.</li>
<li><b><a href="https://www.moonweb.org/">hub.moonweb.org</a></b>: Overview of all sites and projects.</li>
<li><b><a href="https://www.moonweb.org/infra/">infra.moonweb.org</a></b>: Proxmox cluster, Synology NAS, Docker containers, networking, and backup strategies.</li>
<li><b><a href="https://www.moonweb.org/smarthome/">smarthome.moonweb.org</a></b>: Home Assistant, MQTT sensors, AirPlay audio, energy monitoring, and ESP32 dashboards.</li>
<li><b><a href="https://www.moonweb.org/code/">code.moonweb.org</a></b>: Curated catalog of public GitHub projects, sorted by topic.</li>
<li><b><a href="https://www.moonweb.org/retro/">retro.moonweb.org</a></b>: Atari ST, Amiga, DOS, MiSTer FPGA, and the retro computing corner.</li>
</ul>
<h3>Home Network Projects (2025-2026)</h3>
<ul>
<li><b>Docker Host (Proxmox PVE)</b><br>Virtualized Docker host with Debian 12.2 running containerized services including Portainer, Prometheus, Grafana, Gitea, monitoring exporters, and more. Full architecture documented on <a href="https://infra.moonweb.org/docker/">infra.moonweb.org</a>.</li>
<li><b>Docker Host (Proxmox PVE)</b><br>Virtualized Docker host with Debian 12.2 running containerized services including Portainer, Prometheus, Grafana, Gitea, monitoring exporters, and more. Full architecture documented on <a href="https://www.moonweb.org/infra//docker/">infra.moonweb.org</a>.</li>
<li><b>InfluxDB Time-Series Integration</b><br>Deployed InfluxDB 2.x for sensor data collection from Tasmota smart plugs and HomematicIP thermostats, replacing older MySQL-based storage with modern time-series architecture.</li>
<li><b>LED Matrix ESP32 InfluxDB Integration</b><br>Data persistence layer for <a href="/ledmatrix/">LED Matrix ESP32</a> sensor systems (DHT22 temperature/humidity) with InfluxDB 2.x backend. Automated collection at 60-second intervals for historical trend analysis.</li>
<li><b>Air Quality Dashboard</b><br>Dynamic HTML5 dashboard consuming real-time sensor data from InfluxDB via Flux query API. Displays temperature and humidity across all network locations, accessible via nginx reverse proxy. See also <a href="https://smarthome.moonweb.org/air-quality/">smarthome.moonweb.org</a>.</li>
<li><b>Tasmota Energy Monitoring</b><br>Smart plug energy tracking with 8 Tasmota-enabled devices, integrating MQTT data collection and visualization. See also <a href="https://smarthome.moonweb.org/tasmota-energy/">smarthome.moonweb.org</a>.</li>
<li><b>Network Hardware & Monitoring</b><br>Four Zyxel GS1200-8HP managed switches in the server room, living room, and study, plus a <a href="https://infra.moonweb.org/openwrt/">Multi-WAN OpenWrt router</a> for failover and VLAN segmentation. All switches feed into <a href="https://infra.moonweb.org/monitoring/">Prometheus monitoring</a> via SNMP exporters, and IoT devices are isolated in VLAN 189.</li>
<li><b>Air Quality Dashboard</b><br>Dynamic HTML5 dashboard consuming real-time sensor data from InfluxDB via Flux query API. Displays temperature and humidity across all network locations, accessible via nginx reverse proxy. See also <a href="https://www.moonweb.org/smarthome//air-quality/">smarthome.moonweb.org</a>.</li>
<li><b>Tasmota Energy Monitoring</b><br>Smart plug energy tracking with 8 Tasmota-enabled devices, integrating MQTT data collection and visualization. See also <a href="https://www.moonweb.org/smarthome//tasmota-energy/">smarthome.moonweb.org</a>.</li>
<li><b>Network Hardware & Monitoring</b><br>Four Zyxel GS1200-8HP managed switches in the server room, living room, and study, plus a <a href="https://www.moonweb.org/infra//openwrt/">Multi-WAN OpenWrt router</a> for failover and VLAN segmentation. All switches feed into <a href="https://www.moonweb.org/infra//monitoring/">Prometheus monitoring</a> via SNMP exporters, and IoT devices are isolated in VLAN 189.</li>
</ul>
<h3>Retro Computing (2022/2023)</h3>
<ul>
<li>MiSTer and MiST FPGA Systems for preservation (see also <a href="https://retro.moonweb.org/mist-mister-fpga/">retro.moonweb.org</a>).</li>
<li><a href="https://retro.moonweb.org/dos-486-tower/">DOS System</a>, <a href="https://retro.moonweb.org/the-a1200/">Amiga System</a>, and <a href="https://retro.moonweb.org/atari-mega-st-fleet/">Atari ST System</a> with the environment from the early 90s.</li>
<li>MiSTer and MiST FPGA Systems for preservation (see also <a href="https://www.moonweb.org/retro//mist-mister-fpga/">retro.moonweb.org</a>).</li>
<li><a href="https://www.moonweb.org/retro//dos-486-tower/">DOS System</a>, <a href="https://www.moonweb.org/retro//the-a1200/">Amiga System</a>, and <a href="https://www.moonweb.org/retro//atari-mega-st-fleet/">Atari ST System</a> with the environment from the early 90s.</li>
</ul>
<h3>Upgrade of old <a href="https://retro.moonweb.org/atari-mega-st-fleet/">Atari Mega 4</a> (2021)</h3>
<h3>Upgrade of old <a href="https://www.moonweb.org/retro//atari-mega-st-fleet/">Atari Mega 4</a> (2021)</h3>
<ul>
<li>Gotek Drive</li>
<li>More RAM</li>
@@ -67,10 +67,10 @@ layout: stefankoelle.njk
<h3>Various Docker Systems (since 2016)</h3>
<ul>
<li><b>Monitoring:</b> <a href="https://infra.moonweb.org/monitoring/">Prometheus, Grafana, InfluxDB</a>, cAdvisor, Node Exporter, SNMP exporters</li>
<li><b>Smart Home:</b> Mosquitto MQTT broker, <a href="https://smarthome.moonweb.org/homematic-mqtt/">Home Assistant</a></li>
<li><b>Monitoring:</b> <a href="https://www.moonweb.org/infra//monitoring/">Prometheus, Grafana, InfluxDB</a>, cAdvisor, Node Exporter, SNMP exporters</li>
<li><b>Smart Home:</b> Mosquitto MQTT broker, <a href="https://www.moonweb.org/smarthome//homematic-mqtt/">Home Assistant</a></li>
<li><b>Infrastructure:</b> Portainer, Nginx Proxy Manager, Authelia (SSO), Watchtower, Healthchecks</li>
<li><b>Applications:</b> Gitea (GitHub mirror), <a href="https://infra.moonweb.org/docker/">Stirling PDF</a>, <a href="https://smarthome.moonweb.org/tubearchivist/">TubeArchivist</a>, <a href="https://smarthome.moonweb.org/tubearchivist/">TubeSync</a>, <a href="https://infra.moonweb.org/docker/">SearXNG</a></li>
<li><b>Applications:</b> Gitea (GitHub mirror), <a href="https://www.moonweb.org/infra//docker/">Stirling PDF</a>, <a href="https://www.moonweb.org/smarthome//tubearchivist/">TubeArchivist</a>, <a href="https://www.moonweb.org/smarthome//tubearchivist/">TubeSync</a>, <a href="https://www.moonweb.org/infra//docker/">SearXNG</a></li>
</ul>
<h3>Walk-on Song (2019)</h3>
@@ -80,7 +80,7 @@ layout: stefankoelle.njk
<li>MP3 Module for playback on attached speaker.</li>
</ul>
<h3>Kids RFID MP3 Player (2018)</h3>
<h3><a href="https://www.moonweb.org/smarthome//kids-rfid-player/">Kids RFID MP3 Player</a> (2018)</h3>
<ul>
<li>Interactive audio playback system utilizing RFID technology.</li>
<li>Powered by an Arduino Nano for compact and efficient operation.</li>
@@ -146,7 +146,7 @@ layout: stefankoelle.njk
<h3>AI-Assisted Development</h3>
<p>I've fully embraced the AI-assisted development paradigm since early 2025. At work, I've taught <strong>Claude Code</strong> a workflow for handling Jira stories end-to-end: it evaluates requirements, provides feedback to the PO, and creates a detailed implementation plan. From there, it's mostly about supervising the plan while the code generation runs largely autonomously. For code review, we use a second AI (e.g., Codex) as a quality gate to catch issues that might slip through. In migration projects, I define the exact plan upfront and Claude Code executes it with increasing independence. The output gain is massive, delivering in a fraction of the time.</p>
<p>Privately, I work with <strong><a href="https://opencode.ai">OpenCode</a></strong> paired with OpenCode Zen, Merge Gateway, and OpenRouter, mostly for Python-based home lab and automation projects. See my <a href="https://code.moonweb.org">GitHub projects</a> for examples and the <a href="https://infra.moonweb.org/dev-environment/">Dev VM setup</a> for the infrastructure behind this workflow.</p>
<p>Privately, I work with <strong><a href="https://opencode.ai">OpenCode</a></strong> paired with OpenCode Zen, Merge Gateway, and OpenRouter, mostly for Python-based home lab and automation projects. See my <a href="https://www.moonweb.org/code/">GitHub projects</a> for examples and the <a href="https://www.moonweb.org/infra//dev-environment/">Dev VM setup</a> for the infrastructure behind this workflow.</p>
<h3>.NET Core</h3>
<ul>
@@ -243,5 +243,5 @@ layout: stefankoelle.njk
<p>Stefan Kölle<br>Neumarkter Str. 86c<br>81673 München</p>
<p>Phone/Fax: +49-(89)-20006547<br>E-Mail: <a id="email-impressum" href="#">[email protected]</a></p>
<p>Responsible for content according to § 18 (2) MStV: Stefan Kölle, address as above.</p>
<p>Full legal notice & privacy policy for this website and the moonweb.org network: <a href="https://hub.moonweb.org/impressum">hub.moonweb.org/impressum</a>
<p>Full legal notice & privacy policy for this website and the moonweb.org network: <a href="https://www.moonweb.org//impressum/">hub.moonweb.org/impressum</a>
</div>
+3 -3
View File
@@ -134,14 +134,14 @@ description: "WiFi-controlled LED display solution with REST API, DHT22 sensor,
<div class="feature-icon">📊</div>
<div class="feature-text">
<h3>Sensor Readings</h3>
<p>Live temperature and humidity from the on-board DHT22 sensor, stored in <a href="https://smarthome.moonweb.org/homematic-mqtt/">InfluxDB via MQTT</a></p>
<p>Live temperature and humidity from the on-board DHT22 sensor, stored in <a href="https://www.moonweb.org/smarthome//homematic-mqtt/">InfluxDB via MQTT</a></p>
</div>
</div>
<div class="feature">
<div class="feature-icon">🔔</div>
<div class="feature-text">
<h3>Status Messages</h3>
<p>Notifications like "washing machine done" triggered via <a href="https://smarthome.moonweb.org/tasmota-energy/">MQTT and Tasmota energy monitoring</a></p>
<p>Notifications like "washing machine done" triggered via <a href="https://www.moonweb.org/smarthome//tasmota-energy/">MQTT and Tasmota energy monitoring</a></p>
</div>
</div>
<div class="feature">
@@ -575,7 +575,7 @@ ESP32 will be restarted (with display shutdown)</pre>
</div>
<div class="footer-bottom">
<p>&copy; 2026 <a href="https://stefankoelle.de/">Stefan Kölle</a>. LED Matrix WebServer v1.08.</p>
<p><a href="https://hub.moonweb.org/impressum/">Legal Notice &amp; Privacy Policy</a></p>
<p><a href="https://www.moonweb.org//impressum/">Legal Notice &amp; Privacy Policy</a></p>
</div>
</footer>
+6
View File
@@ -17,6 +17,12 @@ echo "==> Installing WeasyPrint..."
pip install -q -r "$REQUIREMENTS"
echo "==> Building stefankoelle with Eleventy..."
# Temporarily hide .eleventyignore so the standalone stefankoelle build works
IGNORE_FILE="$PROJECT_DIR/.eleventyignore"
if [ -f "$IGNORE_FILE" ]; then
mv "$IGNORE_FILE" "$IGNORE_FILE.bak"
trap 'mv "$IGNORE_FILE.bak" "$IGNORE_FILE"' EXIT
fi
npx @11ty/eleventy --config="$PROJECT_DIR/stefankoelle/eleventy.config.js"
echo "==> Generating PDF..."
Binary file not shown.
+38
View File
@@ -0,0 +1,38 @@
const markdownIt = require("markdown-it");
const md = new markdownIt({ html: true, breaks: true });
module.exports = function (eleventyConfig) {
eleventyConfig.addPassthroughCopy({ "src/assets": "/timecapsule/assets" });
eleventyConfig.addPassthroughCopy({ "src/.htaccess": "/timecapsule/.htaccess" });
eleventyConfig.setLibrary("md", md);
eleventyConfig.addFilter("toIsoDate", function(dateStr) {
var parts = String(dateStr).split("/");
if (parts.length === 3) {
var m = parts[0].padStart(2, "0");
var d = parts[1].padStart(2, "0");
var y = parts[2].padStart(4, "0");
return y + "-" + m + "-" + d;
}
return dateStr;
});
eleventyConfig.addPairedShortcode("pseudonymBox", function (content, title) {
const body = md.render(content || "");
return `<p align="right">
<table border="0" cellspacing="1" cellpadding="3" width="350" bgcolor="#555544" class="pseudonym-box">
<tr><td><font color="#CCAA66"><b>${title}</b></font></td></tr>
<tr><td bgcolor="#FFFFFF">${body}</td></tr>
</table>
</p>`;
});
return {
dir: { input: "src", includes: "_includes", data: "_data", output: "_site" },
markdownTemplateEngine: "njk",
htmlTemplateEngine: "njk",
dataTemplateEngine: "njk",
};
};
+2505
View File
File diff suppressed because it is too large Load Diff
+14
View File
@@ -0,0 +1,14 @@
{
"name": "moonweb-timecapsule",
"version": "1.0.0",
"private": true,
"description": "Timecapsule: 2000s retro moonweb.org website",
"scripts": {
"dev": "npx @11ty/eleventy --serve --port=8087",
"build": "npx @11ty/eleventy"
},
"dependencies": {
"@11ty/eleventy": "^2.0.1",
"markdown-it": "^14.1.0"
}
}
+3
View File
@@ -0,0 +1,3 @@
RewriteEngine On
RewriteBase /
ErrorDocument 404 /404/index.html
+69
View File
@@ -0,0 +1,69 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>moonweb.org - Page Not Found</title>
<meta name="description" content="The page you are looking for does not exist on moonweb.org.">
<meta property="og:title" content="moonweb.org - Page Not Found">
<meta property="og:description" content="The page you are looking for does not exist on moonweb.org.">
<meta property="og:type" content="website">
<meta property="og:url" content="https://www.moonweb.org/timecapsule/404/">
<meta property="og:site_name" content="moonweb.org">
<meta name="twitter:card" content="summary">
<meta name="twitter:title" content="moonweb.org - Page Not Found">
<meta name="twitter:description" content="The page you are looking for does not exist on moonweb.org.">
<link rel="stylesheet" type="text/css" href="/timecapsule/assets/css/moonweb.css">
<link rel="icon" href="/timecapsule/assets/images/favicon.ico">
</head>
<body background="/timecapsule/assets/images/background.gif" topmargin="0" leftmargin="0" marginwidth="0" marginheight="0">
<div class="mobile-header">
<img src="/timecapsule/assets/images/logomobile.gif" alt="M O O N W E B" class="mobile-logo">
</div>
<table border="0" cellpadding="0" cellspacing="0" width="750" height="100%" class="layout-main"><tr><td valign="top" class="sidebar-left">
<table border="0" cellpadding="0" cellspacing="0" width="108">
<tr><td>
<img src="/timecapsule/assets/pageImage/beginning.jpg" alt="beginning" height="162" width="108" border="0">
</td></tr>
</table>
</td><td valign="top" class="content-area">
<table border="0" cellpadding="7" cellspacing="0" width="492"><tr><td>
<h1>Page Not Found</h1>
<p>The page you are looking for does not exist on moonweb.org.</p>
<p>It may have been moved, renamed, or removed.</p>
<p><a href="/timecapsule/">Go to the beginning</a></p>
</td></tr></table>
</td><td valign="top" class="sidebar-right">
<table border="0" cellpadding="0" cellspacing="0" width="150">
<tr height="20"><td>&nbsp;</td></tr>
<tr height="295"><td>
<table border="0" cellpadding="0" cellspacing="0"><tr>
<td width="130" valign="top">
</td>
<td width="60"><img src="/timecapsule/assets/images/logo.gif" height="295" width="61" alt="M O O N W E B"></td>
</tr></table>
</td></tr>
</table>
</td></tr>
<tr><td align="center"></td><td colspan="2">
<table border="0" cellpadding="10" cellspacing="0" width="610"><tr><td>
<hr>
<p class="footer">
<a href="/timecapsule/legal-notice/">Legal Notice</a> - (c) 2001-2026 moonweb.org
</p>
</td></tr></table>
</td></tr>
</table>
</body>
</html>
+18
View File
@@ -0,0 +1,18 @@
[
[
"beginning"
],
[
"projects",
"products",
"acoustics"
],
[
"location",
"background",
"partners"
],
[
"contact"
]
]
+4
View File
@@ -0,0 +1,4 @@
{
"domain": "https://www.moonweb.org",
"contactEmail": "webmaster@moonweb.org"
}

Some files were not shown because too many files have changed in this diff Show More