A previous `git pull --rebase origin main` dropped 177 local commits,
losing 3400+ files across admin-v2, backend, studio-v2, website,
klausur-service, and many other services. The partial restore attempt
(660295e2) only recovered some files.
This commit restores all missing files from pre-rebase ref 98933f5e
while preserving post-rebase additions (night-scheduler, night-mode UI,
NightModeWidget dashboard integration).
Restored features include:
- AI Module Sidebar (FAB), OCR Labeling, OCR Compare
- GPU Dashboard, RAG Pipeline, Magic Help
- Klausur-Korrektur (8 files), Abitur-Archiv (5+ files)
- Companion, Zeugnisse-Crawler, Screen Flow
- Full backend, studio-v2, website, klausur-service
- All compliance SDKs, agent-core, voice-service
- CI/CD configs, documentation, scripts
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1281 lines
78 KiB
Python
1281 lines
78 KiB
Python
"""
|
|
Admin Panel HTML Template.
|
|
|
|
Enthält das HTML-Template für das Admin Panel Modal.
|
|
"""
|
|
|
|
|
|
def get_admin_panel_html() -> str:
|
|
"""HTML für Admin Panel zurückgeben"""
|
|
return """
|
|
<!-- Admin Panel Modal -->
|
|
<div id="admin-modal" class="admin-modal">
|
|
<div class="admin-modal-content">
|
|
<div class="admin-modal-header">
|
|
<h2><span>⚙️</span> Consent Admin Panel</h2>
|
|
<button id="admin-modal-close" class="legal-modal-close">×</button>
|
|
</div>
|
|
<div class="admin-tabs">
|
|
<button class="admin-tab active" data-tab="documents">Dokumente</button>
|
|
<button class="admin-tab" data-tab="versions">Versionen</button>
|
|
<button class="admin-tab" data-tab="cookies">Cookie-Kategorien</button>
|
|
<button class="admin-tab" data-tab="stats">Statistiken</button>
|
|
<button class="admin-tab" data-tab="emails">E-Mail Vorlagen</button>
|
|
<button class="admin-tab" data-tab="dsms">DSMS</button>
|
|
<button class="admin-tab" data-tab="gpu">GPU Infra</button>
|
|
<button class="admin-tab" data-tab="klausur-docs">Klausur-Docs</button>
|
|
<button class="admin-tab" data-tab="mac-mini">Mac Mini</button>
|
|
<button class="admin-tab" data-tab="system-info">System-Info</button>
|
|
</div>
|
|
<div class="admin-body">
|
|
<!-- Documents Tab -->
|
|
<div id="admin-documents" class="admin-content active">
|
|
<div class="admin-toolbar">
|
|
<div class="admin-toolbar-left">
|
|
<input type="text" class="admin-search" placeholder="Dokumente suchen..." id="admin-doc-search">
|
|
</div>
|
|
<button class="btn btn-primary btn-sm" onclick="showDocumentForm()">+ Neues Dokument</button>
|
|
</div>
|
|
|
|
<!-- Document Creation Form -->
|
|
<div id="admin-document-form" class="admin-form" style="display: none;">
|
|
<h3 class="admin-form-title" id="admin-document-form-title">Neues Dokument erstellen</h3>
|
|
<input type="hidden" id="admin-document-id">
|
|
<div class="admin-form-row">
|
|
<div class="admin-form-group">
|
|
<label class="admin-form-label">Dokumenttyp *</label>
|
|
<select class="admin-form-select" id="admin-document-type">
|
|
<option value="">-- Typ auswählen --</option>
|
|
<option value="terms">AGB (Allgemeine Geschäftsbedingungen)</option>
|
|
<option value="privacy">Datenschutzerklärung</option>
|
|
<option value="cookies">Cookie-Richtlinie</option>
|
|
<option value="community">Community Guidelines</option>
|
|
<option value="imprint">Impressum</option>
|
|
</select>
|
|
</div>
|
|
<div class="admin-form-group">
|
|
<label class="admin-form-label">Name *</label>
|
|
<input type="text" class="admin-form-input" id="admin-document-name" placeholder="z.B. Allgemeine Geschäftsbedingungen">
|
|
</div>
|
|
</div>
|
|
<div class="admin-form-row">
|
|
<div class="admin-form-group full-width">
|
|
<label class="admin-form-label">Beschreibung</label>
|
|
<input type="text" class="admin-form-input" id="admin-document-description" placeholder="Kurze Beschreibung des Dokuments">
|
|
</div>
|
|
</div>
|
|
<div class="admin-form-row">
|
|
<div class="admin-form-group">
|
|
<label class="admin-form-label">
|
|
<input type="checkbox" id="admin-document-mandatory" style="margin-right: 8px;">
|
|
Pflichtdokument (Nutzer müssen zustimmen)
|
|
</label>
|
|
</div>
|
|
</div>
|
|
<div class="admin-form-actions">
|
|
<button class="btn btn-ghost btn-sm" onclick="hideDocumentForm()">Abbrechen</button>
|
|
<button class="btn btn-primary btn-sm" onclick="saveDocument()">Dokument erstellen</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div id="admin-doc-table-container">
|
|
<div class="admin-loading">Lade Dokumente...</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Versions Tab -->
|
|
<div id="admin-versions" class="admin-content">
|
|
<div class="admin-toolbar">
|
|
<div class="admin-toolbar-left">
|
|
<select class="admin-form-select" id="admin-version-doc-select" onchange="loadVersionsForDocument()">
|
|
<option value="">-- Dokument auswählen --</option>
|
|
</select>
|
|
</div>
|
|
<button class="btn btn-primary btn-sm" onclick="showVersionForm()" id="btn-new-version" disabled>+ Neue Version</button>
|
|
</div>
|
|
|
|
<div id="admin-version-form" class="admin-form">
|
|
<h3 class="admin-form-title" id="admin-version-form-title">Neue Version erstellen</h3>
|
|
<input type="hidden" id="admin-version-id">
|
|
<div class="admin-form-row">
|
|
<div class="admin-form-group">
|
|
<label class="admin-form-label">Version *</label>
|
|
<input type="text" class="admin-form-input" id="admin-version-number" placeholder="z.B. 1.0.0">
|
|
</div>
|
|
<div class="admin-form-group">
|
|
<label class="admin-form-label">Sprache *</label>
|
|
<select class="admin-form-select" id="admin-version-lang">
|
|
<option value="de">Deutsch</option>
|
|
<option value="en">English</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
<div class="admin-form-row">
|
|
<div class="admin-form-group full-width">
|
|
<label class="admin-form-label">Titel *</label>
|
|
<input type="text" class="admin-form-input" id="admin-version-title" placeholder="Titel der Version">
|
|
</div>
|
|
</div>
|
|
<div class="admin-form-row">
|
|
<div class="admin-form-group full-width">
|
|
<label class="admin-form-label">Zusammenfassung</label>
|
|
<input type="text" class="admin-form-input" id="admin-version-summary" placeholder="Kurze Zusammenfassung der Änderungen">
|
|
</div>
|
|
</div>
|
|
<div class="admin-form-row">
|
|
<div class="admin-form-group full-width">
|
|
<label class="admin-form-label">Inhalt *</label>
|
|
<div class="editor-container">
|
|
<div class="editor-toolbar">
|
|
<div class="editor-toolbar-group">
|
|
<button type="button" class="editor-btn" onclick="formatDoc('bold')" title="Fett (Strg+B)"><b>B</b></button>
|
|
<button type="button" class="editor-btn" onclick="formatDoc('italic')" title="Kursiv (Strg+I)"><i>I</i></button>
|
|
<button type="button" class="editor-btn" onclick="formatDoc('underline')" title="Unterstrichen (Strg+U)"><u>U</u></button>
|
|
</div>
|
|
<div class="editor-toolbar-group">
|
|
<button type="button" class="editor-btn" onclick="formatBlock('h1')" title="Überschrift 1">H1</button>
|
|
<button type="button" class="editor-btn" onclick="formatBlock('h2')" title="Überschrift 2">H2</button>
|
|
<button type="button" class="editor-btn" onclick="formatBlock('h3')" title="Überschrift 3">H3</button>
|
|
<button type="button" class="editor-btn" onclick="formatBlock('p')" title="Absatz">P</button>
|
|
</div>
|
|
<div class="editor-toolbar-group">
|
|
<button type="button" class="editor-btn" onclick="formatDoc('insertUnorderedList')" title="Aufzählung">• Liste</button>
|
|
<button type="button" class="editor-btn" onclick="formatDoc('insertOrderedList')" title="Nummerierung">1. Liste</button>
|
|
</div>
|
|
<div class="editor-toolbar-group">
|
|
<button type="button" class="editor-btn" onclick="insertLink()" title="Link einfügen">🔗</button>
|
|
<button type="button" class="editor-btn" onclick="formatDoc('formatBlock', 'blockquote')" title="Zitat">❝</button>
|
|
<button type="button" class="editor-btn" onclick="formatDoc('insertHorizontalRule')" title="Trennlinie">—</button>
|
|
</div>
|
|
<div class="editor-toolbar-group">
|
|
<button type="button" class="editor-btn editor-btn-upload" onclick="document.getElementById('word-upload').click()" title="Word-Dokument importieren">📄 Word Import</button>
|
|
<input type="file" id="word-upload" class="word-upload-input" accept=".docx,.doc" onchange="handleWordUpload(event)">
|
|
</div>
|
|
</div>
|
|
<div id="admin-version-editor" class="editor-content" contenteditable="true" placeholder="Schreiben Sie hier den Inhalt..."></div>
|
|
<div class="editor-status">
|
|
<span id="editor-char-count">0 Zeichen</span> |
|
|
<span style="color: var(--bp-text-muted);">Tipp: Sie können direkt aus Word kopieren und einfügen!</span>
|
|
</div>
|
|
</div>
|
|
<input type="hidden" id="admin-version-content">
|
|
</div>
|
|
</div>
|
|
<div class="admin-form-actions">
|
|
<button class="btn btn-ghost btn-sm" onclick="hideVersionForm()">Abbrechen</button>
|
|
<button class="btn btn-primary btn-sm" onclick="saveVersion()">Speichern</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div id="admin-version-table-container">
|
|
<div class="admin-empty">Wählen Sie ein Dokument aus, um dessen Versionen anzuzeigen.</div>
|
|
</div>
|
|
|
|
<!-- Approval Dialog -->
|
|
<div id="approval-dialog" class="admin-dialog">
|
|
<div class="admin-dialog-content">
|
|
<h3>Version genehmigen</h3>
|
|
<p class="admin-dialog-info">
|
|
Legen Sie einen Veröffentlichungszeitpunkt fest. Die Version wird automatisch zum gewählten Zeitpunkt veröffentlicht.
|
|
</p>
|
|
<div class="admin-form-row">
|
|
<div class="admin-form-group">
|
|
<label class="admin-form-label">Veröffentlichungsdatum *</label>
|
|
<input type="date" class="admin-form-input" id="approval-date" required>
|
|
</div>
|
|
<div class="admin-form-group">
|
|
<label class="admin-form-label">Uhrzeit</label>
|
|
<input type="time" class="admin-form-input" id="approval-time" value="00:00">
|
|
</div>
|
|
</div>
|
|
<div class="admin-form-row">
|
|
<div class="admin-form-group full-width">
|
|
<label class="admin-form-label">Kommentar (optional)</label>
|
|
<input type="text" class="admin-form-input" id="approval-comment" placeholder="z.B. Genehmigt nach rechtlicher Prüfung">
|
|
</div>
|
|
</div>
|
|
<div class="admin-dialog-actions">
|
|
<button class="btn btn-ghost btn-sm" onclick="hideApprovalDialog()">Abbrechen</button>
|
|
<button class="btn btn-primary btn-sm" onclick="submitApproval()">Genehmigen & Planen</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Version Compare View (Full Screen Overlay) -->
|
|
<div id="version-compare-view" class="version-compare-overlay">
|
|
<div class="version-compare-header">
|
|
<h2>Versionsvergleich</h2>
|
|
<div class="version-compare-info">
|
|
<span id="compare-published-info"></span>
|
|
<span class="compare-vs">vs</span>
|
|
<span id="compare-draft-info"></span>
|
|
</div>
|
|
<button class="btn btn-ghost" onclick="hideCompareView()">Schließen</button>
|
|
</div>
|
|
<div class="version-compare-container">
|
|
<div class="version-compare-panel">
|
|
<div class="version-compare-panel-header">
|
|
<span class="compare-label compare-label-published">Veröffentlichte Version</span>
|
|
<span id="compare-published-version"></span>
|
|
</div>
|
|
<div class="version-compare-content" id="compare-content-left"></div>
|
|
</div>
|
|
<div class="version-compare-panel">
|
|
<div class="version-compare-panel-header">
|
|
<span class="compare-label compare-label-draft">Neue Version</span>
|
|
<span id="compare-draft-version"></span>
|
|
</div>
|
|
<div class="version-compare-content" id="compare-content-right"></div>
|
|
</div>
|
|
</div>
|
|
<div class="version-compare-footer">
|
|
<div id="compare-history-container"></div>
|
|
<div id="compare-actions-container" style="display: flex; gap: 12px; justify-content: flex-end; margin-top: 12px;"></div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Cookie Categories Tab -->
|
|
<div id="admin-cookies" class="admin-content">
|
|
<div class="admin-toolbar">
|
|
<div class="admin-toolbar-left">
|
|
<span style="color: var(--bp-text-muted);">Cookie-Kategorien verwalten</span>
|
|
</div>
|
|
<button class="btn btn-primary btn-sm" onclick="showCookieForm()">+ Neue Kategorie</button>
|
|
</div>
|
|
|
|
<div id="admin-cookie-form" class="admin-form">
|
|
<h3 class="admin-form-title">Neue Cookie-Kategorie</h3>
|
|
<input type="hidden" id="admin-cookie-id">
|
|
<div class="admin-form-row">
|
|
<div class="admin-form-group">
|
|
<label class="admin-form-label">Technischer Name *</label>
|
|
<input type="text" class="admin-form-input" id="admin-cookie-name" placeholder="z.B. analytics">
|
|
</div>
|
|
<div class="admin-form-group">
|
|
<label class="admin-form-label">Anzeigename (DE) *</label>
|
|
<input type="text" class="admin-form-input" id="admin-cookie-display-de" placeholder="z.B. Analyse-Cookies">
|
|
</div>
|
|
</div>
|
|
<div class="admin-form-row">
|
|
<div class="admin-form-group">
|
|
<label class="admin-form-label">Anzeigename (EN)</label>
|
|
<input type="text" class="admin-form-input" id="admin-cookie-display-en" placeholder="z.B. Analytics Cookies">
|
|
</div>
|
|
<div class="admin-form-group">
|
|
<label class="admin-form-label">
|
|
<input type="checkbox" id="admin-cookie-mandatory"> Notwendig (kann nicht deaktiviert werden)
|
|
</label>
|
|
</div>
|
|
</div>
|
|
<div class="admin-form-row">
|
|
<div class="admin-form-group full-width">
|
|
<label class="admin-form-label">Beschreibung (DE)</label>
|
|
<input type="text" class="admin-form-input" id="admin-cookie-desc-de" placeholder="Beschreibung auf Deutsch">
|
|
</div>
|
|
</div>
|
|
<div class="admin-form-actions">
|
|
<button class="btn btn-ghost btn-sm" onclick="hideCookieForm()">Abbrechen</button>
|
|
<button class="btn btn-primary btn-sm" onclick="saveCookieCategory()">Speichern</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div id="admin-cookie-table-container">
|
|
<div class="admin-loading">Lade Cookie-Kategorien...</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Statistics Tab -->
|
|
<div id="admin-stats" class="admin-content">
|
|
<div id="admin-stats-container">
|
|
<div class="admin-loading">Lade Statistiken...</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- E-Mail Templates Tab -->
|
|
<div id="admin-emails" class="admin-content">
|
|
<div class="admin-toolbar">
|
|
<div class="admin-toolbar-left">
|
|
<select class="admin-form-select" id="email-template-select" onchange="loadEmailTemplateVersions()">
|
|
<option value="">-- E-Mail-Vorlage auswählen --</option>
|
|
</select>
|
|
</div>
|
|
<button class="btn btn-ghost btn-sm" onclick="initializeEmailTemplates()">Templates initialisieren</button>
|
|
<button class="btn btn-primary btn-sm" onclick="showEmailVersionForm()" id="btn-new-email-version" disabled>+ Neue Version</button>
|
|
</div>
|
|
|
|
<!-- E-Mail Template Info Card -->
|
|
<div id="email-template-info" style="display: none; margin-bottom: 16px;">
|
|
<div style="background: var(--bp-surface-elevated); border-radius: 8px; padding: 16px; border: 1px solid var(--bp-border);">
|
|
<div style="display: flex; justify-content: space-between; align-items: start;">
|
|
<div>
|
|
<h3 style="margin: 0 0 8px 0; font-size: 16px;" id="email-template-name">-</h3>
|
|
<p style="margin: 0; color: var(--bp-text-muted); font-size: 13px;" id="email-template-description">-</p>
|
|
</div>
|
|
<div style="text-align: right;">
|
|
<div class="admin-badge" id="email-template-type-badge">-</div>
|
|
</div>
|
|
</div>
|
|
<div style="margin-top: 12px; padding-top: 12px; border-top: 1px solid var(--bp-border);">
|
|
<span style="font-size: 12px; color: var(--bp-text-muted);">Variablen: </span>
|
|
<span id="email-template-variables" style="font-size: 12px; font-family: monospace; color: var(--bp-primary);"></span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- E-Mail Version Form -->
|
|
<div id="email-version-form" class="admin-form" style="display: none;">
|
|
<h3 class="admin-form-title" id="email-version-form-title">Neue E-Mail-Version erstellen</h3>
|
|
<input type="hidden" id="email-version-id">
|
|
<div class="admin-form-row">
|
|
<div class="admin-form-group">
|
|
<label class="admin-form-label">Version *</label>
|
|
<input type="text" class="admin-form-input" id="email-version-number" placeholder="z.B. 1.0.0">
|
|
</div>
|
|
<div class="admin-form-group">
|
|
<label class="admin-form-label">Sprache *</label>
|
|
<select class="admin-form-select" id="email-version-lang">
|
|
<option value="de">Deutsch</option>
|
|
<option value="en">English</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
<div class="admin-form-row">
|
|
<div class="admin-form-group full-width">
|
|
<label class="admin-form-label">Betreff *</label>
|
|
<input type="text" class="admin-form-input" id="email-version-subject" placeholder="E-Mail Betreff (kann Variablen enthalten)">
|
|
</div>
|
|
</div>
|
|
<div class="admin-form-row">
|
|
<div class="admin-form-group full-width">
|
|
<label class="admin-form-label">HTML-Inhalt *</label>
|
|
<div class="editor-container">
|
|
<div class="editor-toolbar">
|
|
<div class="editor-toolbar-group">
|
|
<button type="button" class="editor-btn" onclick="formatEmailDoc('bold')" title="Fett"><b>B</b></button>
|
|
<button type="button" class="editor-btn" onclick="formatEmailDoc('italic')" title="Kursiv"><i>I</i></button>
|
|
<button type="button" class="editor-btn" onclick="formatEmailDoc('underline')" title="Unterstrichen"><u>U</u></button>
|
|
</div>
|
|
<div class="editor-toolbar-group">
|
|
<button type="button" class="editor-btn" onclick="formatEmailBlock('h1')" title="Überschrift 1">H1</button>
|
|
<button type="button" class="editor-btn" onclick="formatEmailBlock('h2')" title="Überschrift 2">H2</button>
|
|
<button type="button" class="editor-btn" onclick="formatEmailBlock('p')" title="Absatz">P</button>
|
|
</div>
|
|
<div class="editor-toolbar-group">
|
|
<button type="button" class="editor-btn" onclick="insertEmailVariable()" title="Variable einfügen">{{var}}</button>
|
|
<button type="button" class="editor-btn" onclick="insertEmailLink()" title="Link einfügen">🔗</button>
|
|
<button type="button" class="editor-btn" onclick="insertEmailButton()" title="Button einfügen">🔘</button>
|
|
</div>
|
|
</div>
|
|
<div id="email-version-editor" class="editor-content" contenteditable="true" placeholder="HTML-Inhalt der E-Mail..." style="min-height: 200px;"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="admin-form-row">
|
|
<div class="admin-form-group full-width">
|
|
<label class="admin-form-label">Text-Version (Plain Text)</label>
|
|
<textarea class="admin-form-input" id="email-version-text" rows="5" placeholder="Plain-Text-Version der E-Mail (optional, wird aus HTML generiert falls leer)"></textarea>
|
|
</div>
|
|
</div>
|
|
<div class="admin-form-actions">
|
|
<button class="btn btn-ghost btn-sm" onclick="hideEmailVersionForm()">Abbrechen</button>
|
|
<button class="btn btn-ghost btn-sm" onclick="previewEmailVersion()">Vorschau</button>
|
|
<button class="btn btn-primary btn-sm" onclick="saveEmailVersion()">Speichern</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- E-Mail Versions Table -->
|
|
<div id="email-version-table-container">
|
|
<div class="admin-empty">Wählen Sie eine E-Mail-Vorlage aus, um deren Versionen anzuzeigen.</div>
|
|
</div>
|
|
|
|
<!-- E-Mail Preview Dialog -->
|
|
<div id="email-preview-dialog" class="admin-dialog" style="display: none;">
|
|
<div class="admin-dialog-content" style="max-width: 700px; max-height: 80vh; overflow-y: auto;">
|
|
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 16px;">
|
|
<h3 style="margin: 0;">E-Mail Vorschau</h3>
|
|
<button class="btn btn-ghost btn-sm" onclick="hideEmailPreview()">Schließen</button>
|
|
</div>
|
|
<div style="margin-bottom: 12px;">
|
|
<strong>Betreff:</strong> <span id="email-preview-subject"></span>
|
|
</div>
|
|
<div style="border: 1px solid var(--bp-border); border-radius: 8px; padding: 16px; background: white; color: #333;">
|
|
<div id="email-preview-content"></div>
|
|
</div>
|
|
<div style="margin-top: 16px; display: flex; gap: 8px;">
|
|
<input type="email" class="admin-form-input" id="email-test-address" placeholder="Test-E-Mail-Adresse" style="flex: 1;">
|
|
<button class="btn btn-primary btn-sm" onclick="sendTestEmail()">Test-E-Mail senden</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- E-Mail Approval Dialog -->
|
|
<div id="email-approval-dialog" class="admin-dialog" style="display: none;">
|
|
<div class="admin-dialog-content">
|
|
<h3>E-Mail-Version genehmigen</h3>
|
|
<div class="admin-form-row">
|
|
<div class="admin-form-group full-width">
|
|
<label class="admin-form-label">Kommentar (optional)</label>
|
|
<input type="text" class="admin-form-input" id="email-approval-comment" placeholder="z.B. Genehmigt nach Marketing-Prüfung">
|
|
</div>
|
|
</div>
|
|
<div class="admin-dialog-actions">
|
|
<button class="btn btn-ghost btn-sm" onclick="hideEmailApprovalDialog()">Abbrechen</button>
|
|
<button class="btn btn-primary btn-sm" onclick="submitEmailApproval()">Genehmigen</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- DSMS Tab -->
|
|
<div id="admin-dsms" class="admin-content">
|
|
<div class="admin-toolbar">
|
|
<div class="admin-toolbar-left">
|
|
<span style="font-weight: 600; color: var(--bp-primary);">Dezentrales Speichersystem (IPFS)</span>
|
|
</div>
|
|
<div style="display: flex; gap: 8px;">
|
|
<button class="btn btn-ghost btn-sm" onclick="openDsmsWebUI()">DSMS WebUI</button>
|
|
<button class="btn btn-primary btn-sm" onclick="loadDsmsData()">Aktualisieren</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- DSMS Status Cards -->
|
|
<div id="dsms-status-cards" style="display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 16px; margin-bottom: 24px;">
|
|
<div class="admin-loading">Lade DSMS Status...</div>
|
|
</div>
|
|
|
|
<!-- DSMS Tabs -->
|
|
<div style="display: flex; gap: 8px; margin-bottom: 16px; border-bottom: 1px solid var(--bp-border); padding-bottom: 8px;">
|
|
<button class="dsms-subtab active" data-dsms-tab="archives" onclick="switchDsmsTab('archives')">Archivierte Dokumente</button>
|
|
<button class="dsms-subtab" data-dsms-tab="verify" onclick="switchDsmsTab('verify')">Verifizierung</button>
|
|
<button class="dsms-subtab" data-dsms-tab="settings" onclick="switchDsmsTab('settings')">Einstellungen</button>
|
|
</div>
|
|
|
|
<!-- Archives Sub-Tab -->
|
|
<div id="dsms-archives" class="dsms-content active">
|
|
<div class="admin-toolbar" style="margin-bottom: 16px;">
|
|
<div class="admin-toolbar-left">
|
|
<input type="text" class="admin-search" placeholder="CID suchen..." id="dsms-cid-search" style="width: 300px;">
|
|
</div>
|
|
<button class="btn btn-primary btn-sm" onclick="showArchiveForm()">+ Dokument archivieren</button>
|
|
</div>
|
|
|
|
<!-- Archive Form -->
|
|
<div id="dsms-archive-form" class="admin-form" style="display: none; margin-bottom: 16px;">
|
|
<h3 class="admin-form-title">Dokument im DSMS archivieren</h3>
|
|
<div class="admin-form-row">
|
|
<div class="admin-form-group">
|
|
<label class="admin-form-label">Dokument auswählen *</label>
|
|
<select class="admin-form-select" id="dsms-archive-doc-select">
|
|
<option value="">-- Dokument wählen --</option>
|
|
</select>
|
|
</div>
|
|
<div class="admin-form-group">
|
|
<label class="admin-form-label">Version *</label>
|
|
<select class="admin-form-select" id="dsms-archive-version-select" disabled>
|
|
<option value="">-- Erst Dokument wählen --</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
<div class="admin-form-actions">
|
|
<button class="btn btn-ghost btn-sm" onclick="hideArchiveForm()">Abbrechen</button>
|
|
<button class="btn btn-primary btn-sm" onclick="archiveDocumentToDsms()">Archivieren</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div id="dsms-archives-table">
|
|
<div class="admin-loading">Lade archivierte Dokumente...</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Verify Sub-Tab -->
|
|
<div id="dsms-verify" class="dsms-content" style="display: none;">
|
|
<div style="background: var(--bp-surface-elevated); border-radius: 8px; padding: 24px; border: 1px solid var(--bp-border);">
|
|
<h3 style="margin: 0 0 16px 0; font-size: 16px;">Dokumentenintegrität prüfen</h3>
|
|
<p style="color: var(--bp-text-muted); margin-bottom: 16px; font-size: 14px;">
|
|
Geben Sie einen CID (Content Identifier) ein, um die Integrität eines archivierten Dokuments zu verifizieren.
|
|
</p>
|
|
<div style="display: flex; gap: 12px; margin-bottom: 16px;">
|
|
<input type="text" class="admin-form-input" id="dsms-verify-cid" placeholder="Qm... oder bafy..." style="flex: 1;">
|
|
<button class="btn btn-primary btn-sm" onclick="verifyDsmsDocument()">Verifizieren</button>
|
|
</div>
|
|
<div id="dsms-verify-result" style="display: none;"></div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Settings Sub-Tab -->
|
|
<div id="dsms-settings" class="dsms-content" style="display: none;">
|
|
<div style="display: grid; gap: 16px;">
|
|
<!-- Node Info -->
|
|
<div style="background: var(--bp-surface-elevated); border-radius: 8px; padding: 24px; border: 1px solid var(--bp-border);">
|
|
<h3 style="margin: 0 0 16px 0; font-size: 16px;">Node-Informationen</h3>
|
|
<div id="dsms-node-info">
|
|
<div class="admin-loading">Lade Node-Info...</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Quick Links -->
|
|
<div style="background: var(--bp-surface-elevated); border-radius: 8px; padding: 24px; border: 1px solid var(--bp-border);">
|
|
<h3 style="margin: 0 0 16px 0; font-size: 16px;">Schnellzugriff</h3>
|
|
<div style="display: flex; flex-wrap: wrap; gap: 12px;">
|
|
<button class="btn btn-primary btn-sm" onclick="openDsmsWebUI()">DSMS WebUI</button>
|
|
<a href="http://localhost:8082/docs" target="_blank" class="btn btn-ghost btn-sm">DSMS API Docs</a>
|
|
<a href="http://localhost:8085" target="_blank" class="btn btn-ghost btn-sm">IPFS Gateway</a>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Lizenzhinweise -->
|
|
<div style="background: var(--bp-surface-elevated); border-radius: 8px; padding: 24px; border: 1px solid var(--bp-border);">
|
|
<h3 style="margin: 0 0 16px 0; font-size: 16px;">Open Source Lizenzen</h3>
|
|
<p style="color: var(--bp-text-muted); font-size: 13px; margin-bottom: 12px;">
|
|
DSMS verwendet folgende Open-Source-Komponenten:
|
|
</p>
|
|
<ul style="color: var(--bp-text-muted); font-size: 13px; margin: 0; padding-left: 20px;">
|
|
<li><strong>IPFS Kubo</strong> - MIT + Apache 2.0 (Dual License) - Protocol Labs, Inc.</li>
|
|
<li><strong>IPFS WebUI</strong> - MIT License - Protocol Labs, Inc.</li>
|
|
<li><strong>FastAPI</strong> - MIT License</li>
|
|
</ul>
|
|
<p style="color: var(--bp-text-muted); font-size: 12px; margin-top: 12px; font-style: italic;">
|
|
Alle Komponenten erlauben kommerzielle Nutzung.
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- GPU Infrastructure Tab -->
|
|
<div id="admin-content-gpu" class="admin-content">
|
|
<div class="gpu-control-panel">
|
|
<div class="gpu-status-header">
|
|
<h3 style="margin: 0; font-size: 16px;">vast.ai GPU Instance</h3>
|
|
<div id="gpu-status-badge" class="gpu-status-badge stopped">
|
|
<span class="gpu-status-dot"></span>
|
|
<span id="gpu-status-text">Unbekannt</span>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="gpu-info-grid">
|
|
<div class="gpu-info-card">
|
|
<div class="gpu-info-label">GPU</div>
|
|
<div id="gpu-name" class="gpu-info-value">-</div>
|
|
</div>
|
|
<div class="gpu-info-card">
|
|
<div class="gpu-info-label">Kosten/Stunde</div>
|
|
<div id="gpu-dph" class="gpu-info-value cost">-</div>
|
|
</div>
|
|
<div class="gpu-info-card">
|
|
<div class="gpu-info-label">Session</div>
|
|
<div id="gpu-session-time" class="gpu-info-value time">-</div>
|
|
</div>
|
|
<div class="gpu-info-card">
|
|
<div class="gpu-info-label">Gesamt</div>
|
|
<div id="gpu-total-cost" class="gpu-info-value cost">-</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div id="gpu-endpoint" class="gpu-endpoint-url" style="display: none;">
|
|
Endpoint: <span id="gpu-endpoint-url">-</span>
|
|
</div>
|
|
|
|
<div id="gpu-shutdown-warning" class="gpu-shutdown-warning" style="display: none;">
|
|
<svg width="20" height="20" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
|
d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"/>
|
|
</svg>
|
|
<span>Auto-Shutdown in <strong id="gpu-shutdown-minutes">0</strong> Minuten (bei Inaktivitaet)</span>
|
|
</div>
|
|
|
|
<div class="gpu-controls">
|
|
<button id="gpu-btn-start" class="gpu-btn gpu-btn-start" onclick="gpuPowerOn()">
|
|
<svg width="16" height="16" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 3l14 9-14 9V3z"/>
|
|
</svg>
|
|
Starten
|
|
</button>
|
|
<button id="gpu-btn-stop" class="gpu-btn gpu-btn-stop" onclick="gpuPowerOff()" disabled>
|
|
<svg width="16" height="16" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 12a9 9 0 11-18 0 9 9 0 0118 0z"/>
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 10a1 1 0 011-1h4a1 1 0 011 1v4a1 1 0 01-1 1h-4a1 1 0 01-1-1v-4z"/>
|
|
</svg>
|
|
Stoppen
|
|
</button>
|
|
<button class="gpu-btn gpu-btn-refresh" onclick="gpuRefreshStatus()" title="Status aktualisieren">
|
|
<svg width="16" height="16" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
|
d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"/>
|
|
</svg>
|
|
</button>
|
|
</div>
|
|
|
|
<div class="gpu-cost-summary">
|
|
<h4>Kosten-Zusammenfassung</h4>
|
|
<div class="gpu-info-grid">
|
|
<div class="gpu-info-card">
|
|
<div class="gpu-info-label">Laufzeit Gesamt</div>
|
|
<div id="gpu-total-runtime" class="gpu-info-value time">0h 0m</div>
|
|
</div>
|
|
<div class="gpu-info-card">
|
|
<div class="gpu-info-label">Kosten Gesamt</div>
|
|
<div id="gpu-total-cost-all" class="gpu-info-value cost">$0.00</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<details style="margin-top: 20px;">
|
|
<summary style="cursor: pointer; color: var(--bp-text-muted); font-size: 13px;">
|
|
Audit Log (letzte Aktionen)
|
|
</summary>
|
|
<div id="gpu-audit-log" class="gpu-audit-log">
|
|
<div class="gpu-audit-entry">Keine Eintraege</div>
|
|
</div>
|
|
</details>
|
|
</div>
|
|
|
|
<div style="padding: 12px; background: var(--bp-surface-elevated); border-radius: 8px; font-size: 12px; color: var(--bp-text-muted);">
|
|
<strong>Hinweis:</strong> Die GPU-Instanz wird automatisch nach 30 Minuten Inaktivitaet gestoppt.
|
|
Bei jedem LLM-Request wird die Aktivitaet aufgezeichnet und der Timer zurueckgesetzt.
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Klausur Documentation Tab (Audit-Ready) -->
|
|
<div id="admin-klausur-docs" class="admin-content">
|
|
<div class="klausur-docs-panel" style="background: var(--bp-surface-elevated); border-radius: 12px; padding: 24px; border: 1px solid var(--bp-border);">
|
|
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 24px; padding-bottom: 16px; border-bottom: 1px solid var(--bp-border);">
|
|
<h2 style="margin: 0; font-size: 20px; display: flex; align-items: center; gap: 12px;">
|
|
Datenschutz-Dokumentation Klausurkorrektur
|
|
<span style="background: #065f46; color: #6ee7b7; font-size: 11px; padding: 4px 10px; border-radius: 4px; font-weight: 600;">AUDIT-READY</span>
|
|
</h2>
|
|
<button style="background: var(--bp-surface); border: 1px solid var(--bp-border); color: var(--bp-text); padding: 10px 20px; border-radius: 8px; cursor: pointer; font-size: 13px;" onclick="window.print()">
|
|
Drucken / PDF
|
|
</button>
|
|
</div>
|
|
|
|
<!-- Zusammenfassung -->
|
|
<div style="margin-bottom: 32px;">
|
|
<h3 style="color: var(--bp-text); font-size: 16px; margin: 0 0 16px 0; display: flex; align-items: center; gap: 8px;">
|
|
<span style="background: var(--bp-primary); color: white; width: 24px; height: 24px; border-radius: 50%; display: flex; align-items: center; justify-content: center; font-size: 12px;">1</span>
|
|
Zusammenfassung fuer Entscheidungstraeger
|
|
</h3>
|
|
<div style="background: rgba(6, 95, 70, 0.15); border: 1px solid #065f46; border-radius: 8px; padding: 16px; color: #6ee7b7;">
|
|
<h4 style="margin: 0 0 8px 0; font-size: 14px;">Kernaussage</h4>
|
|
<p style="margin: 0;">
|
|
Die KI-gestuetzte Klausurkorrektur verarbeitet <strong>keine personenbezogenen Daten</strong>
|
|
ausserhalb des Geraets der Lehrkraft. Die Verarbeitung ist datenschutzrechtlich
|
|
vergleichbar mit einer Korrektur auf dem eigenen PC der Lehrkraft.
|
|
</p>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Datenfluss -->
|
|
<div style="margin-bottom: 32px;">
|
|
<h3 style="color: var(--bp-text); font-size: 16px; margin: 0 0 16px 0; display: flex; align-items: center; gap: 8px;">
|
|
<span style="background: var(--bp-primary); color: white; width: 24px; height: 24px; border-radius: 50%; display: flex; align-items: center; justify-content: center; font-size: 12px;">2</span>
|
|
Datenfluss (vereinfacht)
|
|
</h3>
|
|
<div style="background: var(--bp-surface); border: 1px solid var(--bp-border); border-radius: 8px; padding: 20px; font-family: monospace; font-size: 12px; overflow-x: auto; white-space: pre; color: var(--bp-text); line-height: 1.6;">
|
|
Lehrer-Browser Server (BreakPilot)
|
|
═══════════════ ════════════════════
|
|
1. Namen eingeben
|
|
[Max, Anna, Tim]
|
|
│
|
|
▼
|
|
2. Lokal verschluesseln ──────────► Verschluesselter Blob
|
|
(AES-256, Passwort) (Server kann NICHT lesen)
|
|
│
|
|
▼
|
|
3. QR-Codes drucken ◄────────── Zufaellige doc_tokens
|
|
[abc123, def456...] (128-bit UUID)
|
|
|
|
═══════════════════════════════════════════════════════════
|
|
4. Klausur scannen ──────────► 5. QR erkennen
|
|
6. Kopfzeile entfernen
|
|
7. NUR Text an KI
|
|
(OHNE Namen!)
|
|
═══════════════════════════════════════════════════════════
|
|
|
|
8. Lokal entschluesseln ◄────────── 9. Ergebnis:
|
|
+ Namen zuordnen abc123 = Note 2+
|
|
|
|
ERGEBNIS: Max = Note 2+
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Vergleichstabelle -->
|
|
<div style="margin-bottom: 32px;">
|
|
<h3 style="color: var(--bp-text); font-size: 16px; margin: 0 0 16px 0; display: flex; align-items: center; gap: 8px;">
|
|
<span style="background: var(--bp-primary); color: white; width: 24px; height: 24px; border-radius: 50%; display: flex; align-items: center; justify-content: center; font-size: 12px;">3</span>
|
|
Vergleich mit anderen Loesungen
|
|
</h3>
|
|
<table style="width: 100%; border-collapse: collapse; font-size: 13px;">
|
|
<thead>
|
|
<tr>
|
|
<th style="padding: 12px; text-align: left; border: 1px solid var(--bp-border); background: var(--bp-surface);">Kriterium</th>
|
|
<th style="padding: 12px; text-align: left; border: 1px solid var(--bp-border); background: var(--bp-surface);">BreakPilot</th>
|
|
<th style="padding: 12px; text-align: left; border: 1px solid var(--bp-border); background: var(--bp-surface);">Typische Cloud-KI</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr>
|
|
<td style="padding: 12px; border: 1px solid var(--bp-border);">Schuelernamen an Server</td>
|
|
<td style="padding: 12px; border: 1px solid var(--bp-border); color: #22c55e;">Nein (nur Tokens)</td>
|
|
<td style="padding: 12px; border: 1px solid var(--bp-border); color: #ef4444;">Ja</td>
|
|
</tr>
|
|
<tr>
|
|
<td style="padding: 12px; border: 1px solid var(--bp-border);">Namen an KI gesendet</td>
|
|
<td style="padding: 12px; border: 1px solid var(--bp-border); color: #22c55e;">Nein</td>
|
|
<td style="padding: 12px; border: 1px solid var(--bp-border); color: #ef4444;">Ja (OpenAI etc.)</td>
|
|
</tr>
|
|
<tr>
|
|
<td style="padding: 12px; border: 1px solid var(--bp-border);">LLM-Hosting</td>
|
|
<td style="padding: 12px; border: 1px solid var(--bp-border); color: #22c55e;">Self-Hosted (DE)</td>
|
|
<td style="padding: 12px; border: 1px solid var(--bp-border); color: #ef4444;">US-Cloud</td>
|
|
</tr>
|
|
<tr>
|
|
<td style="padding: 12px; border: 1px solid var(--bp-border);">Zuordnung abrufbar durch</td>
|
|
<td style="padding: 12px; border: 1px solid var(--bp-border); color: #22c55e;">Nur Lehrer</td>
|
|
<td style="padding: 12px; border: 1px solid var(--bp-border); color: #ef4444;">Anbieter, Dritte</td>
|
|
</tr>
|
|
<tr>
|
|
<td style="padding: 12px; border: 1px solid var(--bp-border);">DSGVO-Risiko</td>
|
|
<td style="padding: 12px; border: 1px solid var(--bp-border); color: #22c55e;">Minimal</td>
|
|
<td style="padding: 12px; border: 1px solid var(--bp-border); color: #ef4444;">Hoch</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
|
|
<!-- Technische Details -->
|
|
<div style="margin-bottom: 32px;">
|
|
<h3 style="color: var(--bp-text); font-size: 16px; margin: 0 0 16px 0; display: flex; align-items: center; gap: 8px;">
|
|
<span style="background: var(--bp-primary); color: white; width: 24px; height: 24px; border-radius: 50%; display: flex; align-items: center; justify-content: center; font-size: 12px;">4</span>
|
|
Technische Sicherheitsmassnahmen
|
|
</h3>
|
|
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 16px;">
|
|
<div style="background: var(--bp-surface); border-radius: 8px; padding: 16px;">
|
|
<h4 style="margin: 0 0 12px 0; color: var(--bp-text); font-size: 14px;">Kryptographie</h4>
|
|
<table style="width: 100%; font-size: 12px;">
|
|
<tr><td style="padding: 6px 0; color: var(--bp-text);">Token</td><td style="color: var(--bp-text-muted);">UUID v4 (128-bit)</td></tr>
|
|
<tr><td style="padding: 6px 0; color: var(--bp-text);">Verschluesselung</td><td style="color: var(--bp-text-muted);">AES-256-GCM</td></tr>
|
|
<tr><td style="padding: 6px 0; color: var(--bp-text);">Key Derivation</td><td style="color: var(--bp-text-muted);">PBKDF2, 100k Iter.</td></tr>
|
|
</table>
|
|
</div>
|
|
<div style="background: var(--bp-surface); border-radius: 8px; padding: 16px;">
|
|
<h4 style="margin: 0 0 12px 0; color: var(--bp-text); font-size: 14px;">Infrastruktur</h4>
|
|
<table style="width: 100%; font-size: 12px;">
|
|
<tr><td style="padding: 6px 0; color: var(--bp-text);">LLM</td><td style="color: var(--bp-text-muted);">SysEleven, Berlin</td></tr>
|
|
<tr><td style="padding: 6px 0; color: var(--bp-text);">Netzwerk</td><td style="color: var(--bp-text-muted);">TLS 1.3</td></tr>
|
|
<tr><td style="padding: 6px 0; color: var(--bp-text);">Retention</td><td style="color: var(--bp-text-muted);">30 Tage, auto-delete</td></tr>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Fazit -->
|
|
<div style="background: rgba(6, 95, 70, 0.15); border: 1px solid #065f46; border-radius: 8px; padding: 20px; color: #6ee7b7;">
|
|
<h4 style="margin: 0 0 12px 0; font-size: 16px;">Bewertung: Datenschutzkonform</h4>
|
|
<p style="margin: 0 0 12px 0;">
|
|
Die Klausurkorrektur mit BreakPilot ist vergleichbar mit einer lokalen Verarbeitung
|
|
auf dem PC der Lehrkraft. Personenbezogene Daten verlassen das Geraet nicht.
|
|
</p>
|
|
<ul style="margin: 0; padding-left: 20px;">
|
|
<li>Keine Uebermittlung personenbezogener Daten an Dritte</li>
|
|
<li>Kein US-Cloud-Transfer (Schrems II konform)</li>
|
|
<li>Volle Kontrolle der Lehrkraft</li>
|
|
<li>Automatische Datenloeschung nach 30 Tagen</li>
|
|
</ul>
|
|
</div>
|
|
|
|
<!-- Footer -->
|
|
<div style="margin-top: 32px; padding-top: 16px; border-top: 1px solid var(--bp-border); color: var(--bp-text-muted); font-size: 12px;">
|
|
<strong>Dokumentversion:</strong> 1.0 |
|
|
<strong>System:</strong> BreakPilot Klausurkorrektur |
|
|
<strong>Kontakt:</strong> datenschutz@breakpilot.de
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Mac Mini Control Tab -->
|
|
<div id="admin-mac-mini" class="admin-content">
|
|
<div class="mac-mini-dashboard" id="mac-mini-dashboard">
|
|
<!-- Header -->
|
|
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 24px;">
|
|
<h1 style="color: var(--bp-text); font-size: 28px; margin: 0; display: flex; align-items: center; gap: 12px;">
|
|
<span style="font-size: 32px;">🖥</span>
|
|
Mac Mini Control
|
|
</h1>
|
|
<span class="mac-mini-status-badge checking" id="mac-mini-overall-status">
|
|
Prüfe...
|
|
</span>
|
|
</div>
|
|
|
|
<!-- Power Controls -->
|
|
<div style="display: flex; gap: 12px; margin-bottom: 24px;">
|
|
<button class="btn btn-primary" onclick="macMiniWake()" id="btn-wake" style="background: #22c55e;">
|
|
⚡ Wake on LAN
|
|
</button>
|
|
<button class="btn btn-primary" onclick="macMiniRestart()" id="btn-restart" style="background: #f59e0b;">
|
|
🔄 Neustart
|
|
</button>
|
|
<button class="btn btn-primary" onclick="macMiniShutdown()" id="btn-shutdown" style="background: #ef4444;">
|
|
⏻ Herunterfahren
|
|
</button>
|
|
<button class="btn btn-ghost" onclick="macMiniRefreshStatus()">
|
|
🔍 Status aktualisieren
|
|
</button>
|
|
</div>
|
|
|
|
<!-- Status Grid -->
|
|
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 16px; margin-bottom: 24px;">
|
|
<!-- Connection Status -->
|
|
<div style="background: var(--bp-surface-elevated); border: 1px solid var(--bp-border); border-radius: 12px; padding: 20px;">
|
|
<h3 style="color: var(--bp-text); font-size: 16px; margin: 0 0 16px 0;">🌐 Verbindung</h3>
|
|
<div style="display: flex; justify-content: space-between; padding: 10px 0; border-bottom: 1px solid var(--bp-border);">
|
|
<span style="color: var(--bp-text-muted);">IP-Adresse</span>
|
|
<span id="mac-mini-ip" style="font-weight: 500;">192.168.178.100</span>
|
|
</div>
|
|
<div style="display: flex; justify-content: space-between; padding: 10px 0; border-bottom: 1px solid var(--bp-border);">
|
|
<span style="color: var(--bp-text-muted);">SSH</span>
|
|
<span id="status-ssh" style="font-weight: 500;">--</span>
|
|
</div>
|
|
<div style="display: flex; justify-content: space-between; padding: 10px 0;">
|
|
<span style="color: var(--bp-text-muted);">Ping</span>
|
|
<span id="status-ping" style="font-weight: 500;">--</span>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Services Status -->
|
|
<div style="background: var(--bp-surface-elevated); border: 1px solid var(--bp-border); border-radius: 12px; padding: 20px;">
|
|
<h3 style="color: var(--bp-text); font-size: 16px; margin: 0 0 16px 0;">⚙ Services</h3>
|
|
<div style="display: flex; justify-content: space-between; padding: 10px 0; border-bottom: 1px solid var(--bp-border);">
|
|
<span style="color: var(--bp-text-muted);">Backend API</span>
|
|
<span id="status-backend" style="font-weight: 500;">--</span>
|
|
</div>
|
|
<div style="display: flex; justify-content: space-between; padding: 10px 0; border-bottom: 1px solid var(--bp-border);">
|
|
<span style="color: var(--bp-text-muted);">Ollama</span>
|
|
<span id="status-ollama" style="font-weight: 500;">--</span>
|
|
</div>
|
|
<div style="display: flex; justify-content: space-between; padding: 10px 0;">
|
|
<span style="color: var(--bp-text-muted);">Docker</span>
|
|
<span id="status-docker" style="font-weight: 500;">--</span>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- System Info -->
|
|
<div style="background: var(--bp-surface-elevated); border: 1px solid var(--bp-border); border-radius: 12px; padding: 20px;">
|
|
<h3 style="color: var(--bp-text); font-size: 16px; margin: 0 0 16px 0;">💻 System</h3>
|
|
<div style="display: flex; justify-content: space-between; padding: 10px 0; border-bottom: 1px solid var(--bp-border);">
|
|
<span style="color: var(--bp-text-muted);">Uptime</span>
|
|
<span id="status-uptime" style="font-weight: 500;">--</span>
|
|
</div>
|
|
<div style="display: flex; justify-content: space-between; padding: 10px 0; border-bottom: 1px solid var(--bp-border);">
|
|
<span style="color: var(--bp-text-muted);">CPU Load</span>
|
|
<span id="status-cpu" style="font-weight: 500;">--</span>
|
|
</div>
|
|
<div style="display: flex; justify-content: space-between; padding: 10px 0;">
|
|
<span style="color: var(--bp-text-muted);">Memory</span>
|
|
<span id="status-memory" style="font-weight: 500;">--</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Docker Containers -->
|
|
<div style="background: var(--bp-surface-elevated); border: 1px solid var(--bp-border); border-radius: 12px; padding: 20px; margin-bottom: 24px;">
|
|
<h3 style="color: var(--bp-text); font-size: 16px; margin: 0 0 16px 0;">🐳 Docker Container</h3>
|
|
<div id="docker-container-list" style="display: flex; flex-direction: column; gap: 8px;">
|
|
<div style="color: var(--bp-text-muted); text-align: center; padding: 20px;">
|
|
Lade Container-Status...
|
|
</div>
|
|
</div>
|
|
<div style="margin-top: 16px; display: flex; gap: 8px;">
|
|
<button class="btn btn-ghost" onclick="macMiniDockerUp()" style="flex: 1;">
|
|
▶ Container starten
|
|
</button>
|
|
<button class="btn btn-ghost" onclick="macMiniDockerDown()" style="flex: 1;">
|
|
⏹ Container stoppen
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Ollama Section -->
|
|
<div style="background: var(--bp-surface-elevated); border: 1px solid var(--bp-border); border-radius: 12px; padding: 20px;">
|
|
<h3 style="color: var(--bp-text); font-size: 18px; margin: 0 0 16px 0;">🤖 Ollama LLM Modelle</h3>
|
|
|
|
<div id="ollama-model-list" style="display: flex; flex-direction: column; gap: 12px; margin-bottom: 20px;">
|
|
<div style="color: var(--bp-text-muted); text-align: center; padding: 20px;">
|
|
Lade Modelle...
|
|
</div>
|
|
</div>
|
|
|
|
<div style="margin-top: 20px; padding-top: 20px; border-top: 1px solid var(--bp-border);">
|
|
<h4 style="color: var(--bp-text); margin: 0 0 12px 0;">📥 Neues Modell herunterladen</h4>
|
|
<div style="display: flex; gap: 12px; margin-bottom: 16px;">
|
|
<input type="text" class="admin-form-input" id="model-download-input"
|
|
placeholder="Modellname (z.B. llama3.2, mistral, qwen2.5:7b)" style="flex: 1;">
|
|
<button class="btn btn-primary" onclick="macMiniPullModel()" id="btn-pull-model">
|
|
Herunterladen
|
|
</button>
|
|
</div>
|
|
|
|
<div id="download-progress" style="display: none;">
|
|
<div style="display: flex; justify-content: space-between; margin-bottom: 8px;">
|
|
<span id="download-model-name" style="color: var(--bp-text); font-weight: 600;">--</span>
|
|
<span id="download-stats" style="color: var(--bp-text-muted); font-size: 13px;">-- / --</span>
|
|
</div>
|
|
<div style="height: 24px; background: var(--bp-surface); border-radius: 12px; overflow: hidden; position: relative;">
|
|
<div id="download-progress-bar" style="height: 100%; background: linear-gradient(90deg, var(--bp-primary), #991b1b); border-radius: 12px; transition: width 0.3s ease; width: 0%;"></div>
|
|
<span id="download-progress-text" style="position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); color: white; font-size: 12px; font-weight: 600; text-shadow: 0 1px 2px rgba(0,0,0,0.5);">0%</span>
|
|
</div>
|
|
<div id="download-log" style="margin-top: 16px; padding: 16px; background: #0a0a0a; border-radius: 8px; font-family: monospace; font-size: 12px; color: #22c55e; max-height: 200px; overflow-y: auto; white-space: pre-wrap;"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- System Info Tab -->
|
|
<div id="admin-content-system-info" class="admin-content">
|
|
<div class="system-info-panel">
|
|
<div style="display: flex; justify-content: space-between; align-items: flex-start; margin-bottom: 24px;">
|
|
<div>
|
|
<h3 style="margin: 0 0 8px 0; font-size: 18px; font-weight: 600;">BreakPilot Python Backend</h3>
|
|
<p style="margin: 0; font-size: 13px; color: var(--bp-text-muted);">
|
|
DSGVO-konformes Consent-Management & Admin-Dashboard
|
|
</p>
|
|
</div>
|
|
<span class="system-info-version">v1.0.0</span>
|
|
</div>
|
|
|
|
<!-- Info Tabs -->
|
|
<div class="system-info-tabs">
|
|
<button class="system-info-tab active" onclick="switchSystemInfoTab('overview')">Übersicht</button>
|
|
<button class="system-info-tab" onclick="switchSystemInfoTab('architecture')">Architektur</button>
|
|
<button class="system-info-tab" onclick="switchSystemInfoTab('roadmap')">Roadmap</button>
|
|
<button class="system-info-tab" onclick="switchSystemInfoTab('technical')">Technisch</button>
|
|
</div>
|
|
|
|
<!-- Overview Tab -->
|
|
<div id="system-info-overview" class="system-info-content active">
|
|
<div class="system-info-grid">
|
|
<div class="system-info-card">
|
|
<div class="system-info-card-icon" style="background: rgba(59, 130, 246, 0.15); color: #3b82f6;">📄</div>
|
|
<div>
|
|
<div class="system-info-card-title">Consent Management</div>
|
|
<div class="system-info-card-desc">DSGVO-konforme Einwilligungsverwaltung</div>
|
|
</div>
|
|
</div>
|
|
<div class="system-info-card">
|
|
<div class="system-info-card-icon" style="background: rgba(34, 197, 94, 0.15); color: #22c55e;">🔐</div>
|
|
<div>
|
|
<div class="system-info-card-title">Cookie-Kategorien</div>
|
|
<div class="system-info-card-desc">Granulare Cookie-Steuerung</div>
|
|
</div>
|
|
</div>
|
|
<div class="system-info-card">
|
|
<div class="system-info-card-icon" style="background: rgba(251, 191, 36, 0.15); color: #fbbf24;">📈</div>
|
|
<div>
|
|
<div class="system-info-card-title">Statistiken</div>
|
|
<div class="system-info-card-desc">Echtzeit-Analyse & Reporting</div>
|
|
</div>
|
|
</div>
|
|
<div class="system-info-card">
|
|
<div class="system-info-card-icon" style="background: rgba(139, 92, 246, 0.15); color: #8b5cf6;">📧</div>
|
|
<div>
|
|
<div class="system-info-card-title">E-Mail Vorlagen</div>
|
|
<div class="system-info-card-desc">Template-Management</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="system-info-privacy-notes">
|
|
<h4 style="margin: 0 0 8px 0; font-size: 13px; font-weight: 600;">Datenschutz-Hinweise</h4>
|
|
<ul style="margin: 0; padding-left: 20px; font-size: 12px; color: var(--bp-text-muted);">
|
|
<li>Alle Daten werden DSGVO-konform verarbeitet</li>
|
|
<li>Consent-Daten werden verschlüsselt gespeichert</li>
|
|
<li>Betroffenenrechte vollständig implementiert</li>
|
|
<li>Audit-Log für alle Änderungen</li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Architecture Tab -->
|
|
<div id="system-info-architecture" class="system-info-content">
|
|
<div class="system-info-architecture-layers">
|
|
<div class="system-info-layer">
|
|
<div class="system-info-layer-name">Präsentation</div>
|
|
<div class="system-info-layer-tech">FastAPI HTML Templates, REST API</div>
|
|
</div>
|
|
<div class="system-info-layer-connector">↓</div>
|
|
<div class="system-info-layer">
|
|
<div class="system-info-layer-name">Geschäftslogik</div>
|
|
<div class="system-info-layer-tech">Python Services, Async Handlers</div>
|
|
</div>
|
|
<div class="system-info-layer-connector">↓</div>
|
|
<div class="system-info-layer">
|
|
<div class="system-info-layer-name">Integration</div>
|
|
<div class="system-info-layer-tech">Go Consent Service, Matrix API</div>
|
|
</div>
|
|
<div class="system-info-layer-connector">↓</div>
|
|
<div class="system-info-layer">
|
|
<div class="system-info-layer-name">Datenhaltung</div>
|
|
<div class="system-info-layer-tech">PostgreSQL, Redis Cache</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Roadmap Tab -->
|
|
<div id="system-info-roadmap" class="system-info-content">
|
|
<div class="system-info-roadmap-list">
|
|
<div class="system-info-roadmap-item completed">
|
|
<div class="system-info-roadmap-status">✓</div>
|
|
<div>
|
|
<div class="system-info-roadmap-title">Consent-Workflow</div>
|
|
<div class="system-info-roadmap-desc">DSGVO-konforme Einwilligungsverwaltung mit Versionshistorie</div>
|
|
</div>
|
|
</div>
|
|
<div class="system-info-roadmap-item completed">
|
|
<div class="system-info-roadmap-status">✓</div>
|
|
<div>
|
|
<div class="system-info-roadmap-title">Cookie-Kategorien</div>
|
|
<div class="system-info-roadmap-desc">Granulare Cookie-Steuerung nach TCF 2.0</div>
|
|
</div>
|
|
</div>
|
|
<div class="system-info-roadmap-item in-progress">
|
|
<div class="system-info-roadmap-status">◯</div>
|
|
<div>
|
|
<div class="system-info-roadmap-title">Erweiterte Statistiken</div>
|
|
<div class="system-info-roadmap-desc">Dashboard mit Zeitreihen und Export-Funktionen</div>
|
|
</div>
|
|
</div>
|
|
<div class="system-info-roadmap-item planned">
|
|
<div class="system-info-roadmap-status">○</div>
|
|
<div>
|
|
<div class="system-info-roadmap-title">Multi-Tenant Support</div>
|
|
<div class="system-info-roadmap-desc">Mandantenfähigkeit für SaaS-Betrieb</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Technical Tab -->
|
|
<div id="system-info-technical" class="system-info-content">
|
|
<div class="system-info-tech-grid">
|
|
<div class="system-info-tech-card">
|
|
<div class="system-info-tech-label">Framework</div>
|
|
<div class="system-info-tech-value">FastAPI 0.104+</div>
|
|
</div>
|
|
<div class="system-info-tech-card">
|
|
<div class="system-info-tech-label">Python</div>
|
|
<div class="system-info-tech-value">3.10+</div>
|
|
</div>
|
|
<div class="system-info-tech-card">
|
|
<div class="system-info-tech-label">Port</div>
|
|
<div class="system-info-tech-value">8000</div>
|
|
</div>
|
|
<div class="system-info-tech-card">
|
|
<div class="system-info-tech-label">Auth</div>
|
|
<div class="system-info-tech-value">JWT / Keycloak</div>
|
|
</div>
|
|
<div class="system-info-tech-card">
|
|
<div class="system-info-tech-label">Datenbank</div>
|
|
<div class="system-info-tech-value">PostgreSQL</div>
|
|
</div>
|
|
<div class="system-info-tech-card">
|
|
<div class="system-info-tech-label">Cache</div>
|
|
<div class="system-info-tech-value">Redis / Valkey</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="system-info-endpoints">
|
|
<h4 style="margin: 0 0 12px 0; font-size: 13px; font-weight: 600;">API Endpoints</h4>
|
|
<div class="system-info-endpoint">
|
|
<code>GET /api/health</code>
|
|
<span>Health Check</span>
|
|
</div>
|
|
<div class="system-info-endpoint">
|
|
<code>GET /api/consent/*</code>
|
|
<span>Consent Service Proxy</span>
|
|
</div>
|
|
<div class="system-info-endpoint">
|
|
<code>GET /app</code>
|
|
<span>Studio Frontend</span>
|
|
</div>
|
|
<div class="system-info-endpoint">
|
|
<code>GET /admin</code>
|
|
<span>Admin Panel</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- DSMS WebUI Modal -->
|
|
<div id="dsms-webui-modal" class="legal-modal" style="display: none;">
|
|
<div class="legal-modal-content" style="max-width: 1200px; width: 95%; height: 90vh;">
|
|
<div class="legal-modal-header" style="border-bottom: 1px solid var(--bp-border);">
|
|
<h2 style="display: flex; align-items: center; gap: 10px;">
|
|
<span style="font-size: 24px;">🌐</span> DSMS WebUI
|
|
</h2>
|
|
<button id="dsms-webui-modal-close" class="legal-modal-close" onclick="closeDsmsWebUI()">×</button>
|
|
</div>
|
|
<div style="display: flex; height: calc(100% - 60px);">
|
|
<!-- Sidebar -->
|
|
<div style="width: 200px; background: var(--bp-surface); border-right: 1px solid var(--bp-border); padding: 16px;">
|
|
<nav style="display: flex; flex-direction: column; gap: 4px;">
|
|
<button class="dsms-webui-nav active" data-section="overview" onclick="switchDsmsWebUISection('overview')">
|
|
<span>📈</span> Übersicht
|
|
</button>
|
|
<button class="dsms-webui-nav" data-section="files" onclick="switchDsmsWebUISection('files')">
|
|
<span>📁</span> Dateien
|
|
</button>
|
|
<button class="dsms-webui-nav" data-section="explore" onclick="switchDsmsWebUISection('explore')">
|
|
<span>🔍</span> Erkunden
|
|
</button>
|
|
<button class="dsms-webui-nav" data-section="peers" onclick="switchDsmsWebUISection('peers')">
|
|
<span>🌐</span> Peers
|
|
</button>
|
|
<button class="dsms-webui-nav" data-section="config" onclick="switchDsmsWebUISection('config')">
|
|
<span>⚙</span> Konfiguration
|
|
</button>
|
|
</nav>
|
|
</div>
|
|
<!-- Main Content -->
|
|
<div style="flex: 1; overflow-y: auto; padding: 24px;" id="dsms-webui-content">
|
|
<!-- Overview Section (default) -->
|
|
<div id="dsms-webui-overview" class="dsms-webui-section active">
|
|
<h3 style="margin: 0 0 24px 0;">Node Übersicht</h3>
|
|
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 16px; margin-bottom: 24px;">
|
|
<div class="dsms-webui-stat-card">
|
|
<div class="dsms-webui-stat-label">Status</div>
|
|
<div class="dsms-webui-stat-value" id="webui-status">--</div>
|
|
</div>
|
|
<div class="dsms-webui-stat-card">
|
|
<div class="dsms-webui-stat-label">Node ID</div>
|
|
<div class="dsms-webui-stat-value" id="webui-node-id" style="font-size: 11px; word-break: break-all;">--</div>
|
|
</div>
|
|
<div class="dsms-webui-stat-card">
|
|
<div class="dsms-webui-stat-label">Protokoll</div>
|
|
<div class="dsms-webui-stat-value" id="webui-protocol">--</div>
|
|
</div>
|
|
<div class="dsms-webui-stat-card">
|
|
<div class="dsms-webui-stat-label">Agent</div>
|
|
<div class="dsms-webui-stat-value" id="webui-agent">--</div>
|
|
</div>
|
|
</div>
|
|
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); gap: 16px;">
|
|
<div class="dsms-webui-stat-card">
|
|
<div class="dsms-webui-stat-label">Repo Größe</div>
|
|
<div class="dsms-webui-stat-value" id="webui-repo-size">--</div>
|
|
<div class="dsms-webui-stat-sub" id="webui-storage-info">--</div>
|
|
</div>
|
|
<div class="dsms-webui-stat-card">
|
|
<div class="dsms-webui-stat-label">Objekte</div>
|
|
<div class="dsms-webui-stat-value" id="webui-num-objects">--</div>
|
|
</div>
|
|
<div class="dsms-webui-stat-card">
|
|
<div class="dsms-webui-stat-label">Gepinnte Dokumente</div>
|
|
<div class="dsms-webui-stat-value" id="webui-pinned-count">--</div>
|
|
</div>
|
|
</div>
|
|
<div style="margin-top: 24px;">
|
|
<h4 style="margin: 0 0 12px 0;">Adressen</h4>
|
|
<div id="webui-addresses" style="background: var(--bp-input-bg); border-radius: 8px; padding: 12px; font-family: monospace; font-size: 12px; max-height: 150px; overflow-y: auto;">
|
|
Lade...
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Files Section -->
|
|
<div id="dsms-webui-files" class="dsms-webui-section" style="display: none;">
|
|
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 24px;">
|
|
<h3 style="margin: 0;">Dateien hochladen</h3>
|
|
</div>
|
|
<div class="dsms-webui-upload-zone" id="dsms-upload-zone" ondrop="handleDsmsFileDrop(event)" ondragover="handleDsmsDragOver(event)" ondragleave="handleDsmsDragLeave(event)">
|
|
<div style="text-align: center;">
|
|
<div style="font-size: 48px; margin-bottom: 16px;">📥</div>
|
|
<p style="color: var(--bp-text); margin-bottom: 8px;">Dateien hierher ziehen</p>
|
|
<p style="color: var(--bp-text-muted); font-size: 13px;">oder</p>
|
|
<input type="file" id="dsms-file-input" style="display: none;" onchange="handleDsmsFileSelect(event)" multiple>
|
|
<button class="btn btn-primary btn-sm" onclick="document.getElementById('dsms-file-input').click()">Dateien auswählen</button>
|
|
</div>
|
|
</div>
|
|
<div id="dsms-upload-progress" style="display: none; margin-top: 16px;">
|
|
<div style="background: var(--bp-surface-elevated); border-radius: 8px; padding: 16px;">
|
|
<div id="dsms-upload-status">Hochladen...</div>
|
|
<div style="background: var(--bp-border); border-radius: 4px; height: 8px; margin-top: 8px; overflow: hidden;">
|
|
<div id="dsms-upload-bar" style="background: var(--bp-primary); height: 100%; width: 0%; transition: width 0.3s;"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div id="dsms-upload-results" style="margin-top: 24px;"></div>
|
|
</div>
|
|
|
|
<!-- Explore Section -->
|
|
<div id="dsms-webui-explore" class="dsms-webui-section" style="display: none;">
|
|
<h3 style="margin: 0 0 24px 0;">IPFS Explorer</h3>
|
|
<div style="display: flex; gap: 12px; margin-bottom: 24px;">
|
|
<input type="text" class="admin-search" placeholder="CID eingeben (z.B. QmXyz...)" id="webui-explore-cid" style="flex: 1;">
|
|
<button class="btn btn-primary btn-sm" onclick="exploreDsmsCid()">Erkunden</button>
|
|
</div>
|
|
<div id="dsms-explore-result" style="display: none;">
|
|
<div style="background: var(--bp-surface-elevated); border-radius: 8px; padding: 16px;">
|
|
<div id="dsms-explore-content"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Peers Section -->
|
|
<div id="dsms-webui-peers" class="dsms-webui-section" style="display: none;">
|
|
<h3 style="margin: 0 0 24px 0;">Verbundene Peers</h3>
|
|
<p style="color: var(--bp-text-muted); margin-bottom: 16px;">
|
|
Hinweis: In einem privaten DSMS-Netzwerk sind normalerweise keine externen Peers verbunden.
|
|
</p>
|
|
<div id="webui-peers-list">
|
|
<div class="admin-loading">Lade Peers...</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Config Section -->
|
|
<div id="dsms-webui-config" class="dsms-webui-section" style="display: none;">
|
|
<h3 style="margin: 0 0 24px 0;">Konfiguration</h3>
|
|
<div style="display: grid; gap: 16px;">
|
|
<div style="background: var(--bp-surface-elevated); border-radius: 8px; padding: 16px;">
|
|
<h4 style="margin: 0 0 12px 0;">API Endpoints</h4>
|
|
<table style="width: 100%; font-size: 13px;">
|
|
<tr>
|
|
<td style="padding: 8px 0; color: var(--bp-text-muted);">IPFS API</td>
|
|
<td style="padding: 8px 0; font-family: monospace;">http://localhost:5001</td>
|
|
</tr>
|
|
<tr>
|
|
<td style="padding: 8px 0; color: var(--bp-text-muted);">DSMS Gateway</td>
|
|
<td style="padding: 8px 0; font-family: monospace;">http://localhost:8082</td>
|
|
</tr>
|
|
<tr>
|
|
<td style="padding: 8px 0; color: var(--bp-text-muted);">IPFS Gateway</td>
|
|
<td style="padding: 8px 0; font-family: monospace;">http://localhost:8085</td>
|
|
</tr>
|
|
<tr>
|
|
<td style="padding: 8px 0; color: var(--bp-text-muted);">Swarm P2P</td>
|
|
<td style="padding: 8px 0; font-family: monospace;">:4001</td>
|
|
</tr>
|
|
</table>
|
|
</div>
|
|
<div style="background: var(--bp-surface-elevated); border-radius: 8px; padding: 16px;">
|
|
<h4 style="margin: 0 0 12px 0;">Aktionen</h4>
|
|
<div style="display: flex; flex-wrap: wrap; gap: 12px;">
|
|
<button class="btn btn-ghost btn-sm" onclick="runDsmsGarbageCollection()">🗑 Garbage Collection</button>
|
|
<button class="btn btn-ghost btn-sm" onclick="loadDsmsWebUIData()">↻ Daten aktualisieren</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
"""
|
|
|
|
|