Embeds a DB-IP Lite country MMDB (~5MB) in the binary via go:embed, keeping the single-binary deployment story clean. Country codes are stored alongside login attempts and sessions, shown in the dashboard (Top IPs, Top Countries card, Recent/Active Sessions, session detail). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
113 lines
3.7 KiB
HTML
113 lines
3.7 KiB
HTML
{{define "content"}}
|
|
<section id="stats-section" hx-get="/fragments/stats" hx-trigger="every 30s" hx-swap="innerHTML">
|
|
{{template "stats" .Stats}}
|
|
</section>
|
|
|
|
<section>
|
|
<h3>Top Credentials & IPs</h3>
|
|
<div class="top-grid">
|
|
<article>
|
|
<header>Top Usernames</header>
|
|
<table>
|
|
<thead>
|
|
<tr><th>Username</th><th>Attempts</th></tr>
|
|
</thead>
|
|
<tbody>
|
|
{{range .TopUsernames}}
|
|
<tr><td>{{.Value}}</td><td>{{.Count}}</td></tr>
|
|
{{else}}
|
|
<tr><td colspan="2">No data</td></tr>
|
|
{{end}}
|
|
</tbody>
|
|
</table>
|
|
</article>
|
|
<article>
|
|
<header>Top Passwords</header>
|
|
<table>
|
|
<thead>
|
|
<tr><th>Password</th><th>Attempts</th></tr>
|
|
</thead>
|
|
<tbody>
|
|
{{range .TopPasswords}}
|
|
<tr><td>{{.Value}}</td><td>{{.Count}}</td></tr>
|
|
{{else}}
|
|
<tr><td colspan="2">No data</td></tr>
|
|
{{end}}
|
|
</tbody>
|
|
</table>
|
|
</article>
|
|
<article>
|
|
<header>Top IPs</header>
|
|
<table>
|
|
<thead>
|
|
<tr><th>IP</th><th>Country</th><th>Attempts</th></tr>
|
|
</thead>
|
|
<tbody>
|
|
{{range .TopIPs}}
|
|
<tr><td>{{.Value}}</td><td>{{.Country}}</td><td>{{.Count}}</td></tr>
|
|
{{else}}
|
|
<tr><td colspan="3">No data</td></tr>
|
|
{{end}}
|
|
</tbody>
|
|
</table>
|
|
</article>
|
|
<article>
|
|
<header>Top Countries</header>
|
|
<table>
|
|
<thead>
|
|
<tr><th>Country</th><th>Attempts</th></tr>
|
|
</thead>
|
|
<tbody>
|
|
{{range .TopCountries}}
|
|
<tr><td>{{.Value}}</td><td>{{.Count}}</td></tr>
|
|
{{else}}
|
|
<tr><td colspan="2">No data</td></tr>
|
|
{{end}}
|
|
</tbody>
|
|
</table>
|
|
</article>
|
|
</div>
|
|
</section>
|
|
|
|
<section>
|
|
<h3>Active Sessions</h3>
|
|
<div id="active-sessions" hx-get="/fragments/active-sessions" hx-trigger="every 10s" hx-swap="innerHTML">
|
|
{{template "active_sessions" .ActiveSessions}}
|
|
</div>
|
|
</section>
|
|
|
|
<section>
|
|
<h3>Recent Sessions</h3>
|
|
<table>
|
|
<thead>
|
|
<tr>
|
|
<th>ID</th>
|
|
<th>IP</th>
|
|
<th>Country</th>
|
|
<th>Username</th>
|
|
<th>Shell</th>
|
|
<th>Score</th>
|
|
<th>Connected</th>
|
|
<th>Disconnected</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{{range .RecentSessions}}
|
|
<tr>
|
|
<td><a href="/sessions/{{.ID}}"><code>{{truncateID .ID}}</code></a></td>
|
|
<td>{{.IP}}</td>
|
|
<td>{{.Country}}</td>
|
|
<td>{{.Username}}</td>
|
|
<td>{{.ShellName}}</td>
|
|
<td>{{if .HumanScore}}{{if gt (derefFloat .HumanScore) 0.6}}<mark>{{formatScore .HumanScore}}</mark>{{else}}{{formatScore .HumanScore}}{{end}}{{else}}-{{end}}</td>
|
|
<td>{{formatTime .ConnectedAt}}</td>
|
|
<td>{{if .DisconnectedAt}}{{formatTime (derefTime .DisconnectedAt)}}{{else}}<mark>active</mark>{{end}}</td>
|
|
</tr>
|
|
{{else}}
|
|
<tr><td colspan="8">No sessions</td></tr>
|
|
{{end}}
|
|
</tbody>
|
|
</table>
|
|
</section>
|
|
{{end}}
|