Initial commit: IT Nexus Web-App
This commit is contained in:
232
nexus-scanner/internal/web/templates/snmp.html
Normal file
232
nexus-scanner/internal/web/templates/snmp.html
Normal file
@@ -0,0 +1,232 @@
|
||||
{{define "content"}}
|
||||
<div class="page-header">
|
||||
<div>
|
||||
<h1 class="page-title">SNMP</h1>
|
||||
<p class="page-sub">Geräte-Informationen via SNMP — Drucker, Switches, USVs.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{if .AddErr}}<div class="error-msg" style="margin-bottom:16px;">{{.AddErr}}</div>{{end}}
|
||||
|
||||
{{if .Targets}}
|
||||
<div style="display:grid;grid-template-columns:repeat(auto-fill,minmax(340px,1fr));gap:16px;margin-bottom:24px;">
|
||||
{{range .Targets}}
|
||||
<div class="card" style="overflow:visible;">
|
||||
<!-- Card Header -->
|
||||
<div style="padding:16px 20px;border-bottom:1px solid var(--border);display:flex;align-items:center;justify-content:space-between;">
|
||||
<div style="display:flex;align-items:center;gap:10px;">
|
||||
<div style="width:32px;height:32px;border-radius:var(--r-sm);background:var(--teal-50);color:var(--teal);display:flex;align-items:center;justify-content:center;flex-shrink:0;">
|
||||
{{if eq .Target.Type "printer"}}
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" style="width:16px;height:16px;"><polyline points="6 9 6 2 18 2 18 9"/><path d="M6 18H4a2 2 0 0 1-2-2v-5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v5a2 2 0 0 1-2 2h-2"/><rect x="6" y="14" width="12" height="8"/></svg>
|
||||
{{else if eq .Target.Type "switch"}}
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" style="width:16px;height:16px;"><rect x="2" y="7" width="20" height="10" rx="2"/><path d="M6 12h.01M10 12h.01M14 12h.01M18 12h.01"/></svg>
|
||||
{{else}}
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" style="width:16px;height:16px;"><rect x="4" y="4" width="16" height="16" rx="2"/><path d="M9 9h6M9 12h6M9 15h4"/></svg>
|
||||
{{end}}
|
||||
</div>
|
||||
<div>
|
||||
<div style="font-weight:600;font-size:14px;">{{.Target.Name}}</div>
|
||||
<div style="font-size:12px;color:var(--text-3);font-family:monospace;">{{.Target.IP}}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div style="display:flex;align-items:center;gap:8px;">
|
||||
{{if .HasData}}
|
||||
<span class="badge badge-green" style="font-size:11px;">Online</span>
|
||||
{{else}}
|
||||
<span class="badge badge-gray" style="font-size:11px;">Kein Datum</span>
|
||||
{{end}}
|
||||
<form method="POST" action="/snmp/targets/{{.Target.ID}}/delete" style="margin:0;">
|
||||
<button type="submit" class="icon-btn" onclick="return confirm('{{.Target.Name}} löschen?')" style="color:var(--text-3);" title="Löschen">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"><polyline points="3 6 5 6 21 6"/><path d="M19 6l-1 14H6L5 6"/><path d="M10 11v6M14 11v6"/><path d="M9 6V4h6v2"/></svg>
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Card Body -->
|
||||
<div style="padding:16px 20px;">
|
||||
{{if .HasData}}
|
||||
{{if .SysName}}<div style="margin-bottom:8px;"><span style="font-size:11px;color:var(--text-3);font-weight:600;text-transform:uppercase;">Name</span><div style="font-size:13px;color:var(--text-1);margin-top:2px;">{{.SysName}}</div></div>{{end}}
|
||||
{{if .SysDescr}}<div style="margin-bottom:8px;"><span style="font-size:11px;color:var(--text-3);font-weight:600;text-transform:uppercase;">Beschreibung</span><div style="font-size:12px;color:var(--text-2);margin-top:2px;">{{.SysDescr}}</div></div>{{end}}
|
||||
{{if .SysUpTime}}<div style="margin-bottom:8px;"><span style="font-size:11px;color:var(--text-3);font-weight:600;text-transform:uppercase;">Uptime</span><div style="font-size:13px;color:var(--text-2);margin-top:2px;">{{.SysUpTime}}</div></div>{{end}}
|
||||
{{if .SysLocation}}<div style="margin-bottom:8px;"><span style="font-size:11px;color:var(--text-3);font-weight:600;text-transform:uppercase;">Standort</span><div style="font-size:13px;color:var(--text-2);margin-top:2px;">{{.SysLocation}}</div></div>{{end}}
|
||||
|
||||
{{if eq .Target.Type "switch"}}
|
||||
<!-- Switch Stats -->
|
||||
<div style="margin-top:10px;padding-top:10px;border-top:1px solid var(--border);">
|
||||
<div style="display:grid;grid-template-columns:1fr 1fr;gap:10px;">
|
||||
{{if .PortsTotal}}
|
||||
<div style="background:var(--surface-2);border-radius:var(--r-sm);padding:10px 12px;">
|
||||
<div style="font-size:11px;color:var(--text-3);font-weight:600;text-transform:uppercase;margin-bottom:4px;">Ports</div>
|
||||
<div style="font-size:20px;font-weight:600;color:var(--text-1);">{{.PortsUp}}<span style="font-size:13px;color:var(--text-3);font-weight:400;"> / {{.PortsTotal}}</span></div>
|
||||
<div style="font-size:11px;color:var(--success);margin-top:2px;">● aktiv</div>
|
||||
</div>
|
||||
{{end}}
|
||||
{{if .TrafficIn}}
|
||||
<div style="background:var(--surface-2);border-radius:var(--r-sm);padding:10px 12px;">
|
||||
<div style="font-size:11px;color:var(--text-3);font-weight:600;text-transform:uppercase;margin-bottom:4px;">Traffic</div>
|
||||
<div style="font-size:12px;color:var(--text-2);margin-top:2px;">↓ {{.TrafficIn}}</div>
|
||||
<div style="font-size:12px;color:var(--text-2);margin-top:2px;">↑ {{.TrafficOut}}</div>
|
||||
</div>
|
||||
{{end}}
|
||||
{{if .MemTotal}}
|
||||
<div style="background:var(--surface-2);border-radius:var(--r-sm);padding:10px 12px;">
|
||||
<div style="font-size:11px;color:var(--text-3);font-weight:600;text-transform:uppercase;margin-bottom:4px;">RAM</div>
|
||||
<div style="font-size:13px;color:var(--text-1);">{{.MemUsed}} / {{.MemTotal}}</div>
|
||||
<div style="background:var(--surface-3);border-radius:3px;height:5px;margin-top:5px;overflow:hidden;">
|
||||
<div style="height:100%;border-radius:3px;width:{{.MemPct}}%;background:{{if gt .MemPct 85}}var(--danger){{else if gt .MemPct 70}}var(--warning){{else}}var(--teal){{end}};"></div>
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
{{if .CpuLoad}}
|
||||
<div style="background:var(--surface-2);border-radius:var(--r-sm);padding:10px 12px;">
|
||||
<div style="font-size:11px;color:var(--text-3);font-weight:600;text-transform:uppercase;margin-bottom:4px;">CPU</div>
|
||||
<div style="font-size:20px;font-weight:600;color:var(--text-1);">{{.CpuLoad}}<span style="font-size:13px;font-weight:400;color:var(--text-3);">%</span></div>
|
||||
<div style="background:var(--surface-3);border-radius:3px;height:5px;margin-top:5px;overflow:hidden;">
|
||||
<div style="height:100%;border-radius:3px;width:{{.CpuLoad}}%;background:var(--teal);"></div>
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
|
||||
{{if eq .Target.Type "printer"}}
|
||||
<div style="margin-top:10px;padding-top:10px;border-top:1px solid var(--border);">
|
||||
{{if .PrinterStatus}}
|
||||
<div style="display:inline-flex;align-items:center;gap:5px;margin-bottom:12px;background:{{if eq .PrinterStatus "Druckt"}}rgba(0,132,135,.1){{else}}var(--surface-2){{end}};padding:4px 10px;border-radius:20px;">
|
||||
<span style="width:6px;height:6px;border-radius:50%;background:{{if eq .PrinterStatus "Druckt"}}var(--teal){{else}}var(--success){{end}};display:inline-block;"></span>
|
||||
<span style="font-size:12px;font-weight:500;color:var(--text-2);">{{.PrinterStatus}}</span>
|
||||
</div>
|
||||
{{end}}
|
||||
{{if .TonerLevel}}
|
||||
<div style="margin-bottom:4px;display:flex;justify-content:space-between;align-items:baseline;">
|
||||
<span style="font-size:12px;font-weight:600;color:var(--text-2);">Toner</span>
|
||||
<span style="font-size:13px;font-weight:600;color:{{if lt .TonerPct 20}}#ff3b30{{else if lt .TonerPct 40}}var(--warning){{else}}var(--text-1){{end}};">{{.TonerPct}}%</span>
|
||||
</div>
|
||||
<div style="background:var(--surface-3);border-radius:6px;height:10px;overflow:hidden;">
|
||||
<div style="height:100%;border-radius:6px;width:{{.TonerPct}}%;background:{{if lt .TonerPct 20}}#ff3b30{{else if lt .TonerPct 40}}#ff9500{{else}}var(--success){{end}};transition:width .4s;"></div>
|
||||
</div>
|
||||
{{if lt .TonerPct 20}}<div style="font-size:11px;color:#ff3b30;margin-top:5px;font-weight:500;">⚠ Toner bald leer — bitte nachbestellen</div>{{end}}
|
||||
{{end}}
|
||||
{{if .PageCount}}
|
||||
<div style="display:flex;align-items:center;gap:6px;margin-top:12px;padding-top:10px;border-top:1px solid var(--border);">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" style="width:14px;height:14px;color:var(--text-3);flex-shrink:0;"><polyline points="6 9 6 2 18 2 18 9"/><path d="M6 18H4a2 2 0 0 1-2-2v-5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v5a2 2 0 0 1-2 2h-2"/><rect x="6" y="14" width="12" height="8"/></svg>
|
||||
<span style="font-size:12px;color:var(--text-3);">Gesamtseiten: <strong style="color:var(--text-2);">{{.PageCount}}</strong></span>
|
||||
</div>
|
||||
{{end}}
|
||||
</div>
|
||||
{{end}}
|
||||
{{else}}
|
||||
<div style="color:var(--text-3);font-size:13px;text-align:center;padding:16px 0;">Noch kein Scan — läuft alle {{.Interval}}.</div>
|
||||
{{end}}
|
||||
</div>
|
||||
|
||||
<!-- Card Footer -->
|
||||
<div style="padding:8px 20px;border-top:1px solid var(--border);display:flex;justify-content:space-between;align-items:center;">
|
||||
<span style="font-size:11px;color:var(--text-3);">Community: <code style="background:var(--surface-3);padding:1px 5px;border-radius:3px;">{{.Target.Community}}</code> · {{.Target.Version}}</span>
|
||||
<div style="display:flex;gap:8px;align-items:center;">
|
||||
{{if .HasData}}
|
||||
<button onclick="openDetail('modal-{{.Target.ID}}')" class="btn" style="font-size:11px;padding:4px 10px;height:auto;">
|
||||
Details
|
||||
</button>
|
||||
{{end}}
|
||||
<span class="badge badge-gray" style="font-size:11px;text-transform:capitalize;">{{.Target.Type}}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Detail Modal -->
|
||||
{{if .HasData}}
|
||||
<div id="modal-{{.Target.ID}}" style="display:none;position:fixed;inset:0;z-index:1000;background:rgba(0,0,0,.45);backdrop-filter:blur(2px);" onclick="if(event.target===this)closeDetail('modal-{{.Target.ID}}')">
|
||||
<div style="position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);width:min(560px,95vw);max-height:85vh;overflow-y:auto;background:var(--surface);border:1px solid var(--border);border-radius:var(--r-lg);box-shadow:0 8px 40px rgba(0,0,0,.25);">
|
||||
<div style="padding:18px 22px;border-bottom:1px solid var(--border);display:flex;align-items:center;justify-content:space-between;position:sticky;top:0;background:var(--surface);z-index:1;">
|
||||
<div>
|
||||
<div style="font-weight:600;font-size:15px;">{{.Target.Name}}</div>
|
||||
<div style="font-size:12px;color:var(--text-3);font-family:monospace;margin-top:2px;">{{.Target.IP}} · {{.Target.Type}}</div>
|
||||
</div>
|
||||
<button onclick="closeDetail('modal-{{.Target.ID}}')" class="icon-btn" style="color:var(--text-3);">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="width:18px;height:18px;"><path d="M18 6 6 18M6 6l12 12"/></svg>
|
||||
</button>
|
||||
</div>
|
||||
<div style="padding:20px 22px;">
|
||||
<table style="width:100%;border-collapse:collapse;font-size:13px;">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="text-align:left;font-size:11px;font-weight:600;color:var(--text-3);text-transform:uppercase;letter-spacing:.06em;padding:0 0 10px;border-bottom:1px solid var(--border);width:40%;">Feld</th>
|
||||
<th style="text-align:left;font-size:11px;font-weight:600;color:var(--text-3);text-transform:uppercase;letter-spacing:.06em;padding:0 0 10px 16px;border-bottom:1px solid var(--border);">Wert</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{range $k, $v := .AllValues}}
|
||||
<tr>
|
||||
<td style="padding:9px 0;border-bottom:1px solid var(--border);color:var(--text-3);font-size:12px;vertical-align:top;font-weight:500;">{{$k}}</td>
|
||||
<td style="padding:9px 0 9px 16px;border-bottom:1px solid var(--border);color:var(--text-1);font-family:{{if or (eq $k "sysDescr") (eq $k "sysName") (eq $k "sysLocation") (eq $k "sysContact")}}inherit{{else}}monospace{{end}};font-size:13px;word-break:break-all;">{{$v}}</td>
|
||||
</tr>
|
||||
{{end}}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
{{end}}
|
||||
</div>
|
||||
{{else}}
|
||||
<div class="card" style="margin-bottom:24px;">
|
||||
<div style="text-align:center;padding:48px;color:var(--text-3);">
|
||||
<div style="font-size:32px;margin-bottom:8px;">📡</div>
|
||||
<div style="font-weight:600;margin-bottom:4px;">Noch keine SNMP-Geräte</div>
|
||||
<div style="font-size:13px;">Füge unten dein erstes Gerät hinzu.</div>
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
|
||||
<!-- Add Target Form -->
|
||||
<div class="card">
|
||||
<div style="padding:16px 20px;border-bottom:1px solid var(--border);">
|
||||
<div style="font-weight:600;font-size:14px;">SNMP-Gerät hinzufügen</div>
|
||||
<div style="font-size:12px;color:var(--text-3);margin-top:2px;">Drucker (Kyocera), Switches, USVs — Community string "public" ist Standard.</div>
|
||||
</div>
|
||||
<form method="POST" action="/snmp/targets/add" style="padding:16px 20px;">
|
||||
<div style="display:grid;grid-template-columns:1fr 160px 110px 110px 110px;gap:12px;margin-bottom:14px;">
|
||||
<div>
|
||||
<label class="input-label">Name</label>
|
||||
<input class="input" type="text" name="name" placeholder="z.B. Kyocera EcoSys" required>
|
||||
</div>
|
||||
<div>
|
||||
<label class="input-label">IP-Adresse</label>
|
||||
<input class="input" type="text" name="ip" placeholder="192.168.0.50" required>
|
||||
</div>
|
||||
<div>
|
||||
<label class="input-label">Community</label>
|
||||
<input class="input" type="text" name="community" placeholder="public" value="public">
|
||||
</div>
|
||||
<div>
|
||||
<label class="input-label">Version</label>
|
||||
<select class="select" name="version" style="width:100%;">
|
||||
<option value="v2c">SNMPv2c</option>
|
||||
<option value="v1">SNMPv1</option>
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label class="input-label">Typ</label>
|
||||
<select class="select" name="type" style="width:100%;">
|
||||
<option value="generic">Allgemein</option>
|
||||
<option value="printer">Drucker</option>
|
||||
<option value="switch">Switch</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div style="display:flex;justify-content:flex-end;">
|
||||
<button type="submit" class="btn btn-primary">+ Gerät hinzufügen</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function openDetail(id) { document.getElementById(id).style.display = 'block'; document.body.style.overflow = 'hidden'; }
|
||||
function closeDetail(id) { document.getElementById(id).style.display = 'none'; document.body.style.overflow = ''; }
|
||||
document.addEventListener('keydown', function(e) { if(e.key === 'Escape') { document.querySelectorAll('[id^="modal-"]').forEach(m => { m.style.display='none'; }); document.body.style.overflow=''; } });
|
||||
</script>
|
||||
{{end}}
|
||||
Reference in New Issue
Block a user