From 1d7e7c41c91abf7a645b5b4ca95e4ac9cbd42c84 Mon Sep 17 00:00:00 2001 From: Schubert Ferenc Date: Fri, 3 Jul 2026 22:25:04 +0200 Subject: [PATCH 01/12] fix(admin): persist data directory and pass env vars --- .dockerignore | 3 + .env.example | 6 + .gitignore | 6 + README.md | 45 +++- app/admin/einstellungen/page.tsx | 28 +++ app/admin/kontaktanfragen/page.tsx | 48 +++++ app/admin/layout.tsx | 5 + app/admin/login/page.tsx | 13 ++ app/admin/medien/page.tsx | 47 +++++ app/admin/page.tsx | 46 +++++ app/admin/reparaturanfragen/page.tsx | 51 +++++ app/admin/seo/page.tsx | 24 +++ app/admin/smtp/page.tsx | 28 +++ app/admin/system/page.tsx | 36 ++++ app/admin/website/page.tsx | 38 ++++ app/api/admin/contact/[id]/route.ts | 19 ++ app/api/admin/login/route.ts | 16 ++ app/api/admin/logout/route.ts | 8 + app/api/admin/media/route.ts | 38 ++++ app/api/admin/repair/[id]/route.ts | 19 ++ app/api/admin/settings/route.ts | 9 + app/api/admin/smtp/route.ts | 9 + app/api/contact/route.ts | 9 +- app/api/repair/route.ts | 14 +- app/globals.css | 296 ++++++++++++++++++++++++++- app/layout.tsx | 7 +- components/ContactForm.tsx | 11 +- components/SiteFrame.tsx | 21 ++ components/admin/AdminLoginForm.tsx | 50 +++++ components/admin/AdminShell.tsx | 48 +++++ components/admin/StatusSelect.tsx | 34 +++ docker-compose.yml | 19 +- lib/admin/auth.ts | 69 +++++++ lib/admin/store.ts | 139 +++++++++++++ lib/admin/types.ts | 48 +++++ proxy.ts | 20 ++ public/admin-workbench-bg.png | Bin 0 -> 1712924 bytes public/uploads/.gitkeep | 1 + public/uploads/images/.gitkeep | 1 + 39 files changed, 1297 insertions(+), 32 deletions(-) create mode 100644 app/admin/einstellungen/page.tsx create mode 100644 app/admin/kontaktanfragen/page.tsx create mode 100644 app/admin/layout.tsx create mode 100644 app/admin/login/page.tsx create mode 100644 app/admin/medien/page.tsx create mode 100644 app/admin/page.tsx create mode 100644 app/admin/reparaturanfragen/page.tsx create mode 100644 app/admin/seo/page.tsx create mode 100644 app/admin/smtp/page.tsx create mode 100644 app/admin/system/page.tsx create mode 100644 app/admin/website/page.tsx create mode 100644 app/api/admin/contact/[id]/route.ts create mode 100644 app/api/admin/login/route.ts create mode 100644 app/api/admin/logout/route.ts create mode 100644 app/api/admin/media/route.ts create mode 100644 app/api/admin/repair/[id]/route.ts create mode 100644 app/api/admin/settings/route.ts create mode 100644 app/api/admin/smtp/route.ts create mode 100644 components/SiteFrame.tsx create mode 100644 components/admin/AdminLoginForm.tsx create mode 100644 components/admin/AdminShell.tsx create mode 100644 components/admin/StatusSelect.tsx create mode 100644 lib/admin/auth.ts create mode 100644 lib/admin/store.ts create mode 100644 lib/admin/types.ts create mode 100644 proxy.ts create mode 100644 public/admin-workbench-bg.png create mode 100644 public/uploads/.gitkeep create mode 100644 public/uploads/images/.gitkeep diff --git a/.dockerignore b/.dockerignore index 5b02fd0..afe0bde 100644 --- a/.dockerignore +++ b/.dockerignore @@ -5,3 +5,6 @@ node_modules npm-debug.log* .git README.md +data/*.json +public/uploads/images/* +!public/uploads/images/.gitkeep diff --git a/.env.example b/.env.example index 5a68e82..4f85a3e 100644 --- a/.env.example +++ b/.env.example @@ -1,5 +1,11 @@ NEXT_PUBLIC_SITE_URL=http://localhost:3010 +# Admin Foundation +ADMIN_EMAIL=admin@funktechnik-schubert.local +ADMIN_PASSWORD=change-me +ADMIN_SESSION_SECRET=change-me-with-a-long-random-secret +AUTH_COOKIE_SECURE=false + # Optional spaetere Integration. Nicht im Browser verwenden. OLYMPUS_INTAKE_API_URL= OLYMPUS_INTAKE_API_TOKEN= diff --git a/.gitignore b/.gitignore index ef9c7d0..3338a15 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,9 @@ dist .env .env.local npm-debug.log* +data/*.json +public/uploads/images/* +!public/uploads/.gitkeep +!public/uploads/images/.gitkeep +data/ +.DS_Store diff --git a/README.md b/README.md index e6cb911..00c7f1c 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,8 @@ Eigenständige öffentliche Firmenwebsite für Funktechnik Schubert. Dieses Proj - Next.js 16 App Router - TypeScript strict - Serverseitige API-Routen für Kontakt und Reparaturannahme +- Geschützter Admin-Bereich unter `/admin` +- Lokale Foundation-Datenablage für Anfragen und Einstellungen - Docker - Nginx/Reverse-Proxy-fähig - SEO Metadata, Sitemap und robots.txt @@ -21,6 +23,16 @@ Eigenständige öffentliche Firmenwebsite für Funktechnik Schubert. Dieses Proj - `/kontakt` - `/impressum` - `/datenschutz` +- `/admin/login` +- `/admin` +- `/admin/kontaktanfragen` +- `/admin/reparaturanfragen` +- `/admin/website` +- `/admin/medien` +- `/admin/einstellungen` +- `/admin/smtp` +- `/admin/seo` +- `/admin/system` ## Lokale Entwicklung @@ -72,17 +84,48 @@ cp .env.example .env Variablen: - `NEXT_PUBLIC_SITE_URL` +- `ADMIN_EMAIL` +- `ADMIN_PASSWORD` +- `ADMIN_SESSION_SECRET` +- `AUTH_COOKIE_SECURE` - `OLYMPUS_INTAKE_API_URL` - `OLYMPUS_INTAKE_API_TOKEN` 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` -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. +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-Inhaltsverwaltung vorbereitet +- Firmendaten pflegen +- SMTP-Konfiguration vorbereiten +- Medien hochladen +- SEO-Übersicht +- Systemübersicht + +SMTP-Versand, Publishing von Website-Inhalten und Olympus-Übernahme sind bewusst noch nicht aktiv gekoppelt. ## Nginx diff --git a/app/admin/einstellungen/page.tsx b/app/admin/einstellungen/page.tsx new file mode 100644 index 0000000..de7a795 --- /dev/null +++ b/app/admin/einstellungen/page.tsx @@ -0,0 +1,28 @@ +import { redirect } from "next/navigation"; +import AdminShell from "@/components/admin/AdminShell"; +import { requireAdminSession } from "@/lib/admin/auth"; +import { getSiteSettings } from "@/lib/admin/store"; + +export default async function AdminSettingsPage() { + if (!await requireAdminSession()) redirect("/admin/login"); + const settings = await getSiteSettings(); + + return ( + +
+

Konfiguration

+

Firmeneinstellungen

+
+
+ + + + + + +