diff --git a/server.ts b/server.ts index 94d8208..f15571c 100644 --- a/server.ts +++ b/server.ts @@ -44,6 +44,7 @@ async function initDatabase(): Promise { const defaults = defaultAuthorData[pKey]; const keysToEnsure: (keyof AuthorProfile)[] = [ "customSectionTitle", "customSectionContent", + "customDomain", "customPath", "badgeText", "aboutTitle", "projectsTitle", "projectsSubtitle", "booksTitle", "booksSubtitle", "footerText", "accentColor", "secondaryColor", "backgroundColor", "cardBgColor", diff --git a/src/App.tsx b/src/App.tsx index a1ac216..4c4c836 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -42,33 +42,51 @@ export default function App() { // Ermittelt die aktive Suite basierend auf Pfad-Überschreibungen oder dem Domainnamen (Hostname) const getActiveSuite = (): "erotica" | "clara" | "scifi" | "renee" => { const hostname = window.location.hostname.toLowerCase(); + const profileKeys: ("erotica" | "clara" | "renee" | "scifi")[] = ["erotica", "clara", "renee", "scifi"]; - // 1. Pfad-basierte Routen (haben Vorrang, z.B. für die Vorschau oder Admin-Interaktionen) - if (currentPath === "/sensual-moments" || currentPath.startsWith("/sensual-moments/")) { - return "erotica"; - } - if (currentPath === "/clara-finch" || currentPath.startsWith("/clara-finch/")) { - return "clara"; - } - if (currentPath === "/renee-heart" || currentPath.startsWith("/renee-heart/") || currentPath === "/renee" || currentPath.startsWith("/renee/")) { - return "renee"; - } - if (currentPath === "/sci-fi" || currentPath.startsWith("/sci-fi/")) { - return "scifi"; + // 1. Pfad-basierte Routen (haben Vorrang, z.B. für Vorschau & Direktlinks) + for (const key of profileKeys) { + const p = authorData?.[key]; + const customPaths: string[] = p?.customPath + ? p.customPath.split(",").map((s) => s.trim().toLowerCase()).filter(Boolean) + : []; + + // Statische Standard-Fallbacks + if (key === "erotica") customPaths.push("/sensual-moments", "/annie-slone", "/marc-velvet"); + if (key === "clara") customPaths.push("/clara-finch", "/clara"); + if (key === "renee") customPaths.push("/renee-heart", "/renee"); + if (key === "scifi") customPaths.push("/sci-fi", "/daniel-hesse"); + + for (const prefix of customPaths) { + if (prefix) { + const formattedPrefix = prefix.startsWith("/") ? prefix : "/" + prefix; + if (currentPath === formattedPrefix || currentPath.startsWith(formattedPrefix + "/")) { + return key; + } + } + } } - // 2. Domain-basierte Routen (für den Live-Betrieb hinter einem Reverse Proxy) - if (hostname.includes("annieslone.de")) { - return "erotica"; - } - if (hostname.includes("clarafinch.de")) { - return "clara"; - } - if (hostname.includes("reneeheart.de") || hostname.includes("renee-heart.de")) { - return "renee"; - } - if (hostname.includes("hesse-sf.de")) { - return "scifi"; + // 2. Dynamische Domain-basierte Routen (für Live-Domains) + for (const key of profileKeys) { + const p = authorData?.[key]; + const customDomains: string[] = p?.customDomain + ? p.customDomain.split(",").map((s) => s.trim().toLowerCase()).filter(Boolean) + : []; + + // Statische Standard-Fallbacks + if (key === "erotica") customDomains.push("annieslone.de", "marcvelvet.de"); + if (key === "clara") customDomains.push("clarafinch.de", "clara-finch.de"); + if (key === "renee") customDomains.push("reneeheart.de", "renee-heart.de"); + if (key === "scifi") customDomains.push("hesse-sf.de", "danielhesse.de"); + + for (const domain of customDomains) { + // Entferne evtl. eingegebene Protokolle (http://, https://) oder führendes "www." + const cleanDomain = domain.replace(/^https?:\/\//, "").replace(/^www\./, "").trim(); + if (cleanDomain && (hostname === cleanDomain || hostname.includes(cleanDomain))) { + return key; + } + } } // Standard-Fallback (Daniel Hesse SciFi) diff --git a/src/components/AdminPanel.tsx b/src/components/AdminPanel.tsx index 3fba463..e01e52f 100644 --- a/src/components/AdminPanel.tsx +++ b/src/components/AdminPanel.tsx @@ -3,7 +3,7 @@ import { AuthorData, AuthorProfile, Book, Project } from "../types"; import { Lock, Save, Plus, Trash2, Edit2, Check, RefreshCw, Sparkles, BookOpen, Compass, Clipboard, Eye, Music, LogOut, ArrowLeftRight, - Palette, Type, Image as ImageIcon + Palette, Type, Image as ImageIcon, Globe } from "lucide-react"; import ImagePicker from "./ImagePicker"; @@ -594,6 +594,43 @@ export default function AdminPanel({ onLogout }: AdminPanelProps) { /> + {/* Custom Domain & Routing Section */} +
+
+

+ + Domain- & Routing-Einstellungen +

+

Legen Sie fest, unter welchen Domains und Pfaden dieses Profil geladen werden soll. Sobald Besucher eine dieser Domains aufrufen, zeigt die Applikation automatisch diese Autoren-Suite an.

+
+ +
+
+ + updateProfileField("customDomain", e.target.value)} + placeholder="z.B. reneeheart.de, renee-heart.de" + className="w-full px-3 py-2 bg-slate-950 border border-slate-800 rounded-xl text-white outline-none focus:border-indigo-500 text-sm font-mono" + /> +

Kommagetrennte Domainnamen. Bsp: reneeheart.de, renee-heart.de

+
+ +
+ + updateProfileField("customPath", e.target.value)} + placeholder="z.B. /renee-heart, /renee" + className="w-full px-3 py-2 bg-slate-950 border border-slate-800 rounded-xl text-white outline-none focus:border-indigo-500 text-sm font-mono" + /> +

Kommagetrennte Pfade für Direktlinks & Vorschau. Bsp: /renee-heart, /renee

+
+
+
+ {/* Custom Highlight Section */}
@@ -616,7 +653,7 @@ export default function AdminPanel({ onLogout }: AdminPanelProps) { />
- +