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
+119
View File
@@ -0,0 +1,119 @@
// Produktseite des Bots: was er kann, wie er aussieht, wo es weitergeht.
// Bewusst ohne „Einladen"-Button — der Bot ist auf einen Server ausgelegt.
import { useEffect, useState } from 'react';
import { Link } from 'react-router-dom';
import { apiGet } from '../api.js';
import {
IconDevlog, IconLevel, IconRoles, IconServer, IconSupport, IconGiveaway,
IconBirthday, IconShield, IconMagic, IconKey, IconArrowRight, IconBot,
} from '../icons.jsx';
const HIGHLIGHTS = [
[IconDevlog, 'Devlogs & Feeds', 'Entwicklungs-Berichte landen als gebrandetes Embed im Discord und werden gleichzeitig durchsuchbar archiviert. Commits und Releases aus Gitea genauso.'],
[IconServer, 'Server-Monitor', 'Über 300 Spiele per gamedig, dazu FiveM und einfache Erreichbarkeits-Checks. Ein Live-Embed pro Server, Alarm bei Ausfall, Spielerzahl in der Bot-Präsenz.'],
[IconLevel, 'Level & Aktivität', 'XP fürs Mitreden mit Rollen-Belohnungen, öffentliche Bestenliste und Aktivitäts-Statistik des Servers.'],
[IconRoles, 'Rollen-Menüs', 'Selbstbedienung per Knopfdruck — im Discord und im Browser. Farben pro Knopf, wahlweise exklusiv.'],
[IconSupport, 'Support & Kontakt', 'Tickets als private Threads mit Gesprächsprotokoll, dazu Modmail: eine Direktnachricht an den Bot landet beim Team.'],
[IconShield, 'Moderation', 'Verwarnungen mit Verlauf, Auszeiten, Aufräumen — alles im Mod-Log, dazu Protokoll für Beitritte, Namens- und Rollenwechsel.'],
[IconGiveaway, 'Verlosungen', 'Teilnahme per Knopf, automatische Ziehung, Neuauslosung wenn sich niemand meldet.'],
[IconBirthday, 'Geburtstage', 'Wer mag, trägt seinen Tag ein — der Bot gratuliert morgens und vergibt eine Rolle für den Tag.'],
[IconMagic, 'Willkommens-Karten', 'Neue Mitglieder bekommen ein gerendertes Bild mit Avatar und Mitglieds-Nummer.'],
];
export default function BotLanding() {
const [status, setStatus] = useState(null);
const [hub, setHub] = useState(null);
useEffect(() => {
apiGet('/api/legal').then((d) => setHub(d)).catch(() => {});
apiGet('/api/serverstats').then(setStatus).catch(() => {});
window.scrollTo(0, 0);
}, []);
return (
<>
<section className="page-header bot-hero">
<div className="page-bg-text">BOT</div>
<div className="page-header-grid" aria-hidden="true" />
<div className="page-header-content">
<div className="page-tag">// Selbst gebaut, keine Paywall</div>
<h1 className="page-title">
Ein Discord-Bot,<br />der alles macht
</h1>
<p className="page-subtitle">
Devlogs, Level, Moderation, Server-Monitoring, Verlosungen, Tickets
was sonst drei Bots mit Abo-Modell erledigen, läuft hier in einem,
mit eigenem Webinterface.
</p>
<div className="home-cta">
<Link className="btn btn-save with-icon" to="/features">
Alle Funktionen <IconArrowRight size={15} />
</Link>
<Link className="btn btn-ghost with-icon" to="/commands">
<IconBot /> Befehle ansehen
</Link>
</div>
</div>
</section>
<section className="content">
<div className="srv-summary">
<div className="srv-sum-item">
<span className="srv-sum-n">16</span>
<span className="srv-sum-label">Slash-Befehle</span>
</div>
<div className="srv-sum-item">
<span className="srv-sum-n">300<span className="srv-sum-of">+</span></span>
<span className="srv-sum-label">Spiele im Monitor</span>
</div>
<div className="srv-sum-item">
<span className="srv-sum-n">0<span className="srv-sum-of"> </span></span>
<span className="srv-sum-label">Kosten, keine Premium-Stufen</span>
</div>
</div>
<h2 className="settings-title" style={{ marginTop: '2.5rem' }}>// Was er kann</h2>
<div className="home-tiles">
{HIGHLIGHTS.map(([Icon, title, text]) => (
<div className="card home-tile" key={title}>
<span className="home-tile-icon"><Icon size={24} /></span>
<span className="home-tile-title">{title}</span>
<span className="home-tile-desc">{text}</span>
</div>
))}
</div>
<div className="roadmap-group" style={{ marginTop: '2.5rem' }}>
<h2 className="settings-title">// Für Entwickler</h2>
<div className="home-tiles">
<div className="card home-tile">
<span className="home-tile-icon"><IconKey size={24} /></span>
<span className="home-tile-title">API &amp; Single Sign-On</span>
<span className="home-tile-desc">
Eigene Skripte posten über die API v1 mit Bearer-Schlüsseln und Bereichs-Rechten.
Andere Dienste nutzen den Discord-Login des Bots mit und bekommen die Rollen
gleich mitgeliefert.
</span>
</div>
<div className="card home-tile">
<span className="home-tile-icon"><IconServer size={24} /></span>
<span className="home-tile-title">Läuft überall</span>
<span className="home-tile-desc">
Node und SQLite in einem Container, ein Volume für die Daten.
Nächtliche Sicherungen inklusive von der Datenbank und allen Repositories.
</span>
</div>
</div>
</div>
{hub?.publicUrl && (
<p className="notice" style={{ marginTop: '2rem' }}>
Der Bot läuft für die D4RKST3R-Community
{status?.members ? ` mit ${status.members} Mitgliedern` : ''}
{' '}<a href={hub.publicUrl} style={{ textDecoration: 'underline' }}>zum Community-Hub</a>.
</p>
)}
</section>
</>
);
}