Host-Routing: Bot-Produktseite und Community-Hub getrennt
Deploy / check (push) Has been cancelled
Deploy / deploy (push) Has been cancelled

Eine Anwendung, zwei Gesichter — der Server erkennt an der Domain, welche
Seite gefragt ist, und schreibt das als data-site an den <body>. Das
Frontend rendert daraufhin entweder den Community-Hub wie bisher oder die
neue Produktseite.

- Bot-Produktseite (BotApp): Landing mit Feature-Übersicht, Befehls-
  referenz, Dashboard unter /dashboard, Orange als Leitfarbe
- Weiterleitungen: Community-Routen auf der Bot-Domain und umgekehrt
  werden dauerhaft (301) auf die richtige Adresse geschickt — geteilte
  Devlog-Permalinks laufen also nicht ins Leere. Rechtstexte bleiben auf
  beiden erreichbar.
- Open-Graph-Tags je Domain, inklusive eigener Vorschau für frei
  angelegte Seiten (Entwürfe bekommen bewusst keine)
- Login: neue Einstellung für die Cookie-Domain, damit die Anmeldung auf
  beiden Seiten gilt; nach dem Discord-Login landet man wieder auf der
  Seite, von der man gestartet ist (vorher immer auf der Hauptadresse)
- Alles greift erst, wenn beide Adressen im Setup eingetragen sind —
  bis dahin verhält sich die Anwendung unverändert

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-31 15:20:53 +02:00
co-authored by Claude Fable 5
parent e8158ce90b
commit 079c75b184
13 changed files with 515 additions and 14 deletions
+106
View File
@@ -0,0 +1,106 @@
// Rahmen der Bot-Produktseite — eigene Navigation, orange Leitfarbe.
// Das Dashboard ist dieselbe Setup-Seite wie bisher, nur unter /dashboard.
import { useEffect, useState } from 'react';
import { Routes, Route, NavLink, Navigate, useLocation } from 'react-router-dom';
import { apiGet } from './api.js';
import BotLanding from './pages/BotLanding.jsx';
import BotCommands from './pages/BotCommands.jsx';
import Settings from './pages/Settings.jsx';
import Impressum from './pages/Impressum.jsx';
import Datenschutz from './pages/Datenschutz.jsx';
import { IconMenu, IconX } from './icons.jsx';
export default function BotApp() {
const [me, setMe] = useState({ user: null, admin: false, scopes: [], loading: true });
const [scrolled, setScrolled] = useState(false);
const [drawer, setDrawer] = useState(false);
const [hub, setHub] = useState(null);
const location = useLocation();
useEffect(() => { setDrawer(false); }, [location.pathname]);
useEffect(() => {
apiGet('/api/me')
.then((data) => setMe({ scopes: [], ...data, loading: false }))
.catch(() => setMe({ user: null, admin: false, scopes: [], loading: false }));
apiGet('/api/legal').then((d) => setHub(d.publicUrl)).catch(() => {});
}, []);
useEffect(() => {
const onScroll = () => setScrolled(window.scrollY > 24);
window.addEventListener('scroll', onScroll, { passive: true });
return () => window.removeEventListener('scroll', onScroll);
}, []);
const canDashboard = me.admin || me.scopes?.length > 0;
return (
<div className="app site-bot">
<header className={`nav ${scrolled ? 'scrolled' : ''}`}>
<NavLink to="/" className="nav-logo">
D4RKBOT<span>.</span>
</NavLink>
<nav className="nav-links">
<NavLink to="/features">Funktionen</NavLink>
<NavLink to="/commands">Befehle</NavLink>
{canDashboard && <NavLink to="/dashboard">Dashboard</NavLink>}
{hub && <a href={hub}>Community</a>}
</nav>
<div className="auth">
{me.loading ? null : me.user ? (
<span className="user-chip">
{me.user.avatar && <img className="avatar" src={me.user.avatar} alt="" />}
<span className="username">{me.user.username}</span>
<a className="logout" href="/auth/logout" title="Abmelden">
<IconX size={13} />
</a>
</span>
) : (
<a className="btn-login" href="/auth/login">Anmelden</a>
)}
</div>
<button className="burger" onClick={() => setDrawer((d) => !d)} aria-label="Menü">
{drawer ? <IconX size={20} /> : <IconMenu size={20} />}
</button>
</header>
{drawer && (
<nav className="drawer" onClick={() => setDrawer(false)}>
<NavLink to="/features">Funktionen</NavLink>
<NavLink to="/commands">Befehle</NavLink>
{canDashboard && <NavLink to="/dashboard">Dashboard</NavLink>}
{hub && <a href={hub}>Community</a>}
{me.user
? <a href="/auth/logout">Abmelden</a>
: <a href="/auth/login">Anmelden</a>}
</nav>
)}
<main className="main">
<Routes>
<Route path="/" element={<BotLanding />} />
<Route path="/features" element={<BotLanding />} />
<Route path="/commands" element={<BotCommands />} />
<Route path="/dashboard" element={<Settings me={me} />} />
<Route path="/impressum" element={<Impressum />} />
<Route path="/datenschutz" element={<Datenschutz />} />
<Route path="*" element={<Navigate to="/" replace />} />
</Routes>
</main>
<footer className="footer">
<span className="footer-logo">D4RKBOT<span>.</span></span>
<span className="footer-sep">//</span>
<a href="https://git.d4rkst3r.de/D4rkst3r/d4rkbot" target="_blank" rel="noreferrer">
Quellcode
</a>
<span className="footer-sep">//</span>
<NavLink to="/impressum">Impressum</NavLink>
<NavLink to="/datenschutz">Datenschutz</NavLink>
</footer>
</div>
);
}