+ {title} +
++ {children} +
+ +
+ {terminal}
+
+
+ + {tip} +
+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 (
+ 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.
+ Active: maintenance (Teepause) since vorhin
+ //JOB504 JOB (1976),'SEITE',CLASS=A
+ GO TO 404
+ rm -rf am Werk.
+
- Je schlauer, umso Linux! + Je schlauer, desto Linux!
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 */
+
+export function Cmd({ children }: { children: ReactNode }) {
+ return (
+ <>
+ $ {children}{'\n'}
+ >
+ )
+}
+
+export function Out({ children }: { children: ReactNode }) {
+ return (
+ <>
+ {children}{'\n'}
+ >
+ )
+}
+
+export function Err({ children }: { children: ReactNode }) {
+ return (
+ <>
+ {children}{'\n'}
+ >
+ )
+}
+
+export function Code({ children }: { children: ReactNode }) {
+ return (
+ <>
+ {children}{'\n'}
+ >
+ )
+}
+
+export function Cursor() {
+ return (
+ <>
+ ${' '}
+
+ >
+ )
+}
+
+type ErrorPageProps = {
+ code: number
+ label: string
+ title: string
+ children: ReactNode
+ terminal: ReactNode
+ tip: ReactNode
+}
+
+export function ErrorPage({ code, label, title, children, terminal, tip }: ErrorPageProps) {
+ return (
+
+ {/* Fly.io-style gradient mesh background (dark mode only) */}
+
+
+ {/* Subtle grid overlay */}
+
+
+ {/* Logo */}
+
+
+
+ Jamulix
+
+
+
+
+
+
+
+ {/* Text Content */}
+
+
+
+ Fehler {code} · {label}
+
+
+
+
+
+ {title}
+
+
+
+
+ {children}
+
+
+
+
+
+
+
+
+ {/* Terminal */}
+
+
+
+
+
+
+
+ jamulix@jamulix.de: ~
+
+
+
+
+ {terminal}
+
+
+
+
+
+ {tip}
+
+
+
+
+
+
+ )
+}
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.
{
+ 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..b9f023d 100755
--- a/deploy_to_localhost.sh
+++ b/deploy_to_localhost.sh
@@ -4,6 +4,7 @@
set -e
LOCAL_DIR="/home/dschlueter/neue_jamulix.de_landing_page/jamulix.de_webpage_2"
+ERROR_PAGES="401 403 404 500 502 503 504"
cd "$LOCAL_DIR"
@@ -17,6 +18,12 @@ 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 Fehlerseiten: landingpage/.html → .html"
+for c in $ERROR_PAGES; do
+ sudo cp "landingpage/$c.html" "/var/www/html/$c.html"
+ sudo chown www-data:www-data "/var/www/html/$c.html"
+done
+
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..e6ea84a 100755
--- a/deploy_to_production.sh
+++ b/deploy_to_production.sh
@@ -8,6 +8,7 @@ REMOTE_HOST="jamulix.de"
REMOTE_DIR="/var/www/jamulix.de/html"
LOCAL_DIR="/home/dschlueter/neue_jamulix.de_landing_page/jamulix.de_webpage_2"
SSH_SOCKET="/tmp/ssh-jamulix-deploy-$$"
+ERROR_PAGES="401 403 404 500 502 503 504"
cleanup() {
ssh -S "$SSH_SOCKET" -O exit "$REMOTE_USER@$REMOTE_HOST" 2>/dev/null || true
@@ -30,5 +31,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 Fehlerseiten: landingpage/.html → .html"
+ssh -S "$SSH_SOCKET" "$REMOTE_USER@$REMOTE_HOST" "for c in $ERROR_PAGES; do cp '$REMOTE_DIR'/landingpage/\$c.html '$REMOTE_DIR'/\$c.html; done"
+
echo ""
echo "Deployment abgeschlossen: https://jamulix.de"