# frozen_string_literal: true class EventsController < ApplicationController def show @event = Event.includes(:network_range).find(params[:id]) # Use denormalized network_range_id if available (much faster) @network_range = @event.network_range # Fallback to IP lookup if network_range_id is missing unless @network_range @network_range = NetworkRange.contains_ip(@event.ip_address.to_s).first # Auto-generate network range if no match found unless @network_range @network_range = NetworkRangeGenerator.find_or_create_for_ip(@event.ip_address) Rails.logger.debug "Auto-generated network range #{@network_range&.cidr} for IP #{@event.ip_address}" if @network_range end end end def index # Build filters hash from params filters = {} filters[:ip] = params[:ip] if params[:ip].present? filters[:waf_action] = params[:waf_action] if params[:waf_action].present? filters[:country] = params[:country] if params[:country].present? filters[:rule_id] = params[:rule_id] if params[:rule_id].present? filters[:company] = params[:company] if params[:company].present? filters[:network_type] = params[:network_type] if params[:network_type].present? filters[:asn] = params[:asn] if params[:asn].present? filters[:exclude_bots] = params[:exclude_bots] if params[:exclude_bots] == "true" # Handle network_cidr filter (requires NetworkRange lookup) if params[:network_cidr].present? range = NetworkRange.find_by(network: params[:network_cidr]) filters[:network_range_id] = range.id if range end # Try DuckDB first, fallback to PostgreSQL if unavailable result = EventDdb.search(filters, page: params[:page]&.to_i || 1, per_page: 50) if result # DuckDB query succeeded @pagy = Pagy.new(count: result[:total_count], page: result[:page], items: result[:per_page]) @events = result[:events] # Load network_range associations for events that have network_range_id network_range_ids = @events.map(&:network_range_id).compact.uniq if network_range_ids.any? network_ranges = NetworkRange.where(id: network_range_ids).index_by(&:id) @events.each do |event| event.network_range = network_ranges[event.network_range_id] if event.network_range_id end end # Load rule associations if needed rule_ids = @events.map(&:rule_id).compact.uniq if rule_ids.any? rules = Rule.where(id: rule_ids).index_by(&:id) @events.each do |event| event.rule = rules[event.rule_id] if event.rule_id end end Rails.logger.debug "[DuckDB] Found #{result[:total_count]} total events, showing page #{result[:page]}" else # Fallback to PostgreSQL Rails.logger.warn "[EventsController] DuckDB unavailable, falling back to PostgreSQL" @events = Event.includes(:network_range, :rule).order(timestamp: :desc) # Apply filters using ActiveRecord scopes @events = @events.by_ip(params[:ip]) if params[:ip].present? @events = @events.by_waf_action(params[:waf_action]) if params[:waf_action].present? @events = @events.by_country(params[:country]) if params[:country].present? @events = @events.where(rule_id: params[:rule_id]) if params[:rule_id].present? @events = @events.by_company(params[:company]) if params[:company].present? @events = @events.by_network_type(params[:network_type]) if params[:network_type].present? @events = @events.by_asn(params[:asn]) if params[:asn].present? @events = @events.by_network_cidr(params[:network_cidr]) if params[:network_cidr].present? @events = @events.exclude_bots if params[:exclude_bots] == "true" # Paginate @pagy, @events = pagy(@events, items: 50) Rails.logger.debug "[PostgreSQL] Events count: #{@pagy.count} total, #{@pagy.pages} pages" end end end