Feature 4: Webinterface — React-Frontend, Discord-OAuth2, REST-API
- Discord-OAuth2-Login (identify-Scope, CSRF-State, signierte Session-Cookies, keine Token-Speicherung) - REST-API: /api/devlogs (öffentlich), /api/commits (nur ADMIN_DISCORD_ID), /api/me - React + Vite Frontend: Devlog-Archiv mit Mini-Markdown-Renderer, Commit-Tabelle, dunkles EcoGame-Theme - Fastify liefert frontend/dist mit SPA-Fallback aus; Vite-Dev-Proxy für lokale Entwicklung - Multi-Stage-Dockerfile (Frontend-Build im Image), neue Env-Vars in Compose + .env.example - README: OAuth2-Setup (Redirect-URLs, Client Secret) und Frontend-Workflow Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,41 @@
|
||||
// Mini-Markdown-Renderer für Devlog-Prosa (Discord-Stil): **fett**, *kursiv*,
|
||||
// `code`, [Links](url). Bewusst ohne Library — Input wird escaped, kein HTML-Injection.
|
||||
const TOKEN = /(\*\*[^*]+\*\*|\*[^*]+\*|`[^`]+`|\[[^\]]+\]\(https?:\/\/[^\s)]+\))/g;
|
||||
|
||||
function renderInline(text) {
|
||||
return text.split(TOKEN).map((part, i) => {
|
||||
// Fett/Kursiv rekursiv rendern, damit z. B. Links darin funktionieren
|
||||
if (part.startsWith('**') && part.endsWith('**')) {
|
||||
return <strong key={i}>{renderInline(part.slice(2, -2))}</strong>;
|
||||
}
|
||||
if (part.startsWith('*') && part.endsWith('*') && part.length > 2) {
|
||||
return <em key={i}>{renderInline(part.slice(1, -1))}</em>;
|
||||
}
|
||||
if (part.startsWith('`') && part.endsWith('`')) {
|
||||
return <code key={i}>{part.slice(1, -1)}</code>;
|
||||
}
|
||||
const link = part.match(/^\[([^\]]+)\]\((https?:\/\/[^\s)]+)\)$/);
|
||||
if (link) {
|
||||
return (
|
||||
<a key={i} href={link[2]} target="_blank" rel="noreferrer">
|
||||
{link[1]}
|
||||
</a>
|
||||
);
|
||||
}
|
||||
return part;
|
||||
});
|
||||
}
|
||||
|
||||
/** Devlog-Text als React-Elemente (Absätze + Inline-Formatierung) */
|
||||
export function Markdown({ text }) {
|
||||
return text.split(/\n{2,}/).map((block, i) => (
|
||||
<p key={i}>
|
||||
{block.split('\n').map((line, j, arr) => (
|
||||
<span key={j}>
|
||||
{renderInline(line)}
|
||||
{j < arr.length - 1 && <br />}
|
||||
</span>
|
||||
))}
|
||||
</p>
|
||||
));
|
||||
}
|
||||
Reference in New Issue
Block a user