""" Admin DSMS Component - DSMS/IPFS WebUI, Archive Management """ def get_admin_dsms_css() -> str: """CSS für DSMS zurückgeben""" return """ /* DSMS Styles */ .dsms-subtab { padding: 6px 14px; border: none; background: transparent; color: var(--bp-text-muted); cursor: pointer; font-size: 13px; border-radius: 4px; transition: all 0.2s ease; } .dsms-subtab:hover { background: var(--bp-border-subtle); color: var(--bp-text); } .dsms-subtab.active { background: var(--bp-primary); color: white; } .dsms-content { display: none; } .dsms-content.active { display: block; } .dsms-status-card { background: var(--bp-surface-elevated); border: 1px solid var(--bp-border); border-radius: 8px; padding: 16px; } .dsms-status-card h4 { margin: 0 0 8px 0; font-size: 12px; color: var(--bp-text-muted); text-transform: uppercase; letter-spacing: 0.05em; } .dsms-status-card .value { font-size: 24px; font-weight: 600; color: var(--bp-text); } .dsms-status-card .value.online { color: var(--bp-accent); } .dsms-status-card .value.offline { color: var(--bp-danger); } .dsms-verify-success { background: var(--bp-accent-soft); border: 1px solid var(--bp-accent); border-radius: 8px; padding: 16px; color: var(--bp-accent); } .dsms-verify-error { background: rgba(239, 68, 68, 0.1); border: 1px solid var(--bp-danger); border-radius: 8px; padding: 16px; color: var(--bp-danger); } /* DSMS WebUI Styles */ .dsms-webui-nav { display: flex; align-items: center; gap: 10px; padding: 10px 12px; border: none; background: transparent; color: var(--bp-text-muted); font-size: 14px; border-radius: 6px; cursor: pointer; text-align: left; width: 100%; transition: all 0.2s; } .dsms-webui-nav:hover { background: var(--bp-surface-elevated); color: var(--bp-text); } .dsms-webui-nav.active { background: var(--bp-primary-soft); color: var(--bp-primary); font-weight: 500; } .dsms-webui-section { display: none; } .dsms-webui-section.active { display: block; } .dsms-webui-stat-card { background: var(--bp-surface-elevated); border: 1px solid var(--bp-border); border-radius: 8px; padding: 16px; } .dsms-webui-stat-label { font-size: 12px; color: var(--bp-text-muted); margin-bottom: 4px; } .dsms-webui-stat-value { font-size: 18px; font-weight: 600; color: var(--bp-text); } .dsms-webui-stat-sub { font-size: 11px; color: var(--bp-text-muted); margin-top: 4px; } .dsms-webui-upload-zone { border: 2px dashed var(--bp-border); border-radius: 12px; padding: 48px 24px; background: var(--bp-input-bg); transition: all 0.2s; } .dsms-webui-upload-zone.dragover { border-color: var(--bp-primary); background: var(--bp-primary-soft); } .dsms-webui-file-item { display: flex; align-items: center; justify-content: space-between; padding: 12px 16px; background: var(--bp-surface-elevated); border: 1px solid var(--bp-border); border-radius: 8px; margin-bottom: 8px; } .dsms-webui-file-item .cid { font-family: monospace; font-size: 12px; color: var(--bp-text-muted); word-break: break-all; } .main-layout { display: grid; grid-template-columns: 240px minmax(0, 1fr); height: 100%; min-height: 0; } .sidebar { border-right: 1px solid var(--bp-border-subtle); background: var(--bp-gradient-sidebar); padding: 14px 10px; display: flex; flex-direction: column; gap: 18px; min-width: 0; overflow: hidden; transition: background 0.3s ease, border-color 0.3s ease; } .sidebar-section-title { font-size: 12px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.08em; color: var(--bp-text); padding: 8px 6px 6px 6px; margin-top: 12px; } .sidebar-menu { display: flex; flex-direction: column; gap: 4px; } .sidebar-item { display: flex; align-items: center; justify-content: space-between; padding: 8px 10px; border-radius: 9px; cursor: pointer; font-size: 13px; color: var(--bp-text-muted); } .sidebar-item.active { background: var(--bp-surface-elevated); color: var(--bp-accent); border: 1px solid var(--bp-accent-soft); } [data-theme="light"] .sidebar-item.active { background: var(--bp-primary-soft); color: var(--bp-primary); border: 1px solid var(--bp-primary); } .sidebar-item-label { display: flex; align-items: center; gap: 7px; } .sidebar-item-badge { font-size: 10px; border-radius: 999px; padding: 2px 7px; border: 1px solid var(--bp-border-subtle); } .sidebar-footer { margin-top: auto; font-size: 11px; color: var(--bp-text-muted); padding: 0 6px; } .content { padding: 14px 16px 16px 16px; display: flex; flex-direction: column; gap: 14px; height: 100%; min-height: 0; overflow: hidden; } .panel { background: var(--bp-gradient-surface); border-radius: 16px; border: 1px solid var(--bp-border-subtle); padding: 12px 14px; display: flex; flex-direction: column; min-height: 0; height: 100%; flex: 1; overflow: hidden; transition: background 0.3s ease, border-color 0.3s ease; } [data-theme="light"] .panel { box-shadow: 0 2px 12px rgba(0,0,0,0.08); } .panel-header { display: flex; align-items: center; justify-content: space-between; margin-bottom: 8px; } .panel-title { font-size: 18px; font-weight: 700; color: var(--bp-text); margin-bottom: 4px; } .panel-subtitle { font-size: 12px; color: var(--bp-text-muted); line-height: 1.5; } .panel-body { flex: 1; min-height: 0; display: flex; flex-direction: column; gap: 8px; overflow: auto; } .small-pill { font-size: 11px; font-weight: 600; padding: 4px 10px; border-radius: 999px; border: 1px solid var(--bp-border-subtle); color: var(--bp-text); background: var(--bp-surface-elevated); } .upload-inline { display: flex; flex-direction: column; gap: 8px; padding: 10px; background: var(--bp-surface-elevated); border-radius: 8px; border: 1px solid var(--bp-border-subtle); } [data-theme="light"] .upload-inline { background: var(--bp-surface); box-shadow: inset 0 1px 3px rgba(0,0,0,0.05); } .upload-inline input[type=file] { font-size: 11px; padding: 6px; background: var(--bp-input-bg); border: 1px solid var(--bp-border-subtle); border-radius: 6px; color: var(--bp-text); cursor: pointer; } .upload-inline input[type=file]::file-selector-button { background: var(--bp-accent-soft); border: 1px solid var(--bp-accent); border-radius: 4px; padding: 4px 10px; color: var(--bp-accent); font-size: 10px; font-weight: 600; cursor: pointer; margin-right: 8px; } [data-theme="light"] .upload-inline input[type=file]::file-selector-button { background: var(--bp-primary-soft); border-color: var(--bp-primary); color: var(--bp-primary); } .upload-inline input[type=file]::file-selector-button:hover { opacity: 0.8; } .file-list { list-style: none; margin: 0; padding: 0; max-height: 150px; overflow-y: auto; border-radius: 10px; border: 1px solid var(--bp-border-subtle); background: var(--bp-surface-elevated); } [data-theme="light"] .file-list { border-color: var(--bp-primary); background: var(--bp-surface); } .file-item { font-size: 12px; padding: 8px 10px; cursor: pointer; display: flex; align-items: center; justify-content: space-between; gap: 8px; transition: background 0.15s ease; } .file-item:nth-child(odd) { background: var(--bp-surface-elevated); } [data-theme="light"] .file-item:nth-child(odd) { background: rgba(108, 27, 27, 0.03); } .file-item:hover { background: var(--bp-accent-soft); } [data-theme="light"] .file-item:hover { background: var(--bp-primary-soft); } .file-item.active { background: var(--bp-accent-soft); } .file-item-name { overflow: hidden; white-space: nowrap; text-overflow: ellipsis; flex: 1; min-width: 0; } .file-item-delete { font-size: 14px; color: #f97316; cursor: pointer; padding: 4px 6px; border-radius: 4px; transition: all 0.15s ease; flex-shrink: 0; } .file-item-delete:hover { color: #fb923c; background: rgba(249,115,22,0.15); } .file-empty { font-size: 12px; color: var(--bp-text-muted); } .inline-process { display: flex; justify-content: flex-end; margin-top: 12px; padding-top: 12px; border-top: 1px solid rgba(148,163,184,0.2); } .preview-container { flex: 1; border-radius: 12px; border: 1px solid var(--bp-border-subtle); background: var(--bp-gradient-sidebar); overflow: hidden; display: flex; align-items: stretch; justify-content: center; position: relative; min-height: 750px; transition: all 0.3s ease; } [data-theme="light"] .preview-container { background: var(--bp-bg); border: 2px solid var(--bp-primary); box-shadow: 0 4px 20px rgba(108, 27, 27, 0.1); } .preview-placeholder { font-size: 13px; color: var(--bp-text-muted); display: flex; align-items: center; justify-content: center; text-align: center; padding: 20px; } .compare-wrapper { display: grid; grid-template-columns: minmax(0, 1fr) 110px minmax(0, 1fr); gap: 8px; width: 100%; height: 100%; position: relative; } .compare-section { position: relative; border-radius: 10px; border: 1px solid var(--bp-border-subtle); background: var(--bp-gradient-sidebar); display: flex; flex-direction: column; overflow: hidden; min-height: 0; transition: all 0.3s ease; } [data-theme="light"] .compare-section { border: 2px solid var(--bp-primary); box-shadow: 0 4px 20px rgba(108, 27, 27, 0.1); } .compare-header { padding: 6px 10px; font-size: 12px; border-bottom: 1px solid var(--bp-border-subtle); display: flex; justify-content: space-between; align-items: center; gap: 6px; } [data-theme="light"] .compare-header { background: var(--bp-primary-soft); border-bottom: 1px solid var(--bp-primary); } .compare-header span { color: var(--bp-text-muted); } [data-theme="light"] .compare-header span { color: var(--bp-primary); font-weight: 600; } .compare-body { flex: 1; min-height: 0; position: relative; display: flex; align-items: center; justify-content: center; padding: 6px; } .compare-body-inner { position: relative; width: 100%; height: 100%; display: flex; align-items: center; justify-content: center; } .preview-img { max-width: 100%; max-height: 100%; object-fit: contain; box-shadow: 0 18px 40px rgba(0,0,0,0.5); border-radius: 10px; } [data-theme="light"] .preview-img { box-shadow: 0 8px 24px rgba(108, 27, 27, 0.15); } .clean-frame { width: 100%; height: 100%; border: none; border-radius: 10px; background: white; } .preview-nav { position: absolute; inset: 0; display: flex; align-items: center; justify-content: space-between; pointer-events: none; padding: 0 4px; } .preview-nav button { pointer-events: auto; width: 28px; height: 28px; border-radius: 999px; border: 1px solid var(--bp-border-subtle); background: var(--bp-surface-elevated); color: var(--bp-text); cursor: pointer; font-size: 14px; display: flex; align-items: center; justify-content: center; transition: all 0.2s ease; } [data-theme="light"] .preview-nav button { border: 2px solid var(--bp-primary); background: var(--bp-surface); color: var(--bp-primary); font-weight: 700; } .preview-nav button:hover:not(:disabled) { border-color: var(--bp-primary); color: var(--bp-primary); } [data-theme="light"] .preview-nav button:hover:not(:disabled) { background: var(--bp-primary); color: white; } .preview-nav button:disabled { opacity: 0.35; cursor: default; } .preview-nav span { position: absolute; bottom: 6px; left: 50%; transform: translateX(-50%); font-size: 11px; padding: 2px 8px; border-radius: 999px; background: var(--bp-surface-elevated); border: 1px solid var(--bp-border-subtle); } [data-theme="light"] .preview-nav span { background: var(--bp-surface); border: 1px solid var(--bp-primary); color: var(--bp-primary); } .preview-thumbnails { display: flex; flex-direction: column; gap: 8px; padding: 8px 4px; overflow-y: auto; align-items: center; } .preview-thumb { min-width: 90px; width: 90px; height: 70px; border-radius: 8px; border: 2px solid rgba(148,163,184,0.25); background: rgba(15,23,42,0.5); cursor: pointer; overflow: hidden; display: flex; align-items: center; justify-content: center; position: relative; flex-shrink: 0; } .preview-thumb:hover { border-color: var(--bp-accent); } .preview-thumb.active { border-color: var(--bp-accent); border-width: 3px; } .preview-thumb img { width: 100%; height: 100%; object-fit: cover; } .preview-thumb-label { position: absolute; bottom: 0; left: 0; right: 0; font-size: 9px; padding: 2px; background: rgba(0,0,0,0.8); color: white; text-align: center; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } .pager { grid-column: 1 / -1; display: flex; align-items: center; justify-content: center; gap: 8px; padding: 2px 0; font-size: 11px; } .pager button { width: 24px; height: 24px; border-radius: 999px; border: 1px solid var(--bp-border-subtle); background: var(--bp-surface-elevated); color: var(--bp-text); cursor: pointer; transition: all 0.2s ease; } .pager button:hover { border-color: var(--bp-primary); color: var(--bp-primary); } [data-theme="light"] .pager button { border: 2px solid var(--bp-primary); background: var(--bp-surface); color: var(--bp-primary); font-weight: 700; } [data-theme="light"] .pager button:hover { background: var(--bp-primary); color: white; } .status-bar { position: fixed; right: 18px; bottom: 18px; padding: 8px 12px; border-radius: 999px; background: var(--bp-surface-elevated); border: 1px solid var(--bp-border-subtle); display: flex; align-items: center; gap: 8px; font-size: 12px; min-width: 230px; transition: all 0.3s ease; } [data-theme="light"] .status-bar { background: var(--bp-surface); border: 2px solid var(--bp-primary); box-shadow: 0 4px 16px rgba(108, 27, 27, 0.15); } .status-dot { width: 10px; height: 10px; border-radius: 999px; background: var(--bp-text-muted); } .status-dot.busy { background: var(--bp-accent); } .status-dot.error { background: var(--bp-danger); } .status-text-main { font-size: 12px; } .status-text-sub { font-size: 11px; color: var(--bp-text-muted); } .footer { display: flex; align-items: center; justify-content: center; gap: 12px; font-size: 11px; color: var(--bp-text-muted); border-top: 1px solid var(--bp-border-subtle); background: var(--bp-surface-elevated); transition: background 0.3s ease; } .footer a { color: var(--bp-text-muted); text-decoration: none; } .footer a:hover { text-decoration: underline; } .btn { font-size: 13px; font-weight: 500; padding: 8px 16px; border-radius: 8px; border: 1px solid var(--bp-border-subtle); background: var(--bp-surface-elevated); color: var(--bp-text); cursor: pointer; transition: all 0.2s ease; } .btn:hover:not(:disabled) { border-color: var(--bp-primary); transform: translateY(-1px); } .btn-primary { border-color: var(--bp-accent); background: var(--bp-btn-primary-bg); color: white; } .btn-primary:hover:not(:disabled) { background: var(--bp-btn-primary-hover); box-shadow: 0 4px 12px rgba(34,197,94,0.3); } [data-theme="light"] .btn-primary:hover:not(:disabled) { box-shadow: 0 4px 12px rgba(108, 27, 27, 0.3); } .btn-ghost { background: transparent; } .btn-ghost:hover:not(:disabled) { background: var(--bp-surface-elevated); } [data-theme="light"] .btn-ghost { border-color: var(--bp-primary); color: var(--bp-primary); } [data-theme="light"] .btn-ghost:hover:not(:disabled) { background: var(--bp-primary-soft); } .btn-sm { padding: 6px 12px; font-size: 12px; } .btn:disabled { opacity: 0.5; cursor: not-allowed; } .panel-tools-header { display: flex; align-items: center; justify-content: space-between; gap: 8px; margin-bottom: 6px; } .card-toggle-bar { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 8px; } .toggle-pill { font-size: 11px; padding: 4px 8px; border-radius: 999px; border: 1px solid var(--bp-border-subtle); background: var(--bp-surface-elevated); color: var(--bp-text-muted); cursor: pointer; transition: all 0.2s ease; } .toggle-pill.active { border-color: var(--bp-accent); color: var(--bp-accent); background: var(--bp-accent-soft); } [data-theme="light"] .toggle-pill.active { border-color: var(--bp-primary); color: var(--bp-primary); background: var(--bp-primary-soft); } .cards-grid { flex: 1; display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); grid-auto-rows: 1fr; gap: 10px; min-height: 0; align-items: stretch; } .card { border-radius: 14px; border: 1px solid var(--bp-border-subtle); background: var(--bp-card-bg); padding: 10px; display: flex; flex-direction: column; cursor: pointer; min-height: 0; transition: all 0.3s ease; } [data-theme="light"] .card { border: 2px solid var(--bp-primary); box-shadow: 0 4px 20px rgba(108, 27, 27, 0.1); } [data-theme="light"] .card:hover { box-shadow: 0 6px 28px rgba(108, 27, 27, 0.2); transform: translateY(-2px); } .card-header { display: flex; align-items: center; justify-content: space-between; margin-bottom: 6px; } [data-theme="light"] .card-header { border-bottom: 1px solid var(--bp-border-subtle); padding-bottom: 8px; margin-bottom: 10px; } .card-title { font-size: 13px; font-weight: 500; } [data-theme="light"] .card-title { font-size: 15px; font-weight: 700; color: var(--bp-text); letter-spacing: -0.02em; } .card-badge { font-size: 10px; border-radius: 999px; padding: 2px 7px; border: 1px solid var(--bp-border-subtle); color: var(--bp-text-muted); } [data-theme="light"] .card-badge { background: linear-gradient(135deg, var(--bp-accent) 0%, #4CAF50 100%); color: white; border: none; font-weight: 600; padding: 4px 10px; } .card-body { flex: 1; display: flex; flex-direction: column; gap: 5px; font-size: 11px; color: var(--bp-text-muted); } [data-theme="light"] .card-body { font-size: 12px; line-height: 1.6; } .card-actions { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 8px; } .card-hidden { display: none; } .card-full { grid-column: 1 / -1; min-height: 220px; } """ def get_admin_dsms_html() -> str: """HTML für DSMS WebUI Modal zurückgeben""" return """ """ def get_admin_dsms_js() -> str: """JavaScript für DSMS Functions zurückgeben""" return """ const DSMS_GATEWAY_URL = 'http://localhost:8082'; let dsmsArchives = []; function switchDsmsTab(tabName) { document.querySelectorAll('.dsms-subtab').forEach(t => t.classList.remove('active')); document.querySelectorAll('.dsms-content').forEach(c => c.classList.remove('active')); document.querySelector(`.dsms-subtab[data-dsms-tab="${tabName}"]`)?.classList.add('active'); document.getElementById(`dsms-${tabName}`)?.classList.add('active'); // Load data for specific tabs if (tabName === 'settings') { loadDsmsNodeInfo(); } } async function loadDsmsData() { await Promise.all([ loadDsmsStatus(), loadDsmsArchives(), loadDsmsDocumentSelect() ]); } async function loadDsmsStatus() { const container = document.getElementById('dsms-status-cards'); container.innerHTML = '
Lade DSMS Status...
'; try { const [healthRes, nodeRes] = await Promise.all([ fetch(`${DSMS_GATEWAY_URL}/health`).catch(() => null), fetch(`${DSMS_GATEWAY_URL}/api/v1/node/info`).catch(() => null) ]); const health = healthRes?.ok ? await healthRes.json() : null; const nodeInfo = nodeRes?.ok ? await nodeRes.json() : null; const isOnline = health?.ipfs_connected === true; const repoSize = nodeInfo?.repo_size ? formatBytes(nodeInfo.repo_size) : '-'; const storageMax = nodeInfo?.storage_max ? formatBytes(nodeInfo.storage_max) : '-'; const numObjects = nodeInfo?.num_objects ?? '-'; container.innerHTML = `

Status

${isOnline ? 'Online' : 'Offline'}

Speicher verwendet

${repoSize}

Max. Speicher

${storageMax}

Objekte

${numObjects}
`; } catch(e) { container.innerHTML = `

Status

Nicht erreichbar

DSMS Gateway ist nicht verfügbar. Stellen Sie sicher, dass die Container laufen.

`; } } async function loadDsmsArchives() { const container = document.getElementById('dsms-archives-table'); container.innerHTML = '
Lade archivierte Dokumente...
'; try { const token = localStorage.getItem('bp_token') || ''; const res = await fetch(`${DSMS_GATEWAY_URL}/api/v1/documents`, { headers: { 'Authorization': `Bearer ${token}` } }); if (!res.ok) { throw new Error('Fehler beim Laden'); } const data = await res.json(); dsmsArchives = data.documents || []; if (dsmsArchives.length === 0) { container.innerHTML = `

Keine archivierten Dokumente vorhanden.

Klicken Sie auf "+ Dokument archivieren" um ein Legal Document im DSMS zu speichern.

`; return; } container.innerHTML = ` ${dsmsArchives.map(doc => ` `).join('')}
CID Dokument Version Archiviert am Aktionen
${doc.cid.substring(0, 12)}... ${doc.metadata?.document_id || doc.filename || '-'} ${doc.metadata?.version || '-'} ${doc.metadata?.created_at ? new Date(doc.metadata.created_at).toLocaleString('de-DE') : '-'}
`; } catch(e) { container.innerHTML = `
Fehler: ${e.message}
`; } } async function loadDsmsDocumentSelect() { const select = document.getElementById('dsms-archive-doc-select'); if (!select) return; try { const res = await fetch('/api/consent/admin/documents'); if (!res.ok) return; const data = await res.json(); const docs = data.documents || []; select.innerHTML = '' + docs.map(d => ``).join(''); } catch(e) { console.error('Error loading documents:', e); } } async function loadDsmsVersionSelect() { const docSelect = document.getElementById('dsms-archive-doc-select'); const versionSelect = document.getElementById('dsms-archive-version-select'); const docId = docSelect?.value; if (!docId) { versionSelect.innerHTML = ''; versionSelect.disabled = true; return; } versionSelect.disabled = false; versionSelect.innerHTML = ''; try { const res = await fetch(`/api/consent/admin/documents/${docId}/versions`); if (!res.ok) throw new Error('Fehler'); const data = await res.json(); const versions = data.versions || []; if (versions.length === 0) { versionSelect.innerHTML = ''; return; } versionSelect.innerHTML = '' + versions.map(v => ``).join(''); } catch(e) { versionSelect.innerHTML = ''; } } // Attach event listener for doc select change document.getElementById('dsms-archive-doc-select')?.addEventListener('change', loadDsmsVersionSelect); function showArchiveForm() { document.getElementById('dsms-archive-form').style.display = 'block'; loadDsmsDocumentSelect(); } function hideArchiveForm() { document.getElementById('dsms-archive-form').style.display = 'none'; } async function archiveDocumentToDsms() { const docSelect = document.getElementById('dsms-archive-doc-select'); const versionSelect = document.getElementById('dsms-archive-version-select'); const selectedOption = versionSelect.options[versionSelect.selectedIndex]; if (!docSelect.value || !versionSelect.value) { alert('Bitte Dokument und Version auswählen'); return; } const content = decodeURIComponent(selectedOption.dataset.content || ''); const version = selectedOption.dataset.version; const docId = docSelect.value; if (!content) { alert('Die ausgewählte Version hat keinen Inhalt'); return; } try { const token = localStorage.getItem('bp_token') || ''; const params = new URLSearchParams({ document_id: docId, version: version, content: content, language: 'de' }); const res = await fetch(`${DSMS_GATEWAY_URL}/api/v1/legal-documents/archive?${params}`, { method: 'POST', headers: { 'Authorization': `Bearer ${token}` } }); if (!res.ok) { const err = await res.json(); throw new Error(err.detail || 'Archivierung fehlgeschlagen'); } const result = await res.json(); alert(`Dokument erfolgreich archiviert!\\n\\nCID: ${result.cid}\\nChecksum: ${result.checksum}`); hideArchiveForm(); loadDsmsArchives(); } catch(e) { alert('Fehler: ' + e.message); } } async function verifyDsmsDocument() { const cidInput = document.getElementById('dsms-verify-cid'); const resultDiv = document.getElementById('dsms-verify-result'); const cid = cidInput?.value?.trim(); if (!cid) { alert('Bitte CID eingeben'); return; } await verifyDsmsDocumentByCid(cid); } async function verifyDsmsDocumentByCid(cid) { const resultDiv = document.getElementById('dsms-verify-result'); resultDiv.style.display = 'block'; resultDiv.innerHTML = '
Verifiziere...
'; // Switch to verify tab switchDsmsTab('verify'); document.getElementById('dsms-verify-cid').value = cid; try { const res = await fetch(`${DSMS_GATEWAY_URL}/api/v1/verify/${cid}`); const data = await res.json(); if (data.exists && data.integrity_valid) { resultDiv.innerHTML = `

✓ Dokument verifiziert

CID: ${cid}
Integrität: Gültig
Typ: ${data.metadata?.document_type || '-'}
Dokument-ID: ${data.metadata?.document_id || '-'}
Version: ${data.metadata?.version || '-'}
Erstellt: ${data.metadata?.created_at ? new Date(data.metadata.created_at).toLocaleString('de-DE') : '-'}
Checksum: ${data.stored_checksum || '-'}
`; } else if (data.exists && !data.integrity_valid) { resultDiv.innerHTML = `

⚠ Integritätsfehler

Das Dokument existiert, aber die Prüfsumme stimmt nicht überein.

Gespeichert: ${data.stored_checksum}

Berechnet: ${data.calculated_checksum}

`; } else { resultDiv.innerHTML = `

✗ Nicht gefunden

Kein Dokument mit diesem CID gefunden.

${data.error ? `

${data.error}

` : ''}
`; } } catch(e) { resultDiv.innerHTML = `

Fehler

${e.message}

`; } } async function loadDsmsNodeInfo() { const container = document.getElementById('dsms-node-info'); container.innerHTML = '
Lade Node-Info...
'; try { const res = await fetch(`${DSMS_GATEWAY_URL}/api/v1/node/info`); if (!res.ok) throw new Error('Nicht erreichbar'); const info = await res.json(); container.innerHTML = `
Node ID: ${info.node_id || '-'}
Agent: ${info.agent_version || '-'}
Repo-Größe: ${info.repo_size ? formatBytes(info.repo_size) : '-'}
Max. Speicher: ${info.storage_max ? formatBytes(info.storage_max) : '-'}
Objekte: ${info.num_objects ?? '-'}
Adressen:
`; } catch(e) { container.innerHTML = `
DSMS nicht erreichbar: ${e.message}
`; } } function formatBytes(bytes) { if (bytes === 0) return '0 B'; const k = 1024; const sizes = ['B', 'KB', 'MB', 'GB', 'TB']; const i = Math.floor(Math.log(bytes) / Math.log(k)); return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]; } function copyToClipboard(text) { navigator.clipboard.writeText(text).then(() => { // Optional: Show toast }).catch(err => { console.error('Copy failed:', err); }); } // ========================================== // DSMS WEBUI FUNCTIONS // ========================================== function openDsmsWebUI() { document.getElementById('dsms-webui-modal').style.display = 'flex'; loadDsmsWebUIData(); } function closeDsmsWebUI() { document.getElementById('dsms-webui-modal').style.display = 'none'; } function switchDsmsWebUISection(section) { // Update nav buttons document.querySelectorAll('.dsms-webui-nav').forEach(btn => { btn.classList.toggle('active', btn.dataset.section === section); }); // Update sections document.querySelectorAll('.dsms-webui-section').forEach(sec => { sec.classList.remove('active'); sec.style.display = 'none'; }); const activeSection = document.getElementById('dsms-webui-' + section); if (activeSection) { activeSection.classList.add('active'); activeSection.style.display = 'block'; } // Load section-specific data if (section === 'peers') loadDsmsPeers(); } async function loadDsmsWebUIData() { try { // Load node info const infoRes = await fetch(DSMS_GATEWAY_URL + '/api/v1/node/info'); const info = await infoRes.json(); document.getElementById('webui-status').innerHTML = 'Online'; document.getElementById('webui-node-id').textContent = info.node_id || '--'; document.getElementById('webui-protocol').textContent = info.protocol_version || '--'; document.getElementById('webui-agent').textContent = info.agent_version || '--'; document.getElementById('webui-repo-size').textContent = formatBytes(info.repo_size || 0); document.getElementById('webui-storage-info').textContent = 'Max: ' + formatBytes(info.storage_max || 0); document.getElementById('webui-num-objects').textContent = (info.num_objects || 0).toLocaleString(); // Addresses const addresses = info.addresses || []; document.getElementById('webui-addresses').innerHTML = addresses.length > 0 ? addresses.map(a => '
' + a + '
').join('') : 'Keine Adressen verfügbar'; // Load pinned count const token = localStorage.getItem('bp_token') || ''; const docsRes = await fetch(DSMS_GATEWAY_URL + '/api/v1/documents', { headers: { 'Authorization': 'Bearer ' + token } }); if (docsRes.ok) { const docs = await docsRes.json(); document.getElementById('webui-pinned-count').textContent = docs.total || 0; } } catch (e) { console.error('Failed to load WebUI data:', e); document.getElementById('webui-status').innerHTML = 'Offline'; } } async function loadDsmsPeers() { const container = document.getElementById('webui-peers-list'); try { // IPFS peers endpoint via proxy would need direct IPFS API access // For now, show info that private network has no peers container.innerHTML = `
🔒

Privates Netzwerk

DSMS läuft als isolierter Node. Keine externen Peers verbunden.

`; } catch (e) { container.innerHTML = '
Fehler beim Laden der Peers
'; } } // File upload handlers function handleDsmsDragOver(e) { e.preventDefault(); e.stopPropagation(); document.getElementById('dsms-upload-zone').classList.add('dragover'); } function handleDsmsDragLeave(e) { e.preventDefault(); e.stopPropagation(); document.getElementById('dsms-upload-zone').classList.remove('dragover'); } async function handleDsmsFileDrop(e) { e.preventDefault(); e.stopPropagation(); document.getElementById('dsms-upload-zone').classList.remove('dragover'); const files = e.dataTransfer.files; if (files.length > 0) { await uploadDsmsFiles(files); } } async function handleDsmsFileSelect(e) { const files = e.target.files; if (files.length > 0) { await uploadDsmsFiles(files); } } async function uploadDsmsFiles(files) { const token = localStorage.getItem('bp_token') || ''; const progressDiv = document.getElementById('dsms-upload-progress'); const statusDiv = document.getElementById('dsms-upload-status'); const barDiv = document.getElementById('dsms-upload-bar'); const resultsDiv = document.getElementById('dsms-upload-results'); progressDiv.style.display = 'block'; resultsDiv.innerHTML = ''; const results = []; for (let i = 0; i < files.length; i++) { const file = files[i]; statusDiv.textContent = 'Lade hoch: ' + file.name + ' (' + (i+1) + '/' + files.length + ')'; barDiv.style.width = ((i / files.length) * 100) + '%'; try { const formData = new FormData(); formData.append('file', file); formData.append('document_type', 'legal_document'); const res = await fetch(DSMS_GATEWAY_URL + '/api/v1/documents', { method: 'POST', headers: { 'Authorization': 'Bearer ' + token }, body: formData }); if (res.ok) { const data = await res.json(); results.push({ file: file.name, cid: data.cid, success: true }); } else { results.push({ file: file.name, error: 'Upload fehlgeschlagen', success: false }); } } catch (e) { results.push({ file: file.name, error: e.message, success: false }); } } barDiv.style.width = '100%'; statusDiv.textContent = 'Upload abgeschlossen!'; // Show results resultsDiv.innerHTML = '

Ergebnisse

' + results.map(r => `
${r.file}
${r.success ? '
CID: ' + r.cid + '
' : '
' + r.error + '
' }
${r.success ? '' : ''}
`).join(''); setTimeout(() => { progressDiv.style.display = 'none'; barDiv.style.width = '0%'; }, 2000); } async function exploreDsmsCid() { const cid = document.getElementById('webui-explore-cid').value.trim(); if (!cid) return; const resultDiv = document.getElementById('dsms-explore-result'); const contentDiv = document.getElementById('dsms-explore-content'); resultDiv.style.display = 'block'; contentDiv.innerHTML = '
Lade...
'; try { const res = await fetch(DSMS_GATEWAY_URL + '/api/v1/verify/' + cid); const data = await res.json(); if (data.exists) { contentDiv.innerHTML = `
${data.integrity_valid ? '✓' : '✗'} ${data.integrity_valid ? 'Dokument verifiziert' : 'Integritätsfehler'}
CID ${cid}
Typ ${data.metadata?.document_type || '--'}
Erstellt ${data.metadata?.created_at ? new Date(data.metadata.created_at).toLocaleString('de-DE') : '--'}
Checksum ${data.stored_checksum || '--'}
Im Gateway öffnen
`; } else { contentDiv.innerHTML = `
Nicht gefunden
CID existiert nicht im DSMS: ${cid}
`; } } catch (e) { contentDiv.innerHTML = `
Fehler
${e.message}
`; } } async function runDsmsGarbageCollection() { if (!confirm('Garbage Collection ausführen? Dies entfernt nicht gepinnte Objekte.')) return; try { // Note: Direct GC requires IPFS API access - show info for now alert('Garbage Collection wird im Hintergrund ausgeführt. Dies kann einige Minuten dauern.'); } catch (e) { alert('Fehler: ' + e.message); } } // Close modal on escape key document.addEventListener('keydown', (e) => { if (e.key === 'Escape') { closeDsmsWebUI(); } }); // Close modal on backdrop click document.getElementById('dsms-webui-modal')?.addEventListener('click', (e) => { if (e.target.id === 'dsms-webui-modal') { closeDsmsWebUI(); } }); """