Files
oc-sentinel/infra/debug-dashboard/templates/network.html
OfficeCom Codex 0207d84775
All checks were successful
OfficeCom Sentinel Client / validate-client (push) Successful in 23s
OfficeCom Sentinel Client / build-client-windows (push) Successful in 48s
Build interactive access topology map
2026-07-30 01:37:50 +02:00

33 lines
5.3 KiB
HTML

{% extends "base.html" %}
{% block title %}Zugriffswege - OfficeCom Sentinel{% endblock %}
{% block content %}
<section class="hero compact"><span class="eyebrow">Letzte 14 Tage</span><h1>Zugriffswege im Blick</h1><p>Verdichtete fehlgeschlagene Anmeldungen. Jeder Pfad verbindet Quell-IP, Zielgeraet und betroffenes Konto.</p></section>
<section class="metrics compact-metrics"><article><span>Quell-IPs</span><strong>{{ source_count }}</strong></article><article><span>Zielgeraete</span><strong>{{ target_count }}</strong></article><article><span>Beobachtete Versuche</span><strong>{{ total_events }}</strong></article><article><span>Anzeige</span><strong class="timestamp">{{ path_count }} Pfade</strong></article></section>
<section class="panel network-map-panel"><div class="panel-heading"><span class="eyebrow">Interaktive Karte</span><h2>Zugriffs-Topologie <small>Zoomen, verschieben oder einen Knoten bzw. Pfad auswaehlen</small></h2></div><div class="map-toolbar"><label>Filtern<input id="map-filter" type="search" placeholder="IP oder Geraet suchen"></label><button type="button" id="map-fit">Gesamtansicht</button><button type="button" id="map-clear">Auswahl loeschen</button></div><div class="network-map-layout"><div id="network-map" aria-label="Interaktive Netzwerk- und Zugriffskarte"></div><aside id="network-inspector"><span class="eyebrow">Details</span><strong>Kein Element ausgewaehlt</strong><p>Waehle eine Quell-IP, ein Zielgeraet oder eine Verbindung in der Karte.</p></aside></div><p class="chart-note"><span class="legend source"></span>Quell-IP <span class="legend target"></span>Zielgeraet <span class="legend critical"></span>Verbindung mit hoher Aktivitaet</p></section>
<script src="{{ url_for('static', filename='vendor/cytoscape.min.js') }}"></script>
<script>
(() => {
const inspector = document.getElementById('network-inspector');
const filter = document.getElementById('map-filter');
fetch('{{ url_for("network_api") }}').then(response => response.json()).then(graph => {
const cy = cytoscape({ container: document.getElementById('network-map'), elements: graph.elements, minZoom: 0.3, maxZoom: 2.4,
style: [
{ selector: 'node', style: { 'label': 'data(label)', 'font-family': 'Roboto', 'font-size': 11, 'font-weight': 700, 'color': '#132a3d', 'text-valign': 'bottom', 'text-margin-y': 7, 'width': 46, 'height': 46, 'border-width': 3, 'border-color': '#fff' } },
{ selector: 'node[kind = "source"]', style: { 'background-color': '#245a85', 'shape': 'ellipse' } },
{ selector: 'node[kind = "target"]', style: { 'background-color': '#14735b', 'shape': 'round-rectangle' } },
{ selector: 'edge', style: { 'width': 'mapData(count, 1, ' + graph.max_count + ', 2, 9)', 'line-color': '#91b5ca', 'target-arrow-color': '#91b5ca', 'target-arrow-shape': 'triangle', 'curve-style': 'bezier', 'opacity': .72 } },
{ selector: 'edge[count >= 5]', style: { 'line-color': '#a55a0a', 'target-arrow-color': '#a55a0a' } },
{ selector: '.selected', style: { 'border-color': '#b8e36a', 'border-width': 6, 'line-color': '#b8e36a', 'target-arrow-color': '#b8e36a', 'opacity': 1, 'z-index': 20 } },
{ selector: '.muted', style: { 'opacity': .12 } }
], layout: { name: 'cose', animate: false, padding: 38, nodeRepulsion: 8200, idealEdgeLength: 130, gravity: .22 } });
const show = element => { const data = element.data(); if (element.isEdge()) { inspector.innerHTML = '<span class="eyebrow">Zugriffsweg</span><strong>' + data.source.replace('source:', '') + ' <span class="inspector-arrow">to</span> ' + data.machine_name + '</strong><dl><dt>Versuche</dt><dd>' + data.count + '</dd><dt>Konto</dt><dd>' + data.account + '</dd><dt>Vorfall</dt><dd>' + data.event_type + '</dd><dt>Letzter Scan</dt><dd>' + data.last_seen + '</dd></dl>'; } else { const connected = element.connectedEdges(); inspector.innerHTML = '<span class="eyebrow">' + (data.kind === 'source' ? 'Quell-IP' : 'Zielgeraet') + '</span><strong>' + data.label + '</strong><p>' + connected.length + ' beobachtete Zugriffswege im Zeitraum.</p>'; } };
cy.on('tap', 'node, edge', event => { cy.elements().removeClass('selected'); event.target.addClass('selected'); if (event.target.isNode()) { event.target.connectedEdges().addClass('selected'); } show(event.target); });
cy.on('tap', event => { if (event.target === cy) { cy.elements().removeClass('selected'); inspector.innerHTML = '<span class="eyebrow">Details</span><strong>Kein Element ausgewaehlt</strong><p>Waehle eine Quell-IP, ein Zielgeraet oder eine Verbindung in der Karte.</p>'; } });
filter.addEventListener('input', () => { const value = filter.value.trim().toLowerCase(); cy.elements().removeClass('muted'); if (!value) return; cy.elements().addClass('muted'); const hits = cy.nodes().filter(node => node.data('label').toLowerCase().includes(value)); hits.removeClass('muted'); hits.connectedEdges().removeClass('muted'); hits.connectedNodes().removeClass('muted'); });
document.getElementById('map-fit').addEventListener('click', () => cy.fit(cy.elements(), 35));
document.getElementById('map-clear').addEventListener('click', () => { filter.value = ''; cy.elements().removeClass('muted selected'); });
}).catch(() => { document.getElementById('network-map').textContent = 'Die Netzwerkdaten konnten nicht geladen werden.'; });
})();
</script>
{% endblock %}