mirror of
https://github.com/skoelle/moonweb-site.git
synced 2026-09-17 17:00:25 +00:00
Compare commits
31
Commits
a97c3d3a89
...
a3829f1c48
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a3829f1c48 | ||
|
|
b202efaefc | ||
|
|
2e9f9a2395 | ||
|
|
8e54383b5a | ||
|
|
19f492b7e0 | ||
|
|
a5b4a20895 | ||
|
|
9ffbadb3db | ||
|
|
01097ff89a | ||
|
|
79a47ceeaa | ||
|
|
26ec966b51 | ||
|
|
09a170d5bf | ||
|
|
4ebd767cd1 | ||
|
|
9a1d1ebbfe | ||
|
|
7843d0061a | ||
|
|
ecbda171a6 | ||
|
|
5b70c7df2f | ||
|
|
177142fcf5 | ||
|
|
9c736532e1 | ||
|
|
401868cde5 | ||
|
|
dbb791b1a6 | ||
|
|
c77ad91e0e | ||
|
|
10b05736c8 | ||
|
|
84cd3f8562 | ||
|
|
9cda842250 | ||
|
|
37f7d8f1fb | ||
|
|
c76de3ec36 | ||
|
|
7524210768 | ||
|
|
41a9236ca7 | ||
|
|
7c3e29d282 | ||
|
|
2b997c11d0 | ||
|
|
466df3bd04 |
@@ -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
|
||||||
@@ -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/moonweb -maxdepth 2 -type f | sort | head -40
|
||||||
|
echo "..."
|
||||||
|
echo "Total files:"
|
||||||
|
find dist/moonweb -type f | wc -l
|
||||||
|
|
||||||
|
# Verify key directories exist
|
||||||
|
for dir in "" infra smarthome code retro timecapsule; do
|
||||||
|
if [ -n "$dir" ] && [ ! -d "dist/moonweb/$dir" ]; then
|
||||||
|
echo "Error: dist/moonweb/$dir not found"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
- name: Upload build artifact
|
||||||
|
uses: actions/upload-artifact@v7
|
||||||
|
with:
|
||||||
|
name: dist-moonweb
|
||||||
|
path: dist/moonweb
|
||||||
|
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/moonweb
|
||||||
|
|
||||||
|
- name: Deploy via SFTP
|
||||||
|
uses: wlixcc/SFTP-Deploy-Action@v1.2.6
|
||||||
|
with:
|
||||||
|
local_path: './dist/moonweb/*'
|
||||||
|
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 job 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
|
||||||
@@ -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)
|
||||||
@@ -15,3 +15,6 @@ dist/
|
|||||||
.DS_Store
|
.DS_Store
|
||||||
*.swp
|
*.swp
|
||||||
*.swo
|
*.swo
|
||||||
|
timecapsule/_site/
|
||||||
|
timecapsule/node_modules/
|
||||||
|
.tmp/
|
||||||
|
|||||||
@@ -1,18 +1,19 @@
|
|||||||
# AGENTS.md — moonweb-site
|
# 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 |
|
| Site | URL | Zweck | Status |
|
||||||
|------|--------|-------|--------|
|
|------|-----|-------|--------|
|
||||||
| hub | hub.moonweb.org | Zentrale Indexseite, verlinkt alles | Fertig |
|
| hub | www.moonweb.org/ | Zentrale Indexseite, verlinkt alles | Fertig |
|
||||||
| infra | infra.moonweb.org | Infrastruktur-Übersicht (Proxmox, Synology, Netzwerk) | Übersicht + 3 Detailseiten |
|
| infra | www.moonweb.org/infra/ | Infrastruktur-Uebersicht (Proxmox, Synology, Netzwerk) | Uebersicht + 8 Detailseiten |
|
||||||
| smarthome | smarthome.moonweb.org | Smart Home Projekte und Dashboards | Übersicht + 6 Detailseiten |
|
| smarthome | www.moonweb.org/smarthome/ | Smart Home Projekte und Dashboards | Uebersicht + 9 Detailseiten |
|
||||||
| code | code.moonweb.org | GitHub-Projekte (aggregiert via .moonweb.yml) | Fertig (14 Repos) |
|
| code | www.moonweb.org/code/ | GitHub-Projekte (aggregiert via .moonweb.yml) | Fertig |
|
||||||
| retro | retro.moonweb.org | Physische Retro-Hardware | Nur Übersicht (WIP) |
|
| 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)
|
### Externe Sites (SFTP-Deployment)
|
||||||
|
|
||||||
@@ -22,19 +23,22 @@ Monorepo für 5 statische Websites unter moonweb.org + stefankoelle.de, basieren
|
|||||||
|
|
||||||
### Andere Sites (nicht im Monorepo)
|
### Andere Sites (nicht im Monorepo)
|
||||||
|
|
||||||
- www.moonweb.org — 2000er Internet-Zeitkapsel
|
|
||||||
- 28k8.moonweb.org — 90er BBS/Scene-Archiv
|
- 28k8.moonweb.org — 90er BBS/Scene-Archiv
|
||||||
|
- buildbroken.moonweb.org — .NET Open Space Blog Archiv
|
||||||
|
|
||||||
## Dateistruktur
|
## Dateistruktur
|
||||||
|
|
||||||
```
|
```
|
||||||
moonweb-site/
|
moonweb-site/
|
||||||
├── hub/ # Eleventy-Config + index.njk
|
├── hub/ # Index + Redirects (.htm) + impressum.njk
|
||||||
├── infra/ # Eleventy-Config + index.njk + 3 Subseiten
|
├── infra/ # Index.njk + 8 Subseiten
|
||||||
├── smarthome/ # Eleventy-Config + index.njk + 6 Subseiten
|
├── smarthome/ # Index.njk + 9 Subseiten
|
||||||
├── code/ # Eleventy-Config + index.njk
|
├── code/ # Index.njk
|
||||||
│ └── _data/repos.json
|
├── retro/ # Index.njk + 13 Subseiten
|
||||||
├── retro/ # Eleventy-Config + index.njk
|
├── timecapsule/ # Eleventy 2.x (eigene Config)
|
||||||
|
│ ├── eleventy.config.js
|
||||||
|
│ ├── package.json
|
||||||
|
│ └── src/ # 2001er Retro-Content
|
||||||
├── stefankoelle/ # Eleventy-Config + Onepager
|
├── stefankoelle/ # Eleventy-Config + Onepager
|
||||||
│ ├── eleventy.config.js
|
│ ├── eleventy.config.js
|
||||||
│ ├── _includes/
|
│ ├── _includes/
|
||||||
@@ -48,14 +52,20 @@ moonweb-site/
|
|||||||
├── shared/ # Gemeinsame Komponenten
|
├── shared/ # Gemeinsame Komponenten
|
||||||
│ ├── _includes/
|
│ ├── _includes/
|
||||||
│ │ ├── base.njk # Basis-Layout (Header, Site-Switcher, Footer)
|
│ │ ├── base.njk # Basis-Layout (Header, Site-Switcher, Footer)
|
||||||
│ │ └── card-grid.njk # Card-Grid Template
|
│ │ ├── card-grid.njk # Card-Grid Template
|
||||||
│ ├── base.css
|
│ │ └── sitemap.njk # Zentrale Sitemap
|
||||||
│ └── theme-*.css
|
│ ├── base.css # Shared CSS (Layout, Cards, Typografie)
|
||||||
|
│ └── favicon/ # Favicon-SVGs pro Section
|
||||||
|
├── _data/
|
||||||
|
│ └── repos.json # GitHub-Aggregator Output
|
||||||
├── scripts/
|
├── scripts/
|
||||||
│ └── github-aggregator/
|
│ ├── github-aggregator/ # Python: liest .moonweb.yml -> repos.json
|
||||||
|
│ └── cloudflare/ # Redirect-Setup fuer alte Subdomains
|
||||||
├── .github/workflows/
|
├── .github/workflows/
|
||||||
│ ├── build-deploy.yml # CI/CD: Cloudflare Pages (5 Sites)
|
│ ├── build-deploy-moonweb.yml # CI/CD: IONOS SFTP (www.moonweb.org)
|
||||||
│ └── deploy-stefankoelle.yml # CI/CD: IONOS SFTP (stefankoelle.de)
|
│ └── deploy-stefankoelle.yml # CI/CD: IONOS SFTP (stefankoelle.de)
|
||||||
|
├── eleventy.config.js # Zentrale Eleventy-Config (alle moonweb Sites)
|
||||||
|
├── .eleventyignore # Schliesst stefankoelle/, timecapsule/ aus
|
||||||
├── DESIGN.md
|
├── DESIGN.md
|
||||||
├── SPEC.md
|
├── SPEC.md
|
||||||
├── PLAN.md
|
├── PLAN.md
|
||||||
@@ -66,10 +76,11 @@ moonweb-site/
|
|||||||
|
|
||||||
## Technischer Stack
|
## Technischer Stack
|
||||||
|
|
||||||
- **SSG:** Eleventy (11ty) v3.1.6
|
- **SSG:** Eleventy (11ty) v3.1.6 (hub, infra, smarthome, code, retro) - zentrale Config
|
||||||
|
- **SSG:** Eleventy v2.0.1 (timecapsule - retro 2001 Design)
|
||||||
- **Templates:** Nunjucks (.njk)
|
- **Templates:** Nunjucks (.njk)
|
||||||
- **CSS:** Variables-basiert mit Accent-Farben pro Site
|
- **CSS:** Variables-basiert mit Accent-Farben (inlined in base.njk)
|
||||||
- **Deploy (moonweb):** Cloudflare Pages (5 separate Projects)
|
- **Deploy (moonweb):** IONOS SFTP
|
||||||
- **Deploy (stefankoelle):** IONOS SFTP
|
- **Deploy (stefankoelle):** IONOS SFTP
|
||||||
- **CI/CD:** GitHub Actions (2 Workflows)
|
- **CI/CD:** GitHub Actions (2 Workflows)
|
||||||
|
|
||||||
@@ -77,32 +88,51 @@ moonweb-site/
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
npm install
|
npm install
|
||||||
npm run prebuild # Pre-Build Tasks (CV PDF generieren)
|
cd timecapsule && npm install # Timecapsule Dependencies
|
||||||
npm run dev:hub # localhost:8081
|
npm run prebuild:cv # Pre-Build Tasks (CV PDF generieren)
|
||||||
npm run dev:infra # localhost:8082
|
npm run dev # Moonweb Sites (localhost:8081)
|
||||||
npm run dev:smarthome # localhost:8083
|
npm run dev:stefankoelle # stefankoelle.de (localhost:8086)
|
||||||
npm run dev:code # localhost:8084
|
npm run dev:timecapsule # Timecapsule (localhost:8087)
|
||||||
npm run dev:retro # localhost:8085
|
npm run build # Alle moonweb Sites
|
||||||
npm run dev:stefankoelle # localhost:8086
|
npm run build:stefankoelle # Nur stefankoelle
|
||||||
npm run build # Alle Sites bauen
|
npm run build:timecapsule # Nur timecapsule
|
||||||
|
npm run build:moonweb # Moonweb + Timecapsule (fuer Deployment)
|
||||||
```
|
```
|
||||||
|
|
||||||
## Design-Prinzipien
|
## Design-Prinzipien
|
||||||
|
|
||||||
1. **Header konsistent** — Identischer Site-Switcher auf allen Home-Sites
|
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
|
3. **Accent-Farben:** hub=#3b6ea5, infra=#99333A, smarthome=#1f8a8a, code=#3E5098, retro=#8a6d3b (inlined in base.njk)
|
||||||
4. **Englisch** — Alle Sites komplett auf Englisch
|
4. **Englisch** — Alle Sites komplett auf Englisch
|
||||||
5. **Keine Analytics** — Keine Tracking-Tools
|
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
|
## stefankoelle.de
|
||||||
|
|
||||||
- Eigene Eleventy-Config (nicht shared base.njk)
|
- Eigene Eleventy-Config (nicht shared base.njk)
|
||||||
- Eigenes CSS-Design (nicht moonweb design system)
|
- Eigenes CSS-Design (nicht moonweb design system)
|
||||||
- Onepager mit Anchor-Links (bleibt so)
|
- Onepager mit Anchor-Links (bleibt so)
|
||||||
- Deploy via SFTP auf IONOS `/deploy/stefankoelle/`
|
- Deploy via SFTP auf IONOS `/websites/stefankoelle/`
|
||||||
- CV-Partial zentral pflegbar (einmal aendern → Web + PDF aktualisieren)
|
- CV-Partial zentral pflegbar (einmal aendern -> Web + PDF aktualisieren)
|
||||||
- LED Matrix als eigene Seite unter stefankoelle.de/ledmatrix/
|
- LED Matrix als eigene Seite unter stefankoelle.de/ledmatrix/
|
||||||
|
|
||||||
### CV PDF Generierung
|
### CV PDF Generierung
|
||||||
@@ -111,12 +141,12 @@ Das CV-PDF wird via WeasyPrint generiert:
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
npm run pdf:cv # Einzelnes PDF generieren
|
npm run pdf:cv # Einzelnes PDF generieren
|
||||||
npm run prebuild # Alle Pre-Build Tasks (inkl. CV PDF)
|
npm run prebuild:cv # Alle Pre-Build Tasks (inkl. CV PDF)
|
||||||
```
|
```
|
||||||
|
|
||||||
Dateien:
|
Dateien:
|
||||||
- `stefankoelle/pdf/cv-style.css` — WeasyPrint-Stylesheet (A4, Typografie)
|
- `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/cv-print.njk` — Standalone HTML-Template (nur CV-Content)
|
||||||
- `stefankoelle/pdf/cv.pdf` — Generiertes PDF (Output)
|
- `stefankoelle/pdf/cv.pdf` — Generiertes PDF (Output)
|
||||||
|
|
||||||
@@ -124,21 +154,22 @@ Wichtig: Das PDF wird via Eleventy-Passthrough ins Build-Output kopiert (`dist/s
|
|||||||
|
|
||||||
### CSS Cache-Busting
|
### 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/index.njk`: `<link rel="stylesheet" href="/assets/style.css?v=N">`
|
||||||
- `stefankoelle/ledmatrix/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
|
## CI/CD
|
||||||
|
|
||||||
### Cloudflare Pages (moonweb)
|
### IONOS SFTP (www.moonweb.org)
|
||||||
`.github/workflows/build-deploy.yml` baut hub, infra, smarthome, code, retro.
|
`.github/workflows/build-deploy-moonweb.yml` baut alle moonweb Sites (hub, infra, smarthome, code, retro, timecapsule) und deployed per SFTP.
|
||||||
|
|
||||||
Benötigte Secrets:
|
Benötigte Secrets:
|
||||||
- `CLOUDFLARE_API_TOKEN`
|
- `IONOS_SFTP_HOST`
|
||||||
- `CLOUDFLARE_ACCOUNT_ID`
|
- `IONOS_SFTP_USER`
|
||||||
|
- `IONOS_SFTP_PASSWORD`
|
||||||
|
|
||||||
### IONOS SFTP (stefankoelle.de)
|
### IONOS SFTP (stefankoelle.de)
|
||||||
`.github/workflows/deploy-stefankoelle.yml` baut stefankoelle.de und deployed per SFTP.
|
`.github/workflows/deploy-stefankoelle.yml` baut stefankoelle.de und deployed per SFTP.
|
||||||
@@ -150,13 +181,15 @@ Benötigte Secrets:
|
|||||||
|
|
||||||
## Offene Punkte
|
## Offene Punkte
|
||||||
|
|
||||||
- retro/ ist bewusst rudimentär gehalten
|
- retro/ ist bewusst rudimentaer gehalten
|
||||||
- Querverlinkungen stefankoelle.de <-> smarthome (zukuenftig)
|
- Querverlinkungen stefankoelle.de <-> smarthome (zukuenftig)
|
||||||
- Ledmatrix ggf. nach smarthome verschieben (when ready)
|
- Ledmatrix ggf. nach smarthome verschieben (when ready)
|
||||||
|
- Cloudflare Redirects einrichten (nach Deploy)
|
||||||
|
- Google Search Console: Neue Property www.moonweb.org
|
||||||
|
|
||||||
## GitHub Aggregator
|
## GitHub Aggregator
|
||||||
|
|
||||||
Das Skript `scripts/github-aggregator/aggregate.py` liest aus jedem public Repo unter `skoelle` die `.moonweb.yml` und generiert `code/_data/repos.json`.
|
Das Skript `scripts/github-aggregator/aggregate.py` liest aus jedem public Repo unter `skoelle` die `.moonweb.yml` und generiert `_data/repos.json`.
|
||||||
|
|
||||||
### .moonweb.yml Format
|
### .moonweb.yml Format
|
||||||
|
|
||||||
@@ -191,10 +224,10 @@ python3 -m venv .venv
|
|||||||
|
|
||||||
## Text / Stil
|
## Text / Stil
|
||||||
|
|
||||||
- Keine Em-Dashes (—) verwenden, stattdessen umformulieren (Komma, Satzzeichen, neu formulieren)
|
- Keine Em-Dashes verwenden, stattdessen umformulieren (Komma, Satzzeichen, neu formulieren)
|
||||||
|
|
||||||
## Python / pip
|
## Python / pip
|
||||||
|
|
||||||
- Niemals `pip install` direkt ausführen
|
- Niemals `pip install` direkt ausfuehren
|
||||||
- Immer ein virtuelles Umfeld (`.venv`) anlegen und darin arbeiten
|
- Immer ein virtuelles Umfeld (`.venv`) anlegen und darin arbeiten
|
||||||
- `python3 -m venv .venv` im Projektverzeichnis
|
- `python3 -m venv .venv` im Projektverzeichnis
|
||||||
|
|||||||
@@ -1,64 +1,74 @@
|
|||||||
# moonweb.org — Implementation Plan
|
# moonweb.org — Implementation Plan
|
||||||
|
|
||||||
Companion to `SPEC.md`. Defines order of work. All sites (hub, infra, smarthome, code, retro, stefankoelle) launch **simultaneously** — no staged rollout by domain.
|
Companion to `SPEC.md`. Documents the completed migration from separate subdomain sites to a unified `www.moonweb.org` subdirectory structure.
|
||||||
|
|
||||||
## Phase 0 — Repo & tooling setup
|
## Phase 0 — Repo & tooling setup
|
||||||
|
|
||||||
1. Create the `moonweb-site` monorepo (private until first launch, then can stay private since Pages deploys the built output, not the source).
|
1. Created the `moonweb-site` monorepo.
|
||||||
2. Initialize Eleventy project structure per `SPEC.md §10`.
|
2. Initialized Eleventy project structure per `SPEC.md §10`.
|
||||||
3. Set up `shared/layout-base.njk` (header + card-grid) and one `theme-*.css` per accent color (hub, infra, smarthome, code, retro).
|
3. Set up `shared/_includes/base.njk` (header + card-grid) with accent colors inlined.
|
||||||
4. Verify local dev server works per site (`npm run dev:<site>`) before any content work starts.
|
4. Verified local dev server works per site.
|
||||||
|
|
||||||
## Phase 1 — Content migration & authoring (per domain)
|
## Phase 1 — Content migration & authoring (per domain)
|
||||||
|
|
||||||
Work order within this phase is flexible since all domains launch together; suggested sequence based on how self-contained each domain's content is:
|
1. **code** — GitHub aggregator, `_data/repos.json`, overview page grouped by subcategory.
|
||||||
|
2. **smarthome** — overview + detail pages where content exists.
|
||||||
1. **code** — simplest case, no detail pages, no sensitive-data filtering needed.
|
3. **infra** — shallow overview with redaction pass (no IPs, keys, passwords).
|
||||||
- Add `.moonweb.yml` to each GitHub repo (see `SPEC.md §8`).
|
4. **retro** — minimal overview with WIP notices.
|
||||||
- Build the aggregator script, run it once, generate `code/_data/repos.json`.
|
5. **hub** — central index linking all sites, including redirect `.htm` files for old www.moonweb.org paths.
|
||||||
- Build the overview page grouped by subcategory (Automation & Sync, Dev-Tools, Web Apps, Firmware/Hardware, Misc).
|
6. **timecapsule** — integrated from moonweb-www, preserves original 2001 design under `/timecapsule/`.
|
||||||
2. **smarthome** — overview first, detail pages only where content already exists.
|
|
||||||
- Draft the overview: how the smart home is structured (buttons/control, sensors, calendar, weather, media).
|
|
||||||
- For each existing topic with enough material (HomematicIP/MQTT, Tasmota, LED-Matrix *reference only, no migration*, M5Stack/WT32SC01 dashboards, WetterAPI, AirPlay, OctoPi, TubeArchivist), decide case by case: enough content → detail page; too thin → mention in overview only, no placeholder.
|
|
||||||
- Translate source material to English during authoring (one-time AI pass per document, per `SPEC.md §7`).
|
|
||||||
3. **infra** — overview only, redaction pass required.
|
|
||||||
- Draft a shallow, structured overview: Proxmox/pve2, Synology, VLANs, Fritz!Box mesh, SSO, Docker hosting model, plus the "how I work" section (OpenClaw/OpenCode setup, dev workflow).
|
|
||||||
- Apply the redaction rule from `SPEC.md §6` while translating — strip IPs, keys, credentials, internal hostnames from every source document before it becomes public content.
|
|
||||||
- Skip detail pages unless a topic can be described without any sensitive detail.
|
|
||||||
4. **retro** — minimal effort, rudimentary only.
|
|
||||||
- One overview page listing what hardware exists.
|
|
||||||
- Add "work in progress" notices for anything that isn't ready — better an honest short page than none.
|
|
||||||
5. **hub** — build last within this phase since it links to all the others.
|
|
||||||
- One-line description + link per destination (infra, smarthome, code, retro, stefankoelle, and external links to www.moonweb.org, 28k8.moonweb.org).
|
|
||||||
|
|
||||||
## Phase 2 — CI/CD
|
## Phase 2 — CI/CD
|
||||||
|
|
||||||
1. Write `.github/workflows/build-deploy.yml`: builds all five sites in one job (or matrix), then deploys each output folder to its corresponding Cloudflare Pages project via `wrangler pages deploy`.
|
1. GitHub Actions workflow `build-deploy-moonweb.yml`: single Eleventy build for all moonweb sites + timecapsule build, deploy via IONOS SFTP.
|
||||||
2. Create five Cloudflare Pages projects (hub, infra, smarthome, code, retro), each bound to its target custom domain (DNS already on Cloudflare, no extra setup needed).
|
2. GitHub Actions workflow `deploy-stefankoelle.yml`: stefankoelle.de build, deploy via IONOS SFTP.
|
||||||
3. Do one full dry run per site locally before the first real deploy.
|
3. Cloudflare redirect rules for old subdomains (`scripts/cloudflare/`).
|
||||||
|
|
||||||
## Phase 3 — Launch
|
## Phase 3 — Consolidation to www.moonweb.org
|
||||||
|
|
||||||
1. Deploy all five sites simultaneously.
|
All moonweb.org sites now live under `www.moonweb.org` as subdirectories:
|
||||||
2. Point the respective custom domains at their Pages projects.
|
|
||||||
3. Apex domain `moonweb.org` keeps redirecting to `www.moonweb.org` for now (no change in this launch).
|
|
||||||
4. Smoke-test cross-linking: hub → each site, site-switcher on infra/smarthome/code/retro, smarthome → external ledmatrix link, code cards → GitHub links.
|
|
||||||
|
|
||||||
## Phase 4 — Explicitly deferred (not part of this build)
|
| 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 |
|
||||||
|
|
||||||
- Deciding on 28k8.moonweb.org's future (stay separate vs. eventual monorepo inclusion).
|
Cloudflare redirects forward old subdomains:
|
||||||
- Building any Perplexity-backchannel mechanism for reusing published site content in this project.
|
- `hub.moonweb.org/*` → `www.moonweb.org/*`
|
||||||
- Redirecting the apex domain from `www.moonweb.org` to `hub.moonweb.org`.
|
- `infra.moonweb.org/*` → `www.moonweb.org/infra/*`
|
||||||
- Any analytics, "last updated" timestamps, or scheduled/automated content generation beyond the one-time GitHub aggregator run.
|
- `smarthome.moonweb.org/*` → `www.moonweb.org/smarthome/*`
|
||||||
|
- `code.moonweb.org/*` → `www.moonweb.org/code/*`
|
||||||
|
- `retro.moonweb.org/*` → `www.moonweb.org/retro/*`
|
||||||
|
|
||||||
## Definition of done for this build
|
### Technical consolidation
|
||||||
|
|
||||||
- All sites live on their respective platforms:
|
- Single `eleventy.config.js` at root with computed `pathPrefix` per section.
|
||||||
- hub, infra, smarthome, code, retro on Cloudflare Pages under their intended domains.
|
- Shared `base.njk` layout with inlined accent colors (no per-site theme CSS).
|
||||||
- stefankoelle.de on IONOS via SFTP deployment.
|
- Central `_data/repos.json` (moved from `code/_data/`).
|
||||||
- code.moonweb.org reflects the current GitHub repos via the `.moonweb.yml` aggregator, grouped by subcategory.
|
- Central sitemap (`shared/_includes/sitemap.njk`) with all 36 pages.
|
||||||
- smarthome.moonweb.org gives an accurate picture of what's running on the homelab today, with detail pages only where content already existed.
|
- Single `robots.txt` at root.
|
||||||
- infra.moonweb.org describes the stack shallowly with zero sensitive data leaked.
|
- `.eleventyignore` excludes `stefankoelle/` and `timecapsule/` (built separately).
|
||||||
- retro.moonweb.org exists with an honest, minimal overview (WIP notices allowed).
|
- `build-pdf.sh` temporarily renames `.eleventyignore` for stefankoelle build.
|
||||||
- hub.moonweb.org correctly links everything, including the external sites (www.moonweb.org, 28k8.moonweb.org).
|
|
||||||
- stefankoelle.de is a working onepager with CV, Projects, Languages, Impressum, and the LED Matrix documentation sub-page.
|
## Definition of done — Achieved
|
||||||
|
|
||||||
|
- All sites live under `www.moonweb.org` as subdirectories, deployed via IONOS SFTP.
|
||||||
|
- `www.moonweb.org/code/` reflects the current GitHub repos via the `.moonweb.yml` aggregator.
|
||||||
|
- `www.moonweb.org/smarthome/` gives an accurate picture of what's running on the homelab.
|
||||||
|
- `www.moonweb.org/infra/` describes the stack shallowly with zero sensitive data leaked.
|
||||||
|
- `www.moonweb.org/retro/` exists with an honest, minimal overview.
|
||||||
|
- `www.moonweb.org/` correctly links everything.
|
||||||
|
- `stefankoelle.de` is a working onepager with CV, Projects, Languages, Impressum, and LED Matrix documentation.
|
||||||
|
- Old subdomains redirect via Cloudflare to new paths.
|
||||||
|
- Old www.moonweb.org content preserved under `/timecapsule/`.
|
||||||
|
|
||||||
|
## Remaining items
|
||||||
|
|
||||||
|
- Cloudflare redirect rules need activation (script ready, needs `CLOUDFLARE_API_TOKEN` and `CLOUDFLARE_ZONE_ID` secrets, or manual dashboard setup).
|
||||||
|
- Google Search Console: new property `www.moonweb.org` not yet created.
|
||||||
|
- `moonweb-www` repository not yet archived.
|
||||||
|
|||||||
@@ -1,182 +1,157 @@
|
|||||||
# 🌙 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
|
www.moonweb.org/ -> Central index & gateway
|
||||||
infra.moonweb.org → 🏗️ Infrastructure overview (Proxmox, Synology, Docker)
|
www.moonweb.org/infra/ -> Infrastructure overview (Proxmox, Synology, Docker)
|
||||||
smarthome.moonweb.org → 🏡 Smart home projects & dashboards
|
www.moonweb.org/smarthome/ -> Smart home projects & dashboards
|
||||||
code.moonweb.org → 💻 Curated GitHub project catalog
|
www.moonweb.org/code/ -> Curated GitHub project catalog
|
||||||
retro.moonweb.org → 🕹️ Physical retro hardware collection
|
www.moonweb.org/retro/ -> Physical retro hardware collection
|
||||||
stefankoelle.de → 👤 CV, career, personal site (LED Matrix docs)
|
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 │
|
| GitHub Actions CI |
|
||||||
│ ┌───────────┐ ┌────────────┐ ┌─────────────┐ ┌─────────┐ │
|
| +-------------------+ +---------------------+ |
|
||||||
│ │ build:hub │ │build:infra │ │ build:smart │ │ build:… │ │
|
| | build-deploy- | | deploy-stefankoelle | |
|
||||||
│ └─────┬─────┘ └─────┬──────┘ └──────┬──────┘ └────┬────┘ │
|
| | moonweb | | | |
|
||||||
│ │ │ │ │ │
|
| +--------+----------+ +----------+----------+ |
|
||||||
│ ▼ ▼ ▼ ▼ │
|
| | | |
|
||||||
│ dist/hub/ dist/infra/ dist/smarthome/ dist/…/ │
|
| v v |
|
||||||
└────────┬───────────────────────────────────────────┬────────┘
|
| npm run build npm run build:stefankoelle |
|
||||||
│ │
|
| + build:timecapsule | |
|
||||||
▼ ▼
|
| | | |
|
||||||
┌──────────────────┐ ┌─────────────────┐
|
| v v |
|
||||||
│ Cloudflare Pages │ │ IONOS SFTP │
|
| dist/moonweb/ dist/stefankoelle/ |
|
||||||
│ (5 sites) │ │ (stefankoelle) │
|
+-----------+------------------------+-------------------------------+
|
||||||
└────────┬─────────┘ └────────┬────────┘
|
| |
|
||||||
▼ ▼
|
v v
|
||||||
hub / infra / ... stefankoelle.de
|
+------------------+ +------------------+
|
||||||
|
| IONOS SFTP | | IONOS SFTP |
|
||||||
|
| (www.moonweb.org)| | (stefankoelle.de)|
|
||||||
|
+--------+---------+ +--------+---------+
|
||||||
|
v v
|
||||||
|
www.moonweb.org/* stefankoelle.de
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 🛠️ Tech Stack
|
## Tech Stack
|
||||||
|
|
||||||
| Layer | Technology | Why |
|
| 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 |
|
| **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, accent colors inlined in `base.njk` |
|
||||||
| **Fonts** | [Lobster](https://fonts.google.com/specimen/Lobster) (Google Fonts) | Distinctive heading font across all sites |
|
| **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 |
|
| **CI/CD** | [GitHub Actions](https://github.com/features/actions) | Single build job, parallel SFTP deploy |
|
||||||
| **Deploy** | [Cloudflare Workers](https://workers.cloudflare.com/) | Static asset hosting via `wrangler pages deploy`, one worker per site |
|
| **Deploy** | IONOS SFTP | Static hosting via SFTP upload |
|
||||||
| **DNS** | Cloudflare | Already managing DNS — zero additional setup for Pages custom domains |
|
| **DNS** | Cloudflare | DNS management + redirects from old subdomains |
|
||||||
| **GitHub Catalog** | Python aggregator | Reads `.moonweb.yml` from each repo, outputs `repos.json` |
|
| **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
|
## Local Development
|
||||||
|
|
||||||
### 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
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
npm install # install Eleventy + deps
|
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 # moonweb sites (localhost:8081)
|
||||||
npm run dev:infra # http://localhost:8082
|
npm run dev:stefankoelle # stefankoelle.de (localhost:8086)
|
||||||
npm run dev:smarthome # http://localhost:8083
|
npm run dev:timecapsule # timecapsule (localhost:8087)
|
||||||
npm run dev:code # http://localhost:8084
|
|
||||||
npm run dev:retro # http://localhost:8085
|
|
||||||
npm run dev:stefankoelle # http://localhost:8086
|
|
||||||
|
|
||||||
npm run dev # all 6 in parallel
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Each site has its own minimal Eleventy config (`<site>/eleventy.config.js`). Live reload is built in.
|
|
||||||
|
|
||||||
### Build
|
### Build
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
npm run prebuild # pre-build tasks (CV PDF)
|
npm run prebuild:cv # pre-build tasks (CV PDF)
|
||||||
npm run build # builds all 5 → dist/<site>/
|
npm run build # builds all moonweb sites (hub, infra, smarthome, code, retro)
|
||||||
npm run build:hub # build single site
|
npm run build:stefankoelle # builds stefankoelle.de
|
||||||
|
npm run build:timecapsule # builds timecapsule
|
||||||
|
npm run build:moonweb # builds moonweb + timecapsule (for deployment)
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 📦 Project Structure
|
## Project Structure
|
||||||
|
|
||||||
```
|
```
|
||||||
moonweb-site/
|
moonweb-site/
|
||||||
├── hub/ # 🏠 Central index & gateway
|
├── hub/ # Central index + redirect .htm files
|
||||||
├── infra/ # 🏗️ Infra overview + 3 detail pages
|
├── infra/ # Infra overview + 8 detail pages
|
||||||
│ ├── backup-strategy/
|
├── smarthome/ # Smart home overview + 9 detail pages
|
||||||
│ ├── monitoring/
|
├── code/ # GitHub catalog
|
||||||
│ └── dev-environment/
|
├── retro/ # Retro hardware + 13 detail pages
|
||||||
├── smarthome/ # 🏡 Smart home overview + 6 detail pages
|
├── timecapsule/ # 2000s retro design (Eleventy 2.x)
|
||||||
│ ├── homematic-mqtt/
|
|
||||||
│ ├── tasmota-energy/
|
|
||||||
│ ├── balkonpi/
|
|
||||||
│ ├── airplay-audio/
|
|
||||||
│ ├── octoprint/
|
|
||||||
│ └── tubearchivist/
|
|
||||||
├── code/ # 💻 GitHub catalog
|
|
||||||
│ └── _data/repos.json # populated by the aggregator
|
|
||||||
├── retro/ # 🕹️ Retro hardware (WIP)
|
|
||||||
├── stefankoelle/ # 👤 CV, career, personal site
|
|
||||||
│ ├── eleventy.config.js
|
│ ├── eleventy.config.js
|
||||||
│ ├── index.njk # Onepager (CV, Projects, Languages)
|
│ ├── package.json
|
||||||
│ ├── cv-print.njk # CV-only for PDF generation
|
│ └── src/
|
||||||
│ ├── ledmatrix/ # LED Matrix WebServer documentation
|
├── stefankoelle/ # CV, career, personal site
|
||||||
│ └── assets/ # CSS, JS, images, favicons
|
│ ├── eleventy.config.js
|
||||||
├── shared/ # 🔧 Shared components
|
│ ├── index.njk
|
||||||
|
│ ├── cv-print.njk
|
||||||
|
│ ├── ledmatrix/
|
||||||
|
│ └── assets/
|
||||||
|
├── shared/ # Shared components
|
||||||
│ ├── _includes/
|
│ ├── _includes/
|
||||||
│ │ ├── base.njk # base layout (header, site-switcher, footer)
|
│ │ ├── base.njk # base layout (header, site-switcher, footer)
|
||||||
│ │ └── card-grid.njk # card-grid template with emoji support
|
│ │ ├── card-grid.njk # card-grid template
|
||||||
│ ├── base.css # shared CSS (layout, cards, typography)
|
│ │ └── sitemap.njk # central sitemap template
|
||||||
│ └── theme-*.css # accent colors per domain
|
│ ├── base.css # shared CSS
|
||||||
|
│ └── favicon/ # favicon SVGs per section
|
||||||
|
├── _data/
|
||||||
|
│ └── repos.json # populated by the GitHub aggregator
|
||||||
├── scripts/
|
├── scripts/
|
||||||
│ └── github-aggregator/ # 🐍 Python: reads .moonweb.yml → repos.json
|
│ ├── github-aggregator/ # Python: reads .moonweb.yml -> repos.json
|
||||||
│ ├── aggregate.py
|
│ └── cloudflare/ # redirect setup for old subdomains
|
||||||
│ ├── example.moonweb.yml
|
|
||||||
│ └── README.md
|
|
||||||
├── .github/workflows/
|
├── .github/workflows/
|
||||||
│ └── build-deploy.yml # ⚙️ CI/CD: build + deploy to Cloudflare
|
│ ├── build-deploy-moonweb.yml # CI/CD: IONOS SFTP (www.moonweb.org)
|
||||||
├── DESIGN.md # 📋 Initial concept (German)
|
│ └── deploy-stefankoelle.yml # CI/CD: IONOS SFTP (stefankoelle.de)
|
||||||
├── SPEC.md # 📋 Full specification (English, 159 lines)
|
├── eleventy.config.js # single config for all moonweb sites
|
||||||
├── PLAN.md # 📋 Implementation plan
|
├── .eleventyignore # excludes stefankoelle/, timecapsule/
|
||||||
├── TODO.md # 📋 Open items & workflow
|
├── DESIGN.md
|
||||||
└── package.json # npm scripts for dev/build
|
├── SPEC.md
|
||||||
|
├── PLAN.md
|
||||||
|
├── TODO.md
|
||||||
|
└── package.json
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 🔄 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
|
push to main
|
||||||
│
|
|
|
||||||
├── Build (matrix: hub, infra, smarthome, code, retro)
|
+-- Build
|
||||||
│ ├── checkout → setup-node (22) → npm ci
|
| +-- checkout -> setup-node (22) -> npm ci
|
||||||
│ ├── npm run build:<site>
|
| +-- npm run build (all moonweb sites in one Eleventy run)
|
||||||
│ ├── validate dist/<site>/ exists & non-empty
|
| +-- npm run build:timecapsule
|
||||||
│ └── upload artifact (7-day retention)
|
|
|
||||||
│
|
+-- Deploy
|
||||||
└── Deploy (matrix: 5 Cloudflare Workers)
|
+-- SFTP upload to IONOS /websites/moonweb/
|
||||||
├── download artifact
|
|
||||||
├── generate wrangler.toml
|
|
||||||
└── wrangler pages deploy
|
|
||||||
```
|
```
|
||||||
|
|
||||||
**Required secrets:**
|
**Required secrets:**
|
||||||
- `CLOUDFLARE_API_TOKEN` — Workers:Edit permission
|
- `IONOS_SFTP_HOST`
|
||||||
- `CLOUDFLARE_ACCOUNT_ID` — Cloudflare account ID
|
- `IONOS_SFTP_USER`
|
||||||
|
- `IONOS_SFTP_PASSWORD`
|
||||||
|
|
||||||
### IONOS SFTP (stefankoelle.de)
|
### IONOS SFTP (stefankoelle.de)
|
||||||
|
|
||||||
@@ -184,12 +159,12 @@ Defined in `.github/workflows/deploy-stefankoelle.yml`:
|
|||||||
|
|
||||||
```
|
```
|
||||||
push to main (paths: stefankoelle/**)
|
push to main (paths: stefankoelle/**)
|
||||||
│
|
|
|
||||||
├── Build stefankoelle
|
+-- Build stefankoelle
|
||||||
│ └── npm run build:stefankoelle
|
| +-- npm run build:stefankoelle
|
||||||
│
|
|
|
||||||
└── Deploy via SFTP
|
+-- Deploy via SFTP
|
||||||
└── lftp mirror → IONOS /deploy/stefankoelle/
|
+-- SFTP upload to IONOS /websites/stefankoelle/
|
||||||
```
|
```
|
||||||
|
|
||||||
**Required secrets:**
|
**Required secrets:**
|
||||||
@@ -199,7 +174,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:
|
`scripts/github-aggregator/aggregate.py` automatically builds the project catalog:
|
||||||
|
|
||||||
@@ -207,14 +205,14 @@ push to main (paths: stefankoelle/**)
|
|||||||
2. Reads `.moonweb.yml` from each repo root
|
2. Reads `.moonweb.yml` from each repo root
|
||||||
3. Filters for `category: code` entries
|
3. Filters for `category: code` entries
|
||||||
4. Sorts by subcategory + title
|
4. Sorts by subcategory + title
|
||||||
5. Writes combined result to `code/_data/repos.json`
|
5. Writes combined result to `_data/repos.json`
|
||||||
|
|
||||||
### `.moonweb.yml` schema
|
### `.moonweb.yml` schema
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
title: "MVG Departures"
|
title: "MVG Departures"
|
||||||
category: code # code | smarthome | infra
|
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
|
status: active
|
||||||
stack: [Python, FastAPI]
|
stack: [Python, FastAPI]
|
||||||
hosted_on: "Docker Host Debian (PVE)"
|
hosted_on: "Docker Host Debian (PVE)"
|
||||||
@@ -225,46 +223,47 @@ repo_url: "https://github.com/skoelle/mvg-departures"
|
|||||||
### Manual run
|
### Manual run
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
export GITHUB_TOKEN=ghp_xxx
|
python3 -m venv .venv
|
||||||
python scripts/github-aggregator/aggregate.py
|
.venv/bin/pip install pyyaml
|
||||||
|
.venv/bin/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 |
|
| Site | Detail pages? | Rule |
|
||||||
|------|--------------|------|
|
|------|--------------|------|
|
||||||
| smarthome | ✅ Yes | When enough content exists — no placeholder cards |
|
| smarthome | Yes | When enough content exists - no placeholder cards |
|
||||||
| infra | ⚠️ Rarely | Deliberately shallow — sensitive data (IPs, keys, passwords) stripped |
|
| infra | Rarely | Deliberately shallow - sensitive data (IPs, keys, passwords) stripped |
|
||||||
| code | ❌ Never | Overview cards + GitHub links only — no README duplication |
|
| code | Never | Overview cards + GitHub links only - no README duplication |
|
||||||
| retro | 🔨 Minimal | Still WIP — honest minimal overview, no over-investment |
|
| 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.
|
**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 |
|
| File | Purpose |
|
||||||
|------|---------|
|
|------|---------|
|
||||||
| `DESIGN.md` | Initial concept and design decisions (German) |
|
| `DESIGN.md` | Initial concept and design decisions |
|
||||||
| `SPEC.md` | Complete specification — what gets built (English) |
|
| `SPEC.md` | Complete specification - what gets built |
|
||||||
| `PLAN.md` | Phased implementation plan |
|
| `PLAN.md` | Phased implementation plan |
|
||||||
| `TODO.md` | Open items, workflow, and current status |
|
| `TODO.md` | Open items, workflow, and current status |
|
||||||
| `README.md` | This file — project overview for GitHub |
|
| `README.md` | This file - project overview for GitHub |
|
||||||
|
| `AGENTS.md` | AI agent instructions for this codebase |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 📄 License
|
## License
|
||||||
|
|
||||||
[](https://creativecommons.org/licenses/by-nc-sa/4.0/)
|
[](https://creativecommons.org/licenses/by-nc-sa/4.0/)
|
||||||
|
|
||||||
|
|||||||
@@ -1,63 +1,69 @@
|
|||||||
# moonweb.org — Specification
|
# moonweb.org — Specification
|
||||||
|
|
||||||
Status: Concept finalized. This document defines *what* gets built. See `PLAN.md` for *how and in what order*.
|
Status: Migration complete. All sites live under `www.moonweb.org` as subdirectories, deployed via IONOS SFTP.
|
||||||
|
|
||||||
## 1. Purpose
|
## 1. Purpose
|
||||||
|
|
||||||
A personal homelab hub consisting of a central index (`hub`) and several themed static sites, replacing the current unstructured presentation of infrastructure, smart home projects, and GitHub repos. cv (stefankoelle.de) remains external, linked from the hub and site-switcher.
|
A personal homelab hub consisting of a central index (`hub` at root) and several themed static sites, all served under `www.moonweb.org` as subdirectories. The old timecapsule content (2001 design) is preserved under `/timecapsule/`. cv (stefankoelle.de) remains external, linked from the hub and site-switcher.
|
||||||
|
|
||||||
## 2. Sitemap
|
## 2. Sitemap
|
||||||
|
|
||||||
```
|
```
|
||||||
moonweb-site (monorepo)
|
moonweb-site (monorepo)
|
||||||
├── hub/ → hub.moonweb.org Central index & gateway
|
├── hub/ → www.moonweb.org/ Central index & gateway
|
||||||
├── infra/ → infra.moonweb.org System architecture / stack overview
|
├── infra/ → www.moonweb.org/infra/ System architecture / stack overview
|
||||||
├── smarthome/ → smarthome.moonweb.org What the homelab actually runs, and why
|
├── smarthome/ → www.moonweb.org/smarthome/ What the homelab actually runs, and why
|
||||||
├── code/ → code.moonweb.org Curated GitHub catalog
|
├── code/ → www.moonweb.org/code/ Curated GitHub catalog
|
||||||
├── retro/ → retro.moonweb.org Physical retro hardware collection
|
├── retro/ → www.moonweb.org/retro/ Physical retro hardware collection
|
||||||
└── stefankoelle/→ stefankoelle.de CV, career, personal site (SFTP deploy)
|
├── timecapsule/ → www.moonweb.org/timecapsule/ 2001-era internet time capsule
|
||||||
|
└── stefankoelle/→ stefankoelle.de CV, career, personal site (SFTP deploy)
|
||||||
|
|
||||||
outside the monorepo, untouched:
|
outside the monorepo, untouched:
|
||||||
├── www.moonweb.org finished, no overlap (2001-style internet time capsule)
|
├── 28k8.moonweb.org 90s BBS/scene archive
|
||||||
├── 28k8.moonweb.org 90s BBS/scene archive, separate approach, may migrate later (undecided)
|
├── buildbroken.moonweb.org .NET Open Space blog archive
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Apex domain `moonweb.org` currently redirects to `www.moonweb.org`. This stays as-is for now; a later redirect to `hub.moonweb.org` is possible but out of scope for this build.
|
Old subdomain redirects (via Cloudflare):
|
||||||
|
- `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/*`
|
||||||
|
|
||||||
## 3. Domain purposes
|
## 3. Domain purposes
|
||||||
|
|
||||||
| Domain | Purpose | Tone |
|
| Domain | URL | Purpose | Tone |
|
||||||
|---|---|---|
|
|---|---|---|---|
|
||||||
| hub | Gateway, links to everything, one-line description per destination | Minimal |
|
| hub | `/` | Gateway, links to everything, one-line description per destination | Minimal |
|
||||||
| infra | Shallow, structured overview of the stack: Proxmox (PVE + pve2), Synology DS918+, VLANs, Fritz!Box mesh, SSO, plus how I work (OpenCode/OpenClaw dev environment) | Reference, high-level only |
|
| infra | `/infra/` | Shallow, structured overview of the stack: Proxmox, Synology, VLANs, Docker hosting, dev environment | Reference, high-level only |
|
||||||
| smarthome | Why the homelab exists — what's actually running on the homelab as smart home: sensors, automation, calendar/contacts sync, dashboards, media | Project storytelling |
|
| smarthome | `/smarthome/` | Why the homelab exists — sensors, automation, calendar/contacts sync, dashboards, media | Project storytelling |
|
||||||
| code | Curated, sorted GitHub catalog — overview only, always linking out to GitHub | Portfolio |
|
| code | `/code/` | Curated, sorted GitHub catalog — overview only, always linking out to GitHub | Portfolio |
|
||||||
| retro | Physical retro hardware collection (not software/demos — that's 28k8's domain) | Simple, factual |
|
| retro | `/retro/` | Physical retro hardware collection (not software/demos — that's 28k8's domain) | Simple, factual |
|
||||||
|
| timecapsule | `/timecapsule/` | Original www.moonweb.org content from 2001, preserved as-is | Retro 2001 design |
|
||||||
|
|
||||||
## 4. Design system
|
## 4. Design system
|
||||||
|
|
||||||
### 4.1 Header-consistent, content-flexible principle
|
### 4.1 Header-consistent, content-flexible principle
|
||||||
|
|
||||||
- **Header is identical** across infra/smarthome/code/retro: site-switcher (hub · infra · smarthome · code · retro), domain accent color, consistent branding.
|
- **Header is identical** across all sites: site-switcher (home · infra · smarthome · code · retro · cv), section title (`moonweb.org` or `moonweb.org/smarthome`), domain accent color.
|
||||||
- **Overview (index) pages** use a shared card-grid layout (reference: clean card-grid layout with banner header, grouped card sections, sans-serif, generous whitespace, light theme only, no heavy JS).
|
- **Overview (index) pages** use a shared card-grid layout (clean card-grid with banner header, grouped card sections, sans-serif, generous whitespace, light theme only, no heavy JS).
|
||||||
- **Detail/sub-pages** keep the same header but may use a freer layout below it (reference: `/ledmatrix/` on stefankoelle.de today — pin tables, API docs, photos in free layout instead of a rigid card grid).
|
- **Detail/sub-pages** keep the same header but may use a freer layout below it.
|
||||||
|
|
||||||
Note: stefankoelle.de is now part of the monorepo but uses its own independent design — it does not follow this header-consistent principle.
|
Note: stefankoelle.de uses its own independent design — it does not follow this header-consistent principle.
|
||||||
|
|
||||||
### 4.2 Accent colors per domain
|
### 4.2 Accent colors per domain
|
||||||
|
|
||||||
| Domain | Accent |
|
| Domain | Accent | Implementation |
|
||||||
|---|---|
|
|---|---|---|
|
||||||
| hub | Neutral blue |
|
| hub | Neutral blue (#3b6ea5) | Inlined `<style>` in base.njk |
|
||||||
| infra | Grey-blue |
|
| infra | Red (#99333A) | Inlined `<style>` in base.njk |
|
||||||
| smarthome | Teal |
|
| smarthome | Teal (#1f8a8a) | Inlined `<style>` in base.njk |
|
||||||
| code | Violet |
|
| code | Violet (#3E5098) | Inlined `<style>` in base.njk |
|
||||||
| retro | Own accent, still clean card-grid (no 90s styling — that belongs to 28k8) |
|
| retro | Brown (#8a6d3b) | Inlined `<style>` in base.njk |
|
||||||
|
|
||||||
### 4.3 URL convention
|
### 4.3 URL convention
|
||||||
|
|
||||||
`domain/slug/` — lowercase, hyphenated, trailing slash, matching the existing stefankoelle.de pattern (e.g. `/ledmatrix/`) so future detail pages stay consistent even if content later migrates between sites.
|
`/section/slug/` — lowercase, hyphenated, trailing slash. All sites share a single Eleventy build with computed `pathPrefix` per section.
|
||||||
|
|
||||||
## 5. Content depth rules (per domain)
|
## 5. Content depth rules (per domain)
|
||||||
|
|
||||||
@@ -77,20 +83,20 @@ Because infra must stay shallow and public-safe:
|
|||||||
- **Allowed:** architecture level — Proxmox + Synology + Docker host, VLAN concept without concrete internal IP plans, which service types run, which tools are used.
|
- **Allowed:** architecture level — Proxmox + Synology + Docker host, VLAN concept without concrete internal IP plans, which service types run, which tools are used.
|
||||||
- **Not allowed:** concrete IP addresses, WireGuard keys/preshared keys, passwords, internal hostnames that allow inference, backup targets with credentials.
|
- **Not allowed:** concrete IP addresses, WireGuard keys/preshared keys, passwords, internal hostnames that allow inference, backup targets with credentials.
|
||||||
|
|
||||||
This rule applies to any domain but is most relevant for infra, since the source documents (e.g. Heimnetzwerk-Final-v3.2, GL_Flint2_Final_v2) currently contain real IPs and keys that must be actively stripped during migration.
|
This rule applies to any domain but is most relevant for infra, since the source documents currently contain real IPs and keys that must be actively stripped during migration.
|
||||||
|
|
||||||
## 7. Language
|
## 7. Language
|
||||||
|
|
||||||
All sites in the monorepo are written **entirely in English**. Existing German source documents are translated once during migration via a single AI-assisted pass — not a recurring process. New `.moonweb.yml` metadata and generated content are authored in English from the start.
|
All sites in the monorepo are written **entirely in English**. Existing German source documents are translated once during migration via a single AI-assisted pass — not a recurring process. New `.moonweb.yml` metadata and generated content are authored in English from the start.
|
||||||
|
|
||||||
## 8. GitHub automation (code.moonweb.org)
|
## 8. GitHub automation (www.moonweb.org/code/)
|
||||||
|
|
||||||
Each GitHub project repo gets a `.moonweb.yml` in its root:
|
Each GitHub project repo gets a `.moonweb.yml` in its root:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
title: "MVG Departures"
|
title: "MVG Departures"
|
||||||
category: code # code | smarthome | infra
|
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
|
status: active
|
||||||
stack: [Python, FastAPI]
|
stack: [Python, FastAPI]
|
||||||
hosted_on: "Docker Host Debian (PVE)"
|
hosted_on: "Docker Host Debian (PVE)"
|
||||||
@@ -98,7 +104,7 @@ summary: "Compact MVG/S-Bahn departure monitor with configurable stations."
|
|||||||
repo_url: "https://github.com/skoelle/mvg-departures"
|
repo_url: "https://github.com/skoelle/mvg-departures"
|
||||||
```
|
```
|
||||||
|
|
||||||
A local aggregator script reads `.moonweb.yml` from all repos via the GitHub API, an AI pass turns the raw YAML into readable card copy, and the result is committed into `code/_data/repos.json` inside the monorepo. This runs manually, on demand — no scheduled automation for now.
|
A local aggregator script reads `.moonweb.yml` from all repos via the GitHub API, and the result is committed into `_data/repos.json` inside the monorepo. This runs manually, on demand — no scheduled automation for now.
|
||||||
|
|
||||||
## 9. Content maintenance
|
## 9. Content maintenance
|
||||||
|
|
||||||
@@ -112,47 +118,60 @@ A local aggregator script reads `.moonweb.yml` from all repos via the GitHub API
|
|||||||
|
|
||||||
```
|
```
|
||||||
moonweb-site/
|
moonweb-site/
|
||||||
├── hub/
|
├── hub/ # Index + Redirects (.htm) + impressum.njk
|
||||||
├── infra/
|
├── infra/ # Index.njk + 8 Subseiten
|
||||||
├── smarthome/
|
├── smarthome/ # Index.njk + 9 Subseiten
|
||||||
├── code/
|
├── code/ # Index.njk
|
||||||
│ └── _data/repos.json # populated by the GitHub aggregator
|
├── retro/ # Index.njk + 13 Subseiten
|
||||||
├── retro/
|
├── timecapsule/ # Eleventy 2.x (eigene Config)
|
||||||
├── stefankoelle/ # CV, career, personal site
|
|
||||||
│ ├── eleventy.config.js
|
│ ├── eleventy.config.js
|
||||||
│ ├── index.njk # Onepager (CV, Projects, Languages)
|
│ ├── package.json
|
||||||
│ ├── cv-print.njk # CV-only for PDF generation
|
│ └── src/
|
||||||
│ ├── ledmatrix/ # LED Matrix WebServer documentation
|
├── stefankoelle/ # Eleventy-Config + Onepager
|
||||||
│ └── assets/ # CSS, JS, images, favicons
|
│ ├── eleventy.config.js
|
||||||
├── shared/
|
│ ├── index.njk
|
||||||
|
│ ├── cv-print.njk
|
||||||
|
│ ├── ledmatrix/
|
||||||
|
│ └── assets/
|
||||||
|
├── shared/ # Shared components
|
||||||
│ ├── _includes/
|
│ ├── _includes/
|
||||||
│ │ ├── base.njk # shared header + footer layout
|
│ │ ├── base.njk # base layout (header, site-switcher, footer)
|
||||||
│ │ └── card-grid.njk # card-grid template
|
│ │ ├── card-grid.njk # card-grid template
|
||||||
│ ├── base.css # shared CSS variables and layout
|
│ │ └── sitemap.njk # central sitemap template
|
||||||
│ └── theme-*.css # one accent color file per domain
|
│ ├── base.css # shared CSS
|
||||||
|
│ └── favicon/ # favicon SVGs per section
|
||||||
|
├── _data/
|
||||||
|
│ └── repos.json # populated by the GitHub aggregator
|
||||||
├── scripts/
|
├── scripts/
|
||||||
│ └── github-aggregator/ # reads .moonweb.yml from all repos
|
│ ├── github-aggregator/ # reads .moonweb.yml from all repos
|
||||||
├── DESIGN.md # initial concept and design decisions
|
│ └── cloudflare/ # redirect setup for old subdomains
|
||||||
├── SPEC.md # what gets built (this document)
|
├── .github/workflows/
|
||||||
├── PLAN.md # how and in what order
|
│ ├── build-deploy-moonweb.yml # builds all moonweb sites, deploys to IONOS SFTP
|
||||||
├── TODO.md # open items and workflow
|
│ └── deploy-stefankoelle.yml # builds stefankoelle, deploys via IONOS SFTP
|
||||||
└── .github/workflows/
|
├── eleventy.config.js # single Eleventy config for all moonweb sites
|
||||||
├── build-deploy.yml # builds all sites, deploys to Cloudflare Pages
|
├── .eleventyignore # excludes stefankoelle/, timecapsule/
|
||||||
└── deploy-stefankoelle.yml # builds stefankoelle, deploys via IONOS SFTP
|
├── DESIGN.md
|
||||||
|
├── SPEC.md
|
||||||
|
├── PLAN.md
|
||||||
|
├── TODO.md
|
||||||
|
└── package.json
|
||||||
```
|
```
|
||||||
|
|
||||||
## 11. Technical stack
|
## 11. Technical stack
|
||||||
|
|
||||||
- **Static site generator:** Eleventy (11ty) — markdown/YAML-first, minimal JS, `_data` folders map directly onto the `.moonweb.yml` aggregator output, low maintenance for five sites at this scale.
|
- **Static site generator:** Eleventy (11ty) v3.1.6 — single config at root, computed `pathPrefix` per section, per-section collections.
|
||||||
|
- **Static site generator (timecapsule):** Eleventy v2.0.1 — separate config, preserves original 2001 design.
|
||||||
|
- **Templates:** Nunjucks (.njk) — shared `base.njk` layout, `card-grid.njk` for index pages, `sitemap.njk` for central sitemap.
|
||||||
|
- **Styling:** Custom CSS (`base.css`) with accent colors inlined as `<style>` in `base.njk`. No per-site theme CSS files.
|
||||||
- **Build:** entirely in GitHub Actions.
|
- **Build:** entirely in GitHub Actions.
|
||||||
- **Deploy:** Cloudflare Pages — five separate Pages projects (one per public domain: hub, infra, smarthome, code, retro), since Cloudflare Pages binds one custom-domain set per project. stefankoelle.de deploys via IONOS SFTP. Two GitHub Actions workflows handle deployment.
|
- **Deploy:** IONOS SFTP — all sites deployed to `/websites/moonweb/`, stefankoelle.de to `/websites/stefankoelle/`.
|
||||||
- **Runtime:** fully static, no server-side code, no containers for the website itself (distinct from the actual homelab services running on the Docker host).
|
- **DNS:** Cloudflare — DNS management + redirects from old subdomains (hub.moonweb.org, etc.).
|
||||||
- **DNS:** already on Cloudflare — no additional setup step needed for Pages custom domains.
|
- **Runtime:** fully static, no server-side code, no containers for the website itself.
|
||||||
- **Local preview:** Eleventy's built-in dev server with live reload, run per-site (`npm run dev:<site>`) before any commit.
|
- **Local preview:** Eleventy's built-in dev server with live reload (`npm run dev` for all moonweb sites, `npm run dev:stefankoelle`, `npm run dev:timecapsule`).
|
||||||
|
|
||||||
## 12. Explicitly out of scope for this build
|
## 12. Explicitly out of scope
|
||||||
|
|
||||||
- Migrating 28k8.moonweb.org into the monorepo — undecided, revisit later, likely never.
|
- Migrating 28k8.moonweb.org into the monorepo — undecided, revisit later, likely never.
|
||||||
- Any Perplexity-backchannel mechanism (website content reusable inside this project) — deferred, no time invested now.
|
- Any Perplexity-backchannel mechanism — deferred.
|
||||||
- Analytics of any kind.
|
- Analytics of any kind.
|
||||||
- Automated content generation/translation pipelines beyond the one-time GitHub aggregator for code and the one-time translation pass during migration.
|
- Automated content generation/translation pipelines beyond the one-time GitHub aggregator for code.
|
||||||
|
|||||||
@@ -1,76 +1,76 @@
|
|||||||
# TODO — moonweb-site Offene Items
|
# TODO: moonweb-site
|
||||||
|
|
||||||
Stand: 2026-08-14
|
## Status: Consolidation Complete
|
||||||
|
|
||||||
## Aktueller Status
|
All moonweb.org sites are consolidated under `www.moonweb.org` as subdirectories, deployed via IONOS SFTP. PR #3 merged to main.
|
||||||
|
|
||||||
| 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 |
|
|
||||||
|
|
||||||
## Offene Items
|
## Completed
|
||||||
|
|
||||||
### P4 — Deferred (nicht dringend)
|
### Phase 1: Timecapsule Integration
|
||||||
|
- [x] Copy files from moonweb-www/src/ to timecapsule/src/
|
||||||
|
- [x] Create timecapsule/eleventy.config.js (Eleventy 2.x)
|
||||||
|
- [x] Create timecapsule/package.json (own dependencies)
|
||||||
|
- [x] Adapt timecapsule internal paths with /timecapsule/ prefix
|
||||||
|
|
||||||
- [ ] 28k8.moonweb.org Zukunft klären
|
### Phase 2: Single Eleventy Config
|
||||||
- [ ] Perplexity-Rückkanal
|
- [x] Create root eleventy.config.js with computed pathPrefix per section
|
||||||
- [ ] Apex-Domain Redirect (moonweb.org → hub.moonweb.org)
|
- [x] Delete 5 per-site eleventy.config.js files (hub, infra, smarthome, code, retro)
|
||||||
- [ ] PDF-Build fuer CV (WeasyPrint, lokal testen)
|
- [x] Delete scripts/merge-moonweb.sh
|
||||||
- [ ] Querverlinkungen stefankoelle.de <-> smarthome
|
- [x] Update shared/_includes/base.njk (inlined accent colors, root CSS path)
|
||||||
- [ ] Ledmatrix ggf. nach smarthome verschieben
|
- [x] Update hub/index.njk card hrefs (relative paths)
|
||||||
- [ ] **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] Update hub/impressum.njk (hosting sections correct)
|
||||||
|
- [x] Update parent values in detail pages to include section prefix
|
||||||
|
- [x] Add tags to all pages for Eleventy collections
|
||||||
|
|
||||||
### Offene Entscheidungen
|
### Phase 3: Build & Deploy
|
||||||
|
- [x] Create .github/workflows/build-deploy-moonweb.yml (single build job, SFTP deploy)
|
||||||
|
- [x] Delete old build-deploy.yml (matrix builds)
|
||||||
|
- [x] Update .eleventyignore (exclude stefankoelle/, timecapsule/, markdown)
|
||||||
|
- [x] Fix build-pdf.sh to temporarily rename .eleventyignore for stefankoelle build
|
||||||
|
- [x] Fix timecapsule build path (../dist/ not ../../dist/)
|
||||||
|
|
||||||
- [ ] **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.
|
### Phase 4: Redirects & SEO
|
||||||
|
- [x] Create 20 redirect .htm files in hub/ for old www.moonweb.org paths
|
||||||
|
- [x] Create central sitemap.xml (36 pages)
|
||||||
|
- [x] Create single robots.txt at root
|
||||||
|
- [x] Set up Cloudflare redirect rules script (scripts/cloudflare/)
|
||||||
|
|
||||||
## Schon erledigt (Stand 2026-08-14)
|
### Phase 5: Bug Fixes
|
||||||
|
- [x] Fix card links on index pages (add section prefix to local hrefs)
|
||||||
|
- [x] Fix code repos missing (move repos.json to root _data/)
|
||||||
|
- [x] Fix favicons (use section-specific favicon.svg per site)
|
||||||
|
- [x] Fix header (show moonweb.org/smarthome instead of smarthome.moonweb.org)
|
||||||
|
- [x] Rename hub link to home in site-switcher nav
|
||||||
|
- [x] Remove redundant 404 pages, keep only root 404
|
||||||
|
- [x] Add missing beginning subpage redirects (news, report, sitemap)
|
||||||
|
- [x] Fix double-slash URLs in stefankoelle/index.njk
|
||||||
|
- [x] Remove unused theme-*.css files
|
||||||
|
- [x] Update stefankoelle link texts (replace old subdomain names with new paths)
|
||||||
|
|
||||||
### stefankoelle.de Migration (2026-08-14)
|
### Phase 6: Documentation
|
||||||
- [x] Eleventy-Config + package.json erweitert
|
- [x] Update AGENTS.md for new structure
|
||||||
- [x] Onepager-Layout mit Anchor-Links beibehalten
|
- [x] Update README.md for new structure
|
||||||
- [x] CV-Partial (zentral fuer Web + PDF)
|
- [x] Update SPEC.md for new structure
|
||||||
- [x] CSS ausgelagert + modernisiert (Custom Properties, box-sizing)
|
- [x] Update PLAN.md for new structure
|
||||||
- [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)
|
|
||||||
|
|
||||||
### 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
|
|
||||||
|
|
||||||
## Workflow für neue Contexts
|
## Remaining
|
||||||
|
|
||||||
Wenn du einen neuen Context öffnest, lies diese Datei und prüfe:
|
### Cloudflare Redirects
|
||||||
1. Welche P4-Items sind relevant? → Nur wenn angefordert
|
- [ ] Activate redirect rules (needs `CLOUDFLARE_API_TOKEN` and `CLOUDFLARE_ZONE_ID` secrets, or manual dashboard setup)
|
||||||
|
- [ ] Verify redirects work after activation
|
||||||
|
|
||||||
## Definition of done
|
### SEO
|
||||||
|
- [ ] Create Google Search Console property for www.moonweb.org
|
||||||
|
- [ ] Submit sitemap
|
||||||
|
|
||||||
Alle 5 Sites live auf Cloudflare Pages:
|
### Cleanup
|
||||||
- ✅ hub.moonweb.org — verlinkt alles (+ stefankoelle.de extern)
|
- [ ] Archive moonweb-www repository
|
||||||
- ✅ code.moonweb.org — GitHub-Katalog via Aggregator
|
- [ ] Delete Cloudflare Pages projects (after redirect verification)
|
||||||
- ✅ smarthome.moonweb.org — Smart Home Übersicht + Detailseiten
|
|
||||||
- ✅ infra.moonweb.org — Infra-Übersicht (redigiert)
|
|
||||||
- ✅ retro.moonweb.org — Ehrliche minimale Übersicht (WIP)
|
|
||||||
|
|
||||||
stefankoelle.de live auf IONOS (SFTP-Deployment):
|
### Future
|
||||||
- ✅ stefankoelle.de — Onepager mit CV, Projects, Languages, Impressum
|
- [ ] Cross-linking stefankoelle.de <-> smarthome
|
||||||
- ✅ stefankoelle.de/ledmatrix/ — LED Matrix WebServer Dokumentation
|
- [ ] Consider moving LED Matrix to smarthome (when ready)
|
||||||
|
|||||||
@@ -1,12 +0,0 @@
|
|||||||
---
|
|
||||||
title: "Page Not Found"
|
|
||||||
section: "code"
|
|
||||||
description: "The page you are looking for does not exist."
|
|
||||||
layout: base.njk
|
|
||||||
permalink: /404.html
|
|
||||||
---
|
|
||||||
<div class="error-page">
|
|
||||||
<h1>404</h1>
|
|
||||||
<p>The page you are looking for does not exist or has been moved.</p>
|
|
||||||
<a class="back-link" href="/">← Back to code</a>
|
|
||||||
</div>
|
|
||||||
@@ -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,6 +1,7 @@
|
|||||||
---
|
---
|
||||||
title: "Open Source Projects — GitHub Repos & Code | moonweb"
|
title: "Open Source Projects — GitHub Repos & Code | moonweb"
|
||||||
section: "code"
|
section: "code"
|
||||||
|
tags: "code"
|
||||||
description: "Curated catalog of my public GitHub projects — automation, dev tools, web apps, and firmware."
|
description: "Curated catalog of my public GitHub projects — automation, dev tools, web apps, and firmware."
|
||||||
layout: base.njk
|
layout: base.njk
|
||||||
subcategories:
|
subcategories:
|
||||||
|
|||||||
@@ -1,4 +0,0 @@
|
|||||||
User-agent: *
|
|
||||||
Allow: /
|
|
||||||
|
|
||||||
Sitemap: https://code.moonweb.org/sitemap.xml
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
---
|
|
||||||
permalink: /sitemap.xml
|
|
||||||
eleventyExcludeFromCollections: true
|
|
||||||
---
|
|
||||||
{% include "sitemap.njk" %}
|
|
||||||
@@ -0,0 +1,97 @@
|
|||||||
|
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`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
const beginningSubpages = ["news", "report", "sitemap"];
|
||||||
|
for (const name of beginningSubpages) {
|
||||||
|
eleventyConfig.addPassthroughCopy({
|
||||||
|
[`hub/beginning/${name}.htm`]: `beginning/${name}.html`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
dir: {
|
||||||
|
input: ".",
|
||||||
|
includes: "shared/_includes",
|
||||||
|
output: "dist/moonweb",
|
||||||
|
},
|
||||||
|
serverOptions: {
|
||||||
|
host: "0.0.0.0",
|
||||||
|
port: 8081,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
||||||
+2
-1
@@ -1,6 +1,7 @@
|
|||||||
---
|
---
|
||||||
title: "Page Not Found"
|
title: "Page Not Found"
|
||||||
section: "hub"
|
section: "hub"
|
||||||
|
tags: "hub"
|
||||||
description: "The page you are looking for does not exist."
|
description: "The page you are looking for does not exist."
|
||||||
layout: base.njk
|
layout: base.njk
|
||||||
permalink: /404.html
|
permalink: /404.html
|
||||||
@@ -8,5 +9,5 @@ permalink: /404.html
|
|||||||
<div class="error-page">
|
<div class="error-page">
|
||||||
<h1>404</h1>
|
<h1>404</h1>
|
||||||
<p>The page you are looking for does not exist or has been moved.</p>
|
<p>The page you are looking for does not exist or has been moved.</p>
|
||||||
<a class="back-link" href="/">← Back to hub</a>
|
<a class="back-link" href="/">← Back to home</a>
|
||||||
</div>
|
</div>
|
||||||
@@ -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>
|
||||||
@@ -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>
|
||||||
@@ -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>
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta http-equiv="refresh" content="0;url=/timecapsule/beginning/news.html">
|
||||||
|
<title>Redirecting...</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<p>Redirecting to <a href="/timecapsule/beginning/news.html">/timecapsule/beginning/news.html</a>...</p>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta http-equiv="refresh" content="0;url=/timecapsule/beginning/report.html">
|
||||||
|
<title>Redirecting...</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<p>Redirecting to <a href="/timecapsule/beginning/report.html">/timecapsule/beginning/report.html</a>...</p>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta http-equiv="refresh" content="0;url=/timecapsule/beginning/sitemap.html">
|
||||||
|
<title>Redirecting...</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<p>Redirecting to <a href="/timecapsule/beginning/sitemap.html">/timecapsule/beginning/sitemap.html</a>...</p>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -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>
|
||||||
@@ -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
|
|
||||||
}
|
|
||||||
};
|
|
||||||
};
|
|
||||||
@@ -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
@@ -1,6 +1,8 @@
|
|||||||
---
|
---
|
||||||
title: "Legal Notice & Privacy Policy"
|
title: "Legal Notice & Privacy Policy"
|
||||||
section: "hub"
|
section: "hub"
|
||||||
|
tags: "hub"
|
||||||
|
permalink: /impressum/index.html
|
||||||
description: "Legal notice (Impressum) and privacy policy for moonweb.org."
|
description: "Legal notice (Impressum) and privacy policy for moonweb.org."
|
||||||
layout: base.njk
|
layout: base.njk
|
||||||
---
|
---
|
||||||
@@ -19,7 +21,7 @@ layout: base.njk
|
|||||||
</p>
|
</p>
|
||||||
|
|
||||||
<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>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
@@ -41,7 +43,7 @@ layout: base.njk
|
|||||||
|
|
||||||
<h3>2. Purpose of these Websites</h3>
|
<h3>2. Purpose of these Websites</h3>
|
||||||
<p>
|
<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 (home, 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>
|
</p>
|
||||||
|
|
||||||
<h3>3. Hosting</h3>
|
<h3>3. Hosting</h3>
|
||||||
@@ -49,18 +51,23 @@ layout: base.njk
|
|||||||
Two different hosting providers are used within this network:
|
Two different hosting providers are used within this network:
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h4>a) moonweb.org subdomains (hub, smarthome, infra, code, retro, buildbroken) — Cloudflare Pages</h4>
|
<h4>a) www.moonweb.org (home, infra, smarthome, code, retro, timecapsule) — IONOS SE</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>
|
|
||||||
<p>
|
<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).
|
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>
|
</p>
|
||||||
|
|
||||||
|
<h4>b) stefankoelle.de — IONOS SE</h4>
|
||||||
<p>
|
<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 — 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>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Legal basis: Art. 6(1)(f) GDPR (legitimate interest in reliable, secure delivery of the websites).
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h3>4. No Tracking, No Cookies, No Analytics (current state)</h3>
|
<h3>4. No Tracking, No Cookies, No Analytics (current state)</h3>
|
||||||
|
|||||||
+10
-8
@@ -1,6 +1,8 @@
|
|||||||
---
|
---
|
||||||
title: "Stefan Koelle — Homelab, Smart Home, Code & Retro Hardware | moonweb"
|
title: "Stefan Koelle — Homelab, Smart Home, Code & Retro Hardware | moonweb"
|
||||||
section: "hub"
|
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."
|
description: "Stefan Koelle — Senior Software Developer & Architect based in Munich. Homelab projects, smart home automation, open-source code, and retro hardware."
|
||||||
layout: base.njk
|
layout: base.njk
|
||||||
ogImage: "/stefan-koelle-foto.jpg"
|
ogImage: "/stefan-koelle-foto.jpg"
|
||||||
@@ -9,19 +11,19 @@ sections:
|
|||||||
cards:
|
cards:
|
||||||
- title: "infra"
|
- title: "infra"
|
||||||
summary: "Stack overview: Proxmox, Synology, network, backups, and how I work."
|
summary: "Stack overview: Proxmox, Synology, network, backups, and how I work."
|
||||||
href: "https://infra.moonweb.org"
|
href: "/infra/"
|
||||||
emoji: "🏗️"
|
emoji: "🏗️"
|
||||||
- title: "smarthome"
|
- title: "smarthome"
|
||||||
summary: "What's actually running on the homelab, and why."
|
summary: "What's actually running on the homelab, and why."
|
||||||
href: "https://smarthome.moonweb.org"
|
href: "/smarthome/"
|
||||||
emoji: "🏠"
|
emoji: "🏠"
|
||||||
- title: "code"
|
- title: "code"
|
||||||
summary: "Curated catalog of my GitHub projects."
|
summary: "Curated catalog of my GitHub projects."
|
||||||
href: "https://code.moonweb.org"
|
href: "/code/"
|
||||||
emoji: "💻"
|
emoji: "💻"
|
||||||
- title: "retro"
|
- title: "retro"
|
||||||
summary: "Physical retro hardware collection."
|
summary: "Physical retro hardware collection."
|
||||||
href: "https://retro.moonweb.org"
|
href: "/retro/"
|
||||||
emoji: "🕹️"
|
emoji: "🕹️"
|
||||||
- heading: "About me"
|
- heading: "About me"
|
||||||
cards:
|
cards:
|
||||||
@@ -35,9 +37,9 @@ sections:
|
|||||||
summary: "tHE tEMPLE BBS Mailbox and 90s releases archive."
|
summary: "tHE tEMPLE BBS Mailbox and 90s releases archive."
|
||||||
href: "https://28k8.moonweb.org"
|
href: "https://28k8.moonweb.org"
|
||||||
emoji: "💾"
|
emoji: "💾"
|
||||||
- title: "www.moonweb.org"
|
- title: "moonweb.org 2001"
|
||||||
summary: "The 2000s internet projects, networking and engineering."
|
summary: "The original www.moonweb.org as it was in 2001 — preserved as a time capsule."
|
||||||
href: "https://www.moonweb.org"
|
href: "/timecapsule/"
|
||||||
emoji: "🌐"
|
emoji: "🌐"
|
||||||
- title: "inseco.de"
|
- title: "inseco.de"
|
||||||
summary: "Internet services & consulting for gastronomy and clubs, Peak 2003–2008. Custom PHP/CMS."
|
summary: "Internet services & consulting for gastronomy and clubs, Peak 2003–2008. Custom PHP/CMS."
|
||||||
@@ -63,7 +65,7 @@ sections:
|
|||||||
<section class="about-expanded">
|
<section class="about-expanded">
|
||||||
<h2>What this site is about</h2>
|
<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>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>
|
<h2>What I do professionally</h2>
|
||||||
<p>I work as a Senior Software Developer & 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>
|
<p>I work as a Senior Software Developer & 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>
|
||||||
|
|||||||
@@ -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>
|
||||||
@@ -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>
|
||||||
@@ -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>
|
||||||
@@ -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>
|
||||||
@@ -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>
|
||||||
@@ -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>
|
||||||
@@ -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
@@ -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>
|
||||||
@@ -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>
|
||||||
@@ -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>
|
||||||
@@ -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
@@ -1,4 +1,5 @@
|
|||||||
User-agent: *
|
User-agent: *
|
||||||
Allow: /
|
Allow: /
|
||||||
|
|
||||||
Sitemap: https://hub.moonweb.org/sitemap.xml
|
Sitemap: https://www.moonweb.org/sitemap.xml
|
||||||
|
Sitemap: https://www.moonweb.org/timecapsule/sitemap.xml
|
||||||
|
|||||||
@@ -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>
|
||||||
@@ -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>
|
||||||
@@ -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>
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
---
|
|
||||||
title: "Page Not Found"
|
|
||||||
section: "infra"
|
|
||||||
description: "The page you are looking for does not exist."
|
|
||||||
layout: base.njk
|
|
||||||
permalink: /404.html
|
|
||||||
---
|
|
||||||
<div class="error-page">
|
|
||||||
<h1>404</h1>
|
|
||||||
<p>The page you are looking for does not exist or has been moved.</p>
|
|
||||||
<a class="back-link" href="/">← Back to infra</a>
|
|
||||||
</div>
|
|
||||||
@@ -1,7 +1,8 @@
|
|||||||
---
|
---
|
||||||
title: "Backup Strategy"
|
title: "Backup Strategy"
|
||||||
section: "infra"
|
section: "infra"
|
||||||
parent: "/"
|
tags: "infra"
|
||||||
|
parent: "/infra/"
|
||||||
description: "Layered 3-2-1 backup strategy covering NAS snapshots, offsite copies, and cloud storage."
|
description: "Layered 3-2-1 backup strategy covering NAS snapshots, offsite copies, and cloud storage."
|
||||||
layout: base.njk
|
layout: base.njk
|
||||||
---
|
---
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
---
|
---
|
||||||
title: "Xubuntu Dev VM"
|
title: "Xubuntu Dev VM"
|
||||||
section: "infra"
|
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."
|
description: "A disposable, daily-backed-up Xubuntu VM for AI-assisted, remote development via SSH and Chrome Remote Desktop."
|
||||||
layout: base.njk
|
layout: base.njk
|
||||||
---
|
---
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
---
|
---
|
||||||
title: "Docker Container Strategy"
|
title: "Docker Container Strategy"
|
||||||
section: "infra"
|
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."
|
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
|
layout: base.njk
|
||||||
---
|
---
|
||||||
|
|||||||
@@ -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
|
|
||||||
}
|
|
||||||
};
|
|
||||||
};
|
|
||||||
+9
-8
@@ -1,6 +1,7 @@
|
|||||||
---
|
---
|
||||||
title: "Homelab Infrastructure — Proxmox, Docker & Networking | moonweb"
|
title: "Homelab Infrastructure — Proxmox, Docker & Networking | moonweb"
|
||||||
section: "infra"
|
section: "infra"
|
||||||
|
tags: "infra"
|
||||||
description: "Homelab infrastructure overview: Proxmox hosts, Synology NAS, Docker VMs and Setup-Guides."
|
description: "Homelab infrastructure overview: Proxmox hosts, Synology NAS, Docker VMs and Setup-Guides."
|
||||||
layout: base.njk
|
layout: base.njk
|
||||||
sections:
|
sections:
|
||||||
@@ -8,30 +9,30 @@ sections:
|
|||||||
cards:
|
cards:
|
||||||
- title: "🖥️ Proxmox host (PVE)"
|
- title: "🖥️ Proxmox host (PVE)"
|
||||||
summary: "Hosting environment for running production VMs and LXC containers for the homelab."
|
summary: "Hosting environment for running production VMs and LXC containers for the homelab."
|
||||||
href: "/proxmox/"
|
href: "/infra/proxmox/"
|
||||||
- title: "💾 Synology DS918+"
|
- title: "💾 Synology DS918+"
|
||||||
summary: "NAS running Docker services and acting as the primary backup repository."
|
summary: "NAS running Docker services and acting as the primary backup repository."
|
||||||
href: "/synology/"
|
href: "/infra/synology/"
|
||||||
- title: "🐳 Docker VMs (Debian)"
|
- title: "🐳 Docker VMs (Debian)"
|
||||||
summary: "Primary target for new Docker/Compose services, running the monitoring exporters."
|
summary: "Primary target for new Docker/Compose services, running the monitoring exporters."
|
||||||
href: "/docker/"
|
href: "/infra/docker/"
|
||||||
- title: "📦 LxC Container"
|
- title: "📦 LxC Container"
|
||||||
summary: "Pi-hole, Ubuntu-Worker and MariaDB running on slim LxC Containers."
|
summary: "Pi-hole, Ubuntu-Worker and MariaDB running on slim LxC Containers."
|
||||||
href: "/lxc/"
|
href: "/infra/lxc/"
|
||||||
- heading: "Strategies & Setup-Guides"
|
- heading: "Strategies & Setup-Guides"
|
||||||
cards:
|
cards:
|
||||||
- title: "🔄 Backup strategy"
|
- title: "🔄 Backup strategy"
|
||||||
summary: "Layered backup approach covering NAS user content and VM/LXC backups across multiple locations."
|
summary: "Layered backup approach covering NAS user content and VM/LXC backups across multiple locations."
|
||||||
href: "/backup-strategy/"
|
href: "/infra/backup-strategy/"
|
||||||
- title: "📊 Prometheus/Grafana"
|
- title: "📊 Prometheus/Grafana"
|
||||||
summary: "Central metrics collection and dashboards for hosts, containers, and hardware."
|
summary: "Central metrics collection and dashboards for hosts, containers, and hardware."
|
||||||
href: "/monitoring/"
|
href: "/infra/monitoring/"
|
||||||
- title: "💻 Dev environment"
|
- title: "💻 Dev environment"
|
||||||
summary: "A disposable, daily-backed-up Xubuntu VM for AI-assisted, remote development via SSH and Chrome Remote Desktop."
|
summary: "A disposable, daily-backed-up Xubuntu VM for AI-assisted, remote development via SSH and Chrome Remote Desktop."
|
||||||
href: "/dev-environment/"
|
href: "/infra/dev-environment/"
|
||||||
- title: "🌐 OpenWrt Multi-WAN"
|
- title: "🌐 OpenWrt Multi-WAN"
|
||||||
summary: "Multi-WAN router with WiFi and Powerline failover, plus IoT VLAN 189 isolation."
|
summary: "Multi-WAN router with WiFi and Powerline failover, plus IoT VLAN 189 isolation."
|
||||||
href: "/openwrt/"
|
href: "/infra/openwrt/"
|
||||||
---
|
---
|
||||||
<p class="site-intro">A peek under the hood of my homelab. Proxmox, Synology, Docker, and how everything is wired together.</p>
|
<p class="site-intro">A peek under the hood of my homelab. Proxmox, Synology, Docker, and how everything is wired together.</p>
|
||||||
|
|
||||||
|
|||||||
+2
-1
@@ -1,7 +1,8 @@
|
|||||||
---
|
---
|
||||||
title: "LXC Container Strategy"
|
title: "LXC Container Strategy"
|
||||||
section: "infra"
|
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."
|
description: "Overview of the lightweight LXC containers running on the main Proxmox host, and why they are LXC instead of Docker."
|
||||||
layout: base.njk
|
layout: base.njk
|
||||||
---
|
---
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
---
|
---
|
||||||
title: "Monitoring"
|
title: "Monitoring"
|
||||||
section: "infra"
|
section: "infra"
|
||||||
parent: "/"
|
tags: "infra"
|
||||||
|
parent: "/infra/"
|
||||||
description: "Prometheus and Grafana monitoring stack for hosts, containers, and hardware metrics."
|
description: "Prometheus and Grafana monitoring stack for hosts, containers, and hardware metrics."
|
||||||
layout: base.njk
|
layout: base.njk
|
||||||
---
|
---
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
---
|
---
|
||||||
title: "OpenWrt Multi-WAN Router"
|
title: "OpenWrt Multi-WAN Router"
|
||||||
section: "infra"
|
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."
|
description: "Multi-WAN OpenWrt router in the study with WiFi and Powerline failover, plus VLAN 189 for IoT isolation."
|
||||||
layout: base.njk
|
layout: base.njk
|
||||||
---
|
---
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
---
|
---
|
||||||
title: "Proxmox Virtualization"
|
title: "Proxmox Virtualization"
|
||||||
section: "infra"
|
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."
|
description: "What Proxmox VE is, the hardware it runs on in my homelab, its key advantages, and a short setup walkthrough."
|
||||||
layout: base.njk
|
layout: base.njk
|
||||||
---
|
---
|
||||||
|
|||||||
@@ -1,4 +0,0 @@
|
|||||||
User-agent: *
|
|
||||||
Allow: /
|
|
||||||
|
|
||||||
Sitemap: https://infra.moonweb.org/sitemap.xml
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
---
|
|
||||||
permalink: /sitemap.xml
|
|
||||||
eleventyExcludeFromCollections: true
|
|
||||||
---
|
|
||||||
{% include "sitemap.njk" %}
|
|
||||||
@@ -1,7 +1,8 @@
|
|||||||
---
|
---
|
||||||
title: "Synology NAS"
|
title: "Synology NAS"
|
||||||
section: "infra"
|
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."
|
description: "Model, storage setup and current role of the Synology NAS in the homelab after the late-2025 cleanup."
|
||||||
layout: base.njk
|
layout: base.njk
|
||||||
---
|
---
|
||||||
|
|||||||
+9
-16
@@ -1,25 +1,18 @@
|
|||||||
{
|
{
|
||||||
"name": "moonweb-site",
|
"name": "moonweb-site",
|
||||||
"version": "0.2.0",
|
"version": "0.3.0",
|
||||||
"private": true,
|
"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": {
|
"scripts": {
|
||||||
"dev:hub": "npx @11ty/eleventy --config=hub/eleventy.config.js --serve --port=8081",
|
"dev": "npx @11ty/eleventy --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:stefankoelle": "npx @11ty/eleventy --config=stefankoelle/eleventy.config.js --serve --port=8086",
|
"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",
|
"dev:timecapsule": "cd timecapsule && npx @11ty/eleventy --serve --port=8087",
|
||||||
"build:hub": "npx @11ty/eleventy --config=hub/eleventy.config.js",
|
"build": "npx @11ty/eleventy",
|
||||||
"build:infra": "npx @11ty/eleventy --config=infra/eleventy.config.js",
|
"build:stefankoelle": "mv .eleventyignore .eleventyignore.bak && npx @11ty/eleventy --config=stefankoelle/eleventy.config.js; mv .eleventyignore.bak .eleventyignore 2>/dev/null; true",
|
||||||
"build:smarthome": "npx @11ty/eleventy --config=smarthome/eleventy.config.js",
|
"build:timecapsule": "cd timecapsule && npx @11ty/eleventy && mkdir -p ../dist/moonweb/timecapsule && cp -r _site/timecapsule/* ../dist/moonweb/timecapsule/",
|
||||||
"build:code": "npx @11ty/eleventy --config=code/eleventy.config.js",
|
"build:moonweb": "npm run build && npm run build:timecapsule",
|
||||||
"build:retro": "npx @11ty/eleventy --config=retro/eleventy.config.js",
|
|
||||||
"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",
|
|
||||||
"pdf:cv": "bash stefankoelle/pdf/build-pdf.sh",
|
"pdf:cv": "bash stefankoelle/pdf/build-pdf.sh",
|
||||||
"prebuild": "bash prebuild.sh"
|
"prebuild:cv": "bash prebuild.sh"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@11ty/eleventy": "^3.1.6"
|
"@11ty/eleventy": "^3.1.6"
|
||||||
|
|||||||
@@ -1,12 +0,0 @@
|
|||||||
---
|
|
||||||
title: "Page Not Found"
|
|
||||||
section: "retro"
|
|
||||||
description: "The page you are looking for does not exist."
|
|
||||||
layout: base.njk
|
|
||||||
permalink: /404.html
|
|
||||||
---
|
|
||||||
<div class="error-page">
|
|
||||||
<h1>404</h1>
|
|
||||||
<p>The page you are looking for does not exist or has been moved.</p>
|
|
||||||
<a class="back-link" href="/">← Back to retro</a>
|
|
||||||
</div>
|
|
||||||
@@ -1,9 +1,10 @@
|
|||||||
---
|
---
|
||||||
title: "Running AmigaOS in 2026: MiSTer FPGA vs Amiberry on Pi"
|
title: "Running AmigaOS in 2026: MiSTer FPGA vs Amiberry on Pi"
|
||||||
section: "retro"
|
section: "retro"
|
||||||
|
tags: "retro"
|
||||||
description: "Two very different ways to get a fully working AmigaOS 3.2.3 desktop running today."
|
description: "Two very different ways to get a fully working AmigaOS 3.2.3 desktop running today."
|
||||||
layout: base.njk
|
layout: base.njk
|
||||||
parent: "/"
|
parent: "/retro/"
|
||||||
---
|
---
|
||||||
<h1>Running AmigaOS in 2026: MiSTer FPGA vs Amiberry on Pi</h1>
|
<h1>Running AmigaOS in 2026: MiSTer FPGA vs Amiberry on Pi</h1>
|
||||||
<div class="detail-content">
|
<div class="detail-content">
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
---
|
---
|
||||||
title: "Vampire V4, A600GS or MiSTer: Amiga Hardware Isn't a Simple Speed Race"
|
title: "Vampire V4, A600GS or MiSTer: Amiga Hardware Isn't a Simple Speed Race"
|
||||||
section: "retro"
|
section: "retro"
|
||||||
|
tags: "retro"
|
||||||
description: "Why raw benchmark numbers are actively misleading when comparing modern Amiga hardware options."
|
description: "Why raw benchmark numbers are actively misleading when comparing modern Amiga hardware options."
|
||||||
layout: base.njk
|
layout: base.njk
|
||||||
parent: "/"
|
parent: "/retro/"
|
||||||
---
|
---
|
||||||
<h1>Vampire V4, A600GS or MiSTer: Amiga Hardware Isn't a Simple Speed Race</h1>
|
<h1>Vampire V4, A600GS or MiSTer: Amiga Hardware Isn't a Simple Speed Race</h1>
|
||||||
<div class="detail-content">
|
<div class="detail-content">
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
---
|
---
|
||||||
title: "The Atari Mega ST Fleet: Three Machines, Three Jobs"
|
title: "The Atari Mega ST Fleet: Three Machines, Three Jobs"
|
||||||
section: "retro"
|
section: "retro"
|
||||||
|
tags: "retro"
|
||||||
description: "How three Atari Mega ST computers ended up with completely different roles in a modern retro setup."
|
description: "How three Atari Mega ST computers ended up with completely different roles in a modern retro setup."
|
||||||
layout: base.njk
|
layout: base.njk
|
||||||
parent: "/"
|
parent: "/retro/"
|
||||||
---
|
---
|
||||||
<h1>The Atari Mega ST Fleet: Three Machines, Three Jobs</h1>
|
<h1>The Atari Mega ST Fleet: Three Machines, Three Jobs</h1>
|
||||||
<div class="detail-content">
|
<div class="detail-content">
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
---
|
---
|
||||||
title: "The 15kHz Problem: Finding a Monitor That Speaks Atari"
|
title: "The 15kHz Problem: Finding a Monitor That Speaks Atari"
|
||||||
section: "retro"
|
section: "retro"
|
||||||
|
tags: "retro"
|
||||||
description: "Why most modern monitors refuse to display Atari ST colour modes, and which ones actually work."
|
description: "Why most modern monitors refuse to display Atari ST colour modes, and which ones actually work."
|
||||||
layout: base.njk
|
layout: base.njk
|
||||||
parent: "/"
|
parent: "/retro/"
|
||||||
---
|
---
|
||||||
<h1>The 15kHz Problem: Finding a Monitor That Speaks Atari</h1>
|
<h1>The 15kHz Problem: Finding a Monitor That Speaks Atari</h1>
|
||||||
<div class="detail-content">
|
<div class="detail-content">
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
---
|
---
|
||||||
title: "Getting a 40-Year-Old Computer Online: Atari ST Networking and BBS Access"
|
title: "Getting a 40-Year-Old Computer Online: Atari ST Networking and BBS Access"
|
||||||
section: "retro"
|
section: "retro"
|
||||||
|
tags: "retro"
|
||||||
description: "Connecting an Atari ST to the modern internet and to dial-up-era bulletin board systems, warts and all."
|
description: "Connecting an Atari ST to the modern internet and to dial-up-era bulletin board systems, warts and all."
|
||||||
layout: base.njk
|
layout: base.njk
|
||||||
parent: "/"
|
parent: "/retro/"
|
||||||
---
|
---
|
||||||
<h1>Getting a 40-Year-Old Computer Online: Atari ST Networking and BBS Access</h1>
|
<h1>Getting a 40-Year-Old Computer Online: Atari ST Networking and BBS Access</h1>
|
||||||
<div class="detail-content">
|
<div class="detail-content">
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
---
|
---
|
||||||
title: "Batocera in the Living Room: One Box, Every Retro System"
|
title: "Batocera in the Living Room: One Box, Every Retro System"
|
||||||
section: "retro"
|
section: "retro"
|
||||||
|
tags: "retro"
|
||||||
description: "Why the living room emulation station takes a completely different approach than the FPGA rigs upstairs."
|
description: "Why the living room emulation station takes a completely different approach than the FPGA rigs upstairs."
|
||||||
layout: base.njk
|
layout: base.njk
|
||||||
parent: "/"
|
parent: "/retro/"
|
||||||
---
|
---
|
||||||
<h1>Batocera in the Living Room: One Box, Every Retro System</h1>
|
<h1>Batocera in the Living Room: One Box, Every Retro System</h1>
|
||||||
<div class="detail-content">
|
<div class="detail-content">
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
---
|
---
|
||||||
title: "The 486 DX2-66: Rebuilding a DOS Era on MiSTer FPGA"
|
title: "The 486 DX2-66: Rebuilding a DOS Era on MiSTer FPGA"
|
||||||
section: "retro"
|
section: "retro"
|
||||||
|
tags: "retro"
|
||||||
description: "Recreating a Vobis-Highscreen 486 DX2-66 setup on MiSTer FPGA, down to the original DOS configuration."
|
description: "Recreating a Vobis-Highscreen 486 DX2-66 setup on MiSTer FPGA, down to the original DOS configuration."
|
||||||
layout: base.njk
|
layout: base.njk
|
||||||
parent: "/"
|
parent: "/retro/"
|
||||||
---
|
---
|
||||||
<h1>The 486 DX2-66: Rebuilding a DOS Era on MiSTer FPGA</h1>
|
<h1>The 486 DX2-66: Rebuilding a DOS Era on MiSTer FPGA</h1>
|
||||||
<div class="detail-content">
|
<div class="detail-content">
|
||||||
|
|||||||
@@ -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
|
|
||||||
}
|
|
||||||
};
|
|
||||||
};
|
|
||||||
@@ -1,9 +1,10 @@
|
|||||||
---
|
---
|
||||||
title: "Why an ET4000 Graphics Card and a Mega ST Don't Mix (Yet)"
|
title: "Why an ET4000 Graphics Card and a Mega ST Don't Mix (Yet)"
|
||||||
section: "retro"
|
section: "retro"
|
||||||
|
tags: "retro"
|
||||||
description: "A cautionary tale about pushing 1980s power supplies past their limits with a modern-ish graphics card."
|
description: "A cautionary tale about pushing 1980s power supplies past their limits with a modern-ish graphics card."
|
||||||
layout: base.njk
|
layout: base.njk
|
||||||
parent: "/"
|
parent: "/retro/"
|
||||||
---
|
---
|
||||||
<h1>Why an ET4000 Graphics Card and a Mega ST Don't Mix (Yet)</h1>
|
<h1>Why an ET4000 Graphics Card and a Mega ST Don't Mix (Yet)</h1>
|
||||||
<div class="detail-content">
|
<div class="detail-content">
|
||||||
|
|||||||
+14
-13
@@ -1,6 +1,7 @@
|
|||||||
---
|
---
|
||||||
title: "Retro Computing — Vintage Hardware & FPGA | moonweb"
|
title: "Retro Computing — Vintage Hardware & FPGA | moonweb"
|
||||||
section: "retro"
|
section: "retro"
|
||||||
|
tags: "retro"
|
||||||
description: "Physical retro hardware collection — Raspberry Pi units, Pi 400, and vintage computing setups."
|
description: "Physical retro hardware collection — Raspberry Pi units, Pi 400, and vintage computing setups."
|
||||||
layout: base.njk
|
layout: base.njk
|
||||||
sections:
|
sections:
|
||||||
@@ -8,51 +9,51 @@ sections:
|
|||||||
cards:
|
cards:
|
||||||
- title: "🖥️ The Mega ST Fleet"
|
- title: "🖥️ The Mega ST Fleet"
|
||||||
summary: "Three Mega ST 4 machines and a Mega ST 2, each with a clearly defined role from heirloom to workstation."
|
summary: "Three Mega ST 4 machines and a Mega ST 2, each with a clearly defined role from heirloom to workstation."
|
||||||
href: "/atari-mega-st-fleet/"
|
href: "/retro/atari-mega-st-fleet/"
|
||||||
- title: "⚡ ET4000 Experiment"
|
- title: "⚡ ET4000 Experiment"
|
||||||
summary: "Why bolting a graphics card onto a Mega ST overloads a 1980s power supply, and what actually solved the resolution problem."
|
summary: "Why bolting a graphics card onto a Mega ST overloads a 1980s power supply, and what actually solved the resolution problem."
|
||||||
href: "/et4000-experiment/"
|
href: "/retro/et4000-experiment/"
|
||||||
- title: "📺 Monitor Compatibility"
|
- title: "📺 Monitor Compatibility"
|
||||||
summary: "The 15kHz problem explained, and which modern monitor panels actually cooperate with Atari colour modes."
|
summary: "The 15kHz problem explained, and which modern monitor panels actually cooperate with Atari colour modes."
|
||||||
href: "/atari-monitor-compatibility/"
|
href: "/retro/atari-monitor-compatibility/"
|
||||||
- title: "🔌 Networking & BBS"
|
- title: "🔌 Networking & BBS"
|
||||||
summary: "Getting an Atari ST online via STinG and Wi-Fi modem hardware, and dialing into classic bulletin board systems."
|
summary: "Getting an Atari ST online via STinG and Wi-Fi modem hardware, and dialing into classic bulletin board systems."
|
||||||
href: "/atari-networking-bbs/"
|
href: "/retro/atari-networking-bbs/"
|
||||||
- heading: "Amiga Corner"
|
- heading: "Amiga Corner"
|
||||||
cards:
|
cards:
|
||||||
- title: "💾 AmigaOS on Modern Platforms"
|
- title: "💾 AmigaOS on Modern Platforms"
|
||||||
summary: "Running AmigaOS 3.2.3 on MiSTer FPGA versus Amiberry on a Raspberry Pi, and the HDF pitfalls to avoid."
|
summary: "Running AmigaOS 3.2.3 on MiSTer FPGA versus Amiberry on a Raspberry Pi, and the HDF pitfalls to avoid."
|
||||||
href: "/amiga-modern-platforms/"
|
href: "/retro/amiga-modern-platforms/"
|
||||||
- title: "🥧 The PiMiga Journey"
|
- title: "🥧 The PiMiga Journey"
|
||||||
summary: "Three generations of the PiMiga distro compared, and why the newest release isn't automatically the best one."
|
summary: "Three generations of the PiMiga distro compared, and why the newest release isn't automatically the best one."
|
||||||
href: "/pimiga-journey/"
|
href: "/retro/pimiga-journey/"
|
||||||
- title: "⚔️ Vampire, A600GS or MiSTer"
|
- title: "⚔️ Vampire, A600GS or MiSTer"
|
||||||
summary: "Why comparing modern Amiga hardware by benchmark numbers alone is actively misleading."
|
summary: "Why comparing modern Amiga hardware by benchmark numbers alone is actively misleading."
|
||||||
href: "/amiga-hardware-landscape/"
|
href: "/retro/amiga-hardware-landscape/"
|
||||||
- title: "📦 The A1200"
|
- title: "📦 The A1200"
|
||||||
summary: "What's confirmed so far about Retro Games Ltd.'s upcoming Amiga 1200 console, ahead of its 2026 launch."
|
summary: "What's confirmed so far about Retro Games Ltd.'s upcoming Amiga 1200 console, ahead of its 2026 launch."
|
||||||
href: "/the-a1200/"
|
href: "/retro/the-a1200/"
|
||||||
- heading: "FPGA & Emulation"
|
- heading: "FPGA & Emulation"
|
||||||
cards:
|
cards:
|
||||||
- title: "🧩 MiST and MiSTer"
|
- title: "🧩 MiST and MiSTer"
|
||||||
summary: "Two FPGA boards with two very different personalities, and why the newer one doesn't win every category."
|
summary: "Two FPGA boards with two very different personalities, and why the newer one doesn't win every category."
|
||||||
href: "/mist-mister-fpga/"
|
href: "/retro/mist-mister-fpga/"
|
||||||
- title: "🛋️ Batocera in the Living Room"
|
- title: "🛋️ Batocera in the Living Room"
|
||||||
summary: "The couch-friendly, software-emulation counterpart to the FPGA rigs, covering everything from Atari ST to arcade."
|
summary: "The couch-friendly, software-emulation counterpart to the FPGA rigs, covering everything from Atari ST to arcade."
|
||||||
href: "/batocera-living-room/"
|
href: "/retro/batocera-living-room/"
|
||||||
- title: "🕹️ Mini Arcades & Handhelds"
|
- title: "🕹️ Mini Arcades & Handhelds"
|
||||||
summary: "A buyer's guide to compact retro gaming devices, and how to spot a bootleg clone before you buy one."
|
summary: "A buyer's guide to compact retro gaming devices, and how to spot a bootleg clone before you buy one."
|
||||||
href: "/mini-arcade-handheld-roundup/"
|
href: "/retro/mini-arcade-handheld-roundup/"
|
||||||
- heading: "DOS & PC"
|
- heading: "DOS & PC"
|
||||||
cards:
|
cards:
|
||||||
- title: "💻 The 486 DX2-66"
|
- title: "💻 The 486 DX2-66"
|
||||||
summary: "A complete DOS environment rebuilt 1:1 on MiSTer FPGA, down to the original AUTOEXEC.BAT."
|
summary: "A complete DOS environment rebuilt 1:1 on MiSTer FPGA, down to the original AUTOEXEC.BAT."
|
||||||
href: "/dos-486-tower/"
|
href: "/retro/dos-486-tower/"
|
||||||
- heading: "Overview"
|
- heading: "Overview"
|
||||||
cards:
|
cards:
|
||||||
- title: "🏠 The Retro Corner Today"
|
- title: "🏠 The Retro Corner Today"
|
||||||
summary: "A room-by-room snapshot of every retro system currently active across the home."
|
summary: "A room-by-room snapshot of every retro system currently active across the home."
|
||||||
href: "/retro-corner-snapshot/"
|
href: "/retro/retro-corner-snapshot/"
|
||||||
|
|
||||||
---
|
---
|
||||||
<p class="site-intro">Hardware I've collected and kept running over the years. From Atari ST and Amiga to MiSTer FPGA and DOS environments.</p>
|
<p class="site-intro">Hardware I've collected and kept running over the years. From Atari ST and Amiga to MiSTer FPGA and DOS environments.</p>
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
---
|
---
|
||||||
title: "Mini Arcades and Handhelds: Sorting the Real Deal from the Bootlegs"
|
title: "Mini Arcades and Handhelds: Sorting the Real Deal from the Bootlegs"
|
||||||
section: "retro"
|
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."
|
description: "A buyer's guide to compact retro gaming devices, and why the cheapest option is almost never the right one."
|
||||||
layout: base.njk
|
layout: base.njk
|
||||||
parent: "/"
|
parent: "/retro/"
|
||||||
---
|
---
|
||||||
<h1>Mini Arcades and Handhelds: Sorting the Real Deal from the Bootlegs</h1>
|
<h1>Mini Arcades and Handhelds: Sorting the Real Deal from the Bootlegs</h1>
|
||||||
<div class="detail-content">
|
<div class="detail-content">
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
---
|
---
|
||||||
title: "MiST and MiSTer: Two FPGA Boxes, Two Very Different Personalities"
|
title: "MiST and MiSTer: Two FPGA Boxes, Two Very Different Personalities"
|
||||||
section: "retro"
|
section: "retro"
|
||||||
|
tags: "retro"
|
||||||
description: "Why the newer, more powerful MiSTer isn't automatically the better choice for every retro system."
|
description: "Why the newer, more powerful MiSTer isn't automatically the better choice for every retro system."
|
||||||
layout: base.njk
|
layout: base.njk
|
||||||
parent: "/"
|
parent: "/retro/"
|
||||||
---
|
---
|
||||||
<h1>MiST and MiSTer: Two FPGA Boxes, Two Very Different Personalities</h1>
|
<h1>MiST and MiSTer: Two FPGA Boxes, Two Very Different Personalities</h1>
|
||||||
<div class="detail-content">
|
<div class="detail-content">
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
---
|
---
|
||||||
title: "PiMiga 3 to 5: Chasing the Perfect Amiga-on-a-Pi Distro"
|
title: "PiMiga 3 to 5: Chasing the Perfect Amiga-on-a-Pi Distro"
|
||||||
section: "retro"
|
section: "retro"
|
||||||
|
tags: "retro"
|
||||||
description: "Three generations of PiMiga later, here's what actually changed — and why newer isn't always better."
|
description: "Three generations of PiMiga later, here's what actually changed — and why newer isn't always better."
|
||||||
layout: base.njk
|
layout: base.njk
|
||||||
parent: "/"
|
parent: "/retro/"
|
||||||
---
|
---
|
||||||
<h1>PiMiga 3 to 5: Chasing the Perfect Amiga-on-a-Pi Distro</h1>
|
<h1>PiMiga 3 to 5: Chasing the Perfect Amiga-on-a-Pi Distro</h1>
|
||||||
<div class="detail-content">
|
<div class="detail-content">
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
---
|
---
|
||||||
title: "The Retro Corner Today: A Snapshot of the Whole Setup"
|
title: "The Retro Corner Today: A Snapshot of the Whole Setup"
|
||||||
section: "retro"
|
section: "retro"
|
||||||
|
tags: "retro"
|
||||||
description: "A tour through every retro system currently active across the home, room by room."
|
description: "A tour through every retro system currently active across the home, room by room."
|
||||||
layout: base.njk
|
layout: base.njk
|
||||||
parent: "/"
|
parent: "/retro/"
|
||||||
---
|
---
|
||||||
<h1>The Retro Corner Today: A Snapshot of the Whole Setup</h1>
|
<h1>The Retro Corner Today: A Snapshot of the Whole Setup</h1>
|
||||||
<div class="detail-content">
|
<div class="detail-content">
|
||||||
|
|||||||
@@ -1,4 +0,0 @@
|
|||||||
User-agent: *
|
|
||||||
Allow: /
|
|
||||||
|
|
||||||
Sitemap: https://retro.moonweb.org/sitemap.xml
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
---
|
|
||||||
permalink: /sitemap.xml
|
|
||||||
eleventyExcludeFromCollections: true
|
|
||||||
---
|
|
||||||
{% include "sitemap.njk" %}
|
|
||||||
@@ -1,9 +1,10 @@
|
|||||||
---
|
---
|
||||||
title: "The A1200: What We Know Before It Ships"
|
title: "The A1200: What We Know Before It Ships"
|
||||||
section: "retro"
|
section: "retro"
|
||||||
|
tags: "retro"
|
||||||
description: "A close look at Retro Games Ltd.'s upcoming Amiga 1200 console, based on everything public so far."
|
description: "A close look at Retro Games Ltd.'s upcoming Amiga 1200 console, based on everything public so far."
|
||||||
layout: base.njk
|
layout: base.njk
|
||||||
parent: "/"
|
parent: "/retro/"
|
||||||
---
|
---
|
||||||
<h1>The A1200: What We Know Before It Ships</h1>
|
<h1>The A1200: What We Know Before It Ships</h1>
|
||||||
<div class="detail-content">
|
<div class="detail-content">
|
||||||
|
|||||||
@@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
Executable
+172
@@ -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
|
||||||
@@ -14,7 +14,7 @@ import urllib.request
|
|||||||
GITHUB_USER = "skoelle"
|
GITHUB_USER = "skoelle"
|
||||||
API_ROOT = "https://api.github.com"
|
API_ROOT = "https://api.github.com"
|
||||||
OUTPUT_PATH = os.path.join(
|
OUTPUT_PATH = os.path.join(
|
||||||
os.path.dirname(__file__), "..", "..", "code", "_data", "repos.json"
|
os.path.dirname(__file__), "..", "..", "_data", "repos.json"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+12
-12
@@ -17,12 +17,12 @@
|
|||||||
<meta name="twitter:title" content="{{ title }}{% if 'moonweb' not in title and 'stefankoelle' not in title %} · moonweb.org{% endif %}">
|
<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 }}">
|
<meta name="twitter:description" content="{{ description }}">
|
||||||
{% if ogImage %}<meta name="twitter:image" content="{{ site.url }}{{ ogImage }}">{% endif %}
|
{% 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.googleapis.com">
|
||||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||||
<link href="https://fonts.googleapis.com/css2?family=Lobster&display=swap" rel="stylesheet">
|
<link href="https://fonts.googleapis.com/css2?family=Lobster&display=swap" rel="stylesheet">
|
||||||
|
<link rel="icon" href="{% if section and section != 'hub' %}/{{ section }}/favicon.svg{% else %}/favicon.svg{% endif %}">
|
||||||
<link rel="stylesheet" href="/shared-base.css">
|
<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">
|
<script type="application/ld+json">
|
||||||
{
|
{
|
||||||
"@context": "https://schema.org",
|
"@context": "https://schema.org",
|
||||||
@@ -45,8 +45,8 @@
|
|||||||
"@context": "https://schema.org",
|
"@context": "https://schema.org",
|
||||||
"@type": "Person",
|
"@type": "Person",
|
||||||
"name": "Stefan Koelle",
|
"name": "Stefan Koelle",
|
||||||
"url": "https://hub.moonweb.org",
|
"url": "https://www.moonweb.org",
|
||||||
"image": "https://hub.moonweb.org/stefan-koelle-foto.jpg",
|
"image": "https://www.moonweb.org/stefan-koelle-foto.jpg",
|
||||||
"jobTitle": "Senior Software Developer & Architect",
|
"jobTitle": "Senior Software Developer & Architect",
|
||||||
"address": {
|
"address": {
|
||||||
"@type": "PostalAddress",
|
"@type": "PostalAddress",
|
||||||
@@ -66,14 +66,14 @@
|
|||||||
<body>
|
<body>
|
||||||
<header class="site-header">
|
<header class="site-header">
|
||||||
<div class="site-banner">
|
<div class="site-banner">
|
||||||
<span class="site-title">{{ section }}.moonweb.org</span>
|
<span class="site-title">moonweb.org{% if section and section != 'hub' %}/{{ section }}{% endif %}</span>
|
||||||
</div>
|
</div>
|
||||||
<nav class="site-switcher">
|
<nav class="site-switcher">
|
||||||
<a href="https://hub.moonweb.org">hub</a>
|
<a href="/">home</a>
|
||||||
<a href="https://infra.moonweb.org">infra</a>
|
<a href="/infra/">infra</a>
|
||||||
<a href="https://smarthome.moonweb.org">smarthome</a>
|
<a href="/smarthome/">smarthome</a>
|
||||||
<a href="https://code.moonweb.org">code</a>
|
<a href="/code/">code</a>
|
||||||
<a href="https://retro.moonweb.org">retro</a>
|
<a href="/retro/">retro</a>
|
||||||
<a href="https://stefankoelle.de">cv</a>
|
<a href="https://stefankoelle.de">cv</a>
|
||||||
</nav>
|
</nav>
|
||||||
</header>
|
</header>
|
||||||
@@ -84,8 +84,8 @@
|
|||||||
</main>
|
</main>
|
||||||
|
|
||||||
<footer class="site-footer">
|
<footer class="site-footer">
|
||||||
<p>Part of the <a href="https://hub.moonweb.org">moonweb.org</a> homelab.</p>
|
<p>Part of the <a href="/">moonweb.org</a> homelab.</p>
|
||||||
<p><a href="https://hub.moonweb.org/impressum/">Legal Notice & Privacy Policy</a></p>
|
<p><a href="/impressum/">Legal Notice & Privacy Policy</a></p>
|
||||||
</footer>
|
</footer>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
:root { --accent: #3E5098; }
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
:root { --accent: #99333A; }
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
:root { --accent: #8a6d3b; }
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
:root { --accent: #1f8a8a; }
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
---
|
|
||||||
title: "Page Not Found"
|
|
||||||
section: "smarthome"
|
|
||||||
description: "The page you are looking for does not exist."
|
|
||||||
layout: base.njk
|
|
||||||
permalink: /404.html
|
|
||||||
---
|
|
||||||
<div class="error-page">
|
|
||||||
<h1>404</h1>
|
|
||||||
<p>The page you are looking for does not exist or has been moved.</p>
|
|
||||||
<a class="back-link" href="/">← Back to smarthome</a>
|
|
||||||
</div>
|
|
||||||
@@ -1,7 +1,8 @@
|
|||||||
---
|
---
|
||||||
title: "Air Quality Dashboard"
|
title: "Air Quality Dashboard"
|
||||||
section: "smarthome"
|
section: "smarthome"
|
||||||
parent: "/"
|
tags: "smarthome"
|
||||||
|
parent: "/smarthome/"
|
||||||
description: "Real-time temperature and humidity monitoring across all rooms, powered by InfluxDB and Chart.js."
|
description: "Real-time temperature and humidity monitoring across all rooms, powered by InfluxDB and Chart.js."
|
||||||
layout: base.njk
|
layout: base.njk
|
||||||
---
|
---
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
---
|
---
|
||||||
title: "AirPlay Audio"
|
title: "AirPlay Audio"
|
||||||
section: "smarthome"
|
section: "smarthome"
|
||||||
parent: "/"
|
tags: "smarthome"
|
||||||
|
parent: "/smarthome/"
|
||||||
description: "Multi-room AirPlay receivers with software volume boost on Raspberry Pi devices."
|
description: "Multi-room AirPlay receivers with software volume boost on Raspberry Pi devices."
|
||||||
layout: base.njk
|
layout: base.njk
|
||||||
---
|
---
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
---
|
---
|
||||||
title: "Balkon Pi"
|
title: "Balkon Pi"
|
||||||
section: "smarthome"
|
section: "smarthome"
|
||||||
parent: "/"
|
tags: "smarthome"
|
||||||
|
parent: "/smarthome/"
|
||||||
description: "Balcony sensor, lighting, and audio automation running on a Raspberry Pi Zero W."
|
description: "Balcony sensor, lighting, and audio automation running on a Raspberry Pi Zero W."
|
||||||
layout: base.njk
|
layout: base.njk
|
||||||
---
|
---
|
||||||
|
|||||||
@@ -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
|
|
||||||
}
|
|
||||||
};
|
|
||||||
};
|
|
||||||
@@ -1,9 +1,10 @@
|
|||||||
---
|
---
|
||||||
title: "Home Control Buttons"
|
title: "Home Control Buttons"
|
||||||
section: "smarthome"
|
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."
|
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
|
layout: base.njk
|
||||||
parent: "/"
|
parent: "/smarthome/"
|
||||||
---
|
---
|
||||||
<h1>Home Control Buttons</h1>
|
<h1>Home Control Buttons</h1>
|
||||||
<div class="detail-content">
|
<div class="detail-content">
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
---
|
---
|
||||||
title: "HomematicIP + MQTT"
|
title: "HomematicIP + MQTT"
|
||||||
section: "smarthome"
|
section: "smarthome"
|
||||||
parent: "/"
|
tags: "smarthome"
|
||||||
|
parent: "/smarthome/"
|
||||||
description: "How HomematicIP room sensors are bridged into MQTT and stored in InfluxDB for dashboards."
|
description: "How HomematicIP room sensors are bridged into MQTT and stored in InfluxDB for dashboards."
|
||||||
layout: base.njk
|
layout: base.njk
|
||||||
---
|
---
|
||||||
|
|||||||
+10
-9
@@ -1,6 +1,7 @@
|
|||||||
---
|
---
|
||||||
title: "Smart Home Projects — Home Assistant, MQTT & IoT | moonweb"
|
title: "Smart Home Projects — Home Assistant, MQTT & IoT | moonweb"
|
||||||
section: "smarthome"
|
section: "smarthome"
|
||||||
|
tags: "smarthome"
|
||||||
description: "Smart home projects: Home Assistant, MQTT sensors, AirPlay audio, OctoPrint, and energy monitoring."
|
description: "Smart home projects: Home Assistant, MQTT sensors, AirPlay audio, OctoPrint, and energy monitoring."
|
||||||
layout: base.njk
|
layout: base.njk
|
||||||
sections:
|
sections:
|
||||||
@@ -8,19 +9,19 @@ sections:
|
|||||||
cards:
|
cards:
|
||||||
- title: "Home dashboard"
|
- title: "Home dashboard"
|
||||||
summary: "Lighting, music, and Tasmota device control shown on the home dashboard."
|
summary: "Lighting, music, and Tasmota device control shown on the home dashboard."
|
||||||
href: "/home-dashboard-controls/"
|
href: "/smarthome/home-dashboard-controls/"
|
||||||
emoji: "🎛️"
|
emoji: "🎛️"
|
||||||
- title: "Tasmota & energy monitoring"
|
- title: "Tasmota & energy monitoring"
|
||||||
summary: "Smart plugs and power monitoring across the flat, feeding daily usage stats."
|
summary: "Smart plugs and power monitoring across the flat, feeding daily usage stats."
|
||||||
href: "/tasmota-energy/"
|
href: "/smarthome/tasmota-energy/"
|
||||||
emoji: "⚡"
|
emoji: "⚡"
|
||||||
- title: "HomematicIP + MQTT"
|
- title: "HomematicIP + MQTT"
|
||||||
summary: "How HomematicIP room sensors are bridged into MQTT and stored in InfluxDB."
|
summary: "How HomematicIP room sensors are bridged into MQTT and stored in InfluxDB."
|
||||||
href: "/homematic-mqtt/"
|
href: "/smarthome/homematic-mqtt/"
|
||||||
emoji: "📡"
|
emoji: "📡"
|
||||||
- title: "Balkon Pi"
|
- title: "Balkon Pi"
|
||||||
summary: "Balcony sensor, lighting, and audio automation unit."
|
summary: "Balcony sensor, lighting, and audio automation unit."
|
||||||
href: "/balkonpi/"
|
href: "/smarthome/balkonpi/"
|
||||||
emoji: "🌿"
|
emoji: "🌿"
|
||||||
- heading: "Dashboards & Hardware"
|
- heading: "Dashboards & Hardware"
|
||||||
cards:
|
cards:
|
||||||
@@ -38,7 +39,7 @@ sections:
|
|||||||
emoji: "🖥️"
|
emoji: "🖥️"
|
||||||
- title: "Air quality dashboard"
|
- title: "Air quality dashboard"
|
||||||
summary: "Real-time temperature and humidity monitoring across all rooms with historical charts."
|
summary: "Real-time temperature and humidity monitoring across all rooms with historical charts."
|
||||||
href: "/air-quality/"
|
href: "/smarthome/air-quality/"
|
||||||
emoji: "🌡️"
|
emoji: "🌡️"
|
||||||
- heading: "Personal services"
|
- heading: "Personal services"
|
||||||
cards:
|
cards:
|
||||||
@@ -62,19 +63,19 @@ sections:
|
|||||||
cards:
|
cards:
|
||||||
- title: "AirPlay audio"
|
- title: "AirPlay audio"
|
||||||
summary: "Multi-room AirPlay receivers with software volume boost."
|
summary: "Multi-room AirPlay receivers with software volume boost."
|
||||||
href: "/airplay-audio/"
|
href: "/smarthome/airplay-audio/"
|
||||||
emoji: "🔊"
|
emoji: "🔊"
|
||||||
- title: "OctoPrint"
|
- title: "OctoPrint"
|
||||||
summary: "Remote 3D printer control and camera streaming."
|
summary: "Remote 3D printer control and camera streaming."
|
||||||
href: "/octoprint/"
|
href: "/smarthome/octoprint/"
|
||||||
emoji: "🖨️"
|
emoji: "🖨️"
|
||||||
- title: "YouTube archiving"
|
- title: "YouTube archiving"
|
||||||
summary: "TubeArchivist and TubeSync for self-hosted YouTube downloading on the Synology."
|
summary: "TubeArchivist and TubeSync for self-hosted YouTube downloading on the Synology."
|
||||||
href: "/tubearchivist/"
|
href: "/smarthome/tubearchivist/"
|
||||||
emoji: "📺"
|
emoji: "📺"
|
||||||
- title: "Kids RFID MP3 Player"
|
- title: "Kids RFID MP3 Player"
|
||||||
summary: "Arduino-based audio player for kids using RFID cards, built as a custom alternative to Hoerbert."
|
summary: "Arduino-based audio player for kids using RFID cards, built as a custom alternative to Hoerbert."
|
||||||
href: "/kids-rfid-player/"
|
href: "/smarthome/kids-rfid-player/"
|
||||||
emoji: "🎵"
|
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>
|
<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>
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
---
|
---
|
||||||
title: "Kids RFID MP3 Player"
|
title: "Kids RFID MP3 Player"
|
||||||
section: "smarthome"
|
section: "smarthome"
|
||||||
parent: "/"
|
tags: "smarthome"
|
||||||
|
parent: "/smarthome/"
|
||||||
description: "A custom Arduino-based RFID audio player for kids, inspired by the Hoerbert and Tonuino projects."
|
description: "A custom Arduino-based RFID audio player for kids, inspired by the Hoerbert and Tonuino projects."
|
||||||
layout: base.njk
|
layout: base.njk
|
||||||
---
|
---
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
---
|
---
|
||||||
title: "OctoPrint"
|
title: "OctoPrint"
|
||||||
section: "smarthome"
|
section: "smarthome"
|
||||||
parent: "/"
|
tags: "smarthome"
|
||||||
|
parent: "/smarthome/"
|
||||||
description: "Remote 3D printer control and camera streaming with OctoPrint on Raspberry Pi."
|
description: "Remote 3D printer control and camera streaming with OctoPrint on Raspberry Pi."
|
||||||
layout: base.njk
|
layout: base.njk
|
||||||
---
|
---
|
||||||
|
|||||||
@@ -1,4 +0,0 @@
|
|||||||
User-agent: *
|
|
||||||
Allow: /
|
|
||||||
|
|
||||||
Sitemap: https://smarthome.moonweb.org/sitemap.xml
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
---
|
|
||||||
permalink: /sitemap.xml
|
|
||||||
eleventyExcludeFromCollections: true
|
|
||||||
---
|
|
||||||
{% include "sitemap.njk" %}
|
|
||||||
@@ -1,7 +1,8 @@
|
|||||||
---
|
---
|
||||||
title: "Tasmota & Energy Monitoring"
|
title: "Tasmota & Energy Monitoring"
|
||||||
section: "smarthome"
|
section: "smarthome"
|
||||||
parent: "/"
|
tags: "smarthome"
|
||||||
|
parent: "/smarthome/"
|
||||||
description: "Smart plugs and power monitoring across the flat with daily usage statistics."
|
description: "Smart plugs and power monitoring across the flat with daily usage statistics."
|
||||||
layout: base.njk
|
layout: base.njk
|
||||||
---
|
---
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
---
|
---
|
||||||
title: "YouTube Archiving: TubeArchivist & TubeSync"
|
title: "YouTube Archiving: TubeArchivist & TubeSync"
|
||||||
section: "smarthome"
|
section: "smarthome"
|
||||||
parent: "/"
|
tags: "smarthome"
|
||||||
|
parent: "/smarthome/"
|
||||||
description: "Self-hosted YouTube archiving and media management running on the Synology NAS, comparing TubeArchivist and TubeSync."
|
description: "Self-hosted YouTube archiving and media management running on the Synology NAS, comparing TubeArchivist and TubeSync."
|
||||||
layout: base.njk
|
layout: base.njk
|
||||||
---
|
---
|
||||||
|
|||||||
Executable
+17
@@ -0,0 +1,17 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
cleanup() { kill $(jobs -p) 2>/dev/null; }
|
||||||
|
trap cleanup EXIT
|
||||||
|
|
||||||
|
echo "==> Starting moonweb dev servers..."
|
||||||
|
echo " http://localhost:8081 (moonweb sites)"
|
||||||
|
echo " http://localhost:8086 (stefankoelle.de)"
|
||||||
|
echo " http://localhost:8087 (timecapsule)"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
npx @11ty/eleventy --serve --port=8081 &
|
||||||
|
npx @11ty/eleventy --config=stefankoelle/eleventy.config.js --serve --port=8086 &
|
||||||
|
cd timecapsule && npx @11ty/eleventy --serve --port=8087 &
|
||||||
|
|
||||||
|
wait
|
||||||
@@ -26,11 +26,11 @@
|
|||||||
<span class="site-title">{{ siteTitle | default(section + ".moonweb.org") }}</span>
|
<span class="site-title">{{ siteTitle | default(section + ".moonweb.org") }}</span>
|
||||||
</div>
|
</div>
|
||||||
<nav class="site-switcher">
|
<nav class="site-switcher">
|
||||||
<a href="https://hub.moonweb.org">hub</a>
|
<a href="https://www.moonweb.org/">home</a>
|
||||||
<a href="https://infra.moonweb.org">infra</a>
|
<a href="https://www.moonweb.org/infra/">infra</a>
|
||||||
<a href="https://smarthome.moonweb.org">smarthome</a>
|
<a href="https://www.moonweb.org/smarthome/">smarthome</a>
|
||||||
<a href="https://code.moonweb.org">code</a>
|
<a href="https://www.moonweb.org/code/">code</a>
|
||||||
<a href="https://retro.moonweb.org">retro</a>
|
<a href="https://www.moonweb.org/retro/">retro</a>
|
||||||
<a href="https://stefankoelle.de">cv</a>
|
<a href="https://stefankoelle.de">cv</a>
|
||||||
</nav>
|
</nav>
|
||||||
</header>
|
</header>
|
||||||
@@ -41,8 +41,8 @@
|
|||||||
</main>
|
</main>
|
||||||
|
|
||||||
<footer class="site-footer">
|
<footer class="site-footer">
|
||||||
<p>Part of the <a href="https://hub.moonweb.org">moonweb.org</a> homelab.</p>
|
<p>Part of the <a href="https://www.moonweb.org/">moonweb.org</a> homelab.</p>
|
||||||
<p><a href="https://hub.moonweb.org/impressum/">Legal Notice & Privacy Policy</a></p>
|
<p><a href="https://www.moonweb.org/impressum/">Legal Notice & Privacy Policy</a></p>
|
||||||
</footer>
|
</footer>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user