funktechnik-schubert-website/README.md
2026-07-04 21:06:43 +02:00

11 KiB

Funktechnik Schubert Website

Eigenständige öffentliche Firmenwebsite für Funktechnik Schubert. Dieses Projekt ist nicht Olympus CRM und koppelt nicht direkt an Olympus.

Technologie

  • Next.js 16 App Router
  • TypeScript strict
  • Serverseitige API-Routen für Kontakt und Reparaturannahme
  • Geschützter Admin-Bereich unter /admin
  • Lokale Storage-Datenablage für Anfragen, Einstellungen, Medien und Website-Inhalte
  • Docker
  • Nginx/Reverse-Proxy-fähig
  • SEO Metadata, Sitemap und robots.txt

Version: 0.4.1

Seiten

  • / Startseite
  • /leistungen
  • /funkgeraete-service
  • /reparatur
  • /ueber-uns
  • /kontakt
  • /status/[token]
  • /impressum
  • /datenschutz
  • /admin/login
  • /admin
  • /admin/kontaktanfragen
  • /admin/reparaturanfragen
  • /admin/website
  • /admin/medien
  • /admin/einstellungen
  • /admin/smtp
  • /admin/seo
  • /admin/system

Lokale Entwicklung

npm install
cp .env.example .env
npm run dev

Die Website läuft lokal unter:

http://127.0.0.1:3010

Für den Admin-Bereich müssen in .env mindestens gesetzt sein:

ADMIN_EMAIL
ADMIN_PASSWORD
ADMIN_SESSION_SECRET

Checks

npm run lint
npx next build

Docker

docker compose build
docker compose up -d

Service:

funktechnik-website

Port:

3010:3010

Die Runtime-Daten werden über Docker-Volumes gespeichert:

  • funktechnik-data/app/data
  • funktechnik-storage/app/storage
  • funktechnik-next-cache/app/.next/cache

Dadurch sind keine manuellen chmod- oder chown-Befehle notwendig.

Umgebung

.env.example kopieren:

cp .env.example .env

Variablen:

  • NEXT_PUBLIC_SITE_URL: öffentliche Basis-URL für SEO, Sitemap und Metadaten
  • ADMIN_EMAIL: Admin-Login E-Mail
  • ADMIN_PASSWORD: Admin-Login Passwort
  • ADMIN_SESSION_SECRET: langer Zufallswert zum Signieren der Admin-Session
  • AUTH_COOKIE_SECURE: true in Produktion mit HTTPS, lokal false
  • OLYMPUS_INTAKE_API_URL: vorbereitet für spätere serverseitige Olympus-Anbindung
  • OLYMPUS_INTAKE_API_TOKEN: vorbereitet für spätere serverseitige Olympus-Anbindung
  • OLYMPUS_PUBLIC_STATUS_API_URL: serverseitig erreichbare Olympus-Basis-URL für öffentliche Reparaturstatuslinks
  • OLYMPUS_PUBLIC_STATUS_API_TOKEN: optionaler serverseitiger Token für spätere geschützte Status-API-Zugriffe

Die Intake-Variablen sind für eine spätere serverseitige Integration vorbereitet. Die Status-Variablen werden für /status/[token] serverseitig verwendet. Sie werden nicht im Browser verwendet.

Für Reparaturstatuslinks muss OLYMPUS_PUBLIC_STATUS_API_URL vom Next.js-Server erreichbar sein. In Docker kann das je nach Netzwerk z. B. http://hermes:8000 sein. Produktiv sollte eine interne Server-zu-Server-Adresse oder eine abgesicherte Olympus-URL verwendet werden.

Für lokale Entwicklung kann AUTH_COOKIE_SECURE=false bleiben. Produktiv muss HTTPS verwendet und AUTH_COOKIE_SECURE=true gesetzt werden. ADMIN_SESSION_SECRET muss produktiv ein langer zufälliger Wert sein.

API-Routen

  • POST /api/repair
  • POST /api/contact
  • POST /api/admin/login
  • POST /api/admin/logout
  • PATCH /api/admin/contact/[id]
  • PATCH /api/admin/repair/[id]
  • POST /api/admin/settings
  • POST /api/admin/smtp
  • GET/POST /api/admin/media
  • GET/PUT /api/admin/content
  • GET /api/content/settings
  • GET /api/media/[filename]
  • GET /api/health

Öffentlicher Reparaturstatus

Olympus CRM erzeugt sichere Statuslinks für Reparaturen. Die Website stellt dafür die öffentliche Route bereit:

/status/<token>

Die Seite ruft Olympus ausschließlich serverseitig auf:

${OLYMPUS_PUBLIC_STATUS_API_URL}/public/repairs/status/<token>

Der Browser sieht weder die Olympus-URL noch optionale Server-Tokens. Die öffentliche Statusseite zeigt nur:

  • Reparaturnummer
  • Gerät
  • aktuellen Status
  • letzte Aktualisierung
  • kundenfreundliche Status-Timeline

Nicht angezeigt werden Kundendaten, interne Notizen, Diagnosedetails oder technische Fehlermeldungen.

Fehlerverhalten:

  • ungültiger oder abgelaufener Token: freundliche Meldung mit Links zu Reparatur und Kontakt
  • Olympus nicht erreichbar: neutrale Meldung ohne technische Details

Aktuell validieren die Routen serverseitig, geben klare JSON-Antworten zurück und schreiben nur technische Metadaten in Server-Logs. Es werden keine Nachrichteninhalte oder Tokens geloggt. Kontakt- und Reparaturanfragen werden lokal unter data/*.json gespeichert und nicht versioniert.

Admin-Bereich

Der geschützte Admin-Bereich ist unter /admin/login erreichbar. Er verwendet ein signiertes HttpOnly-Cookie und ist für Desktop und Tablet ausgelegt.

Funktionen:

  • Dashboard mit Kennzahlen
  • Kontaktanfragen verwalten
  • Reparaturanfragen verwalten
  • Website-Inhalte pflegen und sofort veröffentlichen
  • Firmendaten pflegen
  • SMTP-Konfiguration speichern und Testmail senden
  • Medien hochladen
  • SEO-Übersicht
  • Systemübersicht

Website-Inhalte werden unter /admin/website gepflegt und beim Speichern sofort auf der öffentlichen Website wirksam. Die Content-Architektur ist bewusst über einen zentralen ContentService gekapselt, damit später Olympus CMS oder PostgreSQL als Backend angebunden werden können. SMTP-Versand fuer Kontakt- und Reparaturanfragen ist serverseitig angebunden, sofern /admin/smtp vollstaendig konfiguriert ist.

Runtime Data

Im Repository liegen nur:

data/.gitkeep
data/contact-inquiries.example.json
data/repair-inquiries.example.json

Echte Runtime-Dateien werden nicht committed:

data/contact-inquiries.json
data/repair-inquiries.json
data/site-settings.json

SMTP-Konfiguration wird unter storage/config/smtp.json gespeichert und nicht committed. Das SMTP-Passwort wird nicht im Admin-Formular ausgegeben.

Uploads liegen unter storage/uploads/images/, werden über /api/media/[filename] ausgeliefert und nicht committed.

Alte Uploads aus public/uploads/images werden beim Start einmalig nach storage/uploads/images migriert, falls sie dort noch nicht vorhanden sind.

Website-Inhalte liegen unter storage/content/ und werden nicht committed:

storage/content/home.json
storage/content/services.json
storage/content/radio-service.json
storage/content/repair.json
storage/content/about.json
storage/content/contact.json
storage/content/settings.json
storage/content/backups/

Jede Seite besitzt eine eigene JSON-Datei. React-Komponenten lesen diese Dateien nicht direkt, sondern ausschließlich über lib/content/service.ts. Vor jedem Speichern erzeugt der Service eine Backup-Datei unter storage/content/backups/ und behält pro Dokument die letzten 20 Versionen. Änderungen benötigen keinen Neustart und keinen Docker-Build.

Bearbeitbar sind aktuell:

  • Startseite mit Hero, Call-to-Actions, Featureboxen, Leistungsboxen und Kundenversprechen
  • Leistungen mit beliebig vielen sortierbaren und ein-/ausblendbaren Einträgen
  • Funkgeräte-Service mit Einleitung, Marken, Fehlerbildern, Ablauf, Messmöglichkeiten, Abgleich und Reparaturtexten
  • Reparaturannahme mit SEO-Daten und Seitentexten
  • Über Uns mit Firmenbeschreibung, Werkstattbeschreibung und Philosophie
  • Kontakt mit Kontaktinformationen, Öffnungszeiten, Maps-Link und Seitentexten
  • Footer, Logo, Copyright, Header-CTA und globale SEO-Einstellungen
  • SEO pro Seite inklusive Meta Title, Meta Description, Keywords, OpenGraph und Social Image

Bilder für Logo und Social Image werden aus dem Medienbestand ausgewählt. Neue Uploads bleiben im privaten Storage und werden über /api/media/[filename] ausgeliefert.

Roadmap Kundenportal

Ein vollständiges Kundenportal ist vorbereitet, aber noch nicht öffentlich implementiert. Die öffentliche Statuslink-Seite ist bereits vorhanden. Für spätere Ausbaustufen sind folgende Routen vorgesehen:

  • /status/[token]: öffentliche Statuslink-Seite für Olympus
  • /reparatur/status: Statusabfrage für Reparaturanfragen
  • /portal/login: geschützter Kundenlogin

Diese Routen sind bewusst noch nicht angelegt. Die spätere Umsetzung soll serverseitig erfolgen, ohne Tokens im Browser-JavaScript zu speichern und ohne bestehende Admin- oder CMS-Funktionen zu umgehen.

Healthcheck

scripts/healthcheck.sh

Oder direkt:

curl http://127.0.0.1:3010/api/health

Antwort:

{
  "status": "ok",
  "version": "0.4.1",
  "storage": "ok",
  "admin": "configured",
  "smtp": "configured",
  "timestamp": "..."
}

SMTP

SMTP wird im Adminbereich unter /admin/smtp konfiguriert. Die Konfiguration wird persistent unter storage/config/smtp.json gespeichert und liegt damit im Docker-Storage-Volume, nicht in public/.

Pflichtfelder:

  • SMTP Host
  • SMTP Port
  • SMTP Benutzername
  • SMTP Passwort
  • Verschlüsselung
  • Absenderadresse
  • Empfängeradresse

Für Apple Mail/iCloud Mail:

Host: smtp.mail.me.com
Port: 587
Verschlüsselung: STARTTLS
Benutzername: vollständige E-Mail-Adresse
Passwort: app-spezifisches Passwort

Nicht das normale Apple-ID-Passwort verwenden. In der Apple-ID-Verwaltung ein app-spezifisches Passwort erzeugen und dieses als SMTP-Passwort speichern.

Nach dem Speichern kann im Adminbereich eine Testmail gesendet werden. Kontakt- und Reparaturanfragen werden weiterhin lokal gespeichert. Wenn SMTP konfiguriert ist, wird zusaetzlich eine E-Mail an die konfigurierte Empfaengeradresse gesendet. Schlaegt der Mailversand fehl, bleibt die Anfrage gespeichert; der Besucher sieht keine technische Fehlermeldung.

Troubleshooting:

  • Host, Port und Verschlüsselung prüfen
  • bei Apple Mail STARTTLS und Port 587 verwenden
  • vollständige E-Mail-Adresse als Benutzername verwenden
  • app-spezifisches Passwort neu erzeugen
  • letzte Testmail und letzte Fehlermeldung unter /admin/smtp prüfen

Deployment

scripts/deploy.sh

Das Skript führt aus:

  • docker compose config
  • docker compose build
  • docker compose up -d
  • Healthcheck über /api/health

Produktiv muss die .env auf dem Zielsystem gepflegt werden. Secrets werden nicht ins Repository aufgenommen.

Update

git pull
docker compose config
docker compose build
docker compose up -d
scripts/healthcheck.sh

Backup

scripts/backup.sh

Das Backup enthält data/ und storage/. Die .env wird bewusst nicht automatisch gesichert. Sie muss separat sicher abgelegt werden.

Restore

scripts/restore.sh backups/funktechnik-data-YYYYMMDD-HHMMSS.tar.gz

Danach Container neu starten:

docker compose up -d
scripts/healthcheck.sh

Nginx

Siehe nginx.example.conf.

Beispiel-Domain:

funktechnik-schubert.de

Produktiv HTTPS aktivieren, z. B. mit Certbot:

certbot --nginx -d funktechnik-schubert.de -d www.funktechnik-schubert.de

Offene manuelle Punkte

  • TODO: Rechtliche Angaben ergänzen
  • Spätere Olympus-Reparaturannahme serverseitig anbinden
  • Finale Domain und HTTPS-Konfiguration setzen