Files
breakpilot-core/nginx/html/index.html
Benjamin Boenisch 3739d2b8b9 chore: Update nginx config and add static HTML pages
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-12 20:29:28 +01:00

1246 lines
43 KiB
HTML

<!DOCTYPE html>
<html lang="de" data-theme="light">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>BreakPilot Portal</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
/* ── Theme Variables ── */
:root {
/* Brand Colors */
--lehrer-500: #0ea5e9;
--lehrer-400: #38bdf8;
--lehrer-600: #0284c7;
--lehrer-bg: rgba(14, 165, 233, 0.08);
--lehrer-bg-hover: rgba(14, 165, 233, 0.14);
--lehrer-border: rgba(14, 165, 233, 0.25);
--compliance-500: #8b5cf6;
--compliance-400: #a78bfa;
--compliance-600: #7c3aed;
--compliance-bg: rgba(139, 92, 246, 0.08);
--compliance-bg-hover: rgba(139, 92, 246, 0.14);
--compliance-border: rgba(139, 92, 246, 0.25);
--core-500: #64748b;
--core-400: #94a3b8;
--core-600: #475569;
--core-bg: rgba(100, 116, 139, 0.08);
--core-bg-hover: rgba(100, 116, 139, 0.14);
--core-border: rgba(100, 116, 139, 0.25);
--docs-core: #14b8a6;
--docs-lehrer: #0ea5e9;
--docs-compliance: #8b5cf6;
}
/* ── Light Theme ── */
[data-theme="light"] {
--bg: #f8fafc;
--bg-subtle: #f1f5f9;
--surface: #ffffff;
--surface-hover: #f8fafc;
--border: #e2e8f0;
--border-subtle: #f1f5f9;
--text-primary: #0f172a;
--text-secondary: #475569;
--text-muted: #94a3b8;
--text-faint: #cbd5e1;
--shadow: rgba(0, 0, 0, 0.06);
--shadow-hover: rgba(0, 0, 0, 0.1);
--toggle-bg: #e2e8f0;
--toggle-fg: #f59e0b;
--divider: #e2e8f0;
--arch-box-bg: #f8fafc;
--arch-box-border: #e2e8f0;
--change-old: #94a3b8;
--change-new: #16a34a;
--link-color: #0ea5e9;
}
/* ── Dark Theme ── */
[data-theme="dark"] {
--bg: #0f172a;
--bg-subtle: #1e293b;
--surface: rgba(255, 255, 255, 0.05);
--surface-hover: rgba(255, 255, 255, 0.1);
--border: rgba(255, 255, 255, 0.08);
--border-subtle: rgba(255, 255, 255, 0.04);
--text-primary: #f1f5f9;
--text-secondary: #94a3b8;
--text-muted: #64748b;
--text-faint: #475569;
--shadow: rgba(0, 0, 0, 0.2);
--shadow-hover: rgba(0, 0, 0, 0.4);
--toggle-bg: #334155;
--toggle-fg: #60a5fa;
--divider: rgba(255, 255, 255, 0.06);
--arch-box-bg: rgba(255, 255, 255, 0.03);
--arch-box-border: rgba(255, 255, 255, 0.06);
--change-old: #64748b;
--change-new: #4ade80;
--link-color: #60a5fa;
}
/* Dark theme brand adjustments */
[data-theme="dark"] {
--lehrer-bg: rgba(14, 165, 233, 0.1);
--lehrer-bg-hover: rgba(14, 165, 233, 0.18);
--lehrer-border: rgba(56, 189, 248, 0.2);
--compliance-bg: rgba(139, 92, 246, 0.1);
--compliance-bg-hover: rgba(139, 92, 246, 0.18);
--compliance-border: rgba(167, 139, 250, 0.2);
--core-bg: rgba(100, 116, 139, 0.1);
--core-bg-hover: rgba(100, 116, 139, 0.18);
--core-border: rgba(148, 163, 184, 0.15);
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
background: var(--bg);
min-height: 100vh;
color: var(--text-primary);
padding: 2rem;
transition: background 0.3s, color 0.3s;
}
/* ── Header ── */
.header {
text-align: center;
margin-bottom: 2.5rem;
position: relative;
}
.header h1 {
font-size: 2rem;
font-weight: 700;
letter-spacing: -0.02em;
}
.header p {
color: var(--text-secondary);
margin-top: 0.5rem;
font-size: 0.95rem;
}
/* ── Theme Toggle ── */
.theme-toggle {
position: absolute;
top: 0;
right: 0;
background: var(--toggle-bg);
border: none;
border-radius: 99px;
width: 52px;
height: 28px;
cursor: pointer;
display: flex;
align-items: center;
padding: 0 4px;
transition: background 0.3s;
}
.theme-toggle .knob {
width: 20px;
height: 20px;
border-radius: 50%;
background: var(--toggle-fg);
transition: transform 0.3s, background 0.3s;
display: flex;
align-items: center;
justify-content: center;
font-size: 0.7rem;
}
[data-theme="light"] .theme-toggle .knob { transform: translateX(0); }
[data-theme="dark"] .theme-toggle .knob { transform: translateX(24px); }
/* ── Sections ── */
.section-title {
font-size: 0.75rem;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.08em;
color: var(--text-muted);
margin-bottom: 1rem;
padding-left: 0.25rem;
}
.grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
gap: 0.85rem;
max-width: 1100px;
margin: 0 auto 2.5rem;
}
/* ── Cards ── */
.card {
border-radius: 14px;
padding: 1.25rem 1.5rem;
text-decoration: none;
color: inherit;
transition: all 0.2s ease;
display: flex;
align-items: center;
gap: 1rem;
border: 1px solid var(--border);
background: var(--surface);
}
.card:hover {
transform: translateY(-2px);
box-shadow: 0 6px 24px var(--shadow-hover);
}
/* Brand-colored cards */
.card-lehrer {
border-color: var(--lehrer-border);
background: var(--lehrer-bg);
}
.card-lehrer:hover { background: var(--lehrer-bg-hover); }
.card-compliance {
border-color: var(--compliance-border);
background: var(--compliance-bg);
}
.card-compliance:hover { background: var(--compliance-bg-hover); }
.card-core {
border-color: var(--core-border);
background: var(--core-bg);
}
.card-core:hover { background: var(--core-bg-hover); }
.card-icon {
width: 44px;
height: 44px;
border-radius: 12px;
display: flex;
align-items: center;
justify-content: center;
font-size: 1.3rem;
flex-shrink: 0;
}
.card-body h3 {
font-size: 0.95rem;
font-weight: 600;
margin-bottom: 0.15rem;
}
.card-body p {
font-size: 0.8rem;
color: var(--text-secondary);
line-height: 1.4;
}
.card-body .url {
font-size: 0.7rem;
color: var(--text-muted);
margin-top: 0.35rem;
font-family: 'SF Mono', Monaco, monospace;
}
/* ── Color stripe on left ── */
.stripe {
width: 4px;
height: 36px;
border-radius: 4px;
flex-shrink: 0;
}
.stripe-lehrer { background: var(--lehrer-500); }
.stripe-compliance { background: var(--compliance-500); }
.stripe-core { background: var(--core-500); }
.stripe-docs-core { background: var(--docs-core); }
.stripe-docs-lehrer { background: var(--docs-lehrer); }
.stripe-docs-compliance { background: var(--docs-compliance); }
.divider {
max-width: 1100px;
margin: 0 auto 2rem;
border: none;
border-top: 1px solid var(--divider);
}
/* ── Architecture Section ── */
.arch {
max-width: 1100px;
margin: 0 auto 2rem;
}
.arch h2 {
font-size: 1.25rem;
font-weight: 700;
margin-bottom: 0.5rem;
}
.arch h3 {
font-size: 0.95rem;
font-weight: 600;
color: var(--text-secondary);
margin: 1.5rem 0 0.75rem;
}
.arch p, .arch li {
font-size: 0.85rem;
color: var(--text-secondary);
line-height: 1.6;
}
.arch code {
font-family: 'SF Mono', Monaco, monospace;
font-size: 0.78rem;
}
.arch-table {
width: 100%;
border-collapse: collapse;
margin: 0.75rem 0 1.5rem;
font-size: 0.82rem;
}
.arch-table th {
text-align: left;
padding: 0.5rem 0.75rem;
color: var(--text-muted);
font-weight: 600;
border-bottom: 1px solid var(--divider);
font-size: 0.7rem;
text-transform: uppercase;
letter-spacing: 0.05em;
}
.arch-table td {
padding: 0.4rem 0.75rem;
border-bottom: 1px solid var(--border-subtle);
}
.arch-table a {
color: var(--link-color);
text-decoration: none;
}
.arch-table a:hover { text-decoration: underline; }
.badge {
display: inline-block;
padding: 0.1rem 0.5rem;
border-radius: 6px;
font-size: 0.7rem;
font-weight: 600;
}
.badge-core { background: rgba(100,116,139,0.15); color: var(--core-400); }
.badge-lehrer { background: rgba(14,165,233,0.15); color: var(--lehrer-400); }
.badge-compliance { background: rgba(139,92,246,0.15); color: var(--compliance-400); }
[data-theme="light"] .badge-core { color: var(--core-600); }
[data-theme="light"] .badge-lehrer { color: var(--lehrer-600); }
[data-theme="light"] .badge-compliance { color: var(--compliance-600); }
.arch-box {
background: var(--arch-box-bg);
border: 1px solid var(--arch-box-border);
border-radius: 12px;
padding: 1.25rem;
margin: 1rem 0;
}
.arch-cols {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
gap: 1rem;
margin: 1rem 0;
}
.arch-cols .arch-box {
border-left: 3px solid var(--core-500);
}
.arch-cols .arch-box.box-lehrer { border-left-color: var(--lehrer-500); }
.arch-cols .arch-box.box-compliance { border-left-color: var(--compliance-500); }
.change-row {
display: grid;
grid-template-columns: 1fr auto 1fr;
gap: 0.75rem;
align-items: center;
padding: 0.5rem 0;
border-bottom: 1px solid var(--border-subtle);
font-size: 0.82rem;
}
.change-row:last-child { border-bottom: none; }
.change-row .old { color: var(--change-old); }
.change-row .arrow { color: var(--text-muted); }
.change-row .new { color: var(--change-new); }
/* ── Developer Guide Section ── */
.dev {
max-width: 1100px;
margin: 0 auto 2rem;
}
.dev h2 {
font-size: 1.25rem;
font-weight: 700;
margin-bottom: 0.5rem;
}
.dev h3 {
font-size: 0.95rem;
font-weight: 600;
color: var(--text-secondary);
margin: 1.5rem 0 0.75rem;
}
.dev p, .dev li {
font-size: 0.85rem;
color: var(--text-secondary);
line-height: 1.6;
}
.dev ul, .dev ol {
padding-left: 1.25rem;
margin: 0.5rem 0;
}
.dev li { padding: 0.15rem 0; }
.dev code {
font-family: 'SF Mono', Monaco, monospace;
font-size: 0.78rem;
}
.cmd-block {
background: var(--arch-box-bg);
border: 1px solid var(--arch-box-border);
border-radius: 10px;
padding: 0.85rem 1rem;
margin: 0.6rem 0;
font-family: 'SF Mono', Monaco, monospace;
font-size: 0.78rem;
color: var(--text-primary);
overflow-x: auto;
white-space: pre;
line-height: 1.7;
}
.cmd-comment {
color: var(--text-muted);
}
.step-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
gap: 0.75rem;
margin: 0.75rem 0;
}
.step-card {
background: var(--arch-box-bg);
border: 1px solid var(--arch-box-border);
border-radius: 10px;
padding: 1rem;
}
.step-card .step-num {
display: inline-flex;
align-items: center;
justify-content: center;
width: 24px;
height: 24px;
border-radius: 50%;
font-size: 0.72rem;
font-weight: 700;
margin-bottom: 0.5rem;
}
.step-num-lehrer { background: rgba(14,165,233,0.15); color: var(--lehrer-500); }
.step-num-compliance { background: rgba(139,92,246,0.15); color: var(--compliance-500); }
.step-num-core { background: rgba(100,116,139,0.15); color: var(--core-400); }
.step-num-default { background: rgba(100,116,139,0.15); color: var(--text-secondary); }
.step-card h4 {
font-size: 0.85rem;
font-weight: 600;
margin-bottom: 0.25rem;
}
.step-card p {
font-size: 0.78rem;
color: var(--text-muted);
line-height: 1.4;
}
.tech-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
gap: 0.75rem;
margin: 0.75rem 0;
}
.tech-card {
background: var(--arch-box-bg);
border: 1px solid var(--arch-box-border);
border-radius: 10px;
padding: 1rem 1.25rem;
border-left: 3px solid var(--core-500);
}
.tech-card.tech-lehrer { border-left-color: var(--lehrer-500); }
.tech-card.tech-compliance { border-left-color: var(--compliance-500); }
.tech-card h4 {
font-size: 0.88rem;
font-weight: 600;
margin-bottom: 0.4rem;
}
.tech-tag {
display: inline-block;
padding: 0.1rem 0.45rem;
border-radius: 5px;
font-size: 0.7rem;
font-weight: 500;
background: var(--arch-box-border);
color: var(--text-secondary);
margin: 0.15rem 0.15rem 0.15rem 0;
}
.gotcha-list {
list-style: none;
padding: 0;
margin: 0.5rem 0;
}
.gotcha-list li {
padding: 0.6rem 0;
border-bottom: 1px solid var(--border-subtle);
display: grid;
grid-template-columns: auto 1fr;
gap: 0.6rem;
align-items: start;
}
.gotcha-list li:last-child { border-bottom: none; }
.gotcha-icon {
font-size: 0.85rem;
margin-top: 0.1rem;
}
.gotcha-list .fix {
font-size: 0.78rem;
color: var(--change-new);
margin-top: 0.2rem;
}
</style>
</head>
<body>
<div class="header">
<h1>BreakPilot Portal</h1>
<p>Projekt-Frontends &amp; Dokumentation</p>
<button class="theme-toggle" onclick="toggleTheme()" title="Modus wechseln">
<div class="knob"></div>
</button>
</div>
<!-- ── Projekte ── -->
<div style="max-width: 1100px; margin: 0 auto;">
<div class="section-title">Projekte</div>
</div>
<div class="grid">
<a class="card card-core" href="https://macmini:3008/dashboard">
<div class="stripe stripe-core"></div>
<div class="card-body">
<h3>Admin Core</h3>
<p>Infrastruktur, Services, Monitoring</p>
<div class="url">macmini:3008/dashboard</div>
</div>
</a>
<a class="card card-lehrer" href="https://macmini:3002/dashboard">
<div class="stripe stripe-lehrer"></div>
<div class="card-body">
<h3>Admin Lehrer</h3>
<p>Verwaltung, AI Tools, Klausuren</p>
<div class="url">macmini:3002</div>
</div>
</a>
<a class="card card-compliance" href="https://macmini:3007/sdk">
<div class="stripe stripe-compliance"></div>
<div class="card-body">
<h3>Compliance SDK</h3>
<p>DSGVO, Audit, GRC &mdash; Alle SDK-Module</p>
<div class="url">macmini:3007/sdk</div>
</div>
</a>
<a class="card card-lehrer" href="https://macmini">
<div class="stripe stripe-lehrer"></div>
<div class="card-body">
<h3>Studio v2</h3>
<p>Lehrer- und Schueler-Interface</p>
<div class="url">macmini</div>
</div>
</a>
<a class="card card-compliance" href="https://macmini:3007/dashboard">
<div class="stripe stripe-compliance"></div>
<div class="card-body">
<h3>Compliance Dashboard</h3>
<p>Kataloge, Statistiken, Verwaltung</p>
<div class="url">macmini:3007/dashboard</div>
</div>
</a>
<a class="card card-lehrer" href="https://macmini:3000">
<div class="stripe stripe-lehrer"></div>
<div class="card-body">
<h3>Website</h3>
<p>Oeffentliche BreakPilot Website</p>
<div class="url">macmini:3000</div>
</div>
</a>
<a class="card card-compliance" href="https://macmini:3007/dashboard">
<div class="stripe stripe-compliance"></div>
<div class="card-body">
<h3>Katalogverwaltung</h3>
<p>SDK-Kataloge &amp; Auswahltabellen</p>
<div class="url">macmini:3007/dashboard</div>
</div>
</a>
<a class="card card-compliance" href="https://macmini:3010/compliance-hub/">
<div class="stripe stripe-compliance"></div>
<div class="card-body">
<h3>Comply Website</h3>
<p>Marketing-Website fuer den KI Compliance Hub</p>
<div class="url">macmini:3010/compliance-hub</div>
</div>
</a>
</div>
<hr class="divider">
<!-- ── Dokumentation ── -->
<div style="max-width: 1100px; margin: 0 auto;">
<div class="section-title">Dokumentation</div>
</div>
<div class="grid">
<a class="card card-core" href="http://macmini:8009/">
<div class="stripe stripe-docs-core"></div>
<div class="card-body">
<h3>Core Dokumentation</h3>
<p>Architektur, Auth, DevSecOps, RAG</p>
<div class="url">macmini:8009</div>
</div>
</a>
<a class="card card-lehrer" href="http://macmini:8010/">
<div class="stripe stripe-docs-lehrer"></div>
<div class="card-body">
<h3>Lehrer Dokumentation</h3>
<p>Klausur, Voice, Agent-Core, Studio</p>
<div class="url">macmini:8010</div>
</div>
</a>
<a class="card card-compliance" href="http://macmini:8011/">
<div class="stripe stripe-docs-compliance"></div>
<div class="card-body">
<h3>Compliance Dokumentation</h3>
<p>AI-SDK, Auditor-Doku, SBOM</p>
<div class="url">macmini:8011</div>
</div>
</a>
</div>
<hr class="divider">
<!-- ── Architecture Overview ── -->
<div class="arch">
<h2>Architektur-Uebersicht</h2>
<p>
BreakPilot besteht aus <strong>zwei Plattformen</strong>:
</p>
<ul style="margin: 0.5rem 0 0.75rem; padding-left: 1.25rem;">
<li><span class="badge badge-lehrer" style="margin-right: 0.3rem;">Lehrer</span> <strong>KI-Plattform fuer Lehrer</strong> &mdash; Unterrichtsvorbereitung, Klausurkorrektur, Sprachassistenz, OCR-Tools</li>
<li style="margin-top: 0.4rem;"><span class="badge badge-compliance" style="margin-right: 0.3rem;">Compliance</span> <strong>KI Compliance Hub fuer Unternehmen</strong> &mdash; umfasst:
<ul style="margin: 0.3rem 0 0; padding-left: 1rem;">
<li>DSGVO-Compliance-SDK (VVT, DSFA, TOM, Loeschfristen, Vendor-Compliance)</li>
<li>Datenschutzkonformer KI-Chat auf europaeischen Servern</li>
<li>Edge-Hardware-Loesung zum Self-Hosting (Mac Mini / Mac Studio)</li>
<li>Software-Compliance fuer automatisierte Risikobeurteilungen (Anlagen- &amp; Maschinensoftware / Firmware) <em style="color: var(--text-muted);">&mdash; in Entwicklung</em></li>
<li>Integrierter KI Embedded Code Assistant <em style="color: var(--text-muted);">&mdash; in Entwicklung</em></li>
</ul>
</li>
</ul>
<p>
Technisch aufgeteilt in <strong>3 Projekte</strong> mit insgesamt ~35 Docker-Containern auf einem Mac Mini M4 Pro.
</p>
<h3>Die 3 Projekte</h3>
<div class="arch-cols">
<div class="arch-box">
<span class="badge badge-core">Core</span>
<code style="margin-left: 0.5rem;">breakpilot-core</code>
<p style="margin-top: 0.5rem;">
Infrastruktur &amp; gemeinsame Dienste: PostgreSQL, Valkey, MinIO, Qdrant,
Vault, Nginx, Gitea, Woodpecker CI, Jitsi, Embedding-Service, RAG-Service,
Night-Scheduler, Health-Aggregator.
</p>
<div style="margin-top: 0.5rem; font-size: 0.72rem; color: var(--text-muted);">~25 Container</div>
</div>
<div class="arch-box box-lehrer">
<span class="badge badge-lehrer">Lehrer</span>
<code style="margin-left: 0.5rem;">breakpilot-lehrer</code>
<p style="margin-top: 0.5rem;">
Lehrer- &amp; Schueler-Funktionen: Studio v2, Admin Lehrer, Website,
Klausur-Service (OCR, Korrektur, Vocab), School-Service, Backend Lehrer.
</p>
<div style="margin-top: 0.5rem; font-size: 0.72rem; color: var(--text-muted);">~7 Container</div>
</div>
<div class="arch-box box-compliance">
<span class="badge badge-compliance">Compliance</span>
<code style="margin-left: 0.5rem;">breakpilot-compliance</code>
<p style="margin-top: 0.5rem;">
Datenschutz &amp; GRC: Admin Compliance (SDK), Backend Compliance,
AI Compliance SDK, DSFA, VVT, TOM, Loeschfristen, Vendor-Compliance.
</p>
<div style="margin-top: 0.5rem; font-size: 0.72rem; color: var(--text-muted);">~3 Container</div>
</div>
</div>
<h3>Frontends</h3>
<table class="arch-table">
<thead><tr><th>URL</th><th>Projekt</th><th>Beschreibung</th></tr></thead>
<tbody>
<tr>
<td><a href="https://macmini/">macmini</a></td>
<td><span class="badge badge-lehrer">Lehrer</span></td>
<td>Studio v2 (Lehrer-/Schueler-Interface)</td>
</tr>
<tr>
<td><a href="https://macmini:3000/">macmini:3000</a></td>
<td><span class="badge badge-lehrer">Lehrer</span></td>
<td>Website (oeffentlich)</td>
</tr>
<tr>
<td><a href="https://macmini:3002/">macmini:3002</a></td>
<td><span class="badge badge-lehrer">Lehrer</span></td>
<td>Admin Lehrer (Dashboard, AI-Tools, Architektur)</td>
</tr>
<tr>
<td><a href="https://macmini:3008/">macmini:3008</a></td>
<td><span class="badge badge-core">Core</span></td>
<td>Admin Core (Infrastruktur, Services, Monitoring)</td>
</tr>
<tr>
<td><a href="https://macmini:3007/">macmini:3007</a></td>
<td><span class="badge badge-compliance">Compliance</span></td>
<td>Admin Compliance (SDK, DSFA, VVT, TOM)</td>
</tr>
<tr>
<td><a href="https://macmini:8443/">macmini:8443</a></td>
<td><span class="badge badge-core">Core</span></td>
<td>Jitsi Meet (Videokonferenzen)</td>
</tr>
</tbody>
</table>
<h3>Backend-APIs</h3>
<table class="arch-table">
<thead><tr><th>URL</th><th>Projekt</th><th>Beschreibung</th></tr></thead>
<tbody>
<tr>
<td><a href="https://macmini:8000/">macmini:8000</a></td>
<td><span class="badge badge-core">Core</span></td>
<td>Backend Core (Auth, RBAC, Notifications)</td>
</tr>
<tr>
<td><a href="https://macmini:8001/">macmini:8001</a></td>
<td><span class="badge badge-lehrer">Lehrer</span></td>
<td>Backend Lehrer (Classroom, Units, Meetings)</td>
</tr>
<tr>
<td><a href="https://macmini:8002/">macmini:8002</a></td>
<td><span class="badge badge-compliance">Compliance</span></td>
<td>Backend Compliance (DSGVO, DSR, Consent)</td>
</tr>
<tr>
<td><a href="https://macmini:8086/">macmini:8086</a></td>
<td><span class="badge badge-lehrer">Lehrer</span></td>
<td>Klausur-Service (OCR, Korrektur, Vocab)</td>
</tr>
<tr>
<td><a href="https://macmini:8093/">macmini:8093</a></td>
<td><span class="badge badge-compliance">Compliance</span></td>
<td>AI Compliance SDK API</td>
</tr>
<tr>
<td><a href="https://macmini:8097/">macmini:8097</a></td>
<td><span class="badge badge-core">Core</span></td>
<td>RAG-Service (Vektorsuche)</td>
</tr>
</tbody>
</table>
<h3>Interne Dienste</h3>
<table class="arch-table">
<thead><tr><th>URL</th><th>Beschreibung</th></tr></thead>
<tbody>
<tr><td><a href="http://macmini:3003/">macmini:3003</a></td><td>Gitea (Git-Server)</td></tr>
<tr><td><a href="http://macmini:8090/">macmini:8090</a></td><td>Woodpecker CI</td></tr>
<tr><td><a href="http://macmini:8200/">macmini:8200</a></td><td>Vault (Secrets)</td></tr>
<tr><td><a href="http://macmini:8025/">macmini:8025</a></td><td>Mailpit (E-Mail Dev)</td></tr>
<tr><td><a href="http://macmini:9001/">macmini:9001</a></td><td>MinIO Console (S3)</td></tr>
<tr><td><a href="http://macmini:8096/">macmini:8096</a></td><td>Night Scheduler API</td></tr>
<tr><td><a href="http://macmini:8099/">macmini:8099</a></td><td>Health Aggregator</td></tr>
</tbody>
</table>
<h3>Git Repositories</h3>
<table class="arch-table">
<thead><tr><th>Repository</th><th>Lokal (Gitea)</th><th>Extern (Gitea)</th></tr></thead>
<tbody>
<tr>
<td><span class="badge badge-core">breakpilot-core</span></td>
<td><a href="http://macmini:3003/pilotadmin/breakpilot-core">macmini:3003/.../breakpilot-core</a></td>
<td><a href="https://gitea.meghsakha.com/Benjamin_Boenisch/breakpilot-core">gitea.meghsakha.com/.../breakpilot-core</a></td>
</tr>
<tr>
<td><span class="badge badge-lehrer">breakpilot-lehrer</span></td>
<td><a href="http://macmini:3003/pilotadmin/breakpilot-lehrer">macmini:3003/.../breakpilot-lehrer</a></td>
<td><a href="https://gitea.meghsakha.com/Benjamin_Boenisch/breakpilot-lehrer">gitea.meghsakha.com/.../breakpilot-lehrer</a></td>
</tr>
<tr>
<td><span class="badge badge-compliance">breakpilot-compliance</span></td>
<td><a href="http://macmini:3003/pilotadmin/breakpilot-compliance">macmini:3003/.../breakpilot-compliance</a></td>
<td><a href="https://gitea.meghsakha.com/Benjamin_Boenisch/breakpilot-compliance">gitea.meghsakha.com/.../breakpilot-compliance</a></td>
</tr>
</tbody>
</table>
<h3>Was hat sich gegenueber dem Monorepo geaendert?</h3>
<div class="arch-box">
<div class="change-row">
<span class="old">:3002 = Admin (Lehrer + SDK)</span>
<span class="arrow">&rarr;</span>
<span class="new">:3002 = Nur Lehrer, :3007 = Compliance (eigenes Frontend)</span>
</div>
<div class="change-row">
<span class="old">1 Backend auf :8000</span>
<span class="arrow">&rarr;</span>
<span class="new">3 Backends: :8000 (Core), :8001 (Lehrer), :8002 (Compliance)</span>
</div>
<div class="change-row">
<span class="old">RAG war im Klausur-Service</span>
<span class="arrow">&rarr;</span>
<span class="new">:8097 = Eigener RAG-Service</span>
</div>
<div class="change-row">
<span class="old">1 MkDocs auf :8009</span>
<span class="arrow">&rarr;</span>
<span class="new">3 MkDocs: :8009 (Core), :8010 (Lehrer), :8011 (Compliance)</span>
</div>
<div class="change-row">
<span class="old">Kein Health-Monitoring</span>
<span class="arrow">&rarr;</span>
<span class="new">:8099 = Health Aggregator (NEU)</span>
</div>
</div>
<h3>Routing (Nginx)</h3>
<p>
Alle HTTPS-Ports laufen ueber <code>bp-core-nginx</code> (aus breakpilot-core).
Nginx terminiert TLS mit Vault-Zertifikaten und leitet an die jeweiligen Container weiter.
Die internen Dienste (Gitea, Mailpit, MinIO etc.) sind nur ueber HTTP erreichbar.
</p>
<h3>Hardware (Mac Mini M4 Pro)</h3>
<div class="arch-cols">
<div class="arch-box">
<strong>System</strong>
<table class="arch-table" style="margin-bottom: 0;">
<tbody>
<tr><td style="width: 140px;">Chip</td><td>Apple M4 Pro</td></tr>
<tr><td>CPU-Kerne</td><td>14 (10 Performance + 4 Efficiency)</td></tr>
<tr><td>GPU-Kerne</td><td>20 (Metal 3)</td></tr>
<tr><td>RAM</td><td>64 GB Unified Memory</td></tr>
<tr><td>SSD</td><td>1,8 TB (davon ~1,5 TB frei)</td></tr>
<tr><td>macOS</td><td>Darwin 24.6.0</td></tr>
</tbody>
</table>
</div>
<div class="arch-box">
<strong>Ressourcen-Verteilung</strong>
<p style="margin-top: 0.5rem;">
Die 64 GB Unified Memory werden geteilt zwischen Docker (~35 Container),
Ollama (LLM-Inference) und dem System. Bei grossen Modellen (32B+ Parameter)
belegt Ollama ~20 GB RAM waehrend der Inference.
</p>
<p style="margin-top: 0.4rem;">
Der M4 Pro nutzt Unified Memory &mdash; CPU und GPU teilen sich den RAM.
Das ermoeglicht grosse LLM-Modelle ohne dedizierte GPU.
</p>
</div>
</div>
<h3>KI-Modelle (Ollama)</h3>
<p>Lokal auf dem Mac Mini via <code>http://macmini:11434</code>. Modelle werden ueber die Ollama API geladen.</p>
<table class="arch-table">
<thead><tr><th>Modell</th><th>Parameter</th><th>Groesse</th><th>Quantisierung</th><th>Einsatz</th></tr></thead>
<tbody>
<tr>
<td><strong>qwen3:30b-a3b</strong></td>
<td>30,5 Mrd. (3,3 Mrd. aktiv)</td>
<td>17,3 GB</td>
<td>Q4_K_M</td>
<td><span class="badge badge-compliance">Compliance</span> Haupt-LLM fuer Advisor, SDK, DSFA, VVT (MoE)</td>
</tr>
<tr>
<td><strong>qwen2.5vl:32b</strong></td>
<td>33,5 Mrd.</td>
<td>19,7 GB</td>
<td>Q4_K_M</td>
<td><span class="badge badge-lehrer">Lehrer</span> Vision-Language-Modell fuer Bildanalyse</td>
</tr>
<tr>
<td><strong>llama3.2-vision:11b</strong></td>
<td>10,7 Mrd.</td>
<td>7,3 GB</td>
<td>Q4_K_M</td>
<td><span class="badge badge-lehrer">Lehrer</span> Vision-Modell fuer OCR, Bildanalyse</td>
</tr>
<tr>
<td><strong>llama3.2:latest</strong></td>
<td>3,2 Mrd.</td>
<td>1,9 GB</td>
<td>Q4_K_M</td>
<td>Leichtgewichtig, Schnelltests, Fallback</td>
</tr>
</tbody>
</table>
<div class="arch-box">
<p><strong>Gesamt-Speicherbedarf aller Modelle:</strong> ~54,6 GB auf Disk.
Waehrend der Inference wird jeweils nur das aktive Modell in den RAM geladen.
Bei gleichzeitiger Nutzung mehrerer Modelle steigt der RAM-Bedarf entsprechend.</p>
<div class="cmd-block" style="margin-top: 0.5rem; margin-bottom: 0;"><span class="cmd-comment"># Modelle verwalten</span>
curl http://macmini:11434/api/tags <span class="cmd-comment"># Installierte Modelle anzeigen</span>
curl http://macmini:11434/api/pull -d '{"name":"model:tag"}' <span class="cmd-comment"># Neues Modell laden</span>
curl http://macmini:11434/api/delete -d '{"name":"model:tag"}'<span class="cmd-comment"># Modell entfernen</span></div>
</div>
</div>
<hr class="divider">
<!-- ══════════════════════════════════════════════════ -->
<!-- Developer Guide -->
<!-- ══════════════════════════════════════════════════ -->
<div class="dev">
<h2>Entwickler-Schnelleinstieg</h2>
<p>Alles was du brauchst, um in 15 Minuten produktiv zu sein.</p>
<!-- Getting Started -->
<h3>1. Voraussetzungen</h3>
<p>Du brauchst nur ein <strong>MacBook im lokalen Netzwerk</strong>. Alle Services laufen auf dem Mac Mini.</p>
<div class="step-grid">
<div class="step-card">
<div class="step-num step-num-default">1</div>
<h4>SSH-Zugang testen</h4>
<p>Terminal oeffnen und <code>ssh macmini</code> ausfuehren. Kein Passwort noetig (Key-Auth).</p>
</div>
<div class="step-card">
<div class="step-num step-num-default">2</div>
<h4>Repos klonen</h4>
<p>Auf dem Mac Mini unter <code>/Users/benjaminadmin/Projekte/</code> liegen alle 3 Projekte.</p>
</div>
<div class="step-card">
<div class="step-num step-num-default">3</div>
<h4>Browser oeffnen</h4>
<p>HTTPS-Zertifikate sind selbstsigniert &mdash; beim ersten Besuch im Browser akzeptieren.</p>
</div>
<div class="step-card">
<div class="step-num step-num-default">4</div>
<h4>Dokumentation lesen</h4>
<p>Jedes Projekt hat ein <code>CLAUDE.md</code> und <code>.claude/rules/</code> mit allen Details.</p>
</div>
</div>
<div class="cmd-block"><span class="cmd-comment"># Repos klonen (auf Mac Mini)</span>
cd /Users/benjaminadmin/Projekte
git clone http://localhost:3003/pilotadmin/breakpilot-core.git
git clone http://localhost:3003/pilotadmin/breakpilot-lehrer.git
git clone http://localhost:3003/pilotadmin/breakpilot-compliance.git</div>
<!-- Tech Stack -->
<h3>2. Tech Stack</h3>
<div class="tech-grid">
<div class="tech-card">
<h4><span class="badge badge-core">Core</span> Infrastruktur</h4>
<div style="margin-top: 0.5rem;">
<span class="tech-tag">Python / FastAPI</span>
<span class="tech-tag">Go / Gin</span>
<span class="tech-tag">PostgreSQL 16</span>
<span class="tech-tag">Valkey (Redis)</span>
<span class="tech-tag">Qdrant</span>
<span class="tech-tag">MinIO (S3)</span>
<span class="tech-tag">Vault</span>
<span class="tech-tag">Nginx</span>
</div>
</div>
<div class="tech-card tech-lehrer">
<h4><span class="badge badge-lehrer">Lehrer</span> EdTech</h4>
<div style="margin-top: 0.5rem;">
<span class="tech-tag">Next.js 15</span>
<span class="tech-tag">React</span>
<span class="tech-tag">TailwindCSS</span>
<span class="tech-tag">Python / FastAPI</span>
<span class="tech-tag">PaddleOCR</span>
<span class="tech-tag">Whisper</span>
</div>
</div>
<div class="tech-card tech-compliance">
<h4><span class="badge badge-compliance">Compliance</span> GRC</h4>
<div style="margin-top: 0.5rem;">
<span class="tech-tag">Next.js 15</span>
<span class="tech-tag">React</span>
<span class="tech-tag">TailwindCSS</span>
<span class="tech-tag">Go / Gin</span>
<span class="tech-tag">Ollama / qwen2.5</span>
<span class="tech-tag">RAG</span>
</div>
</div>
</div>
<!-- Development Workflow -->
<h3>3. Entwicklungs-Workflow</h3>
<p>Der typische Zyklus: Code aendern &rarr; Container bauen &rarr; deployen &rarr; testen.</p>
<div class="cmd-block"><span class="cmd-comment"># WICHTIG: Docker-Pfad auf Mac Mini ist /usr/local/bin/docker</span>
<span class="cmd-comment"># Kurzform: DC = docker compose mit Pfad</span>
<span class="cmd-comment"># ── Beispiel: Aenderung am Compliance-Admin deployen ──</span>
<span class="cmd-comment"># 1. Container bauen</span>
ssh macmini "/usr/local/bin/docker compose \
-f /Users/benjaminadmin/Projekte/breakpilot-compliance/docker-compose.yml \
build --no-cache admin-compliance"
<span class="cmd-comment"># 2. Container deployen</span>
ssh macmini "/usr/local/bin/docker compose \
-f /Users/benjaminadmin/Projekte/breakpilot-compliance/docker-compose.yml \
up -d admin-compliance"
<span class="cmd-comment"># 3. Logs pruefen</span>
ssh macmini "/usr/local/bin/docker compose \
-f /Users/benjaminadmin/Projekte/breakpilot-compliance/docker-compose.yml \
logs -f admin-compliance"</div>
<div class="cmd-block"><span class="cmd-comment"># ── Git Workflow (IMMER zu beiden Remotes pushen!) ──</span>
<span class="cmd-comment"># Status pruefen</span>
ssh macmini "git -C /Users/benjaminadmin/Projekte/breakpilot-compliance status"
<span class="cmd-comment"># Commit &amp; Push zu beiden Remotes</span>
ssh macmini "cd /Users/benjaminadmin/Projekte/breakpilot-compliance && \
git add -A && \
git commit -m 'feat: Beschreibung' && \
git push all main"
<span class="cmd-comment"># Pull (IMMER --no-rebase!)</span>
ssh macmini "git -C /Users/benjaminadmin/Projekte/breakpilot-compliance pull --no-rebase origin main"</div>
<div class="cmd-block"><span class="cmd-comment"># ── Nuetzliche Befehle ──</span>
<span class="cmd-comment"># Alle Container anzeigen</span>
ssh macmini "/usr/local/bin/docker ps --format 'table {{.Names}}\t{{.Status}}'"
<span class="cmd-comment"># Container eines Projekts</span>
ssh macmini "/usr/local/bin/docker ps --format '{{.Names}} {{.Status}}' | grep bp-compliance"
<span class="cmd-comment"># In Container einloggen</span>
ssh macmini "/usr/local/bin/docker exec -it bp-compliance-admin sh"
<span class="cmd-comment"># Datenbank-Zugriff</span>
ssh macmini "/usr/local/bin/docker exec -it bp-core-postgres psql -U breakpilot -d breakpilot"</div>
<!-- Docker Compose Pfade -->
<h3>4. Docker-Compose Pfade</h3>
<table class="arch-table">
<thead><tr><th>Projekt</th><th>Pfad</th></tr></thead>
<tbody>
<tr>
<td><span class="badge badge-core">Core</span></td>
<td><code>/Users/benjaminadmin/Projekte/breakpilot-core/docker-compose.yml</code></td>
</tr>
<tr>
<td><span class="badge badge-lehrer">Lehrer</span></td>
<td><code>/Users/benjaminadmin/Projekte/breakpilot-lehrer/docker-compose.yml</code></td>
</tr>
<tr>
<td><span class="badge badge-compliance">Compliance</span></td>
<td><code>/Users/benjaminadmin/Projekte/breakpilot-compliance/docker-compose.yml</code></td>
</tr>
</tbody>
</table>
<!-- Gotchas -->
<h3>5. Haeufige Stolpersteine</h3>
<ul class="gotcha-list">
<li>
<span class="gotcha-icon">&#9888;</span>
<div>
<strong><code>docker: command not found</code> via SSH</strong>
<p>Docker ist nicht im SSH-PATH. Immer den vollen Pfad nutzen.</p>
<div class="fix">Fix: <code>/usr/local/bin/docker</code> statt <code>docker</code></div>
</div>
</li>
<li>
<span class="gotcha-icon">&#9888;</span>
<div>
<strong><code>cd</code> funktioniert nicht direkt via SSH</strong>
<p>Jeder SSH-Befehl startet in einer neuen Shell.</p>
<div class="fix">Fix: <code>ssh macmini "cd /pfad && befehl"</code> oder <code>git -C /pfad</code></div>
</div>
</li>
<li>
<span class="gotcha-icon">&#9888;</span>
<div>
<strong>Ollama aus Docker-Containern nicht erreichbar</strong>
<p>Container koennen <code>localhost</code> des Hosts nicht direkt ansprechen.</p>
<div class="fix">Fix: <code>http://host.docker.internal:11434</code> statt <code>http://localhost:11434</code></div>
</div>
</li>
<li>
<span class="gotcha-icon">&#9888;</span>
<div>
<strong><code>git pull</code> zerstoert lokale Commits</strong>
<p>Rebase auf main mit vielen lokalen Commits fuehrt zu Datenverlust.</p>
<div class="fix">Fix: Immer <code>git pull --no-rebase origin main</code></div>
</div>
</li>
<li>
<span class="gotcha-icon">&#9888;</span>
<div>
<strong>Healthcheck schlaegt fehl mit <code>localhost</code></strong>
<p>IPv6-Aufloesung von <code>localhost</code> kann in Docker fehlschlagen.</p>
<div class="fix">Fix: <code>127.0.0.1</code> statt <code>localhost</code> in Healthchecks</div>
</div>
</li>
<li>
<span class="gotcha-icon">&#9888;</span>
<div>
<strong>Keine PDFs, DOCX oder Binaerdateien committen</strong>
<p>Das Repo wurde von 1.7 GB auf Normalgroesse bereinigt. Bitte sauber halten.</p>
<div class="fix">Fix: <code>.gitignore</code> blockiert *.pdf, *.docx, *.xlsx, kompilierte Binaries</div>
</div>
</li>
</ul>
<!-- Zugaenge -->
<h3>6. Zugaenge &amp; Secrets</h3>
<div class="arch-box">
<p style="margin-bottom: 0.5rem;">
Alle Secrets liegen im <strong>HashiCorp Vault</strong> (<a href="http://macmini:8200/" style="color: var(--link-color);">macmini:8200</a>).
Niemals Passwoerter, Tokens oder API-Keys in Code oder Git committen.
</p>
<table class="arch-table" style="margin-bottom: 0;">
<thead><tr><th>Dienst</th><th>Zugang</th></tr></thead>
<tbody>
<tr>
<td>Gitea (Git)</td>
<td><a href="http://macmini:3003/" style="color: var(--link-color);">macmini:3003</a> &mdash; Credentials: siehe Vault unter <code>secret/gitea</code></td>
</tr>
<tr>
<td>PostgreSQL</td>
<td>Via Container: <code>docker exec -it bp-core-postgres psql -U breakpilot</code></td>
</tr>
<tr>
<td>MinIO (S3)</td>
<td><a href="http://macmini:9001/" style="color: var(--link-color);">macmini:9001</a> &mdash; Credentials: siehe Vault unter <code>secret/minio</code></td>
</tr>
<tr>
<td>Mailpit</td>
<td><a href="http://macmini:8025/" style="color: var(--link-color);">macmini:8025</a> &mdash; Kein Login erforderlich (Dev-Tool)</td>
</tr>
<tr>
<td>Ollama (LLM)</td>
<td><code>http://macmini:11434</code> &mdash; Kein Login, lokale Instanz</td>
</tr>
</tbody>
</table>
</div>
<!-- Coding Conventions -->
<h3>7. Regeln &amp; Konventionen</h3>
<div class="tech-grid">
<div class="tech-card">
<h4>Open Source Policy</h4>
<p style="font-size: 0.82rem; color: var(--text-secondary); margin-top: 0.3rem;">
Nur Lizenzen mit kommerzieller Nutzung erlaubt: MIT, Apache-2.0, BSD, ISC, MPL-2.0, LGPL.
<strong>Verboten:</strong> GPL, AGPL, SSPL, proprietaer. Bei neuen Dependencies SBOM aktualisieren.
</p>
</div>
<div class="tech-card tech-lehrer">
<h4>Tests sind Pflicht</h4>
<p style="font-size: 0.82rem; color: var(--text-secondary); margin-top: 0.3rem;">
Jede Code-Aenderung braucht Tests. Go: <code>go test ./...</code>,
Python: <code>pytest -v</code>. Details in <code>.claude/rules/testing.md</code>.
</p>
</div>
<div class="tech-card tech-compliance">
<h4>DSGVO-Compliance</h4>
<p style="font-size: 0.82rem; color: var(--text-secondary); margin-top: 0.3rem;">
Bei neuen Features mit Nutzerdaten immer die Compliance-Checkliste durchgehen:
Rechtsgrundlage, Datenminimierung, Betroffenenrechte. Siehe <code>.claude/rules/compliance-checklist.md</code>.
</p>
</div>
</div>
<!-- Contact -->
<h3>8. Ansprechpartner &amp; Hilfe</h3>
<div class="arch-box">
<p>
<strong>Projekt-Owner:</strong> Benjamin Boenisch<br>
<strong>Issues:</strong> <a href="http://macmini:3003/" style="color: var(--link-color);">Gitea Issues</a> in den jeweiligen Repos<br>
<strong>CI/CD:</strong> <a href="http://macmini:8090/" style="color: var(--link-color);">Woodpecker CI</a> &mdash; Pipelines werden bei Push automatisch ausgefuehrt<br>
<strong>Monitoring:</strong> <a href="http://macmini:8099/" style="color: var(--link-color);">Health Aggregator</a> &mdash; Zeigt den Status aller Services
</p>
</div>
</div>
<script>
function toggleTheme() {
var html = document.documentElement;
var next = html.getAttribute('data-theme') === 'light' ? 'dark' : 'light';
html.setAttribute('data-theme', next);
localStorage.setItem('bp-portal-theme', next);
}
// Restore saved theme
(function() {
var saved = localStorage.getItem('bp-portal-theme');
if (saved) {
document.documentElement.setAttribute('data-theme', saved);
}
})();
</script>
</body>
</html>