Added Website for amateurs to matrix
This commit is contained in:
@@ -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);
|
||||
@@ -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 & 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 & 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 & 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>
|
||||
@@ -12,6 +12,8 @@
|
||||
rss
|
||||
reminder
|
||||
urban
|
||||
llm
|
||||
wolframalpha
|
||||
];
|
||||
settings = {
|
||||
database = "postgresql:///maubot?host=/run/postgresql";
|
||||
|
||||
@@ -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}")]));
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
'';
|
||||
|
||||
Reference in New Issue
Block a user