SendSecret: Erstimport und Umstellung auf Tag-Deployment

Wrapper-Frontend vor cryptgeon: der Browser verschluesselt lokal, per Mail
geht nur der Link raus.

Fuer das Deployment nach den Konventionen aus DEPLOY.md hergerichtet:

- docker-compose.yml mit festem Projekt- und Container-Namen, kein
  ports-Mapping, Healthcheck als Deploy-Gate. cryptgeon und redis liegen
  im internen Netz, nur app haengt im Web-Netz.
- cryptgeon von latest auf 2.9.3 gepinnt. Das ist derselbe Stand, den
  latest bisher geliefert hat; 2.6.2 existiert nicht.
- /healthz in server.js, vor dem Catch-all-Proxy registriert.
- Dockerfile auf npm ci mit Lockfile und non-root umgestellt.
- .gitea/workflows/deploy.yml: Build und Syntaxpruefung vor dem Deploy,
  .env aus dem Repo-Secret DOTENV.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Lucas Orth
2026-08-25 07:26:15 +02:00
commit 681fc1649a
19 changed files with 3096 additions and 0 deletions

174
app/public/link.html Normal file
View File

@@ -0,0 +1,174 @@
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Link generieren – Lucas Orth</title>
<meta name="robots" content="noindex, nofollow">
<link rel="icon" href="https://lucas-orth.de/wp-content/uploads/2023/06/favicon-scaled.webp">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@500;600;700&family=Manrope:wght@400;500;600&display=swap" rel="stylesheet">
<link rel="stylesheet" href="/style.css">
</head>
<body>
<div class="page">
<!-- ── Header ─────────────────────────────────── -->
<header class="header">
<nav class="nav container">
<a href="https://lucas-orth.de" target="_blank" rel="noopener">
<img
src="https://lucas-orth.de/wp-content/uploads/2023/06/Lucas-Orth-black-high-res_ergebnis-scaled-e1687249204543.webp"
alt="Lucas Orth"
class="logo"
>
</a>
<a href="https://lucas-orth.de/kontakt" target="_blank" rel="noopener" class="btn btn--primary">
Kontakt
</a>
</nav>
</header>
<main style="display:contents">
<!-- ── Hero ───────────────────────────────────── -->
<section class="hero">
<div class="container">
<div class="hero__inner">
<!-- Form State -->
<div id="state-form">
<p class="eyebrow">Für interne Nutzung</p>
<h1 class="hero-title">
Sicheren Link<br>
<span class="accent">erstellen.</span>
</h1>
<p class="lead">
Text eingeben und einen einmalig abrufbaren, verschlüsselten
Link generieren. Der Link wird nicht automatisch versendet.
</p>
<form id="form" class="hero-form" novalidate>
<div class="field">
<label for="text" class="label">Geheimtext</label>
<textarea
id="text"
class="textarea"
placeholder="Geheimtext eingeben …"
rows="6"
required
autocomplete="off"
autocorrect="off"
spellcheck="false"
></textarea>
</div>
<div id="state-error" hidden>
<div class="alert alert--error" role="alert">
<span id="error-msg"></span>
</div>
</div>
<button id="submit" type="submit" class="btn btn--primary">
<span id="submit-label">Link erstellen</span>
</button>
</form>
</div>
<!-- Result State -->
<div id="state-result" hidden>
<p class="eyebrow">Link erstellt</p>
<h2 class="hero-title">
Bereit zum<br>
<span class="accent">Versenden.</span>
</h2>
<p class="lead">
Einmalig abrufbar – nach dem ersten Öffnen wird der Link
automatisch gelöscht.
</p>
<div class="hero-form">
<div class="field">
<label for="link-output" class="label">Ihr sicherer Link</label>
<div class="link-row">
<input
id="link-output"
type="text"
class="input input--mono"
readonly
aria-label="Generierter Link"
>
<button id="copy-btn" type="button" class="btn btn--secondary">
<span id="copy-label">Kopieren</span>
</button>
</div>
</div>
<button type="button" class="btn btn--ghost" onclick="location.reload()">
Neuen Link erstellen
</button>
</div>
</div>
</div>
</div>
</section>
<!-- ── Steps Section ──────────────────────────── -->
<section class="section-steps">
<div class="container">
<div class="section-header">
<p class="eyebrow">So funktioniert es</p>
<h2 class="section-title">Link erstellen &amp; teilen.</h2>
<p class="lead">
Ende-zu-Ende verschlüsselt – der Server sieht niemals den Klartext.
</p>
</div>
<div class="steps-grid">
<div class="step-card">
<span class="step-num">01</span>
<h3 class="step-title">Text eingeben</h3>
<p class="step-text">
Passwort oder Geheimtext ins Formular eingeben.
</p>
</div>
<div class="step-card">
<span class="step-num">02</span>
<h3 class="step-title">Link kopieren</h3>
<p class="step-text">
Den generierten Link mit dem Kopieren-Button in die
Zwischenablage übernehmen.
</p>
</div>
<div class="step-card">
<span class="step-num">03</span>
<h3 class="step-title">Einmalig abrufbar</h3>
<p class="step-text">
Nach dem ersten Öffnen löscht sich der Inhalt automatisch
und unwiderruflich.
</p>
</div>
</div>
</div>
</section>
</main>
<!-- ── Footer ─────────────────────────────────── -->
<footer class="footer">
<div class="container">
<div class="footer__inner">
<span>© 2026 Lucas Orth – Webdesign &amp; IT-Betreuung</span>
<div class="footer__links">
<a href="https://lucas-orth.de/datenschutz" target="_blank" rel="noopener">Datenschutz</a>
<a href="https://lucas-orth.de/impressum" target="_blank" rel="noopener">Impressum</a>
</div>
</div>
</div>
</footer>
</div>
<script src="/link.js"></script>
</body>
</html>