Tidy up homepage and navigation
This commit is contained in:
133
app/controllers/analytics_controller.rb
Normal file
133
app/controllers/analytics_controller.rb
Normal file
@@ -0,0 +1,133 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# AnalyticsController - Overview dashboard with statistics and charts
|
||||
class AnalyticsController < ApplicationController
|
||||
# All actions require authentication
|
||||
|
||||
def index
|
||||
authorize :analytics, :index?
|
||||
|
||||
# Time period selector (default: last 24 hours)
|
||||
@time_period = params[:period]&.to_sym || :day
|
||||
@start_time = calculate_start_time(@time_period)
|
||||
|
||||
# Core statistics
|
||||
@total_events = Event.where("timestamp >= ?", @start_time).count
|
||||
@total_rules = Rule.enabled.count
|
||||
@network_ranges_with_events = NetworkRange.with_events.count
|
||||
@total_network_ranges = NetworkRange.count
|
||||
|
||||
# Event breakdown by action
|
||||
@event_breakdown = Event.where("timestamp >= ?", @start_time)
|
||||
.group(:waf_action)
|
||||
.count
|
||||
.transform_keys do |action_id|
|
||||
case action_id
|
||||
when 0 then 'allow'
|
||||
when 1 then 'deny'
|
||||
when 2 then 'redirect'
|
||||
when 3 then 'challenge'
|
||||
else 'unknown'
|
||||
end
|
||||
end
|
||||
|
||||
# Top countries by event count
|
||||
@top_countries = Event.joins("JOIN network_ranges ON events.ip_address <<= network_ranges.network")
|
||||
.where("timestamp >= ? AND network_ranges.country IS NOT NULL", @start_time)
|
||||
.group("network_ranges.country")
|
||||
.count
|
||||
.sort_by { |_, count| -count }
|
||||
.first(10)
|
||||
|
||||
# Top blocked IPs
|
||||
@top_blocked_ips = Event.where("timestamp >= ?", @start_time)
|
||||
.where(waf_action: 1) # deny action in enum
|
||||
.group(:ip_address)
|
||||
.count
|
||||
.sort_by { |_, count| -count }
|
||||
.first(10)
|
||||
|
||||
# Network range intelligence breakdown
|
||||
@network_intelligence = {
|
||||
datacenter_ranges: NetworkRange.datacenter.count,
|
||||
vpn_ranges: NetworkRange.vpn.count,
|
||||
proxy_ranges: NetworkRange.proxy.count,
|
||||
total_ranges: NetworkRange.count
|
||||
}
|
||||
|
||||
# Recent activity
|
||||
@recent_events = Event.recent.limit(10)
|
||||
@recent_rules = Rule.order(created_at: :desc).limit(5)
|
||||
|
||||
# System health indicators
|
||||
@system_health = {
|
||||
total_users: User.count,
|
||||
active_rules: Rule.enabled.count,
|
||||
disabled_rules: Rule.where(enabled: false).count,
|
||||
recent_errors: Event.where("timestamp >= ? AND waf_action = ?", @start_time, 1).count # 1 = deny
|
||||
}
|
||||
|
||||
# Prepare data for charts
|
||||
@chart_data = prepare_chart_data
|
||||
|
||||
respond_to do |format|
|
||||
format.html
|
||||
format.turbo_stream
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def calculate_start_time(period)
|
||||
case period
|
||||
when :hour
|
||||
1.hour.ago
|
||||
when :day
|
||||
24.hours.ago
|
||||
when :week
|
||||
1.week.ago
|
||||
when :month
|
||||
1.month.ago
|
||||
else
|
||||
24.hours.ago
|
||||
end
|
||||
end
|
||||
|
||||
def prepare_chart_data
|
||||
# Events over time (hourly buckets for last 24 hours)
|
||||
events_by_hour = Event.where("timestamp >= ?", 24.hours.ago)
|
||||
.group("DATE_TRUNC('hour', timestamp)")
|
||||
.count
|
||||
|
||||
# Convert to chart format
|
||||
timeline_data = (0..23).map do |hour_ago|
|
||||
hour_time = hour_ago.hours.ago
|
||||
hour_key = hour_time.strftime("%Y-%m-%d %H:00:00")
|
||||
{
|
||||
time: hour_time.strftime("%H:00"),
|
||||
total: events_by_hour[hour_key] || 0
|
||||
}
|
||||
end.reverse
|
||||
|
||||
# Action distribution for pie chart
|
||||
action_distribution = @event_breakdown.map do |action, count|
|
||||
{
|
||||
action: action.humanize,
|
||||
count: count,
|
||||
percentage: ((count.to_f / [@total_events, 1].max) * 100).round(1)
|
||||
}
|
||||
end
|
||||
|
||||
{
|
||||
timeline: timeline_data,
|
||||
actions: action_distribution,
|
||||
countries: @top_countries.map { |country, count| { country: country, count: count } },
|
||||
network_types: [
|
||||
{ type: "Datacenter", count: @network_intelligence[:datacenter_ranges] },
|
||||
{ type: "VPN", count: @network_intelligence[:vpn_ranges] },
|
||||
{ type: "Proxy", count: @network_intelligence[:proxy_ranges] },
|
||||
{ type: "Standard", count: @network_intelligence[:total_ranges] - @network_intelligence[:datacenter_ranges] - @network_intelligence[:vpn_ranges] - @network_intelligence[:proxy_ranges] }
|
||||
]
|
||||
}
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user