feat: brutalist placeholder site for 0012.de root
Replace ops beacon with sarcastic standalone landing page, centered layout, no leaked data; update SITE.md that monitoring lives elsewhere. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -1,53 +1,23 @@
|
||||
# 0012.de — Landing Page (Konzept)
|
||||
# 0012.de — Website (Konzept)
|
||||
|
||||
Die Domain soll nicht leer bleiben, aber zum Projekt passen: **externer Beobachter**, nicht Marketing-Seite.
|
||||
**0012.de ist eine eigene Website** — Inhalt noch offen. Kein Monitoring-Dashboard auf der Root-Domain.
|
||||
|
||||
## Idee: „Status Beacon“
|
||||
Monitoring, Status-Beacons, `ingest.php` usw. laufen später in **Unterverzeichnissen oder Subdomains** (z. B. `status.0012.de`, `/ops/` — noch festzulegen).
|
||||
|
||||
Eine minimalistische **Mission-Control-Oberfläche** — dunkel, monospace, ohne Tracking-Bloat.
|
||||
## Aktuell (Platzhalter)
|
||||
|
||||
### Tagline
|
||||
Brutalistische Startseite: scharf, dunkel, sarkastisch. Keine Secrets, keine Ops-Daten.
|
||||
|
||||
> **0012** · external observer node
|
||||
> Watching from outside.
|
||||
## Später: Status / Monitoring (nicht Root)
|
||||
|
||||
„0012“ als Name des Knotens (wie ein Kanal/Rufzeichen), nicht als Marketing-Marke.
|
||||
Siehe [`MONITORING.md`](MONITORING.md) und [`todo/06-site-0012-landing.md`](todo/06-site-0012-landing.md) — Zielpfad anpassen, wenn Subdomain/Pfad steht.
|
||||
|
||||
### Inhalt (above the fold)
|
||||
## Was 0012.de werden könnte
|
||||
|
||||
1. **Live-Status-Raster** — 12 Kacheln für die wichtigsten überwachten Endpoints (aus `MONITORING.md` Mindestliste):
|
||||
- schwenk.online, gitea, pics, medis, portainer, vfb.red, …
|
||||
- Farbe: grün / gelb (SSL < 14 Tage, Latenz hoch) / rot (down)
|
||||
- Letzter Check-Zeitstempel
|
||||
Noch unklar. Die Domain bleibt frei für etwas Eigenes — Portfolio, Experiment, Kunstprojekt, Katalog, whatever.
|
||||
|
||||
2. **boka-Heartbeat** — „Last push from boka: 3 min ago“ oder „⚠ no signal > 15 min“
|
||||
## Was bewusst nicht auf 0012.de /
|
||||
|
||||
3. **Meta-Zeile** — „This node runs on wint.global · independent of boka (89.163.231.180)“
|
||||
|
||||
### Datenquelle
|
||||
|
||||
- Gleiche JSON-Datei oder SQLite, die `monitor.php` beim Cron-Lauf schreibt (`data/last-checks.json`)
|
||||
- Landing Page liest nur diese Datei — **kein** Live-curl im Browser (Performance, Timeout)
|
||||
|
||||
### Design
|
||||
|
||||
- Hintergrund: sehr dunkles Grau (`#0d1117`)
|
||||
- Akzent: Amber für Warnung, Grün `#3fb950`, Rot `#f85149` (GitHub-Dark-ähnlich)
|
||||
- Schrift: system-ui oder `JetBrains Mono` via Google Fonts (ein Request)
|
||||
- Kein JS-Framework — plain PHP + minimal CSS, eine Datei
|
||||
|
||||
### Optional (später)
|
||||
|
||||
- `/status.json` — öffentliche API für Uptime (read-only)
|
||||
- RSS-Feed „incidents“ bei längeren Ausfällen
|
||||
- Kleine „About“-Sektion: Link zu GitHub-Repo (wenn public)
|
||||
|
||||
### Was bewusst **nicht**
|
||||
|
||||
- Kein Blog, kein Portfolio — das ist Ops-Infrastruktur
|
||||
- Keine Login-Fläche auf der Startseite
|
||||
- Keine Details zu internen IPs/Ports (nur boka-Heartbeat als Text)
|
||||
|
||||
## Umsetzung
|
||||
|
||||
Siehe [`todo/06-site-0012-landing.md`](todo/06-site-0012-landing.md).
|
||||
- Öffentliche Monitoring-Kacheln (→ Subdomain/Pfad)
|
||||
- IPs, Tokens, `.env`-Inhalte
|
||||
- Comic Sans (never again)
|
||||
|
||||
+211
-52
@@ -2,79 +2,238 @@
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
require_once dirname(__DIR__) . '/private/config/bootstrap.php';
|
||||
|
||||
$domain = env('DOMAIN', '0012.de');
|
||||
$bokaIp = env('BOKA_IP', '89.163.231.180');
|
||||
|
||||
$checksFile = privateDataDir() . '/last-checks.json';
|
||||
$checks = is_readable($checksFile)
|
||||
? json_decode((string) file_get_contents($checksFile), true)
|
||||
: null;
|
||||
|
||||
$lastRun = is_array($checks) && isset($checks['updated_at'])
|
||||
? $checks['updated_at']
|
||||
: null;
|
||||
|
||||
header('Content-Type: text/html; charset=utf-8');
|
||||
header('X-Robots-Tag: noindex');
|
||||
|
||||
$jahr = (int) date('Y');
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title><?= htmlspecialchars($domain, ENT_QUOTES, 'UTF-8') ?> · external observer</title>
|
||||
<title>0012</title>
|
||||
<style>
|
||||
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
||||
:root {
|
||||
--bg: #0d1117;
|
||||
--text: #c9d1d9;
|
||||
--muted: #8b949e;
|
||||
--accent: #58a6ff;
|
||||
--warn: #d29922;
|
||||
--ok: #3fb950;
|
||||
--bg: #0a0a0a;
|
||||
--fg: #e8e8e8;
|
||||
--dim: #666;
|
||||
--line: #333;
|
||||
--acid: #c8ff00;
|
||||
--blood: #ff2a2a;
|
||||
}
|
||||
* { box-sizing: border-box; }
|
||||
html { font-size: 16px; }
|
||||
body {
|
||||
margin: 0;
|
||||
min-height: 100vh;
|
||||
background: var(--bg);
|
||||
color: var(--text);
|
||||
font-family: ui-monospace, "Cascadia Code", "JetBrains Mono", monospace;
|
||||
line-height: 1.5;
|
||||
padding: 2rem 1.25rem 3rem;
|
||||
color: var(--fg);
|
||||
font-family: "IBM Plex Mono", "Courier New", monospace;
|
||||
line-height: 1.45;
|
||||
padding: 0 clamp(1rem, 4vw, 3rem);
|
||||
}
|
||||
main { max-width: 42rem; margin: 0 auto; }
|
||||
h1 { font-size: 1.25rem; font-weight: 600; margin: 0 0 0.25rem; letter-spacing: 0.04em; }
|
||||
.tagline { color: var(--muted); margin: 0 0 2rem; font-size: 0.9rem; }
|
||||
.panel {
|
||||
border: 1px solid #30363d;
|
||||
border-radius: 8px;
|
||||
padding: 1.25rem 1.5rem;
|
||||
background: #161b22;
|
||||
.wrap {
|
||||
width: 100%;
|
||||
max-width: 42rem;
|
||||
margin: 0 auto;
|
||||
min-height: 100vh;
|
||||
border-left: 4px solid var(--acid);
|
||||
border-right: 1px solid var(--line);
|
||||
}
|
||||
header {
|
||||
border-bottom: 1px solid var(--line);
|
||||
padding: 2rem 1.5rem 1.5rem;
|
||||
}
|
||||
.logo {
|
||||
font-size: clamp(4rem, 18vw, 9rem);
|
||||
font-weight: 700;
|
||||
letter-spacing: -0.06em;
|
||||
line-height: 0.85;
|
||||
color: var(--fg);
|
||||
}
|
||||
.logo span { color: var(--acid); }
|
||||
.claim {
|
||||
margin-top: 1rem;
|
||||
font-size: 0.85rem;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.2em;
|
||||
color: var(--dim);
|
||||
}
|
||||
section {
|
||||
border-bottom: 1px solid var(--line);
|
||||
padding: 1.5rem;
|
||||
}
|
||||
h2 {
|
||||
font-size: 0.7rem;
|
||||
font-weight: 400;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.25em;
|
||||
color: var(--dim);
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
p { margin-bottom: 0.75rem; }
|
||||
p:last-child { margin-bottom: 0; }
|
||||
.blink {
|
||||
animation: blink 1.2s step-end infinite;
|
||||
color: var(--blood);
|
||||
}
|
||||
@keyframes blink { 50% { opacity: 0; } }
|
||||
.grid {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 0;
|
||||
border: 1px solid var(--line);
|
||||
}
|
||||
@media (max-width: 520px) {
|
||||
.grid { grid-template-columns: 1fr; }
|
||||
.cell { border-right: none; }
|
||||
}
|
||||
.cell {
|
||||
padding: 1rem 1.25rem;
|
||||
border-right: 1px solid var(--line);
|
||||
border-bottom: 1px solid var(--line);
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
.cell:nth-child(2n) { border-right: none; }
|
||||
.cell strong {
|
||||
display: block;
|
||||
font-size: 0.65rem;
|
||||
letter-spacing: 0.15em;
|
||||
text-transform: uppercase;
|
||||
color: var(--dim);
|
||||
margin-bottom: 0.4rem;
|
||||
}
|
||||
.cell em { color: var(--acid); font-style: normal; }
|
||||
.cell s { color: var(--dim); }
|
||||
blockquote {
|
||||
border-left: 3px solid var(--blood);
|
||||
padding: 0 0 0 1rem;
|
||||
color: var(--dim);
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
.meter {
|
||||
display: flex;
|
||||
align-items: stretch;
|
||||
height: 12px;
|
||||
border: 1px solid var(--line);
|
||||
margin-top: 0.5rem;
|
||||
}
|
||||
.meter-fill {
|
||||
width: 3%;
|
||||
background: var(--acid);
|
||||
}
|
||||
.meter-rest {
|
||||
flex: 1;
|
||||
background: repeating-linear-gradient(
|
||||
90deg,
|
||||
var(--line) 0,
|
||||
var(--line) 2px,
|
||||
transparent 2px,
|
||||
transparent 6px
|
||||
);
|
||||
}
|
||||
footer {
|
||||
padding: 1.5rem;
|
||||
font-size: 0.7rem;
|
||||
color: var(--dim);
|
||||
letter-spacing: 0.05em;
|
||||
}
|
||||
footer a { color: var(--dim); }
|
||||
.cursor {
|
||||
display: inline-block;
|
||||
width: 0.55em;
|
||||
height: 1em;
|
||||
background: var(--fg);
|
||||
vertical-align: text-bottom;
|
||||
margin-left: 2px;
|
||||
animation: blink 0.9s step-end infinite;
|
||||
}
|
||||
.warn {
|
||||
background: var(--blood);
|
||||
color: var(--bg);
|
||||
padding: 0.35rem 0.6rem;
|
||||
font-size: 0.65rem;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.12em;
|
||||
display: inline-block;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
.status { color: var(--warn); margin: 0 0 0.75rem; }
|
||||
.status.ok { color: var(--ok); }
|
||||
.meta { color: var(--muted); font-size: 0.85rem; margin: 1.5rem 0 0; }
|
||||
code { color: var(--accent); }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<main>
|
||||
<h1>0012</h1>
|
||||
<p class="tagline">external observer node · watching from outside</p>
|
||||
<div class="wrap">
|
||||
<header>
|
||||
<div class="logo">00<span>12</span></div>
|
||||
<p class="claim">Eine Website · kein Inhalt · keine Entschuldigung</p>
|
||||
</header>
|
||||
|
||||
<div class="panel">
|
||||
<?php if ($lastRun): ?>
|
||||
<p class="status ok">Monitoring active — last check <?= htmlspecialchars($lastRun, ENT_QUOTES, 'UTF-8') ?></p>
|
||||
<?php else: ?>
|
||||
<p class="status">Monitoring not initialized — awaiting first cron run</p>
|
||||
<?php endif; ?>
|
||||
<p class="meta">
|
||||
Node <code><?= htmlspecialchars($domain, ENT_QUOTES, 'UTF-8') ?></code>
|
||||
· independent of boka (<code><?= htmlspecialchars($bokaIp, ENT_QUOTES, 'UTF-8') ?></code>)
|
||||
<section>
|
||||
<div class="warn">Status: unbelegt</div>
|
||||
<p>
|
||||
Willkommen auf 0012.de. Hier entsteht irgendwann etwas.
|
||||
Was genau, ist noch nicht entschieden — und das ist halb Strategie, halb Bequemlichkeit.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2>Was wir anbieten</h2>
|
||||
<div class="grid">
|
||||
<div class="cell">
|
||||
<strong>Produkt</strong>
|
||||
<s>Alles</s> Noch nichts.<br>
|
||||
Aber mit Überzeugung.
|
||||
</div>
|
||||
<div class="cell">
|
||||
<strong>Support</strong>
|
||||
Schreiben Sie an die Leere.<br>
|
||||
Antwortzeit: <em>∞</em>
|
||||
</div>
|
||||
<div class="cell">
|
||||
<strong>Preise</strong>
|
||||
Kostenlos.<br>
|
||||
Der Preis ist Ihre Zeit, die Sie gerade verschwenden.
|
||||
</div>
|
||||
<div class="cell">
|
||||
<strong>Datenschutz</strong>
|
||||
Wir sammeln nichts.<br>
|
||||
Nicht aus Tugend — aus Inkompetenz.
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2>Roadmap <?= $jahr ?>–<?= $jahr + 3 ?></h2>
|
||||
<p>Q1 — Idee haben<br>Q2 — Idee vergessen<br>Q3 — Diese Seite<br>Q4 — Sie lesen das</p>
|
||||
<p>Fortschritt zur „finalen Vision“:</p>
|
||||
<div class="meter"><div class="meter-fill"></div><div class="meter-rest"></div></div>
|
||||
<p style="margin-top:0.5rem;font-size:0.75rem;color:var(--dim);">3 % · gerundet von 0 %</p>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2>Pressestimmen (intern)</h2>
|
||||
<blockquote>
|
||||
„Sieht aus wie eine Website.“ — Entwickler, bezahlt<br><br>
|
||||
„0012 — eine Zahl vor dem Fehler.“ — Logfile, 03:14 Uhr<br><br>
|
||||
„Wenigstens keine Comic Sans.“ — Besucher mit Standards
|
||||
</blockquote>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2>Live</h2>
|
||||
<p>
|
||||
<span class="blink">■</span>
|
||||
System operational · Inhalt pending · Bedeutung not found
|
||||
<span class="cursor"></span>
|
||||
</p>
|
||||
<p style="font-size:0.8rem;color:var(--dim);margin-top:1rem;">
|
||||
Keine IPs. Keine Tokens. Nichts auf dem Server, der Ihnen zulächelt.
|
||||
Nur diese Seite — und Ihr schlechtes Gewissen, noch nicht weiterzuklicken.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<footer>
|
||||
© <?= $jahr ?> 0012.de · Impressum folgt, wenn jemand klagt
|
||||
</footer>
|
||||
</div>
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Reference in New Issue
Block a user