feat: initial mono-repo with 30 vanity domain sites
Scaffold complete repo structure: - 28 static sites extracted from running containers on mlake - 2 dynamic sites (dasbes.de, dumusst.com) marked for separate handling - Template system with 6 templates (person-dark/light, product-dark, editorial, fun, minimal) - Shared CSS (variables, responsive, animations, noise overlay) - nginx config generator with multi-domain alias support - Build script with Docker-based nginx validation - add-site.sh helper for scaffolding new domains - Dockerfile for single nginx:alpine container Sites: clemensplassmann.de, danosi.de, deinesei.de, derkaiseristnackt.de, elefantenhor.de, fragina.de, frenchkis.de, ichbinaufbali.de, ichbinaufbarley.de, insain.de, julietensity.de, kainco.de (+keinco.de), kainstress.de, keinefreun.de, knzlmgmt.de, kopffrai.de, legalais.de, machesdocheinfach.de, mai-otto.de (+otto.flexsiebels.de, ottomatisch.de, ichbinotto.de), martinsiebels.de, matthiasbreier.de, osterai.de, paragraphenraiter.de, schulfrai.de, smartin3.de, sorgenfrai.de, vonschraitter.de, wartebitte.de Refs: otto#341
This commit is contained in:
190
render.sh
Executable file
190
render.sh
Executable file
@@ -0,0 +1,190 @@
|
||||
#!/bin/bash
|
||||
# Render a site from site.yaml + template
|
||||
# Usage: ./render.sh sites/example.de/site.yaml templates/person-dark.html
|
||||
# Outputs rendered HTML to stdout
|
||||
set -euo pipefail
|
||||
|
||||
SITE_YAML="$1"
|
||||
TEMPLATE_FILE="$2"
|
||||
SITE_DIR=$(dirname "$SITE_YAML")
|
||||
SCRIPT_DIR=$(cd "$(dirname "$0")" && pwd)
|
||||
|
||||
# Check dependencies
|
||||
if ! command -v yq &>/dev/null; then
|
||||
echo "ERROR: yq is required. Install: https://github.com/mikefarah/yq" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Read site config
|
||||
domain=$(yq -r '.domain' "$SITE_YAML")
|
||||
title=$(yq -r '.title // .domain' "$SITE_YAML")
|
||||
description=$(yq -r '.description // ""' "$SITE_YAML")
|
||||
lang=$(yq -r '.lang // "de"' "$SITE_YAML")
|
||||
favicon=$(yq -r '.vars.favicon // ""' "$SITE_YAML")
|
||||
name=$(yq -r '.vars.name // .title // ""' "$SITE_YAML")
|
||||
role=$(yq -r '.vars.role // ""' "$SITE_YAML")
|
||||
initials=$(yq -r '.vars.initials // ""' "$SITE_YAML")
|
||||
tagline=$(yq -r '.vars.tagline // ""' "$SITE_YAML")
|
||||
accent=$(yq -r '.vars.accent // "#c9a84c"' "$SITE_YAML")
|
||||
accent_light=$(yq -r '.vars.accent_light // "rgba(201, 168, 76, 0.1)"' "$SITE_YAML")
|
||||
font_primary=$(yq -r '.vars.font_primary // "Inter"' "$SITE_YAML")
|
||||
font_secondary=$(yq -r '.vars.font_secondary // "Inter"' "$SITE_YAML")
|
||||
emoji=$(yq -r '.vars.emoji // ""' "$SITE_YAML")
|
||||
cta_text=$(yq -r '.vars.cta.text // ""' "$SITE_YAML")
|
||||
cta_href=$(yq -r '.vars.cta.href // "#"' "$SITE_YAML")
|
||||
year=$(date +%Y)
|
||||
|
||||
# Build tags HTML
|
||||
tags_html=""
|
||||
tag_count=$(yq -r '.vars.tags | length' "$SITE_YAML" 2>/dev/null || echo "0")
|
||||
if [ "$tag_count" -gt 0 ]; then
|
||||
for i in $(seq 0 $((tag_count - 1))); do
|
||||
tag=$(yq -r ".vars.tags[$i]" "$SITE_YAML")
|
||||
tags_html="${tags_html} <span class=\"tag\">${tag}</span>\n"
|
||||
done
|
||||
fi
|
||||
|
||||
# Build sections HTML
|
||||
sections_html=""
|
||||
section_count=$(yq -r '.vars.sections | length' "$SITE_YAML" 2>/dev/null || echo "0")
|
||||
if [ "$section_count" -gt 0 ]; then
|
||||
for i in $(seq 0 $((section_count - 1))); do
|
||||
sec_type=$(yq -r ".vars.sections[$i].type" "$SITE_YAML")
|
||||
sec_title=$(yq -r ".vars.sections[$i].title // \"\"" "$SITE_YAML")
|
||||
|
||||
if [ "$sec_type" = "features" ]; then
|
||||
sections_html="${sections_html} <div class=\"section fade-in stagger-$((i+2))\">\n"
|
||||
[ -n "$sec_title" ] && sections_html="${sections_html} <h2>${sec_title}</h2>\n"
|
||||
sections_html="${sections_html} <div class=\"grid\">\n"
|
||||
|
||||
item_count=$(yq -r ".vars.sections[$i].items | length" "$SITE_YAML" 2>/dev/null || echo "0")
|
||||
for j in $(seq 0 $((item_count - 1))); do
|
||||
item_title=$(yq -r ".vars.sections[$i].items[$j].title" "$SITE_YAML")
|
||||
item_desc=$(yq -r ".vars.sections[$i].items[$j].desc // \"\"" "$SITE_YAML")
|
||||
sections_html="${sections_html} <div class=\"card\">\n"
|
||||
sections_html="${sections_html} <h3>${item_title}</h3>\n"
|
||||
[ -n "$item_desc" ] && sections_html="${sections_html} <p>${item_desc}</p>\n"
|
||||
sections_html="${sections_html} </div>\n"
|
||||
done
|
||||
|
||||
sections_html="${sections_html} </div>\n </div>\n"
|
||||
|
||||
elif [ "$sec_type" = "profile" ]; then
|
||||
bio=$(yq -r ".vars.sections[$i].bio // \"\"" "$SITE_YAML")
|
||||
sections_html="${sections_html} <div class=\"section fade-in stagger-$((i+2))\">\n"
|
||||
[ -n "$sec_title" ] && sections_html="${sections_html} <h2>${sec_title}</h2>\n"
|
||||
sections_html="${sections_html} <p class=\"bio\">${bio}</p>\n"
|
||||
sections_html="${sections_html} </div>\n"
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
# Build content HTML (for editorial template)
|
||||
content_html=""
|
||||
content=$(yq -r '.vars.content // ""' "$SITE_YAML")
|
||||
if [ -n "$content" ] && [ "$content" != "null" ]; then
|
||||
# Convert newlines to paragraphs
|
||||
content_html=$(echo "$content" | sed 's/^$/<\/p><p>/g' | sed '1s/^/<p>/' | sed '$s/$/<\/p>/')
|
||||
fi
|
||||
|
||||
# Read template file and extract CSS/body sections
|
||||
template_content=$(cat "$TEMPLATE_FILE")
|
||||
|
||||
# Extract template CSS (between markers)
|
||||
template_css=$(echo "$template_content" | sed -n '/{{template_css_start}}/,/{{template_css_end}}/p' | sed '1d;$d')
|
||||
|
||||
# Extract template body (between markers)
|
||||
template_body=$(echo "$template_content" | sed -n '/{{template_body_start}}/,/{{template_body_end}}/p' | sed '1d;$d')
|
||||
|
||||
# Read base template
|
||||
base=$(cat "$SCRIPT_DIR/templates/base.html")
|
||||
|
||||
# Read shared CSS
|
||||
css_variables=$(cat "$SCRIPT_DIR/shared/css/variables.css")
|
||||
css_responsive=$(cat "$SCRIPT_DIR/shared/css/responsive.css")
|
||||
css_animations=$(cat "$SCRIPT_DIR/shared/css/animations.css")
|
||||
css_noise=$(cat "$SCRIPT_DIR/shared/css/noise.css")
|
||||
fonts=$(cat "$SCRIPT_DIR/shared/fonts.html")
|
||||
|
||||
# Additional font links if non-Inter fonts are used
|
||||
extra_fonts=""
|
||||
if [ "$font_primary" != "Inter" ]; then
|
||||
font_url=$(echo "$font_primary" | tr ' ' '+')
|
||||
extra_fonts="${extra_fonts} <link href=\"https://fonts.googleapis.com/css2?family=${font_url}:wght@300;400;500;600;700&display=swap\" rel=\"stylesheet\">\n"
|
||||
fi
|
||||
if [ "$font_secondary" != "Inter" ] && [ "$font_secondary" != "$font_primary" ]; then
|
||||
font_url=$(echo "$font_secondary" | tr ' ' '+')
|
||||
extra_fonts="${extra_fonts} <link href=\"https://fonts.googleapis.com/css2?family=${font_url}:wght@400;500;700&display=swap\" rel=\"stylesheet\">\n"
|
||||
fi
|
||||
fonts="${fonts}${extra_fonts}"
|
||||
|
||||
# Assemble: substitute into base template
|
||||
output="$base"
|
||||
|
||||
# Replace shared includes
|
||||
output=$(echo "$output" | sed "s|{{fonts}}|$(echo "$fonts" | sed 's/[&/\]/\\&/g; s/$/\\/' | sed '$ s/\\$//')|")
|
||||
|
||||
# For CSS blocks, use temp files to handle multiline
|
||||
tmpdir=$(mktemp -d)
|
||||
trap 'rm -rf "$tmpdir"' EXIT
|
||||
|
||||
echo "$css_variables" > "$tmpdir/css_variables"
|
||||
echo "$css_responsive" > "$tmpdir/css_responsive"
|
||||
echo "$css_animations" > "$tmpdir/css_animations"
|
||||
echo "$css_noise" > "$tmpdir/css_noise"
|
||||
echo "$template_css" > "$tmpdir/template_css"
|
||||
printf '%b' "$template_body" > "$tmpdir/template_body"
|
||||
|
||||
# Use awk for reliable multiline substitution
|
||||
awk -v vars="$tmpdir/css_variables" \
|
||||
-v resp="$tmpdir/css_responsive" \
|
||||
-v anim="$tmpdir/css_animations" \
|
||||
-v noise="$tmpdir/css_noise" \
|
||||
-v tcss="$tmpdir/template_css" \
|
||||
-v tbody="$tmpdir/template_body" \
|
||||
-v fonts_file=<(echo "$fonts") \
|
||||
'
|
||||
function read_file(path, line, content) {
|
||||
content = ""
|
||||
while ((getline line < path) > 0) content = content line "\n"
|
||||
close(path)
|
||||
return content
|
||||
}
|
||||
BEGIN {
|
||||
cv = read_file(vars)
|
||||
cr = read_file(resp)
|
||||
ca = read_file(anim)
|
||||
cn = read_file(noise)
|
||||
tc = read_file(tcss)
|
||||
tb = read_file(tbody)
|
||||
}
|
||||
{
|
||||
gsub(/\{\{css_variables\}\}/, cv)
|
||||
gsub(/\{\{css_responsive\}\}/, cr)
|
||||
gsub(/\{\{css_animations\}\}/, ca)
|
||||
gsub(/\{\{css_noise\}\}/, cn)
|
||||
gsub(/\{\{template_css\}\}/, tc)
|
||||
gsub(/\{\{body\}\}/, tb)
|
||||
print
|
||||
}
|
||||
' <<< "$output" | sed \
|
||||
-e "s|{{title}}|${title}|g" \
|
||||
-e "s|{{description}}|${description}|g" \
|
||||
-e "s|{{lang}}|${lang}|g" \
|
||||
-e "s|{{favicon}}|${favicon}|g" \
|
||||
-e "s|{{name}}|${name}|g" \
|
||||
-e "s|{{role}}|${role}|g" \
|
||||
-e "s|{{initials}}|${initials}|g" \
|
||||
-e "s|{{tagline}}|${tagline}|g" \
|
||||
-e "s|{{accent}}|${accent}|g" \
|
||||
-e "s|{{accent_light}}|${accent_light}|g" \
|
||||
-e "s|{{font_primary}}|${font_primary}|g" \
|
||||
-e "s|{{font_secondary}}|${font_secondary}|g" \
|
||||
-e "s|{{emoji}}|${emoji}|g" \
|
||||
-e "s|{{cta_text}}|${cta_text}|g" \
|
||||
-e "s|{{cta_href}}|${cta_href}|g" \
|
||||
-e "s|{{year}}|${year}|g" \
|
||||
-e "s|{{domain}}|${domain}|g" | \
|
||||
sed "s|{{tags_html}}|$(printf '%b' "$tags_html" | sed 's/[&/\]/\\&/g; s/$/\\/' | sed '$ s/\\$//')|g" | \
|
||||
sed "s|{{sections_html}}|$(printf '%b' "$sections_html" | sed 's/[&/\]/\\&/g; s/$/\\/' | sed '$ s/\\$//')|g" | \
|
||||
sed "s|{{content_html}}|$(printf '%b' "$content_html" | sed 's/[&/\]/\\&/g; s/$/\\/' | sed '$ s/\\$//')|g"
|
||||
Reference in New Issue
Block a user