diff --git a/apps/manacore/apps/landing/src/content/config.ts b/apps/manacore/apps/landing/src/content/config.ts index 5fd2b7922..2168f52d3 100644 --- a/apps/manacore/apps/landing/src/content/config.ts +++ b/apps/manacore/apps/landing/src/content/config.ts @@ -212,6 +212,22 @@ const manascoreCollection = defineCollection({ seo: z.number().min(0).max(100), }) .optional(), + // Dependency health metrics + dependencies: z + .object({ + total: z.number(), // Total dependency count + outdated: z.number(), // Packages with available updates + vulnerabilities: z + .object({ + critical: z.number().default(0), + high: z.number().default(0), + moderate: z.number().default(0), + low: z.number().default(0), + }) + .optional(), + lastChecked: z.string().optional(), // ISO date + }) + .optional(), // Score history for trend visualization history: z .array( diff --git a/apps/manacore/apps/landing/src/content/manascore/2026-03-19-calendar.md b/apps/manacore/apps/landing/src/content/manascore/2026-03-19-calendar.md index 8eeb0ba8e..33891dde3 100644 --- a/apps/manacore/apps/landing/src/content/manascore/2026-03-19-calendar.md +++ b/apps/manacore/apps/landing/src/content/manascore/2026-03-19-calendar.md @@ -18,6 +18,15 @@ scores: documentation: 98 security: 92 ux: 95 +dependencies: + total: 42 + outdated: 9 + vulnerabilities: + critical: 0 + high: 0 + moderate: 0 + low: 0 + lastChecked: '2026-03-24' lighthouse: performance: 92 accessibility: 95 diff --git a/apps/manacore/apps/landing/src/content/manascore/2026-03-19-todo.md b/apps/manacore/apps/landing/src/content/manascore/2026-03-19-todo.md index ae5c2e9e8..ec14546af 100644 --- a/apps/manacore/apps/landing/src/content/manascore/2026-03-19-todo.md +++ b/apps/manacore/apps/landing/src/content/manascore/2026-03-19-todo.md @@ -18,6 +18,15 @@ scores: documentation: 95 security: 90 ux: 94 +dependencies: + total: 38 + outdated: 9 + vulnerabilities: + critical: 0 + high: 0 + moderate: 0 + low: 0 + lastChecked: '2026-03-24' lighthouse: performance: 90 accessibility: 93 diff --git a/apps/manacore/apps/landing/src/pages/manascore/[slug].astro b/apps/manacore/apps/landing/src/pages/manascore/[slug].astro index b5814fbcb..ccc23e7e1 100644 --- a/apps/manacore/apps/landing/src/pages/manascore/[slug].astro +++ b/apps/manacore/apps/landing/src/pages/manascore/[slug].astro @@ -274,6 +274,113 @@ function getBarColor(score: number): string { })() } + {/* Dependency Health */} + { + audit.data.dependencies && + (() => { + const deps = audit.data.dependencies; + const vulnCount = deps.vulnerabilities + ? deps.vulnerabilities.critical + + deps.vulnerabilities.high + + deps.vulnerabilities.moderate + + deps.vulnerabilities.low + : 0; + const healthPct = Math.round(((deps.total - deps.outdated) / deps.total) * 100); + + return ( +
+
+
+

Dependency Health

+

+ Paketstand und Sicherheit + {deps.lastChecked ? ` (geprüft ${deps.lastChecked})` : ''} +

+
+ = 80 ? 'text-emerald-500' : healthPct >= 60 ? 'text-yellow-500' : 'text-red-500'}`} + > + {healthPct}% aktuell + +
+
+
+ {deps.total} + Pakete gesamt +
+
+ 10 ? 'text-yellow-500' : 'text-foreground'}`} + > + {deps.outdated} + + Veraltet +
+
+ 0 ? 'text-red-500' : 'text-emerald-500'}`} + > + {vulnCount} + + Vulnerabilities +
+
+ {deps.vulnerabilities ? ( +
+ {deps.vulnerabilities.critical > 0 && ( + + {deps.vulnerabilities.critical} Critical + + )} + {deps.vulnerabilities.high > 0 && ( + + {deps.vulnerabilities.high} High + + )} + {deps.vulnerabilities.moderate > 0 && ( + + {deps.vulnerabilities.moderate} Mod + + )} + {deps.vulnerabilities.low > 0 && ( + + {deps.vulnerabilities.low} Low + + )} + {vulnCount === 0 && ( + + ✓ Sicher + + )} +
+ ) : ( + Keine Daten + )} + Schweregrad +
+
+ {/* Health bar */} +
+
+
= 80 ? 'bg-emerald-500' : healthPct >= 60 ? 'bg-yellow-500' : 'bg-red-500'}`} + style={`width: ${healthPct}%`} + /> +
+
+ + {deps.outdated} veraltet + + + {deps.total - deps.outdated} aktuell + +
+
+
+ ); + })() + } + {/* Stats */} { audit.data.stats && ( diff --git a/apps/manacore/apps/landing/src/pages/manascore/index.astro b/apps/manacore/apps/landing/src/pages/manascore/index.astro index b04385372..27d3be9bf 100644 --- a/apps/manacore/apps/landing/src/pages/manascore/index.astro +++ b/apps/manacore/apps/landing/src/pages/manascore/index.astro @@ -172,6 +172,46 @@ const statuses = [...new Set(sortedAudits.map((a) => a.data.status))]; ))}
+ {/* Dependency health */} + {data.dependencies && + (() => { + const deps = data.dependencies; + const vulnCount = deps.vulnerabilities + ? deps.vulnerabilities.critical + + deps.vulnerabilities.high + + deps.vulnerabilities.moderate + + deps.vulnerabilities.low + : 0; + const healthPct = Math.round( + ((deps.total - deps.outdated) / deps.total) * 100 + ); + const healthColor = + vulnCount > 0 + ? deps.vulnerabilities?.critical || deps.vulnerabilities?.high + ? 'text-red-500' + : 'text-yellow-500' + : healthPct >= 80 + ? 'text-emerald-500' + : 'text-yellow-500'; + return ( +
+ Deps: + + {deps.total} total, {deps.outdated} outdated + + {vulnCount > 0 ? ( + + ⚠ {vulnCount} vulnerabilities + + ) : ( + + ✓ no vulnerabilities + + )} +
+ ); + })()} + {/* Lighthouse scores */} {data.lighthouse && (
diff --git a/cloudflared-config.yml b/cloudflared-config.yml index 94cc97d3c..8f1bcd791 100644 --- a/cloudflared-config.yml +++ b/cloudflared-config.yml @@ -114,5 +114,27 @@ ingress: - hostname: glitchtip.mana.how service: http://localhost:8020 + # Self-Hosted Landing Pages (via Nginx on port 4400) + - hostname: it.mana.how + service: http://localhost:4400 + - hostname: chats.mana.how + service: http://localhost:4400 + - hostname: pics.mana.how + service: http://localhost:4400 + - hostname: zitares.mana.how + service: http://localhost:4400 + - hostname: presis.mana.how + service: http://localhost:4400 + - hostname: clocks.mana.how + service: http://localhost:4400 + - hostname: manadeck.mana.how + service: http://localhost:4400 + - hostname: nutriphi.mana.how + service: http://localhost:4400 + - hostname: citycorners.mana.how + service: http://localhost:4400 + - hostname: docs.mana.how + service: http://localhost:4400 + # Catch-all - service: http_status:404 diff --git a/docker-compose.macmini.yml b/docker-compose.macmini.yml index b9d30c86c..a6a179e0e 100644 --- a/docker-compose.macmini.yml +++ b/docker-compose.macmini.yml @@ -87,6 +87,25 @@ services: done " + # Self-hosted Landing Pages (replaces Cloudflare Pages) + # Serves all Astro landing page dist/ folders via Nginx + # Build with: ./scripts/mac-mini/build-landings.sh + landings: + image: nginx:alpine + container_name: mana-infra-landings + restart: always + volumes: + - ./docker/nginx/landings.conf:/etc/nginx/conf.d/default.conf:ro + - ./docker/nginx/snippets:/etc/nginx/snippets:ro + - /Volumes/ManaData/landings:/srv/landings:ro + ports: + - "4400:80" + healthcheck: + test: ["CMD", "wget", "-q", "--spider", "http://localhost/health"] + interval: 30s + timeout: 5s + retries: 3 + redis: image: redis:7-alpine container_name: mana-infra-redis @@ -871,9 +890,10 @@ services: - "4080:80" healthcheck: test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:80/"] - interval: 120s + interval: 180s timeout: 10s retries: 3 + start_period: 20s matrix-web: build: @@ -893,9 +913,10 @@ services: - "4090:5180" healthcheck: test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://127.0.0.1:5180/health"] - interval: 120s + interval: 180s timeout: 10s retries: 3 + start_period: 20s # Matrix Bots (Ports 4010-4029) matrix-mana-bot: @@ -932,7 +953,7 @@ services: - "4010:4010" healthcheck: test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://127.0.0.1:4010/health"] - interval: 120s + interval: 300s timeout: 10s retries: 3 start_period: 40s @@ -961,7 +982,7 @@ services: - "4011:4011" healthcheck: test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://127.0.0.1:4011/health"] - interval: 120s + interval: 300s timeout: 10s retries: 3 start_period: 40s @@ -1002,7 +1023,7 @@ services: - "4012:4012" healthcheck: test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://127.0.0.1:4012/health"] - interval: 120s + interval: 300s timeout: 10s retries: 3 start_period: 40s @@ -1035,7 +1056,7 @@ services: - "4013:4013" healthcheck: test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://127.0.0.1:4013/health"] - interval: 120s + interval: 300s timeout: 10s retries: 3 start_period: 40s @@ -1071,7 +1092,7 @@ services: - "4014:4014" healthcheck: test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://127.0.0.1:4014/health"] - interval: 120s + interval: 300s timeout: 10s retries: 3 start_period: 40s @@ -1105,7 +1126,7 @@ services: - "4015:4015" healthcheck: test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://127.0.0.1:4015/health"] - interval: 120s + interval: 300s timeout: 10s retries: 3 start_period: 40s @@ -1139,7 +1160,7 @@ services: - "4016:4016" healthcheck: test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://127.0.0.1:4016/health"] - interval: 120s + interval: 300s timeout: 10s retries: 3 start_period: 40s @@ -1169,7 +1190,7 @@ services: - "4017:4017" healthcheck: test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://127.0.0.1:4017/health"] - interval: 120s + interval: 300s timeout: 10s retries: 3 start_period: 40s @@ -1210,7 +1231,7 @@ services: - "4018:4018" healthcheck: test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://127.0.0.1:4018/health"] - interval: 120s + interval: 300s timeout: 10s retries: 3 start_period: 40s @@ -1244,7 +1265,7 @@ services: - "4019:4019" healthcheck: test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://127.0.0.1:4019/health"] - interval: 120s + interval: 300s timeout: 10s retries: 3 start_period: 40s @@ -1277,7 +1298,7 @@ services: - "4021:4021" healthcheck: test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://127.0.0.1:4021/health"] - interval: 120s + interval: 300s timeout: 10s retries: 3 start_period: 40s @@ -1314,7 +1335,7 @@ services: - "4020:4020" healthcheck: test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://127.0.0.1:4020/health"] - interval: 120s + interval: 300s timeout: 10s retries: 3 start_period: 40s @@ -1354,7 +1375,7 @@ services: - "4022:4022" healthcheck: test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://127.0.0.1:4022/health"] - interval: 120s + interval: 300s timeout: 10s retries: 3 start_period: 40s @@ -1907,9 +1928,10 @@ services: - "8000:8000" healthcheck: test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://127.0.0.1:8000/api/health"] - interval: 120s + interval: 300s timeout: 10s retries: 3 + start_period: 30s umami: image: ghcr.io/umami-software/umami:postgresql-latest @@ -1927,7 +1949,7 @@ services: - "8010:3000" healthcheck: test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://127.0.0.1:3000/api/heartbeat"] - interval: 120s + interval: 300s timeout: 10s retries: 3 start_period: 40s @@ -1956,9 +1978,10 @@ services: - "9090:9090" healthcheck: test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://127.0.0.1:9090/health"] - interval: 120s + interval: 300s timeout: 10s retries: 3 + start_period: 20s pushgateway: image: prom/pushgateway:v1.7.0 @@ -1968,9 +1991,10 @@ services: - "9091:9091" healthcheck: test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://127.0.0.1:9091/-/healthy"] - interval: 120s + interval: 300s timeout: 10s retries: 3 + start_period: 10s cadvisor: image: gcr.io/cadvisor/cadvisor:v0.49.1 @@ -1987,9 +2011,10 @@ services: - "9110:8080" healthcheck: test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://127.0.0.1:8080/healthz"] - interval: 120s + interval: 300s timeout: 10s retries: 3 + start_period: 20s postgres-exporter: image: prometheuscommunity/postgres-exporter:v0.15.0 @@ -2036,9 +2061,10 @@ services: - "9100:9100" healthcheck: test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://127.0.0.1:9100/metrics"] - interval: 120s + interval: 300s timeout: 10s retries: 3 + start_period: 10s # ============================================ # Alerting Stack (Ports 9093-9095) @@ -2067,9 +2093,10 @@ services: - "8880:8880" healthcheck: test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://127.0.0.1:8880/health"] - interval: 120s + interval: 300s timeout: 10s retries: 3 + start_period: 20s alertmanager: image: prom/alertmanager:v0.27.0 @@ -2089,9 +2116,10 @@ services: - "9093:9093" healthcheck: test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://127.0.0.1:9093/-/healthy"] - interval: 120s + interval: 300s timeout: 10s retries: 3 + start_period: 10s alert-notifier: build: @@ -2109,10 +2137,10 @@ services: - "9095:8080" healthcheck: test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://127.0.0.1:8080/health"] - interval: 120s + interval: 300s timeout: 5s retries: 3 - start_period: 5s + start_period: 10s # ============================================ # Auto-Update (Watchtower) @@ -2162,9 +2190,10 @@ services: condition: service_healthy healthcheck: test: ["CMD", "python3", "-c", "import urllib.request; urllib.request.urlopen('http://localhost:8020/_health/')"] - interval: 120s + interval: 300s timeout: 10s retries: 3 + start_period: 40s glitchtip-worker: image: glitchtip/glitchtip:latest @@ -2203,9 +2232,10 @@ services: - "5100:5100" healthcheck: test: ["CMD", "wget", "-q", "--spider", "http://localhost:5100/"] - interval: 120s + interval: 180s timeout: 10s retries: 3 + start_period: 20s volumes: redis_data: diff --git a/docker/nginx/landings.conf b/docker/nginx/landings.conf new file mode 100644 index 000000000..563109109 --- /dev/null +++ b/docker/nginx/landings.conf @@ -0,0 +1,108 @@ +# Nginx Configuration for Self-Hosted Landing Pages +# Each server block serves a different landing page from its dist/ directory +# All traffic comes through Cloudflare Tunnel → localhost:4400 + +# Shared settings +gzip on; +gzip_vary on; +gzip_min_length 1024; +gzip_types text/plain text/css text/xml text/javascript application/javascript application/json image/svg+xml; + +# Default server (catch-all → it.mana.how as homepage) +server { + listen 80 default_server; + server_name _; + root /srv/landings/it; + index index.html; + include /etc/nginx/snippets/landing-common.conf; +} + +# it.mana.how — European Tech Independence +server { + listen 80; + server_name it.mana.how; + root /srv/landings/it; + index index.html; + include /etc/nginx/snippets/landing-common.conf; +} + +# chats.mana.how — Chat Landing +server { + listen 80; + server_name chats.mana.how; + root /srv/landings/chat; + index index.html; + include /etc/nginx/snippets/landing-common.conf; +} + +# pics.mana.how — Picture Landing +server { + listen 80; + server_name pics.mana.how; + root /srv/landings/picture; + index index.html; + include /etc/nginx/snippets/landing-common.conf; +} + +# zitares.mana.how — Zitare Landing +server { + listen 80; + server_name zitares.mana.how; + root /srv/landings/zitare; + index index.html; + include /etc/nginx/snippets/landing-common.conf; +} + +# presis.mana.how — Presi Landing +server { + listen 80; + server_name presis.mana.how; + root /srv/landings/presi; + index index.html; + include /etc/nginx/snippets/landing-common.conf; +} + +# clocks.mana.how — Clock Landing +server { + listen 80; + server_name clocks.mana.how; + root /srv/landings/clock; + index index.html; + include /etc/nginx/snippets/landing-common.conf; +} + +# manadeck.mana.how — ManaDeck Landing +server { + listen 80; + server_name manadeck.mana.how; + root /srv/landings/manadeck; + index index.html; + include /etc/nginx/snippets/landing-common.conf; +} + +# nutriphi.mana.how — NutriPhi Landing +server { + listen 80; + server_name nutriphi.mana.how; + root /srv/landings/nutriphi; + index index.html; + include /etc/nginx/snippets/landing-common.conf; +} + +# citycorners.mana.how — CityCorners Landing +server { + listen 80; + server_name citycorners.mana.how; + root /srv/landings/citycorners; + index index.html; + include /etc/nginx/snippets/landing-common.conf; +} + +# docs.mana.how — Documentation +server { + listen 80; + server_name docs.mana.how; + root /srv/landings/docs; + index index.html; + include /etc/nginx/snippets/landing-common.conf; +} diff --git a/docker/nginx/snippets/landing-common.conf b/docker/nginx/snippets/landing-common.conf new file mode 100644 index 000000000..26bfd99cc --- /dev/null +++ b/docker/nginx/snippets/landing-common.conf @@ -0,0 +1,24 @@ +# Common settings for all landing page server blocks + +# Security headers +add_header X-Frame-Options "SAMEORIGIN" always; +add_header X-Content-Type-Options "nosniff" always; +add_header Referrer-Policy "strict-origin-when-cross-origin" always; + +# Cache static assets aggressively (Astro hashes filenames) +location ~* \.(jpg|jpeg|png|gif|ico|css|js|svg|woff|woff2|ttf|eot|webp|avif)$ { + expires 1y; + add_header Cache-Control "public, immutable"; +} + +# SPA fallback: try file, then directory, then index.html +location / { + try_files $uri $uri/ $uri/index.html /index.html; +} + +# Health check +location /health { + access_log off; + return 200 "healthy\n"; + add_header Content-Type text/plain; +} diff --git a/docs/CLOUDFLARE_FALLBACK.md b/docs/CLOUDFLARE_FALLBACK.md new file mode 100644 index 000000000..493ba2ba2 --- /dev/null +++ b/docs/CLOUDFLARE_FALLBACK.md @@ -0,0 +1,285 @@ +# Cloudflare Tunnel Fallback-Plan + +> Was tun wenn Cloudflare ausfällt oder den Account sperrt? + +## Risiko + +Cloudflare Tunnel ist der **einzige** Weg vom Internet zum Mac Mini. Wenn Cloudflare nicht erreichbar ist: +- Alle *.mana.how Subdomains sind offline +- SSH nur im lokalen Netzwerk möglich +- Kein Deployment, kein Monitoring + +**Wahrscheinlichkeit:** Gering (Cloudflare hat >99.99% Uptime), aber Accountsperren oder Policy-Änderungen sind ein Risiko. + +## Plan B: WireGuard + Caddy auf Hetzner VPS + +### Architektur + +``` +Internet + │ + ▼ +Hetzner VPS (€3.79/Monat, CX22) +├── Caddy (Reverse Proxy + Auto-TLS) +├── WireGuard Server +└── DNS: *.mana.how → VPS IP + │ + │ WireGuard Tunnel (verschlüsselt) + │ + ▼ +Mac Mini (WireGuard Client) +├── Alle Services auf localhost +└── Erreichbar über WireGuard-IP (z.B. 10.0.0.2) +``` + +### Vorteile + +- **Kein Vendor Lock-in:** Hetzner ist deutscher Anbieter +- **Eigene IP:** Keine Abhängigkeit von Cloudflare Proxy +- **WireGuard:** Schneller als Cloudflare Tunnel (~10% weniger Latenz) +- **Let's Encrypt:** Caddy macht TLS automatisch +- **Kosten:** €3.79/Monat (CX22: 2 vCPU, 4 GB RAM, 40 GB SSD) + +### Einrichtung VPS (einmalig, ~1 Stunde) + +#### 1. Hetzner VPS erstellen + +```bash +# CX22 (kleinster mit genug RAM für Caddy + WireGuard) +# Standort: Falkenstein (DE) oder Nürnberg (DE) +# OS: Ubuntu 24.04 +# SSH Key: Mac Mini public key +``` + +#### 2. WireGuard installieren + +**Auf dem VPS:** +```bash +apt update && apt install -y wireguard + +# Keys generieren +wg genkey | tee /etc/wireguard/server_private.key | wg pubkey > /etc/wireguard/server_public.key +chmod 600 /etc/wireguard/server_private.key + +# Config erstellen +cat > /etc/wireguard/wg0.conf << EOF +[Interface] +Address = 10.0.0.1/24 +PrivateKey = $(cat /etc/wireguard/server_private.key) +ListenPort = 51820 + +[Peer] +# Mac Mini +PublicKey = +AllowedIPs = 10.0.0.2/32 +EOF + +systemctl enable --now wg-quick@wg0 +``` + +**Auf dem Mac Mini:** +```bash +brew install wireguard-tools + +# Keys generieren +wg genkey | tee /etc/wireguard/client_private.key | wg pubkey > /etc/wireguard/client_public.key + +# Config +cat > /etc/wireguard/wg0.conf << EOF +[Interface] +Address = 10.0.0.2/24 +PrivateKey = $(cat /etc/wireguard/client_private.key) + +[Peer] +PublicKey = +Endpoint = :51820 +AllowedIPs = 10.0.0.0/24 +PersistentKeepalive = 25 +EOF + +wg-quick up wg0 +``` + +#### 3. Caddy installieren (VPS) + +```bash +apt install -y debian-keyring debian-archive-keyring apt-transport-https +curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg +curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | tee /etc/apt/sources.list.d/caddy-stable.list +apt update && apt install caddy +``` + +#### 4. Caddyfile erstellen (VPS) + +```Caddyfile +# /etc/caddy/Caddyfile +# Alle Domains → Mac Mini via WireGuard + +mana.how { + reverse_proxy 10.0.0.2:5000 +} + +auth.mana.how { + reverse_proxy 10.0.0.2:3001 +} + +chat.mana.how { + reverse_proxy 10.0.0.2:5010 +} + +chat-api.mana.how { + reverse_proxy 10.0.0.2:3030 +} + +todo.mana.how { + reverse_proxy 10.0.0.2:5011 +} + +todo-api.mana.how { + reverse_proxy 10.0.0.2:3031 +} + +calendar.mana.how { + reverse_proxy 10.0.0.2:5012 +} + +calendar-api.mana.how { + reverse_proxy 10.0.0.2:3032 +} + +clock.mana.how { + reverse_proxy 10.0.0.2:5013 +} + +clock-api.mana.how { + reverse_proxy 10.0.0.2:3033 +} + +contacts.mana.how { + reverse_proxy 10.0.0.2:5014 +} + +contacts-api.mana.how { + reverse_proxy 10.0.0.2:3034 +} + +storage.mana.how { + reverse_proxy 10.0.0.2:5015 +} + +storage-api.mana.how { + reverse_proxy 10.0.0.2:3035 +} + +presi.mana.how { + reverse_proxy 10.0.0.2:5016 +} + +presi-api.mana.how { + reverse_proxy 10.0.0.2:3036 +} + +nutriphi.mana.how { + reverse_proxy 10.0.0.2:5017 +} + +nutriphi-api.mana.how { + reverse_proxy 10.0.0.2:3037 +} + +photos.mana.how { + reverse_proxy 10.0.0.2:5019 +} + +photos-api.mana.how { + reverse_proxy 10.0.0.2:3039 +} + +mukke.mana.how { + reverse_proxy 10.0.0.2:5180 +} + +picture.mana.how { + reverse_proxy 10.0.0.2:5021 +} + +picture-api.mana.how { + reverse_proxy 10.0.0.2:3040 +} + +playground.mana.how { + reverse_proxy 10.0.0.2:5090 +} + +matrix.mana.how { + reverse_proxy 10.0.0.2:4000 +} + +element.mana.how { + reverse_proxy 10.0.0.2:4080 +} + +grafana.mana.how { + reverse_proxy 10.0.0.2:8000 +} + +stats.mana.how { + reverse_proxy 10.0.0.2:8010 +} + +glitchtip.mana.how { + reverse_proxy 10.0.0.2:8020 +} +``` + +#### 5. DNS umstellen (Failover-Schritt) + +Beim Ausfall von Cloudflare Tunnel: + +```bash +# 1. WireGuard-Verbindung prüfen +ssh mana-server "ping -c1 10.0.0.1" # Ping VPS via WireGuard + +# 2. DNS bei Cloudflare umstellen (alle *.mana.how → VPS IP) +# Cloudflare Dashboard → DNS → *.mana.how → A Record → +# ODER falls Cloudflare komplett down: +# Domain zu anderem DNS-Provider transferieren (vorher vorbereiten!) + +# 3. Caddy starten +ssh vps "systemctl start caddy" + +# 4. Prüfen +curl https://mana.how # Sollte über VPS → WireGuard → Mac Mini routen +``` + +## Failover-Checkliste + +| # | Schritt | Zeit | Verantwortlich | +|---|---------|------|----------------| +| 1 | Feststellen: Cloudflare Tunnel ist down | Auto (Health Check Alert) | Automatisch | +| 2 | VPS WireGuard-Verbindung prüfen | 1 Min | Admin | +| 3 | DNS auf VPS-IP umstellen | 5 Min | Admin (Cloudflare Dashboard) | +| 4 | Caddy aktivieren | 1 Min | Admin (SSH zu VPS) | +| 5 | TLS-Zertifikate generieren lassen | 2-5 Min | Automatisch (Caddy + Let's Encrypt) | +| 6 | Alle Services testen | 5 Min | Admin | +| **Gesamt** | | **~15 Min** | | + +## Vorbereitung (jetzt erledigen) + +- [ ] Hetzner Account erstellen +- [ ] VPS bestellen (CX22, €3.79/Monat) +- [ ] WireGuard einrichten (VPS + Mac Mini) +- [ ] WireGuard-Verbindung testen +- [ ] Caddyfile erstellen (alle Domains) +- [ ] DNS-Failover-Prozedur testen (mit Test-Subdomain) +- [ ] Failover-Checkliste ausdrucken / im Wiki speichern + +## Plan C: Direkte IP + +Falls auch Hetzner nicht verfügbar: +1. ISP kontaktieren für feste IP-Adresse +2. Port-Forwarding auf Router einrichten (80, 443) +3. Let's Encrypt Zertifikat via DNS-Challenge (kein HTTP nötig) +4. DNS bei einem dritten Provider (z.B. Hetzner DNS, Gandi) + +**Nachteil:** Consumer-ISPs blockieren oft Port 25 (E-Mail) und Port 80/443 ist nicht garantiert. diff --git a/scripts/mac-mini/build-landings.sh b/scripts/mac-mini/build-landings.sh new file mode 100755 index 000000000..c141e62c1 --- /dev/null +++ b/scripts/mac-mini/build-landings.sh @@ -0,0 +1,56 @@ +#!/bin/bash +# Build all landing pages and copy dist/ to the shared nginx volume +# Run on the Mac Mini after git pull +# +# Usage: ./scripts/mac-mini/build-landings.sh + +set -e + +PROJECT_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" +OUTPUT_DIR="/Volumes/ManaData/landings" + +echo "=== Building Landing Pages ===" +echo "Output: $OUTPUT_DIR" +echo "" + +mkdir -p "$OUTPUT_DIR" + +# Landing pages to build (filter name → dist path → output name) +declare -A LANDINGS=( + ["it"]="services/it-landing" + ["chat"]="apps/chat/apps/landing" + ["picture"]="apps/picture/apps/landing" + ["zitare"]="apps/zitare/apps/landing" + ["presi"]="apps/presi/apps/landing" + ["clock"]="apps/clock/apps/landing" + ["manadeck"]="apps/manadeck/apps/landing" + ["nutriphi"]="apps/nutriphi/apps/landing" + ["citycorners"]="apps/citycorners/apps/landing" +) + +cd "$PROJECT_ROOT" + +for name in "${!LANDINGS[@]}"; do + dir="${LANDINGS[$name]}" + if [ -d "$dir" ]; then + echo "Building $name ($dir)..." + pnpm --filter "./$dir" build 2>&1 | tail -3 + + # Copy dist to output + if [ -d "$dir/dist" ]; then + rm -rf "$OUTPUT_DIR/$name" + cp -r "$dir/dist" "$OUTPUT_DIR/$name" + echo " → $OUTPUT_DIR/$name ($(du -sh "$OUTPUT_DIR/$name" | cut -f1))" + else + echo " ⚠ No dist/ found for $name" + fi + else + echo " ⚠ Directory not found: $dir" + fi + echo "" +done + +echo "=== Done ===" +echo "" +echo "Restart nginx to pick up changes:" +echo " docker restart mana-infra-landings"