Öffentliche Firmenwebsite für Funktechnik Schubert
Find a file
2026-07-04 00:58:33 +02:00
app feat(cms): add storage based website content management 2026-07-04 00:58:33 +02:00
components feat(cms): add storage based website content management 2026-07-04 00:58:33 +02:00
data chore(ops): harden deployment and runtime setup 2026-07-03 22:59:08 +02:00
lib feat(cms): add storage based website content management 2026-07-04 00:58:33 +02:00
public fix(admin): serve media from private storage 2026-07-03 23:45:33 +02:00
scripts feat(cms): add storage based website content management 2026-07-04 00:58:33 +02:00
storage feat(cms): add storage based website content management 2026-07-04 00:58:33 +02:00
.dockerignore feat(mail): add smtp email delivery 2026-07-04 00:06:20 +02:00
.env.example chore(ops): harden deployment and runtime setup 2026-07-03 22:59:08 +02:00
.gitignore feat(cms): add storage based website content management 2026-07-04 00:58:33 +02:00
docker-compose.yml feat(cms): add storage based website content management 2026-07-04 00:58:33 +02:00
Dockerfile feat(cms): add storage based website content management 2026-07-04 00:58:33 +02:00
eslint.config.mjs feat: initial Funktechnik Schubert website 2026-07-03 19:14:34 +02:00
next-env.d.ts feat: initial Funktechnik Schubert website 2026-07-03 19:14:34 +02:00
next.config.ts feat: initial Funktechnik Schubert website 2026-07-03 19:14:34 +02:00
nginx.example.conf feat: initial Funktechnik Schubert website 2026-07-03 19:14:34 +02:00
package-lock.json feat(cms): add storage based website content management 2026-07-04 00:58:33 +02:00
package.json feat(cms): add storage based website content management 2026-07-04 00:58:33 +02:00
proxy.ts fix(admin): repair media upload redirect 2026-07-03 23:15:25 +02:00
README.md feat(cms): add storage based website content management 2026-07-04 00:58:33 +02:00
tsconfig.json feat: initial Funktechnik Schubert website 2026-07-03 19:14:34 +02:00

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.0

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

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

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:

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/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
  • Ü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.

Healthcheck

scripts/healthcheck.sh

Oder direkt:

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

Antwort:

{
  "status": "ok",
  "version": "0.4.0",
  "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