funktechnik-schubert-website/README.md
2026-07-04 11:01:29 +02:00

342 lines
9.3 KiB
Markdown

# 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`
- `/impressum`
- `/datenschutz`
- `/admin/login`
- `/admin`
- `/admin/kontaktanfragen`
- `/admin/reparaturanfragen`
- `/admin/website`
- `/admin/medien`
- `/admin/einstellungen`
- `/admin/smtp`
- `/admin/seo`
- `/admin/system`
## Lokale Entwicklung
```bash
npm install
cp .env.example .env
npm run dev
```
Die Website läuft lokal unter:
```text
http://127.0.0.1:3010
```
Für den Admin-Bereich müssen in `.env` mindestens gesetzt sein:
```text
ADMIN_EMAIL
ADMIN_PASSWORD
ADMIN_SESSION_SECRET
```
## Checks
```bash
npm run lint
npx next build
```
## Docker
```bash
docker compose build
docker compose up -d
```
Service:
```text
funktechnik-website
```
Port:
```text
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:
```bash
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
Die Olympus-Variablen sind nur für eine spätere serverseitige Integration vorbereitet. Sie werden nicht im Browser verwendet.
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`
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:
```text
data/.gitkeep
data/contact-inquiries.example.json
data/repair-inquiries.example.json
```
Echte Runtime-Dateien werden nicht committed:
```text
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:
```text
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 Kundenportal ist vorbereitet, aber noch nicht öffentlich implementiert. Für spätere Ausbaustufen sind folgende Routen vorgesehen:
- `/status`: öffentliche Status-Einstiegsseite
- `/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
```bash
scripts/healthcheck.sh
```
Oder direkt:
```bash
curl http://127.0.0.1:3010/api/health
```
Antwort:
```json
{
"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:
```text
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
```bash
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
```bash
git pull
docker compose config
docker compose build
docker compose up -d
scripts/healthcheck.sh
```
## Backup
```bash
scripts/backup.sh
```
Das Backup enthält `data/` und `storage/`. Die `.env` wird bewusst nicht automatisch gesichert. Sie muss separat sicher abgelegt werden.
## Restore
```bash
scripts/restore.sh backups/funktechnik-data-YYYYMMDD-HHMMSS.tar.gz
```
Danach Container neu starten:
```bash
docker compose up -d
scripts/healthcheck.sh
```
## Nginx
Siehe `nginx.example.conf`.
Beispiel-Domain:
```text
funktechnik-schubert.de
```
Produktiv HTTPS aktivieren, z. B. mit Certbot:
```bash
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