update front end
This commit is contained in:
@@ -0,0 +1,34 @@
|
||||
{{ define "title" }}Honeypot Dashboard{{ end }}
|
||||
{{ define "content" }}
|
||||
<div class="space-y-8">
|
||||
<h1 class="text-2xl font-semibold text-white">Honeypot Overview</h1>
|
||||
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4">
|
||||
<div class="bg-gray-800 border border-gray-700 rounded-lg p-4">
|
||||
<div class="text-sm text-gray-400">Total IPs</div>
|
||||
<div class="text-3xl font-bold text-primary-400">{{ index .Stats "total_ips" }}</div>
|
||||
</div>
|
||||
<div class="bg-gray-800 border border-gray-700 rounded-lg p-4">
|
||||
<div class="text-sm text-gray-400">Blacklisted</div>
|
||||
<div class="text-3xl font-bold text-primary-400">{{ index .Stats "blacklisted_ips" }}</div>
|
||||
</div>
|
||||
<div class="bg-gray-800 border border-gray-700 rounded-lg p-4">
|
||||
<div class="text-sm text-gray-400">Connections</div>
|
||||
<div class="text-3xl font-bold text-primary-400">{{ index .Stats "total_connections" }}</div>
|
||||
</div>
|
||||
<div class="bg-gray-800 border border-gray-700 rounded-lg p-4">
|
||||
<div class="text-sm text-gray-400">Auth Attempts</div>
|
||||
<div class="text-3xl font-bold text-primary-400">{{ index .Stats "total_auth_attempts" }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<h2 class="text-xl font-semibold text-white mb-3">Quick Actions</h2>
|
||||
<div class="flex flex-wrap gap-3">
|
||||
<a class="px-4 py-2 bg-primary-600 hover:bg-primary-500 rounded text-white" href="/logs">View Recent Logs</a>
|
||||
<a class="px-4 py-2 bg-gray-700 hover:bg-gray-600 rounded text-white" href="/threats">View Top Threats</a>
|
||||
<a class="px-4 py-2 bg-gray-700 hover:bg-gray-600 rounded text-white" href="/blacklist">Manage Blacklist</a>
|
||||
<a class="px-4 py-2 bg-gray-700 hover:bg-gray-600 rounded text-white" href="/stats">Detailed Statistics</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{ end }}
|
||||
{{ define "index" }}{{ template "layout.html" . }}{{ end }}
|
||||
@@ -0,0 +1,46 @@
|
||||
<!doctype html>
|
||||
<html class="dark h-full">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>{{ block "title" . }}Honeypot Dashboard{{ end }}</title>
|
||||
<script src="https://cdn.tailwindcss.com"></script>
|
||||
<script>
|
||||
tailwind.config = {
|
||||
darkMode: 'class',
|
||||
theme: {
|
||||
extend: {
|
||||
colors: {
|
||||
primary: {
|
||||
50: '#eff6ff', 100: '#dbeafe', 200: '#bfdbfe', 300: '#93c5fd', 400: '#60a5fa',
|
||||
500: '#3b82f6', 600: '#2563eb', 700: '#1d4ed8', 800: '#1e40af', 900: '#1e3a8a'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body class="h-full bg-gray-900 text-gray-100">
|
||||
<div class="min-h-full">
|
||||
<nav class="bg-gray-800 border-b border-gray-700">
|
||||
<div class="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
|
||||
<div class="flex h-16 items-center justify-between">
|
||||
<div class="flex items-center space-x-8">
|
||||
<a href="/" class="text-primary-400 hover:text-primary-300 font-semibold">🍯 Honeypot</a>
|
||||
<a href="/logs" class="text-gray-300 hover:text-white">Recent Logs</a>
|
||||
<a href="/threats" class="text-gray-300 hover:text-white">Top Threats</a>
|
||||
<a href="/blacklist" class="text-gray-300 hover:text-white">Blacklist</a>
|
||||
<a href="/stats" class="text-gray-300 hover:text-white">Statistics</a>
|
||||
</div>
|
||||
<div class="text-xs text-gray-400">{{ .Now }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
<main class="mx-auto max-w-7xl py-6 sm:px-6 lg:px-8">
|
||||
{{ block "content" . }}{{ end }}
|
||||
</main>
|
||||
<footer class="border-t border-gray-800 py-6 text-center text-gray-500 text-sm">Honeypot Dashboard</footer>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,29 @@
|
||||
{{ define "title" }}Recent Logs{{ end }}
|
||||
{{ define "content" }}
|
||||
<h1 class="text-2xl font-semibold text-white mb-4">Recent Logs</h1>
|
||||
<div class="overflow-x-auto">
|
||||
<table class="min-w-full divide-y divide-gray-700">
|
||||
<thead class="bg-gray-800">
|
||||
<tr>
|
||||
<th class="px-4 py-2 text-left text-xs font-medium text-gray-300 uppercase tracking-wider">Time</th>
|
||||
<th class="px-4 py-2 text-left text-xs font-medium text-gray-300 uppercase tracking-wider">Remote</th>
|
||||
<th class="px-4 py-2 text-left text-xs font-medium text-gray-300 uppercase tracking-wider">Service</th>
|
||||
<th class="px-4 py-2 text-left text-xs font-medium text-gray-300 uppercase tracking-wider">Details</th>
|
||||
<th class="px-4 py-2 text-left text-xs font-medium text-gray-300 uppercase tracking-wider">Payload</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="bg-gray-900 divide-y divide-gray-800">
|
||||
{{ range .Rows }}
|
||||
<tr>
|
||||
<td class="px-4 py-2 text-sm text-gray-300">{{ .Timestamp.Format "2006-01-02 15:04:05" }}</td>
|
||||
<td class="px-4 py-2 text-sm text-gray-300">{{ .RemoteAddr }}:{{ .RemotePort }}</td>
|
||||
<td class="px-4 py-2 text-sm text-gray-300">{{ .Service }}</td>
|
||||
<td class="px-4 py-2 text-sm text-gray-300"><pre class="whitespace-pre-wrap max-w-xs overflow-auto">{{ .Details | toJSON }}</pre></td>
|
||||
<td class="px-4 py-2 text-sm text-gray-300"><pre class="whitespace-pre-wrap max-w-xs overflow-auto">{{ .RawPayload }}</pre></td>
|
||||
</tr>
|
||||
{{ end }}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{{ end }}
|
||||
{{ define "logs" }}{{ template "layout.html" . }}{{ end }}
|
||||
+54
-72
@@ -1,78 +1,60 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"html/template"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
"embed"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"html/template"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
var tpl = template.Must(template.New("base").Parse(`
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Honeypot Dashboard</title>
|
||||
<style>
|
||||
body { font-family: Arial, sans-serif; margin: 20px; }
|
||||
nav { background: #f0f0f0; padding: 10px; margin-bottom: 20px; }
|
||||
nav a { margin-right: 15px; text-decoration: none; color: #333; }
|
||||
nav a:hover { color: #007bff; }
|
||||
table { border-collapse: collapse; width: 100%; }
|
||||
th, td { border: 1px solid #ddd; padding: 8px; text-align: left; }
|
||||
th { background-color: #f2f2f2; }
|
||||
.threat-high { background-color: #ffebee; }
|
||||
.threat-medium { background-color: #fff3e0; }
|
||||
.threat-low { background-color: #f3e5f5; }
|
||||
.blacklisted { background-color: #ffcdd2; font-weight: bold; }
|
||||
pre { white-space: pre-wrap; max-width: 300px; overflow: auto; }
|
||||
.stats { display: flex; gap: 20px; margin-bottom: 20px; }
|
||||
.stat-box { background: #f8f9fa; padding: 15px; border-radius: 5px; min-width: 150px; }
|
||||
.stat-number { font-size: 24px; font-weight: bold; color: #007bff; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>🍯 Honeypot Dashboard</h1>
|
||||
<nav>
|
||||
<a href="/">Home</a>
|
||||
<a href="/logs">Recent Logs</a>
|
||||
<a href="/threats">Top Threats</a>
|
||||
<a href="/blacklist">Blacklist</a>
|
||||
<a href="/stats">Statistics</a>
|
||||
</nav>
|
||||
{{ .Body }}
|
||||
</body>
|
||||
</html>
|
||||
`))
|
||||
//go:embed templates/*.html
|
||||
var templatesFS embed.FS
|
||||
|
||||
var templates *template.Template
|
||||
|
||||
func initTemplates() error {
|
||||
funcMap := template.FuncMap{
|
||||
"toJSON": func(v any) string {
|
||||
b, _ := json.Marshal(v)
|
||||
return string(b)
|
||||
},
|
||||
}
|
||||
// Parse layout first, then pages
|
||||
t, err := template.New("layout.html").Funcs(funcMap).ParseFS(templatesFS, "templates/layout.html", "templates/index.html", "templates/logs.html")
|
||||
if err != nil { return err }
|
||||
templates = t
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *App) startWeb() {
|
||||
bind := a.cfg.Web.Bind
|
||||
port := a.cfg.Web.Port
|
||||
addr := fmt.Sprintf("%s:%d", bind, port)
|
||||
mux := http.NewServeMux()
|
||||
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
||||
var sb strings.Builder
|
||||
|
||||
// Get basic stats
|
||||
if a.threatIntel != nil {
|
||||
stats := a.threatIntel.GetStats()
|
||||
sb.WriteString("<div class=\"stats\">")
|
||||
sb.WriteString(fmt.Sprintf("<div class=\"stat-box\"><div class=\"stat-number\">%v</div><div>Total IPs</div></div>", stats["total_ips"]))
|
||||
sb.WriteString(fmt.Sprintf("<div class=\"stat-box\"><div class=\"stat-number\">%v</div><div>Blacklisted</div></div>", stats["blacklisted_ips"]))
|
||||
sb.WriteString(fmt.Sprintf("<div class=\"stat-box\"><div class=\"stat-number\">%v</div><div>Connections</div></div>", stats["total_connections"]))
|
||||
sb.WriteString(fmt.Sprintf("<div class=\"stat-box\"><div class=\"stat-number\">%v</div><div>Auth Attempts</div></div>", stats["total_auth_attempts"]))
|
||||
sb.WriteString("</div>")
|
||||
if templates == nil {
|
||||
if err := initTemplates(); err != nil {
|
||||
log.Printf("template init error: %v", err)
|
||||
}
|
||||
|
||||
sb.WriteString("<h2>Quick Actions</h2>")
|
||||
sb.WriteString("<p><a href=\"/logs\">📋 View Recent Logs</a></p>")
|
||||
sb.WriteString("<p><a href=\"/threats\">⚠️ View Top Threats</a></p>")
|
||||
sb.WriteString("<p><a href=\"/blacklist\">🚫 Manage Blacklist</a></p>")
|
||||
sb.WriteString("<p><a href=\"/stats\">📊 Detailed Statistics</a></p>")
|
||||
tpl.Execute(w, map[string]interface{}{"Body": template.HTML(sb.String())})
|
||||
}
|
||||
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
||||
stats := map[string]any{}
|
||||
if a.threatIntel != nil {
|
||||
stats = a.threatIntel.GetStats()
|
||||
}
|
||||
data := map[string]any{
|
||||
"Now": time.Now().Format("2006-01-02 15:04:05 MST"),
|
||||
"Stats": stats,
|
||||
}
|
||||
if templates != nil {
|
||||
_ = templates.ExecuteTemplate(w, "index", data)
|
||||
return
|
||||
}
|
||||
http.Error(w, "templates not loaded", 500)
|
||||
})
|
||||
mux.HandleFunc("/logs", func(w http.ResponseWriter, r *http.Request) {
|
||||
// display last 200 logs
|
||||
@@ -117,15 +99,15 @@ func (a *App) startWeb() {
|
||||
}
|
||||
}
|
||||
|
||||
// render simple table
|
||||
var sb strings.Builder
|
||||
sb.WriteString("<table border=1 cellpadding=4><tr><th>Time</th><th>Remote</th><th>Service</th><th>Details</th><th>Payload</th></tr>")
|
||||
for _, r := range rows {
|
||||
detB, _ := json.Marshal(r.Details)
|
||||
sb.WriteString(fmt.Sprintf("<tr><td>%s</td><td>%s:%s</td><td>%s</td><td>%s</td><td><pre>%s</pre></td></tr>", r.Timestamp.Format("2006-01-02 15:04:05"), r.RemoteAddr, r.RemotePort, r.Service, template.HTMLEscapeString(string(detB)), template.HTMLEscapeString(r.RawPayload)))
|
||||
data := map[string]any{
|
||||
"Now": time.Now().Format("2006-01-02 15:04:05 MST"),
|
||||
"Rows": rows,
|
||||
}
|
||||
sb.WriteString("</table>")
|
||||
tpl.Execute(w, map[string]interface{}{"Body": template.HTML(sb.String())})
|
||||
if templates != nil {
|
||||
_ = templates.ExecuteTemplate(w, "logs", data)
|
||||
return
|
||||
}
|
||||
http.Error(w, "templates not loaded", 500)
|
||||
})
|
||||
|
||||
srv := &http.Server{Addr: addr, Handler: mux}
|
||||
|
||||
Reference in New Issue
Block a user