From 99c6c9516c0db0b0fc5e589894a94aed620e7591 Mon Sep 17 00:00:00 2001
From: dschlueter
Date: Wed, 7 Oct 2026 09:40:36 +0200
Subject: [PATCH 1/3] =?UTF-8?q?Content:=20Hero-Text=20=C3=BCberarbeitet,?=
=?UTF-8?q?=20Mundorgel=20=E2=86=92=20Maultrommel,=20Jamulix-Gesetz?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Co-Authored-By: Claude Opus 5.5
---
components/about-section.tsx | 2 +-
components/hero.tsx | 13 ++++++++-----
components/projects-section.tsx | 6 +++---
3 files changed, 12 insertions(+), 9 deletions(-)
diff --git a/components/about-section.tsx b/components/about-section.tsx
index cc749c5..af52888 100644
--- a/components/about-section.tsx
+++ b/components/about-section.tsx
@@ -119,7 +119,7 @@ export function AboutSection() {
Und so gilt für mich das erste Jamulix-Gesetz:
- Je schlauer, umso Linux!
+ Je schlauer, desto Linux!
diff --git a/components/hero.tsx b/components/hero.tsx
index e8b93f7..bd89890 100644
--- a/components/hero.tsx
+++ b/components/hero.tsx
@@ -50,11 +50,14 @@ export function Hero() {
transition={{ duration: 0.6, delay: 0.1 }}
className="text-lg lg:text-xl text-muted-foreground leading-relaxed max-w-2xl"
>
- Erste Programmiersprache: Fortran{'\u00A0'}IV im VWL-Studium.
- Dann C und 68000-Assembler auf dem Atari{'\u00A0'}1040ST programmiert.
- An der Universität Unix entdeckt – und dann Linux, das mich nie wieder losgelassen hat.
- Seit Ende 2022 konzentriert auf KI-Tools und KI-Programmierung.
- Heute: KI, Python, Rust, C und vieles mehr.
+ Seit 50 Jahren Code – angefangen mit Fortran IV auf Lochkarten.
+ Über C und 68000-Assembler auf dem Atari ST zu Unix an der Uni
+ und dann zu Linux, das mich seit seinen Anfängen begleitet.
+ Heute entwickle ich KI-Systeme und Agenten mit Python, Rust & Co,
+ erstelle und nutze Agentensysteme sowie angrenzende Technologien
+ moderner Softwareentwicklung und des praktischen KI-Einsatzes.
+ Das Augenmerk liegt dabei auf neuen Methoden, digitaler Souveränität,
+ Open Source und Lizenzkostenersparnis.
Date: Wed, 7 Oct 2026 09:40:36 +0200
Subject: [PATCH 2/3] Feature: Eigene 404-Seite im Landingpage-Stil
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
app/not-found.tsx erzeugt landingpage/404.html (Terminal-Look mit
angefragtem Pfad). Die Deploy-Skripte kopieren sie zusätzlich in den
Webroot, damit sie auch außerhalb von /landingpage/ greift.
Co-Authored-By: Claude Opus 5.5
---
app/not-found.tsx | 127 ++++++++++++++++++++++++++++++++++
components/requested-path.tsx | 19 +++++
deploy_to_localhost.sh | 4 ++
deploy_to_production.sh | 3 +
4 files changed, 153 insertions(+)
create mode 100644 app/not-found.tsx
create mode 100644 components/requested-path.tsx
diff --git a/app/not-found.tsx b/app/not-found.tsx
new file mode 100644
index 0000000..6939106
--- /dev/null
+++ b/app/not-found.tsx
@@ -0,0 +1,127 @@
+import Link from 'next/link'
+import { ArrowLeft, FolderGit2 } from 'lucide-react'
+import { Button } from '@/components/ui/button'
+import { RequestedPath } from '@/components/requested-path'
+
+export const metadata = {
+ title: '404 – Seite nicht gefunden | Jamulix',
+ description: 'Diese Seite ist vom Lochkartenstapel gefallen.',
+ robots: { index: false, follow: true },
+}
+
+export default function NotFound() {
+ return (
+
+ {/* Fly.io-style gradient mesh background (dark mode only) */}
+
+
+ {/* Subtle grid overlay */}
+
+
+ {/* Logo */}
+
+
+
+ Jamulix
+
+
+
+
+
+
+
+ {/* Text Content */}
+
+
+
+ Fehler 404 · Seite nicht gefunden
+
+
+
+ 404
+
+
+
+ Diese Seite ist vom Lochkartenstapel gefallen.
+
+
+
+
+ Seit 50 Jahren Code – und trotzdem verlegt man gelegentlich
+ etwas. Die gesuchte Seite existiert nicht, nicht mehr oder
+ noch nicht. Vielleicht war auch nur ein übereifriges{' '}
+ rm -rf{' '}
+ am Werk.
+
+
+
+
+
+
+
+
+ {/* Terminal */}
+
+
+
+
+
+
+
+ jamulix@jamulix.de: ~
+
+
+
+
+ $ cd {'\n'}
+
+ bash: cd: : Datei oder Verzeichnis nicht gefunden
+ {'\n'}
+ $ find / -name "seite" 2>/dev/null{'\n'}
+ $ cat seite.f{'\n'}
+ C GESTANZT 1976 – STAPEL LEIDER RUNTERGEFALLEN{'\n'}
+ GO TO 404{'\n'}
+ $ sudo !!{'\n'}
+
+ jamulix ist nicht in der sudoers-Datei. Dieser Vorfall wird gemeldet.
+ {'\n'}
+ ${' '}
+
+
+
+
+
+ Tipp: Auch nach 50 Jahren hilft ls meistens weiter.
+
+
+
+
+
+
+ )
+}
diff --git a/components/requested-path.tsx b/components/requested-path.tsx
new file mode 100644
index 0000000..9ceb432
--- /dev/null
+++ b/components/requested-path.tsx
@@ -0,0 +1,19 @@
+'use client'
+
+import { useEffect, useState } from 'react'
+
+// Shows the requested URL path; falls back to a placeholder without JS
+export function RequestedPath() {
+ const [path, setPath] = useState('/gesuchte-seite')
+
+ useEffect(() => {
+ const raw = window.location.pathname
+ try {
+ setPath(decodeURI(raw))
+ } catch {
+ setPath(raw) // malformed percent-encoding
+ }
+ }, [])
+
+ return {path}
+}
diff --git a/deploy_to_localhost.sh b/deploy_to_localhost.sh
index 205ea51..c8cc3d3 100755
--- a/deploy_to_localhost.sh
+++ b/deploy_to_localhost.sh
@@ -17,6 +17,10 @@ echo "Setze symbolischen Link: index.html → landingpage/index.html"
sudo rm -f /var/www/html/index.html
sudo ln -s landingpage/index.html /var/www/html/index.html
+echo "Kopiere 404-Seite: landingpage/404.html → 404.html"
+sudo cp landingpage/404.html /var/www/html/404.html
+sudo chown www-data:www-data /var/www/html/404.html
+
sudo chown -R www-data:www-data /var/www/html/landingpage/
echo ""
diff --git a/deploy_to_production.sh b/deploy_to_production.sh
index 522f953..9daad67 100755
--- a/deploy_to_production.sh
+++ b/deploy_to_production.sh
@@ -30,5 +30,8 @@ rsync -av --delete -e "ssh -S $SSH_SOCKET" landingpage/ "$REMOTE_USER@$REMOTE_HO
echo "Setze symbolischen Link: index.html → landingpage/index.html"
ssh -S "$SSH_SOCKET" "$REMOTE_USER@$REMOTE_HOST" "rm -f '$REMOTE_DIR/index.html' && ln -s landingpage/index.html '$REMOTE_DIR/index.html'"
+echo "Kopiere 404-Seite: landingpage/404.html → 404.html"
+ssh -S "$SSH_SOCKET" "$REMOTE_USER@$REMOTE_HOST" "cp '$REMOTE_DIR/landingpage/404.html' '$REMOTE_DIR/404.html'"
+
echo ""
echo "Deployment abgeschlossen: https://jamulix.de"
From 9c7e1ce4dbfe2f2f21aab90f6a63e266f542e7d0 Mon Sep 17 00:00:00 2001
From: dschlueter
Date: Wed, 7 Oct 2026 09:55:02 +0200
Subject: [PATCH 3/3] Feature: Fehlerseiten 401, 403, 500, 502, 503, 504
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Gemeinsames Layout in components/error-page.tsx, 404 darauf umgestellt.
Jede Route app//page.tsx erzeugt landingpage/.html; die
Deploy-Skripte kopieren alle Fehlerseiten in den Webroot. README
dokumentiert die nötigen ErrorDocument-Zeilen (Server-Konfig unverändert).
Co-Authored-By: Claude Opus 5.5
---
README.md | 35 +++++++++
app/401/page.tsx | 37 +++++++++
app/403/page.tsx | 35 +++++++++
app/500/page.tsx | 34 +++++++++
app/502/page.tsx | 33 ++++++++
app/503/page.tsx | 31 ++++++++
app/504/page.tsx | 32 ++++++++
app/not-found.tsx | 139 ++++++---------------------------
components/error-page.tsx | 157 ++++++++++++++++++++++++++++++++++++++
deploy_to_localhost.sh | 9 ++-
deploy_to_production.sh | 5 +-
11 files changed, 427 insertions(+), 120 deletions(-)
create mode 100644 app/401/page.tsx
create mode 100644 app/403/page.tsx
create mode 100644 app/500/page.tsx
create mode 100644 app/502/page.tsx
create mode 100644 app/503/page.tsx
create mode 100644 app/504/page.tsx
create mode 100644 components/error-page.tsx
diff --git a/README.md b/README.md
index c78a177..151b592 100644
--- a/README.md
+++ b/README.md
@@ -98,6 +98,7 @@ Die E-Mail im Footer (`footer.tsx`) wird als Prop von `app/page.tsx` übergeben:
- Öffnet eine SSH-Master-Verbindung (Passwort nur einmal eingeben)
- rsync nach `jamulix.de:/var/www/jamulix.de/html/landingpage/`
- Setzt Symlink: `/var/www/jamulix.de/html/index.html` → `landingpage/index.html`
+- Kopiert die Fehlerseiten `landingpage/.html` in den Webroot
### Localhost (zum Testen)
@@ -107,6 +108,7 @@ Die E-Mail im Footer (`footer.tsx`) wird als Prop von `app/page.tsx` übergeben:
- rsync nach `/var/www/html/landingpage/`
- Symlink: `/var/www/html/index.html` → `landingpage/index.html`
+- Kopiert die Fehlerseiten in den Webroot
- `sudo chown -R www-data:www-data`
### Warum `/landingpage/` als Unterverzeichnis?
@@ -115,6 +117,39 @@ Die E-Mail im Footer (`footer.tsx`) wird als Prop von `app/page.tsx` übergeben:
unter `jamulix.de/` zu betreiben ohne Konflikte. rsync mit `--delete` löscht
nur Dateien innerhalb von `landingpage/`, nicht andere Verzeichnisse im Webroot.
+### Fehlerseiten
+
+| Code | Quelle | Thema |
+|------|--------|-------|
+| 401 | `app/401/page.tsx` | Autorisierung erforderlich |
+| 403 | `app/403/page.tsx` | Zugriff verweigert |
+| 404 | `app/not-found.tsx` | Seite nicht gefunden |
+| 500 | `app/500/page.tsx` | Interner Serverfehler |
+| 502 | `app/502/page.tsx` | Bad Gateway |
+| 503 | `app/503/page.tsx` | Wartungsarbeiten |
+| 504 | `app/504/page.tsx` | Gateway Timeout |
+
+Gemeinsames Layout: `components/error-page.tsx`. Der Build erzeugt
+`landingpage/.html`; die Deploy-Skripte kopieren sie zusätzlich nach
+`/.html`. Alle Asset-Pfade sind absolut (`/landingpage/_next/…`),
+daher funktionieren die Kopien im Webroot genauso.
+
+Damit Apache sie ausliefert, braucht die vHost-Konfiguration (nicht Teil
+dieses Repos) z. B.:
+
+```apache
+ErrorDocument 401 /401.html
+ErrorDocument 403 /403.html
+ErrorDocument 404 /404.html
+ErrorDocument 500 /500.html
+ErrorDocument 502 /502.html
+ErrorDocument 503 /503.html
+ErrorDocument 504 /504.html
+```
+
+- `ErrorDocument 401` muss ein lokaler Pfad sein, keine vollständige URL.
+- Die 401-Seite erscheint erst, wenn der Login-Dialog des Browsers abgebrochen wird.
+
---
## Design
diff --git a/app/401/page.tsx b/app/401/page.tsx
new file mode 100644
index 0000000..0ff8b06
--- /dev/null
+++ b/app/401/page.tsx
@@ -0,0 +1,37 @@
+import { ErrorPage, Cmd, Out, Err } from '@/components/error-page'
+
+export const metadata = {
+ title: '401 – Autorisierung erforderlich | Jamulix',
+ description: 'Parole? Dieser Bereich ist passwortgeschützt.',
+ robots: { index: false, follow: false },
+}
+
+export default function Unauthorized() {
+ return (
+
+ ssh gast@jamulix.de
+ gast@jamulix.de: Permission denied (publickey,password).
+ sudo !!
+ [sudo] Passwort für gast: ********
+ Sorry, try again.
+ [sudo] Passwort für gast: geheim
+ Sorry, try again.
+ [sudo] Passwort für gast: 123456
+ sudo: 3 incorrect password attempts
+ echo "Sesam, öffne dich"
+ Sesam, öffne dich
+ >
+ }
+ tip={<>Tipp: 123456 ist es auch diesmal nicht.>}
+ >
+ Dieser Bereich ist passwortgeschützt. Wer Zugangsdaten hat, lädt die
+ Seite einfach neu und meldet sich an – alle anderen bekommen immerhin
+ diese hübsche Fehlerseite.
+
+ )
+}
diff --git a/app/403/page.tsx b/app/403/page.tsx
new file mode 100644
index 0000000..f7fabdf
--- /dev/null
+++ b/app/403/page.tsx
@@ -0,0 +1,35 @@
+import { ErrorPage, Cmd, Out, Err } from '@/components/error-page'
+import { RequestedPath } from '@/components/requested-path'
+
+export const metadata = {
+ title: '403 – Zugriff verweigert | Jamulix',
+ description: 'Hier gilt chmod 000.',
+ robots: { index: false, follow: false },
+}
+
+export default function Forbidden() {
+ return (
+
+ ls -l
+ ls: Zugriff auf '' nicht möglich: Keine Berechtigung
+ chmod 777
+ chmod: Ändern der Zugriffsrechte nicht möglich: Operation nicht erlaubt
+ sudo chmod 777
+ gast ist nicht in der sudoers-Datei. Dieser Vorfall wird gemeldet.
+ whoami
+ gast
+ >
+ }
+ tip={<>Tipp: Auch root war einmal gast.>}
+ >
+ Die Seite gibt es – nur eben nicht für dich. Manche Verzeichnisse
+ bleiben aus gutem Grund verschlossen, selbst nach 50 Jahren
+ Unix-Erfahrung und mit freundlichem Fragen.
+
+ )
+}
diff --git a/app/500/page.tsx b/app/500/page.tsx
new file mode 100644
index 0000000..b64d092
--- /dev/null
+++ b/app/500/page.tsx
@@ -0,0 +1,34 @@
+import { ErrorPage, Cmd, Out, Err, Code } from '@/components/error-page'
+
+export const metadata = {
+ title: '500 – Interner Serverfehler | Jamulix',
+ description: 'Der Server hat sich verschluckt.',
+ robots: { index: false, follow: false },
+}
+
+export default function InternalServerError() {
+ return (
+
+ ./server --seite-ausliefern
+ Segmentation fault (core dumped)
+ gdb ./server core
+ #0 0x00000000 in ?? ()
+ #1 0x0000dead in seite_ausliefern (zeiger=NULL)
+ cat notfall.f
+ C WENN ALLES SCHIEFGEHT:
+ STOP 500
+ >
+ }
+ tip={<>Tipp: Kurz warten, dann F5 – das hilft öfter, als man denkt.>}
+ >
+ Irgendwo im Maschinenraum ist ein Zeiger ins Leere gelaufen. Das ist
+ nicht deine Schuld. Der Fehler wird untersucht – vermutlich bei einer
+ Tasse Tee.
+
+ )
+}
diff --git a/app/502/page.tsx b/app/502/page.tsx
new file mode 100644
index 0000000..29c9580
--- /dev/null
+++ b/app/502/page.tsx
@@ -0,0 +1,33 @@
+import { ErrorPage, Cmd, Out, Err } from '@/components/error-page'
+
+export const metadata = {
+ title: '502 – Bad Gateway | Jamulix',
+ description: 'Der Server hinter dem Server schweigt.',
+ robots: { index: false, follow: false },
+}
+
+export default function BadGateway() {
+ return (
+
+ ping -c 3 backend
+ --- backend ping statistics ---
+ 3 packets transmitted, 0 received, 100% packet loss
+ curl http://localhost:8080/
+ curl: (7) Failed to connect to localhost port 8080: Connection refused
+ echo "Hallo? Jemand da?" > /dev/backend
+ …
+ >
+ }
+ tip={<>Tipp: Der Dienst startet meist von selbst neu – in ein paar Minuten nochmal versuchen.>}
+ >
+ Der Webserver ist da und hat brav weitergefragt – aber die Anwendung
+ dahinter antwortet gerade nicht. Wahrscheinlich startet sie neu oder
+ denkt über ihr Leben nach.
+
+ )
+}
diff --git a/app/503/page.tsx b/app/503/page.tsx
new file mode 100644
index 0000000..88a0fa7
--- /dev/null
+++ b/app/503/page.tsx
@@ -0,0 +1,31 @@
+import { ErrorPage, Cmd, Out, Code } from '@/components/error-page'
+
+export const metadata = {
+ title: '503 – Wartungsarbeiten | Jamulix',
+ description: 'Der Server macht gerade Teepause.',
+ robots: { index: false, follow: false },
+}
+
+export default function ServiceUnavailable() {
+ return (
+
+ systemctl status jamulix
+ ● jamulix.service – Jamulix Webdienst
+ Active: maintenance (Teepause) since vorhin
+ Tasks: 1 (Wasser kocht)
+ sleep 300 && tee tasse.txt < kanne
+ Earl Grey, heiß.
+ >
+ }
+ tip={<>Tipp: Ein guter Tee zieht 3–5 Minuten – genau wie diese Wartung.>}
+ >
+ Hier wird gerade gewartet, aktualisiert und ein wenig aufgeräumt.
+ Gleich ist alles wieder da – frisch gepatcht und gut gelaunt.
+
+ )
+}
diff --git a/app/504/page.tsx b/app/504/page.tsx
new file mode 100644
index 0000000..ea0014e
--- /dev/null
+++ b/app/504/page.tsx
@@ -0,0 +1,32 @@
+import { ErrorPage, Cmd, Out, Err, Code } from '@/components/error-page'
+
+export const metadata = {
+ title: '504 – Gateway Timeout | Jamulix',
+ description: 'Der Server denkt noch nach.',
+ robots: { index: false, follow: false },
+}
+
+export default function GatewayTimeout() {
+ return (
+
+ curl --max-time 60 http://backend/
+ curl: (28) Operation timed out after 60000 milliseconds
+ cat jobkarte.jcl
+ //JOB504 JOB (1976),'SEITE',CLASS=A
+ JOB 504 IN WARTESCHLANGE – POSITION 42
+ VORAUSSICHTLICHE AUSGABE: MORGEN FRÜH AM SCHALTER
+ >
+ }
+ tip={<>Tipp: 1976 kam das Ergebnis am nächsten Morgen – heute reicht meist ein Neuladen.>}
+ >
+ Die Anwendung hinter dem Webserver braucht gerade länger als erlaubt.
+ Früher gab man seinen Lochkartenstapel ab und holte das Ergebnis am
+ nächsten Tag – so lange musst du hoffentlich nicht warten.
+
+ )
+}
diff --git a/app/not-found.tsx b/app/not-found.tsx
index 6939106..b5b0518 100644
--- a/app/not-found.tsx
+++ b/app/not-found.tsx
@@ -1,6 +1,4 @@
-import Link from 'next/link'
-import { ArrowLeft, FolderGit2 } from 'lucide-react'
-import { Button } from '@/components/ui/button'
+import { ErrorPage, Cmd, Out, Err, Code } from '@/components/error-page'
import { RequestedPath } from '@/components/requested-path'
export const metadata = {
@@ -11,117 +9,28 @@ export const metadata = {
export default function NotFound() {
return (
-
- {/* Fly.io-style gradient mesh background (dark mode only) */}
-
-
- {/* Subtle grid overlay */}
-
-
- {/* Logo */}
-
-
-
- Jamulix
-
-
-
-
-
-
-
- {/* Text Content */}
-
-
-
- Fehler 404 · Seite nicht gefunden
-
-
-
- 404
-
-
-
- Diese Seite ist vom Lochkartenstapel gefallen.
-
-
-
-
- Seit 50 Jahren Code – und trotzdem verlegt man gelegentlich
- etwas. Die gesuchte Seite existiert nicht, nicht mehr oder
- noch nicht. Vielleicht war auch nur ein übereifriges{' '}
- rm -rf{' '}
- am Werk.
-
-
-
-
-
-
-
-
- {/* Terminal */}
-
-
-
-
-
-
-
- jamulix@jamulix.de: ~
-
-
-
-
- $ cd {'\n'}
-
- bash: cd: : Datei oder Verzeichnis nicht gefunden
- {'\n'}
- $ find / -name "seite" 2>/dev/null{'\n'}
- $ cat seite.f{'\n'}
- C GESTANZT 1976 – STAPEL LEIDER RUNTERGEFALLEN{'\n'}
- GO TO 404{'\n'}
- $ sudo !!{'\n'}
-
- jamulix ist nicht in der sudoers-Datei. Dieser Vorfall wird gemeldet.
- {'\n'}
- ${' '}
-
-
-
-
-
- Tipp: Auch nach 50 Jahren hilft ls meistens weiter.
-
-
-
-
-
-
+
+ cd
+ bash: cd: : Datei oder Verzeichnis nicht gefunden
+ find / -name "seite" 2>/dev/null
+ cat seite.f
+ C GESTANZT 1976 – STAPEL LEIDER RUNTERGEFALLEN
+ GO TO 404
+ sudo !!
+ jamulix ist nicht in der sudoers-Datei. Dieser Vorfall wird gemeldet.
+ >
+ }
+ tip={<>Tipp: Auch nach 50 Jahren hilft ls meistens weiter.>}
+ >
+ Seit 50 Jahren Code – und trotzdem verlegt man gelegentlich etwas.
+ Die gesuchte Seite existiert nicht, nicht mehr oder noch nicht.
+ Vielleicht war auch nur ein übereifriges{' '}
+ rm -rf am Werk.
+
)
}
diff --git a/components/error-page.tsx b/components/error-page.tsx
new file mode 100644
index 0000000..e6ecc22
--- /dev/null
+++ b/components/error-page.tsx
@@ -0,0 +1,157 @@
+import type { ReactNode } from 'react'
+import Link from 'next/link'
+import { ArrowLeft, FolderGit2 } from 'lucide-react'
+import { Button } from '@/components/ui/button'
+
+/* Terminal lines — each renders one line incl. trailing newline inside