12 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/datafunktechnik-storage→/app/storagefunktechnik-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 MetadatenADMIN_EMAIL: Admin-Login E-MailADMIN_PASSWORD: Admin-Login PasswortADMIN_SESSION_SECRET: langer Zufallswert zum Signieren der Admin-SessionAUTH_COOKIE_SECURE:truein Produktion mit HTTPS, lokalfalseOLYMPUS_INTAKE_API_URL: vorbereitet für spätere serverseitige Olympus-AnbindungOLYMPUS_INTAKE_API_TOKEN: vorbereitet für spätere serverseitige Olympus-AnbindungOLYMPUS_PUBLIC_STATUS_API_URL: serverseitig erreichbare Olympus-Basis-URL für öffentliche ReparaturstatuslinksOLYMPUS_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/repairPOST /api/contactPOST /api/admin/loginPOST /api/admin/logoutPATCH /api/admin/contact/[id]PATCH /api/admin/repair/[id]POST /api/admin/settingsPOST /api/admin/smtpGET/POST /api/admin/mediaGET/PUT /api/admin/contentGET /api/content/settingsGET /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
- optionalen Kostenvoranschlag aus Olympus CRM v0.8.6
Wenn Olympus einen Kostenvoranschlag im Status sent liefert, zeigt die Website:
- Nummer, Titel, Nachricht an den Kunden und Gültigkeit
- Positionen mit Menge, Einheit und Betrag
- Zwischensumme, Steuer und Gesamtbetrag
- Aktionen zum Freigeben, Ablehnen oder für eine Rückfrage
Diese Aktionen laufen nicht direkt vom Browser zu Olympus. Der Browser ruft ausschließlich relative Website-Endpunkte auf:
POST /api/status/<token>/estimate/approve
POST /api/status/<token>/estimate/decline
POST /api/status/<token>/estimate/question
Die Website leitet diese Anfragen serverseitig an Olympus weiter:
${OLYMPUS_PUBLIC_STATUS_API_URL}/public/repairs/status/<token>/estimate/<action>
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/smtpprüfen
Deployment
scripts/deploy.sh
Das Skript führt aus:
docker compose configdocker compose builddocker 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