mirror of
https://github.com/skoelle/moonweb-site.git
synced 2026-09-18 01:10:25 +00:00
Consolidate all moonweb sites into single Eleventy config
- Single eleventy.config.js at root with computed pathPrefix per section - Accent colors inlined in base.njk (no per-site theme.css needed) - Hub pages output at root via explicit permalinks - Per-section sitemaps with tag-based collections - .eleventyignore excludes stefankoelle, timecapsule, markdown files - Deleted: 5 per-site configs, merge script - Simplified: package.json (single build/dev commands) - Simplified: GitHub Actions workflow (no matrix, no merge job) - Fixed: parent links for sub-site detail pages
This commit is contained in:
@@ -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
|
||||||
@@ -21,12 +21,8 @@ env:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
name: Build ${{ matrix.site }}
|
name: Build moonweb sites
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
|
||||||
site: [hub, infra, smarthome, code, retro]
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v7
|
- uses: actions/checkout@v7
|
||||||
|
|
||||||
@@ -38,118 +34,45 @@ jobs:
|
|||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: npm ci
|
run: npm ci
|
||||||
|
|
||||||
- name: Build ${{ matrix.site }}
|
- name: Build all moonweb sites
|
||||||
run: npm run build:${{ matrix.site }}
|
run: npm run build
|
||||||
|
|
||||||
- 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
|
|
||||||
|
|
||||||
build-timecapsule:
|
|
||||||
name: Build timecapsule
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v7
|
|
||||||
|
|
||||||
- uses: actions/setup-node@v7
|
|
||||||
with:
|
|
||||||
node-version: ${{ env.NODE_VERSION }}
|
|
||||||
|
|
||||||
- name: Install timecapsule dependencies
|
|
||||||
run: cd timecapsule && npm ci
|
|
||||||
|
|
||||||
- name: Build timecapsule
|
- name: Build timecapsule
|
||||||
run: npm run build:timecapsule
|
run: npm run build:timecapsule
|
||||||
|
|
||||||
- name: Validate build output
|
- name: Validate build output
|
||||||
run: |
|
run: |
|
||||||
if [ ! -d "dist/timecapsule" ]; then
|
|
||||||
echo "Error: dist/timecapsule not found"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
if [ -z "$(ls -A dist/timecapsule 2>/dev/null)" ]; then
|
|
||||||
echo "Error: dist/timecapsule is empty"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
echo "Build output:"
|
echo "Build output:"
|
||||||
find dist/timecapsule -type f | head -20
|
|
||||||
|
|
||||||
- name: Upload build artifact
|
|
||||||
uses: actions/upload-artifact@v7
|
|
||||||
with:
|
|
||||||
name: dist-timecapsule
|
|
||||||
path: dist/timecapsule
|
|
||||||
retention-days: 7
|
|
||||||
|
|
||||||
merge:
|
|
||||||
name: Merge all sites
|
|
||||||
needs: [build, build-timecapsule]
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v7
|
|
||||||
|
|
||||||
- name: Download all artifacts
|
|
||||||
uses: actions/download-artifact@v8
|
|
||||||
with:
|
|
||||||
path: artifacts/
|
|
||||||
|
|
||||||
- name: Merge into dist/
|
|
||||||
run: |
|
|
||||||
mkdir -p dist
|
|
||||||
|
|
||||||
# Hub becomes root
|
|
||||||
cp -r artifacts/dist-hub/* dist/
|
|
||||||
|
|
||||||
# Subdirectories
|
|
||||||
for site in infra smarthome code retro timecapsule; do
|
|
||||||
if [ -d "artifacts/dist-$site" ]; then
|
|
||||||
cp -r "artifacts/dist-$site" "dist/$site"
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
# Copy shared CSS to root (accessible from all subdirectories)
|
|
||||||
cp shared/base.css dist/shared-base.css
|
|
||||||
cp shared/favicon/hub.svg dist/favicon.svg
|
|
||||||
|
|
||||||
echo "Final structure:"
|
|
||||||
find dist -maxdepth 2 -type f | sort | head -40
|
find dist -maxdepth 2 -type f | sort | head -40
|
||||||
echo "..."
|
echo "..."
|
||||||
echo "Total files:"
|
echo "Total files:"
|
||||||
find dist -type f | wc -l
|
find dist -type f | wc -l
|
||||||
|
|
||||||
- name: Upload merged artifact
|
# Verify key directories exist
|
||||||
|
for dir in "" infra smarthome code retro timecapsule; do
|
||||||
|
if [ -n "$dir" ] && [ ! -d "dist/$dir" ]; then
|
||||||
|
echo "Error: dist/$dir not found"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
- name: Upload build artifact
|
||||||
uses: actions/upload-artifact@v7
|
uses: actions/upload-artifact@v7
|
||||||
with:
|
with:
|
||||||
name: dist-moonweb-merged
|
name: dist-moonweb
|
||||||
path: dist/
|
path: dist/
|
||||||
retention-days: 7
|
retention-days: 7
|
||||||
|
|
||||||
deploy:
|
deploy:
|
||||||
name: Deploy to IONOS
|
name: Deploy to IONOS
|
||||||
needs: merge
|
needs: build
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
|
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
|
||||||
steps:
|
steps:
|
||||||
- name: Download merged artifact
|
- name: Download build artifact
|
||||||
uses: actions/download-artifact@v8
|
uses: actions/download-artifact@v8
|
||||||
with:
|
with:
|
||||||
name: dist-moonweb-merged
|
name: dist-moonweb
|
||||||
path: dist/
|
path: dist/
|
||||||
|
|
||||||
- name: Deploy via SFTP
|
- name: Deploy via SFTP
|
||||||
|
|||||||
+2
-1
@@ -1,9 +1,10 @@
|
|||||||
---
|
---
|
||||||
title: "Page Not Found"
|
title: "Page Not Found"
|
||||||
section: "code"
|
section: "code"
|
||||||
|
tags: "code"
|
||||||
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: /code/404.html
|
||||||
---
|
---
|
||||||
<div class="error-page">
|
<div class="error-page">
|
||||||
<h1>404</h1>
|
<h1>404</h1>
|
||||||
|
|||||||
@@ -1,20 +0,0 @@
|
|||||||
module.exports = function (eleventyConfig) {
|
|
||||||
eleventyConfig.addGlobalData("site", { url: "https://www.moonweb.org", pathPrefix: "/code" });
|
|
||||||
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,5 +1,6 @@
|
|||||||
---
|
---
|
||||||
permalink: /sitemap.xml
|
permalink: /code/sitemap.xml
|
||||||
|
sitemapCollection: "code"
|
||||||
eleventyExcludeFromCollections: true
|
eleventyExcludeFromCollections: true
|
||||||
---
|
---
|
||||||
{% include "sitemap.njk" %}
|
{% include "sitemap.njk" %}
|
||||||
|
|||||||
@@ -0,0 +1,93 @@
|
|||||||
|
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" });
|
||||||
|
|
||||||
|
// Per-section assets: favicon, robots.txt
|
||||||
|
for (const [section, cfg] of Object.entries(SITES)) {
|
||||||
|
const prefix = cfg.pathPrefix || ".";
|
||||||
|
eleventyConfig.addPassthroughCopy({
|
||||||
|
[`shared/favicon/${section}.svg`]: `${prefix}/favicon.svg`,
|
||||||
|
});
|
||||||
|
eleventyConfig.addPassthroughCopy({
|
||||||
|
[`${section}/robots.txt`]: `${prefix}/robots.txt`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hub: profile photo
|
||||||
|
eleventyConfig.addPassthroughCopy({
|
||||||
|
"hub/stefan-koelle-foto.jpg": "stefan-koelle-foto.jpg",
|
||||||
|
});
|
||||||
|
|
||||||
|
// Hub: old www.moonweb.org redirects (.htm → .html)
|
||||||
|
const htmRedirects = [
|
||||||
|
"start",
|
||||||
|
"index2",
|
||||||
|
"index3",
|
||||||
|
"new",
|
||||||
|
"links",
|
||||||
|
"infos",
|
||||||
|
"hardware",
|
||||||
|
"users",
|
||||||
|
];
|
||||||
|
for (const name of htmRedirects) {
|
||||||
|
eleventyConfig.addPassthroughCopy({
|
||||||
|
[`hub/${name}.htm`]: `${name}.html`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
const htmSubdirs = [
|
||||||
|
"beginning",
|
||||||
|
"projects",
|
||||||
|
"products",
|
||||||
|
"acoustics",
|
||||||
|
"location",
|
||||||
|
"background",
|
||||||
|
"partners",
|
||||||
|
"contact",
|
||||||
|
"network",
|
||||||
|
"stefan",
|
||||||
|
"legal-notice",
|
||||||
|
];
|
||||||
|
for (const dir of htmSubdirs) {
|
||||||
|
eleventyConfig.addPassthroughCopy({
|
||||||
|
[`hub/${dir}/index.htm`]: `${dir}/index.html`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
dir: {
|
||||||
|
input: ".",
|
||||||
|
includes: "shared/_includes",
|
||||||
|
output: "dist",
|
||||||
|
},
|
||||||
|
serverOptions: {
|
||||||
|
host: "0.0.0.0",
|
||||||
|
port: 8081,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
||||||
@@ -1,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
|
||||||
|
|||||||
@@ -1,46 +0,0 @@
|
|||||||
module.exports = function (eleventyConfig) {
|
|
||||||
eleventyConfig.addGlobalData("site", { url: "https://www.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" });
|
|
||||||
|
|
||||||
// Old www.moonweb.org redirects (root-level HTML shortcuts)
|
|
||||||
// .htm files are excluded from Eleventy processing, renamed to .html on output
|
|
||||||
eleventyConfig.addPassthroughCopy({ "hub/start.htm": "start.html" });
|
|
||||||
eleventyConfig.addPassthroughCopy({ "hub/index2.htm": "index2.html" });
|
|
||||||
eleventyConfig.addPassthroughCopy({ "hub/index3.htm": "index3.html" });
|
|
||||||
eleventyConfig.addPassthroughCopy({ "hub/new.htm": "new.html" });
|
|
||||||
eleventyConfig.addPassthroughCopy({ "hub/links.htm": "links.html" });
|
|
||||||
eleventyConfig.addPassthroughCopy({ "hub/infos.htm": "infos.html" });
|
|
||||||
eleventyConfig.addPassthroughCopy({ "hub/hardware.htm": "hardware.html" });
|
|
||||||
eleventyConfig.addPassthroughCopy({ "hub/users.htm": "users.html" });
|
|
||||||
|
|
||||||
// Old www.moonweb.org redirects (content subdirectories)
|
|
||||||
// .htm files are excluded from Eleventy processing, renamed to .html on output
|
|
||||||
eleventyConfig.addPassthroughCopy({ "hub/beginning/index.htm": "beginning/index.html" });
|
|
||||||
eleventyConfig.addPassthroughCopy({ "hub/projects/index.htm": "projects/index.html" });
|
|
||||||
eleventyConfig.addPassthroughCopy({ "hub/products/index.htm": "products/index.html" });
|
|
||||||
eleventyConfig.addPassthroughCopy({ "hub/acoustics/index.htm": "acoustics/index.html" });
|
|
||||||
eleventyConfig.addPassthroughCopy({ "hub/location/index.htm": "location/index.html" });
|
|
||||||
eleventyConfig.addPassthroughCopy({ "hub/background/index.htm": "background/index.html" });
|
|
||||||
eleventyConfig.addPassthroughCopy({ "hub/partners/index.htm": "partners/index.html" });
|
|
||||||
eleventyConfig.addPassthroughCopy({ "hub/contact/index.htm": "contact/index.html" });
|
|
||||||
eleventyConfig.addPassthroughCopy({ "hub/network/index.htm": "network/index.html" });
|
|
||||||
eleventyConfig.addPassthroughCopy({ "hub/stefan/index.htm": "stefan/index.html" });
|
|
||||||
eleventyConfig.addPassthroughCopy({ "hub/legal-notice/index.htm": "legal-notice/index.html" });
|
|
||||||
|
|
||||||
return {
|
|
||||||
dir: {
|
|
||||||
input: "hub",
|
|
||||||
includes: "../shared/_includes",
|
|
||||||
output: "dist/hub"
|
|
||||||
},
|
|
||||||
serverOptions: {
|
|
||||||
host: "0.0.0.0",
|
|
||||||
port: 8081
|
|
||||||
}
|
|
||||||
};
|
|
||||||
};
|
|
||||||
@@ -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
|
||||||
---
|
---
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
---
|
---
|
||||||
permalink: /sitemap.xml
|
permalink: /sitemap.xml
|
||||||
|
sitemapCollection: "hub"
|
||||||
eleventyExcludeFromCollections: true
|
eleventyExcludeFromCollections: true
|
||||||
---
|
---
|
||||||
{% include "sitemap.njk" %}
|
{% include "sitemap.njk" %}
|
||||||
|
|||||||
+2
-1
@@ -1,9 +1,10 @@
|
|||||||
---
|
---
|
||||||
title: "Page Not Found"
|
title: "Page Not Found"
|
||||||
section: "infra"
|
section: "infra"
|
||||||
|
tags: "infra"
|
||||||
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: /infra/404.html
|
||||||
---
|
---
|
||||||
<div class="error-page">
|
<div class="error-page">
|
||||||
<h1>404</h1>
|
<h1>404</h1>
|
||||||
|
|||||||
@@ -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://www.moonweb.org", pathPrefix: "/infra" });
|
|
||||||
eleventyConfig.addFilter("date", (d) => d.toISOString());
|
|
||||||
eleventyConfig.addPassthroughCopy({ "shared/theme-infra.css": "theme.css" });
|
|
||||||
eleventyConfig.addPassthroughCopy({ "shared/base.css": "shared-base.css" });
|
|
||||||
eleventyConfig.addPassthroughCopy({ "shared/favicon/infra.svg": "favicon.svg" });
|
|
||||||
eleventyConfig.addPassthroughCopy({ "infra/robots.txt": "robots.txt" });
|
|
||||||
|
|
||||||
return {
|
|
||||||
dir: {
|
|
||||||
input: "infra",
|
|
||||||
includes: "../shared/_includes",
|
|
||||||
output: "dist/infra"
|
|
||||||
},
|
|
||||||
serverOptions: {
|
|
||||||
host: "0.0.0.0",
|
|
||||||
port: 8082
|
|
||||||
}
|
|
||||||
};
|
|
||||||
};
|
|
||||||
@@ -1,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:
|
||||||
|
|||||||
+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,5 +1,6 @@
|
|||||||
---
|
---
|
||||||
permalink: /sitemap.xml
|
permalink: /infra/sitemap.xml
|
||||||
|
sitemapCollection: "infra"
|
||||||
eleventyExcludeFromCollections: true
|
eleventyExcludeFromCollections: true
|
||||||
---
|
---
|
||||||
{% include "sitemap.njk" %}
|
{% 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
|
||||||
---
|
---
|
||||||
|
|||||||
+3
-14
@@ -4,24 +4,13 @@
|
|||||||
"private": true,
|
"private": true,
|
||||||
"description": "Monorepo for hub/infra/smarthome/code/retro/timecapsule (Eleventy, static, deployed to IONOS SFTP)",
|
"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:timecapsule": "cd timecapsule && npx @11ty/eleventy --serve --port=8087",
|
"dev:timecapsule": "cd timecapsule && npx @11ty/eleventy --serve --port=8087",
|
||||||
"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 & npm run dev:timecapsule",
|
"build": "npx @11ty/eleventy",
|
||||||
"build:hub": "npx @11ty/eleventy --config=hub/eleventy.config.js",
|
|
||||||
"build:infra": "npx @11ty/eleventy --config=infra/eleventy.config.js",
|
|
||||||
"build:smarthome": "npx @11ty/eleventy --config=smarthome/eleventy.config.js",
|
|
||||||
"build:code": "npx @11ty/eleventy --config=code/eleventy.config.js",
|
|
||||||
"build:retro": "npx @11ty/eleventy --config=retro/eleventy.config.js",
|
|
||||||
"build:stefankoelle": "npx @11ty/eleventy --config=stefankoelle/eleventy.config.js",
|
"build:stefankoelle": "npx @11ty/eleventy --config=stefankoelle/eleventy.config.js",
|
||||||
"build:timecapsule": "cd timecapsule && npx @11ty/eleventy && mkdir -p ../../dist/timecapsule && cp -r _site/timecapsule/* ../../dist/timecapsule/",
|
"build:timecapsule": "cd timecapsule && npx @11ty/eleventy && mkdir -p ../../dist/timecapsule && cp -r _site/timecapsule/* ../../dist/timecapsule/",
|
||||||
"build": "npm run build:hub && npm run build:infra && npm run build:smarthome && npm run build:code && npm run build:retro && npm run build:timecapsule && npm run build:stefankoelle",
|
"build:moonweb": "npm run build && npm run build:timecapsule",
|
||||||
"build:moonweb": "npm run build:hub && npm run build:infra && npm run build:smarthome && npm run build:code && npm run build:retro && npm run build:timecapsule",
|
|
||||||
"merge:moonweb": "bash scripts/merge-moonweb.sh",
|
|
||||||
"pdf:cv": "bash stefankoelle/pdf/build-pdf.sh",
|
"pdf:cv": "bash stefankoelle/pdf/build-pdf.sh",
|
||||||
"prebuild": "bash prebuild.sh"
|
"prebuild": "bash prebuild.sh"
|
||||||
},
|
},
|
||||||
|
|||||||
+2
-1
@@ -1,9 +1,10 @@
|
|||||||
---
|
---
|
||||||
title: "Page Not Found"
|
title: "Page Not Found"
|
||||||
section: "retro"
|
section: "retro"
|
||||||
|
tags: "retro"
|
||||||
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: /retro/404.html
|
||||||
---
|
---
|
||||||
<div class="error-page">
|
<div class="error-page">
|
||||||
<h1>404</h1>
|
<h1>404</h1>
|
||||||
|
|||||||
@@ -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://www.moonweb.org", pathPrefix: "/retro" });
|
|
||||||
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">
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
@@ -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,5 +1,6 @@
|
|||||||
---
|
---
|
||||||
permalink: /sitemap.xml
|
permalink: /retro/sitemap.xml
|
||||||
|
sitemapCollection: "retro"
|
||||||
eleventyExcludeFromCollections: true
|
eleventyExcludeFromCollections: true
|
||||||
---
|
---
|
||||||
{% include "sitemap.njk" %}
|
{% 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">
|
||||||
|
|||||||
@@ -1,49 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
set -e
|
|
||||||
|
|
||||||
echo "Merging moonweb sites for deployment..."
|
|
||||||
|
|
||||||
# Clean previous merge
|
|
||||||
rm -rf dist/root
|
|
||||||
|
|
||||||
# Hub becomes root
|
|
||||||
echo " hub → dist/ (root)"
|
|
||||||
cp -r dist/hub dist/root
|
|
||||||
rm -rf dist/hub
|
|
||||||
|
|
||||||
# Copy timecapsule from its build output
|
|
||||||
if [ -d "timecapsule/_site/timecapsule" ]; then
|
|
||||||
echo " timecapsule → dist/root/timecapsule/"
|
|
||||||
cp -r timecapsule/_site/timecapsule dist/root/timecapsule
|
|
||||||
else
|
|
||||||
echo " WARNING: timecapsule/_site/timecapsule not found, skipping"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Subdirectories
|
|
||||||
for site in infra smarthome code retro; do
|
|
||||||
if [ -d "dist/$site" ]; then
|
|
||||||
echo " $site → dist/root/$site/"
|
|
||||||
cp -r "dist/$site" "dist/root/$site"
|
|
||||||
else
|
|
||||||
echo " WARNING: dist/$site not found, skipping"
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
# Copy CSS to root (accessible from all subdirectories)
|
|
||||||
echo " Copying shared CSS to root..."
|
|
||||||
cp shared/base.css dist/root/shared-base.css
|
|
||||||
cp shared/favicon/hub.svg dist/root/favicon.svg
|
|
||||||
|
|
||||||
# Theme CSS files are already copied during build as theme.css
|
|
||||||
# The root theme is hub's theme
|
|
||||||
if [ -f "dist/root/theme.css" ]; then
|
|
||||||
echo " Root theme.css already in place"
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo ""
|
|
||||||
echo "Merge complete. Final structure:"
|
|
||||||
find dist/root -maxdepth 2 -type f | sort | head -50
|
|
||||||
echo "..."
|
|
||||||
echo ""
|
|
||||||
echo "Total files:"
|
|
||||||
find dist/root -type f | wc -l
|
|
||||||
@@ -20,8 +20,8 @@
|
|||||||
<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="stylesheet" href="{{ site.pathPrefix | default('') }}/shared-base.css">
|
<link rel="stylesheet" href="/shared-base.css">
|
||||||
<link rel="stylesheet" href="{{ site.pathPrefix | default('') }}/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",
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
|
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
|
||||||
{%- for page in collections.all %}
|
{%- set sitemapPages = collections[sitemapCollection] if sitemapCollection else collections.all %}
|
||||||
|
{%- for page in sitemapPages %}
|
||||||
{%- if not page.url.includes("404") %}
|
{%- if not page.url.includes("404") %}
|
||||||
<url>
|
<url>
|
||||||
<loc>{{ site.url }}{{ site.pathPrefix | default("") }}{{ page.url }}</loc>
|
<loc>{{ site.url }}{{ pathPrefix }}{{ page.url }}</loc>
|
||||||
<lastmod>{{ page.date | date }}</lastmod>
|
<lastmod>{{ page.date | date }}</lastmod>
|
||||||
<changefreq>{% if page.url == "/" %}weekly{% else %}monthly{% endif %}</changefreq>
|
<changefreq>{% if page.url == "/" %}weekly{% else %}monthly{% endif %}</changefreq>
|
||||||
<priority>{% if page.url == "/" %}1.0{% elif parent %}0.8{% else %}0.7{% endif %}</priority>
|
<priority>{% if page.url == "/" %}1.0{% elif parent %}0.8{% else %}0.7{% endif %}</priority>
|
||||||
|
|||||||
+2
-1
@@ -1,9 +1,10 @@
|
|||||||
---
|
---
|
||||||
title: "Page Not Found"
|
title: "Page Not Found"
|
||||||
section: "smarthome"
|
section: "smarthome"
|
||||||
|
tags: "smarthome"
|
||||||
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: /smarthome/404.html
|
||||||
---
|
---
|
||||||
<div class="error-page">
|
<div class="error-page">
|
||||||
<h1>404</h1>
|
<h1>404</h1>
|
||||||
|
|||||||
@@ -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://www.moonweb.org", pathPrefix: "/smarthome" });
|
|
||||||
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
|
||||||
---
|
---
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
@@ -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,5 +1,6 @@
|
|||||||
---
|
---
|
||||||
permalink: /sitemap.xml
|
permalink: /smarthome/sitemap.xml
|
||||||
|
sitemapCollection: "smarthome"
|
||||||
eleventyExcludeFromCollections: true
|
eleventyExcludeFromCollections: true
|
||||||
---
|
---
|
||||||
{% include "sitemap.njk" %}
|
{% 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
|
||||||
---
|
---
|
||||||
|
|||||||
Reference in New Issue
Block a user