Files
baffle-hub/app/views/network_ranges/index.html.erb
2025-11-09 20:58:13 +11:00

326 lines
17 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<% content_for :title, "Network Ranges" %>
<div class="space-y-6">
<!-- Header -->
<div class="flex items-center justify-between">
<div>
<h1 class="text-3xl font-bold text-gray-900">Network Ranges</h1>
<p class="mt-2 text-gray-600">Browse and manage network ranges with intelligence data</p>
</div>
<div class="flex space-x-3">
<%= link_to "IP Lookup", lookup_network_ranges_path, class: "inline-flex items-center px-4 py-2 border border-gray-300 rounded-md shadow-sm text-sm font-medium text-gray-700 bg-white hover:bg-gray-50" %>
<%= link_to "Add Range", new_network_range_path, class: "inline-flex items-center px-4 py-2 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-blue-600 hover:bg-blue-700" %>
</div>
</div>
<!-- Active Filters -->
<% if params[:asn].present? || params[:country].present? || params[:company].present? || params[:datacenter].present? || params[:vpn].present? || params[:proxy].present? || params[:source].present? || params[:search].present? %>
<div class="bg-blue-50 border border-blue-200 rounded-lg p-4">
<div class="flex items-center justify-between">
<div>
<h3 class="text-sm font-medium text-blue-900">Active Filters</h3>
<div class="mt-2 flex flex-wrap gap-2">
<% if params[:asn].present? %>
<span class="inline-flex items-center px-3 py-1 rounded-full text-xs font-medium bg-blue-100 text-blue-800">
ASN: <%= params[:asn] %>
<%= link_to "×", network_ranges_path, class: "ml-2 text-blue-600 hover:text-blue-800" %>
</span>
<% end %>
<% if params[:country].present? %>
<span class="inline-flex items-center px-3 py-1 rounded-full text-xs font-medium bg-blue-100 text-blue-800">
Country: <%= params[:country] %>
<%= link_to "×", network_ranges_path, class: "ml-2 text-blue-600 hover:text-blue-800" %>
</span>
<% end %>
<% if params[:company].present? %>
<span class="inline-flex items-center px-3 py-1 rounded-full text-xs font-medium bg-blue-100 text-blue-800">
Company: <%= params[:company] %>
<%= link_to "×", network_ranges_path, class: "ml-2 text-blue-600 hover:text-blue-800" %>
</span>
<% end %>
<% if params[:datacenter].present? %>
<span class="inline-flex items-center px-3 py-1 rounded-full text-xs font-medium bg-blue-100 text-blue-800">
Datacenter
<%= link_to "×", network_ranges_path, class: "ml-2 text-blue-600 hover:text-blue-800" %>
</span>
<% end %>
<% if params[:vpn].present? %>
<span class="inline-flex items-center px-3 py-1 rounded-full text-xs font-medium bg-blue-100 text-blue-800">
VPN
<%= link_to "×", network_ranges_path, class: "ml-2 text-blue-600 hover:text-blue-800" %>
</span>
<% end %>
<% if params[:proxy].present? %>
<span class="inline-flex items-center px-3 py-1 rounded-full text-xs font-medium bg-blue-100 text-blue-800">
Proxy
<%= link_to "×", network_ranges_path, class: "ml-2 text-blue-600 hover:text-blue-800" %>
</span>
<% end %>
<% if params[:source].present? %>
<span class="inline-flex items-center px-3 py-1 rounded-full text-xs font-medium bg-blue-100 text-blue-800">
Source: <%= params[:source] %>
<%= link_to "×", network_ranges_path, class: "ml-2 text-blue-600 hover:text-blue-800" %>
</span>
<% end %>
<% if params[:search].present? %>
<span class="inline-flex items-center px-3 py-1 rounded-full text-xs font-medium bg-blue-100 text-blue-800">
Search: <%= params[:search] %>
<%= link_to "×", network_ranges_path, class: "ml-2 text-blue-600 hover:text-blue-800" %>
</span>
<% end %>
</div>
</div>
<div>
<%= link_to "Clear All", network_ranges_path, class: "text-sm text-blue-600 hover:text-blue-800 font-medium" %>
</div>
</div>
</div>
<% end %>
<!-- Statistics Cards -->
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-5 gap-4 mb-8">
<div class="bg-white overflow-hidden shadow rounded-lg">
<div class="p-5">
<div class="flex items-center">
<div class="flex-shrink-0">
<svg class="h-6 w-6 text-gray-400" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 21V5a2 2 0 00-2-2H7a2 2 0 00-2 2v16m14 0h2m-2 0h-5m-9 0H3m2 0h5M9 7h1m-1 4h1m4-4h1m-1 4h1m-5 10v-5a1 1 0 011-1h2a1 1 0 011 1v5m-4 0h4" />
</svg>
</div>
<div class="ml-5 w-0 flex-1">
<dl>
<dt class="text-sm font-medium text-gray-500 truncate">Total Ranges</dt>
<dd class="text-lg font-medium text-gray-900"><%= number_with_delimiter(@total_ranges) %></dd>
</dl>
</div>
</div>
</div>
</div>
<div class="bg-white overflow-hidden shadow rounded-lg">
<div class="p-5">
<div class="flex items-center">
<div class="flex-shrink-0">
<svg class="h-6 w-6 text-green-400" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
</div>
<div class="ml-5 w-0 flex-1">
<dl>
<dt class="text-sm font-medium text-gray-500 truncate">With Intelligence</dt>
<dd class="text-lg font-medium text-gray-900"><%= number_with_delimiter(@ranges_with_intelligence) %></dd>
</dl>
</div>
</div>
</div>
</div>
<div class="bg-white overflow-hidden shadow rounded-lg">
<div class="p-5">
<div class="flex items-center">
<div class="flex-shrink-0">
<svg class="h-6 w-6 text-orange-400" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8.111 16.404a5.5 5.5 0 017.778 0M12 20h.01m-7.08-7.071c3.904-3.905 10.236-3.905 14.141 0M1.394 9.393c5.857-5.857 15.355-5.857 21.213 0" />
</svg>
</div>
<div class="ml-5 w-0 flex-1">
<dl>
<dt class="text-sm font-medium text-gray-500 truncate">Datacenters</dt>
<dd class="text-lg font-medium text-gray-900"><%= number_with_delimiter(@datacenter_ranges) %></dd>
</dl>
</div>
</div>
</div>
</div>
<div class="bg-white overflow-hidden shadow rounded-lg">
<div class="p-5">
<div class="flex items-center">
<div class="flex-shrink-0">
<svg class="h-6 w-6 text-purple-400" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z" />
</svg>
</div>
<div class="ml-5 w-0 flex-1">
<dl>
<dt class="text-sm font-medium text-gray-500 truncate">VPNs</dt>
<dd class="text-lg font-medium text-gray-900"><%= number_with_delimiter(@vpn_ranges) %></dd>
</dl>
</div>
</div>
</div>
</div>
<div class="bg-white overflow-hidden shadow rounded-lg">
<div class="p-5">
<div class="flex items-center">
<div class="flex-shrink-0">
<svg class="h-6 w-6 text-red-400" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M20.354 15.354A9 9 0 018.646 3.646 9.003 9.003 0 0012 21a9.003 9.003 0 008.354-5.646z" />
</svg>
</div>
<div class="ml-5 w-0 flex-1">
<dl>
<dt class="text-sm font-medium text-gray-500 truncate">Proxies</dt>
<dd class="text-lg font-medium text-gray-900"><%= number_with_delimiter(@proxy_ranges) %></dd>
</dl>
</div>
</div>
</div>
</div>
</div>
<!-- Filters and Search -->
<div class="bg-white shadow rounded-lg mb-6">
<div class="p-4 border-b border-gray-200">
<h3 class="text-lg font-medium text-gray-900">Filters & Search</h3>
</div>
<div class="p-4">
<%= form_with url: network_ranges_path, method: :get, class: "space-y-4" do |form| %>
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4">
<div>
<%= form.label :search, "Search", class: "block text-sm font-medium text-gray-700" %>
<%= form.text_field :search, value: params[:search], placeholder: "CIDR, Company, ASN...", class: "mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 sm:text-sm" %>
</div>
<div>
<%= form.label :country, "Country", class: "block text-sm font-medium text-gray-700" %>
<%= form.select :country, options_for_select([["All Countries", ""]] + @top_countries.map { |c, _| [c, c] }), { selected: params[:country] }, { class: "mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 sm:text-sm" } %>
</div>
<div>
<%= form.label :source, "Source", class: "block text-sm font-medium text-gray-700" %>
<%= form.select :source, options_for_select([["All Sources", ""], "production_import", "user_created", "api_imported", "manual", "auto:scanner_detected"], params[:source]), { }, { class: "mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 sm:text-sm" } %>
</div>
<div>
<%= form.label :flags, "Flags", class: "block text-sm font-medium text-gray-700" %>
<div class="mt-2 space-x-4">
<label class="inline-flex items-center">
<%= check_box_tag "datacenter", "true", params[:datacenter] == "true", class: "rounded border-gray-300 text-blue-600 shadow-sm focus:border-blue-500 focus:ring focus:ring-blue-200 focus:ring-opacity-50" %>
<span class="ml-2 text-sm text-gray-700">Datacenter</span>
</label>
<label class="inline-flex items-center">
<%= check_box_tag "vpn", "true", params[:vpn] == "true", class: "rounded border-gray-300 text-blue-600 shadow-sm focus:border-blue-500 focus:ring focus:ring-blue-200 focus:ring-opacity-50" %>
<span class="ml-2 text-sm text-gray-700">VPN</span>
</label>
<label class="inline-flex items-center">
<%= check_box_tag "proxy", "true", params[:proxy] == "true", class: "rounded border-gray-300 text-blue-600 shadow-sm focus:border-blue-500 focus:ring focus:ring-blue-200 focus:ring-opacity-50" %>
<span class="ml-2 text-sm text-gray-700">Proxy</span>
</label>
</div>
</div>
</div>
<div class="flex justify-end">
<%= form.submit "Apply Filters", class: "inline-flex items-center px-4 py-2 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-blue-600 hover:bg-blue-700" %>
<%= link_to "Clear", network_ranges_path, class: "ml-3 inline-flex items-center px-4 py-2 border border-gray-300 rounded-md shadow-sm text-sm font-medium text-gray-700 bg-white hover:bg-gray-50" %>
</div>
<% end %>
</div>
</div>
<!-- Network Ranges Table -->
<div class="bg-white shadow overflow-hidden sm:rounded-md">
<div class="px-4 py-5 sm:px-6 border-b border-gray-200">
<div class="flex items-center justify-between">
<div>
<h3 class="text-lg font-medium text-gray-900">Network Ranges</h3>
<p class="mt-1 text-sm text-gray-500">Showing <%= @network_ranges.count %> of <%= number_with_delimiter(@total_ranges) %> ranges</p>
</div>
<% if @pagy.present? && @pagy.pages > 1 %>
<span class="text-sm text-gray-500">
Page <%= @pagy.page %> of <%= @pagy.pages %>
</span>
<% end %>
</div>
<!-- Top Pagination -->
<% if @pagy.present? && @pagy.pages > 1 %>
<div class="mt-4">
<%= pagy_nav_tailwind(@pagy, pagy_id: 'network_ranges_top') %>
</div>
<% end %>
</div>
<ul class="divide-y divide-gray-200">
<% @network_ranges.each do |range| %>
<li class="hover:bg-gray-50">
<div class="px-4 py-4 sm:px-6">
<div class="flex items-center justify-between">
<div class="flex items-center space-x-4">
<div>
<div class="flex items-center space-x-2">
<span class="text-sm font-medium text-gray-900"><%= range.cidr %></span>
<% if range.ipv4? %>
<span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-blue-100 text-blue-800">IPv4</span>
<% else %>
<span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-green-100 text-green-800">IPv6</span>
<% end %>
</div>
<div class="mt-1 flex items-center space-x-4 text-sm text-gray-500">
<% if range.company.present? %>
<span><%= range.company %></span>
<% end %>
<% if range.asn.present? %>
<span>ASN <%= range.asn %></span>
<% end %>
<% if range.country.present? %>
<span class="flex items-center">
<svg class="w-4 h-4 mr-1" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M5.05 4.05a7 7 0 119.9 9.9L10 18.9l-4.95-4.95a7 7 0 010-9.9zM10 11a2 2 0 100-4 2 2 0 000 4z" clip-rule="evenodd" />
</svg>
<%= range.country %>
</span>
<% end %>
<span class="text-gray-400">Source: <%= range.source %></span>
</div>
</div>
</div>
<div class="flex items-center space-x-2">
<% if range.is_datacenter? %>
<span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-orange-100 text-orange-800">DC</span>
<% end %>
<% if range.is_vpn? %>
<span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-purple-100 text-purple-800">VPN</span>
<% end %>
<% if range.is_proxy? %>
<span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-red-100 text-red-800">Proxy</span>
<% end %>
<% if range.rules.any? %>
<span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-blue-100 text-blue-800">
<%= range.rules.count %> rule<%= 's' if range.rules.count > 1 %>
</span>
<% end %>
<%= link_to "View", network_range_path(range), class: "text-blue-600 hover:text-blue-900 text-sm font-medium" %>
</div>
</div>
</div>
</li>
<% end %>
</ul>
<% if @network_ranges.empty? %>
<div class="text-center py-12">
<svg class="mx-auto h-12 w-12 text-gray-400" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 21V5a2 2 0 00-2-2H7a2 2 0 00-2 2v16m14 0h2m-2 0h-5m-9 0H3m2 0h5M9 7h1m-1 4h1m4-4h1m-1 4h1m-5 10v-5a1 1 0 011-1h2a1 1 0 011 1v5m-4 0h4" />
</svg>
<h3 class="mt-2 text-sm font-medium text-gray-900">No network ranges found</h3>
<p class="mt-1 text-sm text-gray-500">Get started by importing network data or creating ranges manually.</p>
<div class="mt-6">
<%= link_to "Add Network Range", new_network_range_path, class: "inline-flex items-center px-4 py-2 border border-transparent shadow-sm text-sm font-medium rounded-md text-white bg-blue-600 hover:bg-blue-700" %>
</div>
</div>
<% end %>
</div>
<!-- Bottom Pagination -->
<% if @pagy.present? && @pagy.pages > 1 %>
<div class="mt-6">
<%= pagy_nav_tailwind(@pagy, pagy_id: 'network_ranges_bottom') %>
</div>
<% end %>
</div>