Added Website for amateurs to matrix

This commit is contained in:
2026-05-21 14:41:52 +02:00
parent d320552330
commit 490f334f8a
6 changed files with 1481 additions and 2 deletions
+121
View File
@@ -0,0 +1,121 @@
async function submitRegistration() {
const usernameInput = document.getElementById("reg-username");
const emailInput = document.getElementById("reg-email");
const btn = document.getElementById("reg-btn");
const status = document.getElementById("reg-status");
const username = usernameInput.value.trim();
const email = emailInput.value.trim();
// Validierung
status.className = "reg-status";
status.style.display = "none";
if (!username || !email) {
status.className = "reg-status error";
status.textContent = "⚠️ Bitte Benutzernamen und E-Mail-Adresse eingeben.";
return;
}
if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) {
status.className = "reg-status error";
status.textContent = "⚠️ Bitte eine gültige E-Mail-Adresse eingeben.";
return;
}
btn.disabled = true;
btn.textContent = "Wird übertragen...";
try {
const res = await fetch("register.php", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
username,
email,
website: document.getElementById("reg-website").value,
}),
});
const data = await res.json().catch(() => ({}));
if (res.ok && data.success) {
status.className = "reg-status success";
status.innerHTML =
"✅ <strong>Anfrage erfolgreich gesendet!</strong><br>" +
"Die Administration wurde benachrichtigt. Du erhältst deine Login-Daten in Kürze an die angegebene E-Mail-Adresse.";
usernameInput.value = "";
emailInput.value = "";
} else {
throw new Error(
data.error ||
(res.status === 429
? "Zu viele Anfragen. Bitte warte einige Minuten."
: `HTTP ${res.status}`),
);
}
} catch (e) {
status.className = "reg-status error";
status.textContent = `❌ Übertragung fehlgeschlagen: ${e.message}`;
} finally {
btn.disabled = false;
btn.textContent = "Zugang anfordern";
}
}
// Enter-Taste in Feldern
document.addEventListener("DOMContentLoaded", () => {
["reg-username", "reg-email"].forEach((id) => {
document.getElementById(id).addEventListener("keydown", (e) => {
if (e.key === "Enter") submitRegistration();
});
});
});
const canvas = document.getElementById("matrix-bg");
const ctx = canvas.getContext("2d");
function resizeCanvas() {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
}
resizeCanvas();
window.addEventListener("resize", resizeCanvas);
const katakana =
"アイウエオカキクケコサシスセソタチツテトナニヌネノハヒフヘホマミムメモヤユヨラリルレロワン1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ";
const alphabet = katakana.split("");
const fontSize = 16;
let columns = canvas.width / fontSize;
const rainDrops = [];
for (let x = 0; x < columns; x++) {
rainDrops[x] = 1;
}
window.addEventListener("resize", () => {
columns = canvas.width / fontSize;
for (let x = rainDrops.length; x < columns; x++) {
rainDrops[x] = 1;
}
});
function drawMatrixRain() {
ctx.fillStyle = "rgba(0, 0, 0, 0.05)";
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = "#0f0";
ctx.font = fontSize + "px monospace";
for (let i = 0; i < rainDrops.length; i++) {
const text = alphabet[Math.floor(Math.random() * alphabet.length)];
ctx.fillText(text, i * fontSize, rainDrops[i] * fontSize);
if (rainDrops[i] * fontSize > canvas.height && Math.random() > 0.975) {
rainDrops[i] = 0;
}
rainDrops[i]++;
}
}
setInterval(drawMatrixRain, 30);
+629
View File
@@ -0,0 +1,629 @@
<!doctype html>
<html lang="de">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Matrix Guide - cyperpunk.de</title>
<!-- Favicon Integration -->
<link
rel="icon"
type="image/png"
href="https://img.icons8.com/ios/100/matrix-logo.png"
/>
<link rel="stylesheet" href="style.css" />
<!-- SEO & Meta -->
<meta
name="description"
content="Einstiegspunkt für den cyperpunk.de Matrix-Server. Hier kannst du einen Account beantragen und erfährst alles über Clients, mobile Apps und Server-Funktionen."
/>
<meta name="robots" content="index, follow" />
<!-- Open Graph -->
<meta property="og:type" content="website" />
<meta property="og:url" content="https://matrix.cyperpunk.de/" />
<meta property="og:title" content="Matrix Guide - cyperpunk.de" />
<meta
property="og:description"
content="Einstiegspunkt für den cyperpunk.de Matrix-Server. Hier kannst du einen Account beantragen und erfährst alles über Clients, mobile Apps und Server-Funktionen."
/>
<meta property="og:locale" content="de_DE" />
</head>
<body>
<a href="#main-content" class="skip-link">Zum Inhalt springen</a>
<!-- Matrix Code-Regen Hintergrund -->
<canvas id="matrix-bg" aria-hidden="true"></canvas>
<main class="container" id="main-content">
<!-- Header -->
<header>
<h1>Matrix Guide</h1>
<p class="subtitle">Systemstatus: Aktiv auf Host cyperpunk.de</p>
<p class="admin-tag">Grid-Operator: @dergrumpf:cyperpunk.de</p>
</header>
<!-- Sektion 1: Registrierungs-Widget -->
<section id="registration" aria-labelledby="heading-registration">
<h2 id="heading-registration">
1. Identitäts-Allokation (Registrierung)
</h2>
<p>
Zugangsdaten für diesen Server werden exklusiv durch die
Administration autorisiert. Nutze das untenstehende Interface, um eine
Verbindung anzufordern. Die Übertragung erfolgt direkt in unseren
gesicherten internen Koordinationskanal.
</p>
<!-- REGISTRIERUNGS-WIDGET -->
<div class="reg-form">
<label for="reg-username">Gewünschter Benutzername</label>
<input
type="text"
id="reg-username"
placeholder="z.B. neo"
autocomplete="off"
spellcheck="false"
/>
<label for="reg-email">E-Mail-Adresse (für Login-Daten)</label>
<input
type="email"
id="reg-email"
placeholder="z.B. neo@example.com"
autocomplete="off"
/>
<!-- Honeypot: muss leer bleiben -->
<label for="reg-website" style="display: none" aria-hidden="true"
>Website</label
>
<input
type="text"
id="reg-website"
name="website"
tabindex="-1"
autocomplete="off"
style="display: none"
aria-hidden="true"
/>
<button
class="reg-submit"
id="reg-btn"
onclick="submitRegistration()"
>
Zugang anfordern
</button>
<div
class="reg-status"
id="reg-status"
role="status"
aria-live="polite"
aria-atomic="true"
></div>
</div>
</section>
<!-- Sektion 2: Erster Login & Sicherheit -->
<section id="first-login" aria-labelledby="heading-first-login">
<h2 id="heading-first-login">
2. Protokoll für den initialen Verbindungsaufbau
</h2>
<div class="matrix-alert" role="alert" aria-label="Warnung">
<h3>⚠️ WARNUNG: Kryptografische Schlüssel sichern</h3>
<p>
Dieses Netzwerk erzwingt standardmäßig eine vollständige
Ende-zu-Ende-Verschlüsselung (E2EE). Bei der ersten Verbindung
generiert dein Terminal eine
<strong>Passphrase sowie einen Sicherheitsschlüssel</strong>.
Sichere dieses Krypto-Fragment extern! Solltest du dich abmelden
oder das Gerät wechseln und diesen Schlüssel verlieren, ist dein
dezentraler Datenstrom unwiderruflich verschlüsselt. Die
Administration hat keinerlei Backdoors, um deine Daten
wiederherzustellen.
</p>
</div>
<h3>Schritt-für-Schritt-Protokoll:</h3>
<ol>
<li>
Wähle ein Zugangs-Terminal (Client) aus dem folgenden Abschnitt.
</li>
<li>
Der Zugriff ist entweder über klassische
<strong>User-Credentials</strong> oder über das
<strong>Single-Sign-On-Protokoll (SSO)</strong> möglich.
</li>
<li>
Bei der Nutzung von SSO wirst du automatisch zum zentralen
Identitäts-Node unter
<a
href="https://auth.cyperpunk.de/"
target="_blank"
aria-label="auth.cyperpunk.de (öffnet neuen Tab)"
><strong>auth.cyperpunk.de</strong></a
>
(powered by Kanidm) umgeleitet.
</li>
<li>
Nach erfolgreicher Authentifizierung wird deine Session für das
Matrix-Grid freigeschaltet.
</li>
</ol>
</section>
<!-- Sektion 3: Web-Clients (Linktree) -->
<section id="clients" aria-labelledby="heading-clients">
<h2 id="heading-clients">3. Zugangs-Terminals initialisieren</h2>
<p>
Kopple dein System über eine unserer drei gehosteten
Web-Schnittstellen. Sobald dein Login-Protokoll abgeschlossen ist,
findest du den übergeordneten Hauptbereich (Community Space) im Grid
unter der Adresse <code>#cyperpunk:cyperpunk.de</code>.
</p>
<div class="linktree-container">
<a
href="https://element.cyperpunk.de/"
target="_blank"
aria-label="Element starten (öffnet neuen Tab)"
class="linktree-btn"
>
<img
src="https://img.icons8.com/keek-line/96/physics.png"
alt="Element Icon"
/>
Terminal: Element starten
</a>
<a
href="https://cinny.cyperpunk.de/"
target="_blank"
aria-label="Cinny starten (öffnet neuen Tab)"
class="linktree-btn"
>
<img
src="https://img.icons8.com/ios/100/bird--v1.png"
alt="Cinny Icon"
/>
Terminal: Cinny starten
</a>
<a
href="https://fluffy.cyperpunk.de/"
target="_blank"
aria-label="FluffyChat starten (öffnet neuen Tab)"
class="linktree-btn"
>
<img
src="https://img.icons8.com/pastel-glyph/64/cat--v3.png"
alt="FluffyChat Icon"
/>
Terminal: FluffyChat starten
</a>
</div>
<h3>Welches Programm soll ich wählen?</h3>
<div class="cards-grid">
<div class="card">
<h3 class="card-title">Element</h3>
<p class="card-text">
Der mächtige <strong>Allrounder</strong>. Sieht klassisch aus und
bietet absolut jede Funktion, die Matrix kann. Perfekt, wenn du
Räume selbst verwalten willst.
<strong>Unterstützt Sprach- und Videoanrufe</strong> direkt im
Browser.
</p>
</div>
<div class="card">
<h3 class="card-title">Cinny</h3>
<p class="card-text">
Der <strong>moderne Chat</strong>. Ist extrem schnell,
übersichtlich und orientiert sich vom Design an Discord oder
Slack. Ideal, wenn du einfach nur bequem schreiben willst.
<strong>Unterstützt Sprach- und Videoanrufe</strong> direkt im
Browser.
</p>
</div>
<div class="card" style="border-color: #00aa41">
<h3 class="card-title" style="color: #00aa41">FluffyChat</h3>
<p class="card-text">
Der <strong>einfache Messenger</strong>. Bunt, super unkompliziert
und perfekt für Einsteiger, die den Look von WhatsApp oder Signal
bevorzugen.
<strong style="color: #ff3333; text-shadow: none"
>Achtung:</strong
>
Diese Web-Version hat <strong>keinen Sprach-Chat</strong>.
</p>
</div>
</div>
</section>
<!-- Sektion 4: Mobiler Zugang -->
<section id="mobile" aria-labelledby="heading-mobile">
<h2 id="heading-mobile">4. Mobile App-Konfiguration</h2>
<p>
Möchtest du von unterwegs aus auf das Grid zugreifen? Lade dir einfach
eine passende App für dein Smartphone herunter:
</p>
<div class="app-download-box">
<!-- FluffyChat Download Zeile -->
<div class="app-row">
<div class="app-info">
<span class="app-name">FluffyChat</span>
<p style="margin: 5px 0 0 0; font-size: 0.85rem; color: #a3ffa3">
Bunt, einsteigerfreundlich und ideal für das Smartphone.
</p>
</div>
<div class="app-buttons">
<a
href="https://play.google.com/store/apps/details?id=chat.fluffy.fluffychat&hl=de_DE"
target="_blank"
aria-label="FluffyChat im Google Play Store (öffnet neuen Tab)"
class="app-btn"
>Google Play</a
>
<a
href="https://apps.apple.com/us/app/fluffychat/id1551469600"
target="_blank"
aria-label="FluffyChat im App Store (öffnet neuen Tab)"
class="app-btn"
>App Store</a
>
</div>
</div>
<!-- Element X Download Zeile -->
<div class="app-row">
<div class="app-info">
<span class="app-name">Element X</span>
<p style="margin: 5px 0 0 0; font-size: 0.85rem; color: #a3ffa3">
Die App der nächsten Generation: Extrem schnell, modern und
sicher.
</p>
</div>
<div class="app-buttons">
<a
href="https://play.google.com/store/apps/details?id=io.element.android.x&hl=de_DE"
target="_blank"
aria-label="Element X im Google Play Store (öffnet neuen Tab)"
class="app-btn"
>Google Play</a
>
<a
href="https://apps.apple.com/us/app/element-x-secure-chat-call/id1631335820"
target="_blank"
aria-label="Element X im App Store (öffnet neuen Tab)"
class="app-btn"
>App Store</a
>
</div>
</div>
</div>
<p style="margin-top: 20px">
<strong>Wichtig beim ersten App-Start:</strong>
</p>
<ul>
<li>Überspringe die automatische Registrierung in der App.</li>
<li>
Suche nach der Option <strong>"Heimserver ändern"</strong> (oder
Homeserver wechseln).
</li>
<li>
Trage dort manuell unsere Server-Adresse ein:
<code style="color: #fff">cyperpunk.de</code>
</li>
<li>
Logge dich erst danach wie gewohnt mit deinen Zugangsdaten oder per
SSO ein.
</li>
</ul>
</section>
<!-- Sektion 5: Telefonie & Serverlimits -->
<section id="infrastructure" aria-labelledby="heading-infrastructure">
<h2 id="heading-infrastructure">
5. Sprachkanäle &amp; Speicherplatz limits
</h2>
<p>
Unser Server stellt Kanäle bereit, mit denen du direkt über das
Internet mit anderen Mitgliedern sprechen oder Dateien austauschen
kannst.
</p>
<div class="cards-grid">
<div class="card">
<h3 class="card-title">Anrufe in Gruppen</h3>
<p class="card-text">
Wenn du mit mehreren Personen in einem Raum telefonierst, sorgt
unser Server im Hintergrund automatisch für eine flüssige, stabile
Audio- und Bildübertragung.
</p>
</div>
<div class="card">
<h3 class="card-title">Direktanrufe (1:1)</h3>
<p class="card-text">
Telefonate mit nur einer Person werden sicher und direkt
aufgebaut. Ein integrierter Hilfsdienst sorgt dafür, dass die
Verbindung auch durch Firewalls hinweg klappt.
</p>
</div>
<!-- Volle Breite für die Speicherplatzwarnung -->
<div
class="card full-width-card"
style="border-color: #ff9900; background: rgba(20, 10, 0, 0.6)"
>
<h3 class="card-title" style="color: #ff9900">
Dateigröße &amp; faires Teilen
</h3>
<p class="card-text">
Das Hochladen von Bildern, Videos oder Dokumenten ist auf maximal
<strong>50 MB pro Datei</strong> begrenzt. Da unser
Festplattenplatz auf dem Server nicht unendlich ist, gehe bitte
sparsam mit großen Dateien um.
</p>
</div>
</div>
</section>
<!-- Sektion 6: Server-Strukturen -->
<section
id="server-structures"
aria-labelledby="heading-server-structures"
>
<h2 id="heading-server-structures">
6. Server-Strukturen: Chats und Bereiche
</h2>
<p>
Damit alles ordentlich bleibt und du dich gut zurechtfindest, ist
Matrix logisch aufgeteilt. Es gibt im Grunde zwei Ebenen, um Gespräche
zu sortieren:
</p>
<ol>
<li>
<strong>Spaces (Bereiche / Ordner):</strong> Das ist die
übergeordnete Klammer wie ein kompletter Server bei Discord. Ein
Space sammelt verschiedene Chats an einem Ort, damit es
übersichtlich bleibt.
</li>
<li>
<strong>Rooms (Räume / Chats):</strong> Das sind die eigentlichen
Text- oder Sprachkanäle innerhalb eines Bereichs. Hier wird
geschrieben, telefoniert und Material geteilt.
</li>
</ol>
<p><strong>Tipps für Admins und Moderatoren:</strong></p>
<ul>
<li>
Wenn du eine neue Gruppe gründest, erstelle am besten
<strong>zuerst einen Space (Bereich)</strong>. Danach packst du die
einzelnen Chats geordnet dort hinein.
</li>
<li>
Verwende klare, einfache Namen für deine Räume. Das macht es für
alle Nutzer leichter, die Übersicht in ihrer Seitenleiste zu
behalten.
</li>
</ul>
</section>
<!-- Sektion 7: Codex Tabelle -->
<section id="codex" aria-labelledby="heading-codex">
<h2 id="heading-codex">7. Discord-zu-Matrix Referenz-Codex</h2>
<p>
Verwende diese Referenztabelle, um gewohnte Discord-Strukturen auf den
offenen Matrix-Standard zu mappen:
</p>
<table
class="codex-table"
aria-label="Discord-zu-Matrix Vergleichstabelle"
>
<thead>
<tr>
<th scope="col">Discord-Konstrukt</th>
<th scope="col">Matrix-Spezifikation</th>
<th scope="col">Funktion im Netz</th>
</tr>
</thead>
<tbody>
<tr>
<td>Server / Gilde</td>
<td><strong>Space (Bereich)</strong></td>
<td>
Ein virtueller Container, der Räume und Entitäten unter einem
Thema bündelt.
</td>
</tr>
<tr>
<td>Text- / Sprachkanal</td>
<td><strong>Room (Raum)</strong></td>
<td>
Der eigentliche Endpunkt für Textnachrichten, Bilddaten oder
Telefonie.
</td>
</tr>
<tr>
<td>Direktnachricht (DM)</td>
<td><strong>Direkt-Chat</strong></td>
<td>
Ein kryptografisch isolierter 1:1-Kanal zwischen zwei Accounts.
</td>
</tr>
<tr>
<td>Rollen &amp; Rechte</td>
<td><strong>Power Levels</strong></td>
<td>
Ein numerischer Berechtigungswert (0 bis 100) zur Steuerung von
Moderations-Rechten.
</td>
</tr>
<tr>
<td>Nitro Emojis</td>
<td><strong>Emote-Packs</strong></td>
<td>
Serverweite Sticker- und Emoji-Bibliotheken dezentral und
kostenfrei.
</td>
</tr>
</tbody>
</table>
</section>
<!-- Sektion 8: System-Automatisierungen (Maubots) -->
<section id="maubots" aria-labelledby="heading-maubots">
<h2 id="heading-maubots">8. Aktive Automatisierungs-Bots (Maubot)</h2>
<p>
Das <strong>Maubot</strong>-Framework erlaubt das Einbinden von
automatisierten Skripten und Funktionen in das Raum-Netzwerk. Unsere
Kerninstanz operiert unter der ID:
<code style="color: #fff">@maubot:cyperpunk.de</code>.
</p>
<p>
<strong>Bedienung:</strong> Du kannst die Entität
<strong>in jeden beliebigen Raum einladen</strong>, um die geladenen
Module per Textbefehl zu aktivieren. Aktive Subroutinen:
</p>
<div class="cards-grid">
<!-- Bot 1 -->
<div class="card">
<h3 class="card-title">Urban Dictionary</h3>
<p class="card-text">
Analysiert Slang-Begriffe, Definitionen und sprachliche Variablen
direkt im Raum-Feed.<br /><a
href="https://github.com/dvdgsng/UrbanMaubot/blob/master/README.md"
target="_blank"
aria-label="Urban Dictionary Maubot Dokumentation (öffnet neuen Tab)"
>Dokumentation</a
>
</p>
</div>
<!-- Bot 2 -->
<div class="card">
<h3 class="card-title">RSS News-Feeds</h3>
<p class="card-text">
Abonniert externe Informations-Feeds und speist Updates
vollautomatisch in den Chat-Stream ein.<br /><a
href="https://github.com/maubot/rss/blob/master/README.md"
target="_blank"
aria-label="RSS Maubot Dokumentation (öffnet neuen Tab)"
>Dokumentation</a
>
</p>
</div>
<!-- Bot 3 -->
<div class="card">
<h3 class="card-title">Erinnerungs-Engine</h3>
<p class="card-text">
Ermöglicht das Setzen von Timern, Cron-Triggern und
zeitgesteuerten Alarmen im Raum.<br /><a
href="https://github.com/maubot/reminder/blob/master/README.md"
target="_blank"
aria-label="Reminder Maubot Dokumentation (öffnet neuen Tab)"
>Dokumentation</a
>
</p>
</div>
<!-- Bot 4 -->
<div class="card">
<h3 class="card-title">Wetter-Array</h3>
<p class="card-text">
Fragt aktuelle Klimadaten und meteorologische Berichte für globale
Koordinaten ab.<br /><a
href="https://github.com/kellya/maubot-weather/blob/main/README.md"
target="_blank"
aria-label="Weather Maubot Dokumentation (öffnet neuen Tab)"
>Dokumentation</a
>
</p>
</div>
</div>
</section>
<!-- Sektion 9: Discord Bridge -->
<section id="discord-bridge" aria-labelledby="heading-discord-bridge">
<h2 id="heading-discord-bridge">9. Discord Bridge Link</h2>
<p>
Unsere Brücken-Module spiegeln Daten und Signalkanal-Nachrichten
nahtlos zwischen Matrix und externen Discord-Gilden.
</p>
<p>
<strong>Ident-ID der Discord-Bridge:</strong>
<code style="color: #fff">@discordbot:cyperpunk.de</code>
</p>
<h3>Befehlssatz:</h3>
<ul>
<li>
Öffne eine Direktverbindung (DM) mit dem Bridge-Bot, um Befehle
direkt einzuspeisen.
</li>
<li>
Im privaten Kommunikationskanal versteht der Bot Befehle direkt über
das Text-Kommando <code style="color: #fff">help</code>.
</li>
<li>
Innerhalb von geteilten Räumen muss das Präfix vorangestellt werden:
<code style="color: #fff">!discord help</code>.
</li>
</ul>
</section>
<!-- FAQ -->
<section id="faq" aria-labelledby="heading-faq">
<h2 id="heading-faq">FAQ Häufige Fragen</h2>
<h3>Was ist Matrix?</h3>
<p>
Matrix ist ein offenes, dezentrales Kommunikationsprotokoll ähnlich
wie E-Mail, aber für Echtzeit-Chat und Anrufe. Jeder kann seinen
eigenen Server betreiben, und Server können miteinander kommunizieren.
</p>
<h3>Warum kein Discord?</h3>
<p>
Discord ist ein zentralisierter Dienst, der Nutzerdaten speichert und
auswerten kann. Matrix ist Open Source, selbst gehostet und
Ende-zu-Ende-verschlüsselt du behältst die Kontrolle über deine
Daten.
</p>
<h3>Kann ich meinen bestehenden Matrix-Account verwenden?</h3>
<p>
Ja. Du kannst von jedem Matrix-Server aus unseren Räumen beitreten
einfach die Adresse <code>@deinname:deinserver.de</code> verwenden und
den Raum <code>#cyperpunk:cyperpunk.de</code> suchen.
</p>
<h3>Wie bekomme ich einen Account auf diesem Server?</h3>
<p>
Fülle das Registrierungsformular oben aus. Die Administration prüft
deine Anfrage und schickt dir die Zugangsdaten per E-Mail.
</p>
<h3>Sind meine Nachrichten verschlüsselt?</h3>
<p>
Ja. Dieser Server erzwingt Ende-zu-Ende-Verschlüsselung (E2EE) in
allen privaten Chats. Stelle sicher, dass du deinen
Sicherheitsschlüssel beim ersten Login sicherst.
</p>
<h3>Was kostet das?</h3>
<p>
Nichts. Der Server wird privat betrieben und ist kostenlos nutzbar.
</p>
</section>
</main>
<!-- Footer -->
<footer>
[SYSTEM MESSAGE] // This site was autonomously generated by Gemini //
[EOF]
</footer>
<script src="app.js"></script>
</body>
</html>
+2
View File
@@ -12,6 +12,8 @@
rss
reminder
urban
llm
wolframalpha
];
settings = {
database = "postgresql:///maubot?host=/run/postgresql";
+159
View File
@@ -0,0 +1,159 @@
<?php
// Nur POST-Requests erlauben
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
http_response_code(405);
exit(json_encode(['error' => 'Method not allowed']));
}
header('Content-Type: application/json');
// --- Konfiguration ---
define('MATRIX_HOMESERVER', 'https://cyperpunk.de');
define('MATRIX_ACCESS_TOKEN', 'syt_cmVnaXN0cmF0aW9uLWJvdA_tWjAfJOYDoJSuoCWoYIQ_4YuoMw');
define('MATRIX_ROOM_ID', '!xBizjYatXLfpCorAXt:cyperpunk.de');
// Rate-Limit: max. Anfragen pro Zeitfenster
define('RATE_LIMIT_MAX', 5); // Anfragen
define('RATE_LIMIT_WINDOW', 300); // Sekunden (5 Minuten)
define('RATE_LIMIT_DIR', sys_get_temp_dir() . '/matrix_reg_rl');
// --- IP ermitteln (auch hinter Proxies) ---
function getClientIP(): string {
$headers = ['HTTP_CF_CONNECTING_IP', 'HTTP_X_FORWARDED_FOR', 'HTTP_X_REAL_IP', 'REMOTE_ADDR'];
foreach ($headers as $header) {
if (!empty($_SERVER[$header])) {
$ip = explode(',', $_SERVER[$header])[0];
$ip = trim($ip);
if (filter_var($ip, FILTER_VALIDATE_IP)) {
return $ip;
}
}
}
return 'unknown';
}
// --- Rate-Limiting (dateibasiert) ---
function checkRateLimit(string $ip): bool {
$dir = RATE_LIMIT_DIR;
if (!is_dir($dir)) {
mkdir($dir, 0700, true);
}
$file = $dir . '/' . hash('sha256', $ip) . '.json';
$now = time();
$data = ['count' => 0, 'window_start' => $now];
if (file_exists($file)) {
$data = json_decode(file_get_contents($file), true) ?? $data;
// Zeitfenster abgelaufen → zurücksetzen
if ($now - $data['window_start'] > RATE_LIMIT_WINDOW) {
$data = ['count' => 0, 'window_start' => $now];
}
}
if ($data['count'] >= RATE_LIMIT_MAX) {
return false;
}
$data['count']++;
file_put_contents($file, json_encode($data), LOCK_EX);
return true;
}
// --- Honeypot prüfen ---
$input = json_decode(file_get_contents('php://input'), true);
if (!empty($input['website'])) {
// Bot hat das versteckte Feld ausgefüllt still ablehnen
http_response_code(200);
exit(json_encode(['success' => true]));
}
// --- Rate-Limit prüfen ---
$ip = getClientIP();
if (!checkRateLimit($ip)) {
http_response_code(429);
exit(json_encode(['error' => 'Zu viele Anfragen. Bitte warte einige Minuten.']));
}
// --- Eingabe validieren ---
$username = isset($input['username']) ? trim($input['username']) : '';
$email = isset($input['email']) ? trim($input['email']) : '';
if (empty($username) || empty($email)) {
http_response_code(400);
exit(json_encode(['error' => 'Benutzername und E-Mail sind erforderlich.']));
}
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
http_response_code(400);
exit(json_encode(['error' => 'Ungültige E-Mail-Adresse.']));
}
// Eingaben bereinigen
$username = htmlspecialchars($username, ENT_QUOTES, 'UTF-8');
$email = htmlspecialchars($email, ENT_QUOTES, 'UTF-8');
// --- Nachricht zusammenstellen ---
$timestamp = date('d.m.Y H:i:s T');
$userAgent = $_SERVER['HTTP_USER_AGENT'] ?? 'Unbekannt';
$message = "🔐 Registration Request\n\n"
. "Username: {$username}\n"
. "E-Mail: {$email}\n"
. "IP: {$ip}\n"
. "Zeitstempel: {$timestamp}\n"
. "User-Agent: {$userAgent}";
$formattedMessage = "🔐 <strong>Registration Request</strong><br><br>"
. "<strong>Username:</strong> {$username}<br>"
. "<strong>E-Mail:</strong> {$email}<br>"
. "<strong>IP:</strong> {$ip}<br>"
. "<strong>Zeitstempel:</strong> {$timestamp}<br>"
. "<strong>User-Agent:</strong> {$userAgent}";
// --- An Matrix senden ---
$txnId = 'reg_' . time() . '_' . bin2hex(random_bytes(8));
$url = MATRIX_HOMESERVER
. '/_matrix/client/v3/rooms/'
. urlencode(MATRIX_ROOM_ID)
. '/send/m.room.message/'
. $txnId;
$payload = json_encode([
'msgtype' => 'm.text',
'body' => $message,
'format' => 'org.matrix.custom.html',
'formatted_body' => $formattedMessage,
]);
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'PUT',
CURLOPT_POSTFIELDS => $payload,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . MATRIX_ACCESS_TOKEN,
'Content-Type: application/json',
],
CURLOPT_TIMEOUT => 10,
]);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$curlError = curl_error($ch);
curl_close($ch);
if ($curlError) {
http_response_code(502);
exit(json_encode(['error' => 'Verbindung zum Matrix-Server fehlgeschlagen.']));
}
if ($httpCode >= 200 && $httpCode < 300) {
http_response_code(200);
exit(json_encode(['success' => true]));
} else {
$matrixError = json_decode($response, true);
http_response_code(502);
exit(json_encode(['error' => 'Matrix-Fehler: ' . ($matrixError['error'] ?? "HTTP {$httpCode}")]));
}
+526
View File
@@ -0,0 +1,526 @@
/* Basis-Styles & Reset */
*,
*::before,
*::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
background-color: #000;
color: #00ff41; /* Klassisches Matrix-Grün */
font-family: "Courier New", Courier, monospace;
overflow-x: hidden;
min-height: 100vh;
display: flex;
flex-direction: column;
}
/* Skip-to-content Link für Tastaturnavigation */
.skip-link {
position: absolute;
top: -100%;
left: 0;
background: #00ff41;
color: #000;
padding: 10px 20px;
font-weight: bold;
text-decoration: none;
z-index: 9999;
border-radius: 0 0 4px 0;
}
.skip-link:focus {
top: 0;
}
/* Matrix-Regen Canvas Hintergrund */
#matrix-bg {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: -1;
opacity: 0.12; /* Gedimmter Hintergrund für optimale Lesbarkeit */
}
/* Hauptcontainer */
.container {
max-width: 950px;
margin: 0 auto;
padding: 40px 20px;
position: relative;
z-index: 1;
flex: 1;
}
/* Header / Titel */
header {
text-align: center;
margin-bottom: 50px;
}
h1 {
font-size: 3rem;
text-transform: uppercase;
letter-spacing: 4px;
color: #fff;
text-shadow:
0 0 10px #00ff41,
0 0 20px #00ff41,
0 0 30px #00ff41;
margin-bottom: 10px;
animation: glitch 1s linear infinite alternate;
}
.subtitle {
font-size: 1.1rem;
color: #00ff41;
text-transform: uppercase;
letter-spacing: 2px;
color: #00e63a;
margin-bottom: 15px;
}
.admin-tag {
display: inline-block;
background: rgba(0, 255, 65, 0.1);
border: 1px dashed #00ff41;
padding: 6px 12px;
font-size: 0.9rem;
color: #fff;
border-radius: 4px;
}
/* Sektionen-Styling */
section {
margin-bottom: 60px;
background: rgba(0, 5, 0, 0.85);
border: 1px solid #00ff41;
border-radius: 6px;
padding: 30px;
box-shadow: 0 0 15px rgba(0, 255, 65, 0.1);
}
h2 {
font-size: 1.6rem;
border-bottom: 2px solid #00ff41;
padding-bottom: 8px;
margin-bottom: 25px;
text-transform: uppercase;
letter-spacing: 2px;
color: #fff;
text-shadow: 0 0 5px #00ff41;
}
h3 {
font-size: 1.2rem;
color: #fff;
margin-top: 25px;
margin-bottom: 10px;
text-transform: uppercase;
}
p {
font-size: 0.95rem;
line-height: 1.6;
color: #a3ffa3;
margin-bottom: 15px;
}
ol,
ul {
margin-left: 20px;
margin-bottom: 15px;
color: #a3ffa3;
}
li {
margin-bottom: 8px;
line-height: 1.5;
}
strong {
color: #fff;
text-shadow: 0 0 2px #00ff41;
}
a {
color: #fff;
text-decoration: underline;
}
a:hover {
color: #00ff41;
}
/* --- Linktree-Clients & App-Links --- */
.linktree-container {
max-width: 600px;
margin: 0 auto;
display: flex;
flex-direction: column;
align-items: center;
gap: 15px;
margin-bottom: 30px;
}
.linktree-btn {
display: flex;
align-items: center;
justify-content: center;
gap: 12px;
width: 100%;
padding: 16px 20px;
background: transparent;
border: 2px solid #00ff41;
color: #00ff41;
text-decoration: none;
font-size: 1.1rem;
font-weight: bold;
text-transform: uppercase;
letter-spacing: 2px;
border-radius: 4px;
transition: all 0.3s ease;
box-shadow: inset 0 0 0 0 rgba(0, 255, 65, 0.2);
}
.linktree-btn img {
height: 24px;
width: auto;
object-fit: contain;
filter: invert(53%) sepia(93%) saturate(1353%) hue-rotate(87deg)
brightness(119%) contrast(119%);
transition: all 0.3s ease;
}
.linktree-btn:hover {
background: #00ff41;
color: #000;
box-shadow: 0 0 15px #00ff41;
cursor: pointer;
}
.linktree-btn:hover img {
filter: invert(0%) brightness(0%);
}
/* Mobile Download Bereich */
.app-download-box {
background: rgba(0, 20, 0, 0.4);
border: 1px dashed #00ff41;
border-radius: 4px;
padding: 20px;
margin-top: 20px;
}
.app-row {
display: flex;
flex-wrap: wrap;
align-items: center;
justify-content: space-between;
gap: 15px;
padding: 15px 0;
}
.app-row:not(:last-child) {
border-bottom: 1px solid rgba(0, 255, 65, 0.2);
}
.app-info {
flex: 1;
min-width: 200px;
}
.app-name {
font-weight: bold;
color: #fff;
font-size: 1.1rem;
}
.app-buttons {
display: flex;
gap: 10px;
flex-wrap: wrap;
}
.app-btn {
display: inline-flex;
align-items: center;
gap: 8px;
padding: 8px 16px;
border: 1px solid #00ff41;
color: #00ff41;
text-decoration: none;
font-size: 0.85rem;
border-radius: 4px;
text-transform: uppercase;
transition: all 0.2s ease;
}
.app-btn:hover {
background: rgba(0, 255, 65, 0.2);
color: #fff;
box-shadow: 0 0 8px rgba(0, 255, 65, 0.5);
}
/* --- Karten-Raster (Cards Grid) --- */
.cards-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
gap: 20px;
margin-top: 15px;
}
.card {
background: rgba(0, 0, 0, 0.9);
border: 1px solid #00ff41;
border-radius: 4px;
padding: 20px;
transition: all 0.3s ease;
}
.card:hover {
transform: translateY(-3px);
box-shadow: 0 0 15px rgba(0, 255, 65, 0.4);
border-color: #fff;
}
.card.full-width-card {
grid-column: 1 / -1;
}
.card-title {
font-size: 1.15rem;
color: #fff;
margin-bottom: 10px;
text-transform: uppercase;
letter-spacing: 1px;
border-bottom: 1px dashed #00ff41;
padding-bottom: 5px;
}
.card-text {
font-size: 0.9rem;
line-height: 1.5;
color: #a3ffa3;
margin-bottom: 0;
}
/* --- Registrierungs-Widget --- */
.reg-form {
padding: 10px 0;
margin-top: 15px;
}
.reg-form label {
display: block;
font-size: 0.85rem;
color: #00ff41;
text-transform: uppercase;
letter-spacing: 1px;
margin-bottom: 6px;
}
.reg-form input {
width: 100%;
background: rgba(0, 0, 0, 0.8);
border: 1px solid #00ff41;
color: #fff;
font-family: "Courier New", Courier, monospace;
font-size: 1rem;
padding: 10px 14px;
border-radius: 3px;
margin-bottom: 20px;
outline: none;
transition:
border-color 0.2s,
box-shadow 0.2s;
}
.reg-form input:focus {
border-color: #fff;
box-shadow: 0 0 8px rgba(0, 255, 65, 0.4);
}
.reg-submit {
width: 100%;
padding: 14px;
background: transparent;
border: 2px solid #00ff41;
color: #00ff41;
font-family: "Courier New", Courier, monospace;
font-size: 1rem;
font-weight: bold;
text-transform: uppercase;
letter-spacing: 2px;
border-radius: 3px;
cursor: pointer;
transition: all 0.3s ease;
}
.reg-submit:hover:not(:disabled) {
background: #00ff41;
color: #000;
box-shadow: 0 0 15px #00ff41;
}
.reg-submit:disabled {
opacity: 0.5;
cursor: not-allowed;
}
.reg-status {
margin-top: 20px;
padding: 15px;
border-radius: 3px;
font-size: 0.9rem;
display: none;
}
.reg-status.success {
display: block;
background: rgba(0, 255, 65, 0.1);
border: 1px solid #00ff41;
color: #a3ffa3;
}
.reg-status.error {
display: block;
background: rgba(255, 0, 0, 0.1);
border: 1px solid #ff0000;
color: #ff9999;
}
/* --- Warnungen / Alerts --- */
.matrix-alert {
background: rgba(255, 0, 0, 0.1);
border: 1px solid #ff0000;
color: #ff9999;
padding: 20px;
border-radius: 4px;
margin-bottom: 25px;
}
.matrix-alert h3 {
color: #ff3333;
margin-bottom: 8px;
margin-top: 0;
}
/* --- Codex Tabellen-Styling --- */
.codex-table {
width: 100%;
border-collapse: collapse;
margin-top: 15px;
font-size: 0.95rem;
}
.codex-table th,
.codex-table td {
border: 1px solid #00ff41;
padding: 12px;
text-align: left;
}
.codex-table th {
background: rgba(0, 255, 65, 0.15);
color: #fff;
text-transform: uppercase;
}
.codex-table tr:nth-child(even) {
background: rgba(0, 255, 65, 0.03);
}
/* --- Footer --- */
footer {
text-align: center;
padding: 20px;
background: rgba(0, 5, 0, 0.9);
border-top: 1px dashed #00ff41;
font-size: 0.8rem;
color: #00ff41;
color: rgba(0, 255, 65, 0.7);
position: relative;
z-index: 1;
letter-spacing: 1px;
}
/* Fokus-Styles für Tastaturnavigation */
a:focus-visible,
button:focus-visible,
input:focus-visible {
outline: 2px solid #fff;
outline-offset: 3px;
box-shadow: 0 0 0 4px rgba(0, 255, 65, 0.4);
}
/* Opacity-fix: sicherstellen dass gedimmter Text ausreichend Kontrast hat */
.app-info p {
opacity: 1;
color: #a3ffa3;
}
/* Glitch Animations-Effekt */
@keyframes glitch {
0% {
text-shadow:
2px 2px 0px #ff0000,
-2px -2px 0px #0000ff;
}
50% {
text-shadow:
-2px 2px 0px #00ff41,
2px -2px 0px #ff0000;
}
100% {
text-shadow:
2px -2px 0px #0000ff,
-2px 2px 0px #00ff41;
}
}
/* Animationen deaktivieren für Nutzer die das bevorzugen */
@media (prefers-reduced-motion: reduce) {
h1 {
animation: none;
text-shadow: 0 0 10px #00ff41;
}
#matrix-bg {
display: none;
}
*,
*::before,
*::after {
transition: none !important;
}
}
@media (max-width: 600px) {
h1 {
font-size: 2.2rem;
}
h2 {
font-size: 1.3rem;
}
section {
padding: 15px;
}
.codex-table th,
.codex-table td {
padding: 8px;
font-size: 0.85rem;
}
.app-row {
flex-direction: column;
align-items: flex-start;
}
.app-buttons {
width: 100%;
}
.app-btn {
flex: 1;
justify-content: center;
}
}
+44 -2
View File
@@ -26,6 +26,11 @@ let
restrictBaseUrl = [ "https://matrix.cyperpunk.de" ];
loginFlows = [ "password" ];
};
matrixIndexHtml = pkgs.writeText "matrix-index.html" (builtins.readFile ./index.html);
matrixRegisterPhp = pkgs.writeText "matrix-register.php" (builtins.readFile ./register.php);
matrixStyleCss = pkgs.writeText "matrix-style.css" (builtins.readFile ./style.css);
matrixAppJs = pkgs.writeText "matrix-app.js" (builtins.readFile ./app.js);
in
{
sops.secrets = {
@@ -44,6 +49,29 @@ in
};
};
systemd.tmpfiles.rules = [
"d /var/www/matrix 0755 nginx nginx -"
"L+ /var/www/matrix/index.html 0644 nginx nginx - ${matrixIndexHtml}"
"L+ /var/www/matrix/register.php 0644 nginx nginx - ${matrixRegisterPhp}"
"L+ /var/www/matrix/style.css 0644 nginx nginx - ${matrixStyleCss}"
"L+ /var/www/matrix/app.js 0644 nginx nginx - ${matrixAppJs}"
];
services.phpfpm.pools.matrix = {
user = "nginx";
group = "nginx";
settings = {
"listen.owner" = "nginx";
"listen.group" = "nginx";
"pm" = "dynamic";
"pm.max_children" = 10;
"pm.start_servers" = 2;
"pm.min_spare_servers" = 1;
"pm.max_spare_servers" = 3;
};
phpPackage = pkgs.php.withExtensions ({ enabled, all }: enabled ++ [ all.curl ]);
};
services = {
matrix-synapse = {
enable = true;
@@ -174,6 +202,22 @@ in
proxy_set_header Connection "upgrade";
'';
};
"/" = {
root = "/var/www/matrix";
extraConfig = ''
index index.html;
try_files $uri $uri/ =404;
'';
};
"~ \\.php$" = {
root = "/var/www/matrix";
extraConfig = ''
fastcgi_pass unix:${config.services.phpfpm.pools.matrix.socket};
fastcgi_index index.php;
include ${pkgs.nginx}/conf/fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
'';
};
};
};
};
@@ -188,14 +232,12 @@ in
LC_COLLATE = "C"
LC_CTYPE = "C";
'';
settings = {
wal_level = "replica";
max_wal_senders = 5;
wal_keep_size = "512MB";
listen_addresses = lib.mkForce "127.0.0.1,100.109.10.91";
};
authentication = lib.mkAfter ''
host replication replicator 100.0.0.0/8 scram-sha-256
'';