feat: add charts, world map, and filters to web dashboard

Add Chart.js line/bar charts for attack trends (attempts over time,
hourly pattern), an SVG world map choropleth colored by attack origin
country, and a collapsible filter form (date range, IP, country,
username) that narrows both charts and top-N tables.

New store methods: GetAttemptsOverTime, GetHourlyPattern, GetCountryStats,
and filtered variants of dashboard stats/top-N queries. New JSON API
endpoints at /api/charts/* and an htmx fragment at
/fragments/dashboard-content for filtered table updates.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-16 20:27:15 +01:00
parent 8a631af0d2
commit 7c90c9ed4a
13 changed files with 1480 additions and 41 deletions

View File

@@ -56,6 +56,33 @@ type DashboardStats struct {
ActiveSessions int64
}
// TimeSeriesPoint represents a single data point in a time series.
type TimeSeriesPoint struct {
Timestamp time.Time
Count int64
}
// HourlyCount represents the total attempts for a given hour of day.
type HourlyCount struct {
Hour int // 0-23
Count int64
}
// CountryCount represents the total attempts from a given country.
type CountryCount struct {
Country string
Count int64
}
// DashboardFilter contains optional filters for dashboard queries.
type DashboardFilter struct {
Since *time.Time
Until *time.Time
IP string
Country string
Username string
}
// TopEntry represents a value and its count for top-N queries.
type TopEntry struct {
Value string
@@ -127,6 +154,30 @@ type Store interface {
// sessions left over from a previous unclean shutdown.
CloseActiveSessions(ctx context.Context, disconnectedAt time.Time) (int64, error)
// GetAttemptsOverTime returns daily attempt counts for the last N days.
GetAttemptsOverTime(ctx context.Context, days int, since, until *time.Time) ([]TimeSeriesPoint, error)
// GetHourlyPattern returns total attempts grouped by hour of day (0-23).
GetHourlyPattern(ctx context.Context, since, until *time.Time) ([]HourlyCount, error)
// GetCountryStats returns total attempts per country, ordered by count DESC.
GetCountryStats(ctx context.Context) ([]CountryCount, error)
// GetFilteredDashboardStats returns aggregate counts with optional filters applied.
GetFilteredDashboardStats(ctx context.Context, f DashboardFilter) (*DashboardStats, error)
// GetFilteredTopUsernames returns top usernames with optional filters applied.
GetFilteredTopUsernames(ctx context.Context, limit int, f DashboardFilter) ([]TopEntry, error)
// GetFilteredTopPasswords returns top passwords with optional filters applied.
GetFilteredTopPasswords(ctx context.Context, limit int, f DashboardFilter) ([]TopEntry, error)
// GetFilteredTopIPs returns top IPs with optional filters applied.
GetFilteredTopIPs(ctx context.Context, limit int, f DashboardFilter) ([]TopEntry, error)
// GetFilteredTopCountries returns top countries with optional filters applied.
GetFilteredTopCountries(ctx context.Context, limit int, f DashboardFilter) ([]TopEntry, error)
// Close releases any resources held by the store.
Close() error
}