Fix some blocked/allow laggards after migrating. Add DuckDB for outstanding analyitcs performance. Start adding an import for all bot networks
This commit is contained in:
@@ -141,8 +141,11 @@
|
||||
class: "text-blue-600 hover:text-blue-800 hover:underline font-mono font-medium" %>
|
||||
</div>
|
||||
<div class="text-xs text-gray-500">
|
||||
<% if network.country.present? %>
|
||||
🏳️ <%= network.country %>
|
||||
<% if network.display_country.present? %>
|
||||
🏳️ <%= network.display_country %>
|
||||
<% if network.has_inherited_data? && network.display_country != network.country %>
|
||||
<span class="text-blue-600" title="Inherited from parent network">*</span>
|
||||
<% end %>
|
||||
<% end %>
|
||||
<% if network.asn.present? %>
|
||||
• ASN <%= network.asn %>
|
||||
@@ -150,7 +153,15 @@
|
||||
</div>
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900">
|
||||
<%= network.company || 'Unknown' %>
|
||||
<div>
|
||||
<%= network.display_company || 'Unknown' %>
|
||||
<% if network.has_inherited_data? %>
|
||||
<div class="text-xs text-blue-600">
|
||||
from <%= link_to network.inherited_from, network_range_path(NetworkRange.find_by(network: network.inherited_from)),
|
||||
class: "text-blue-600 hover:text-blue-800 hover:underline" %>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm">
|
||||
<% if network.is_datacenter? %>
|
||||
|
||||
171
app/views/bot_network_ranges/index.html.erb
Normal file
171
app/views/bot_network_ranges/index.html.erb
Normal file
@@ -0,0 +1,171 @@
|
||||
<% content_for :title, "Bot Network Ranges" %>
|
||||
|
||||
<div class="max-w-7xl mx-auto px-4 py-8">
|
||||
<!-- Header -->
|
||||
<div class="mb-8">
|
||||
<h1 class="text-3xl font-bold text-gray-900 mb-2">Bot Network Ranges</h1>
|
||||
<p class="text-gray-600">Import and manage official network ranges for search crawlers and API bots</p>
|
||||
</div>
|
||||
|
||||
<!-- Available Sources -->
|
||||
<div class="bg-white shadow rounded-lg mb-8">
|
||||
<div class="px-6 py-4 border-b border-gray-200">
|
||||
<h2 class="text-lg font-semibold text-gray-900">Available Sources</h2>
|
||||
</div>
|
||||
<div class="p-6">
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||
<% @bot_sources.each do |key, source| %>
|
||||
<div class="border rounded-lg p-4 hover:bg-gray-50 transition-colors">
|
||||
<div class="flex items-start justify-between mb-2">
|
||||
<h3 class="font-medium text-gray-900"><%= source[:name] %></h3>
|
||||
<span class="px-2 py-1 text-xs font-medium rounded-full <%= source[:url] ? 'bg-green-100 text-green-800' : 'bg-yellow-100 text-yellow-800' %>">
|
||||
<%= source[:url] ? 'Available' : 'Manual' %>
|
||||
</span>
|
||||
</div>
|
||||
<p class="text-sm text-gray-600 mb-4"><%= source[:description] %></p>
|
||||
|
||||
<div class="flex flex-wrap gap-2">
|
||||
<%= form_with url: import_bot_network_ranges_path, method: :post, class: "inline" do |f| %>
|
||||
<%= hidden_field_tag :source, key %>
|
||||
<%= f.submit "Import Now",
|
||||
class: "px-3 py-1 text-xs font-medium text-white bg-blue-600 rounded hover:bg-blue-700 transition-colors disabled:opacity-50",
|
||||
disabled: !source[:url] %>
|
||||
<% end %>
|
||||
|
||||
<%= form_with url: import_async_bot_network_ranges_path, method: :post, class: "inline" do |f| %>
|
||||
<%= hidden_field_tag :source, key %>
|
||||
<%= f.submit "Import Async",
|
||||
class: "px-3 py-1 text-xs font-medium text-white bg-purple-600 rounded hover:bg-purple-700 transition-colors disabled:opacity-50",
|
||||
disabled: !source[:url] %>
|
||||
<% end %>
|
||||
|
||||
<%= link_to "View", bot_network_range_path(key),
|
||||
class: "px-3 py-1 text-xs font-medium text-gray-700 bg-gray-200 rounded hover:bg-gray-300 transition-colors" %>
|
||||
</div>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Batch Import -->
|
||||
<div class="bg-white shadow rounded-lg mb-8">
|
||||
<div class="px-6 py-4 border-b border-gray-200">
|
||||
<h2 class="text-lg font-semibold text-gray-900">Batch Import</h2>
|
||||
</div>
|
||||
<div class="p-6">
|
||||
<p class="text-gray-600 mb-4">Import from all available sources (this may take several minutes).</p>
|
||||
|
||||
<%= form_with url: import_all_bot_network_ranges_path, method: :post do |f| %>
|
||||
<div class="flex items-center gap-4">
|
||||
<%= f.submit "Import All Sources",
|
||||
class: "px-6 py-2 font-medium text-white bg-green-600 rounded hover:bg-green-700 transition-colors",
|
||||
confirm: "This will import from all available sources and may take several minutes. Continue?" %>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Recent Imports -->
|
||||
<% if @recent_imports.any? %>
|
||||
<div class="bg-white shadow rounded-lg mb-8">
|
||||
<div class="px-6 py-4 border-b border-gray-200">
|
||||
<h2 class="text-lg font-semibold text-gray-900">Recent Imports</h2>
|
||||
</div>
|
||||
<div class="overflow-x-auto">
|
||||
<table class="min-w-full divide-y divide-gray-200">
|
||||
<thead class="bg-gray-50">
|
||||
<tr>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Source</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Status</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Records</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Date</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Notes</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="bg-white divide-y divide-gray-200">
|
||||
<% @recent_imports.each do |import| %>
|
||||
<tr>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900">
|
||||
<%= import.source.titleize %>
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap">
|
||||
<span class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full <%= import.status == 'completed' ? 'bg-green-100 text-green-800' : 'bg-yellow-100 text-yellow-800' %>">
|
||||
<%= import.status.titleize %>
|
||||
</span>
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
|
||||
<%= import.records_processed&.to_s || '0' %>
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
|
||||
<%= import.created_at.strftime('%Y-%m-%d %H:%M') %>
|
||||
</td>
|
||||
<td class="px-6 py-4 text-sm text-gray-500">
|
||||
<%= import.notes %>
|
||||
</td>
|
||||
</tr>
|
||||
<% end %>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
<!-- Recent Bot Network Ranges -->
|
||||
<% if @bot_network_ranges.any? %>
|
||||
<div class="bg-white shadow rounded-lg">
|
||||
<div class="px-6 py-4 border-b border-gray-200">
|
||||
<h2 class="text-lg font-semibold text-gray-900">Recently Imported Bot Ranges</h2>
|
||||
</div>
|
||||
<div class="overflow-x-auto">
|
||||
<table class="min-w-full divide-y divide-gray-200">
|
||||
<thead class="bg-gray-50">
|
||||
<tr>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Network</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Source</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Company</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Created</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Details</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="bg-white divide-y divide-gray-200">
|
||||
<% @bot_network_ranges.each do |range| %>
|
||||
<tr>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900">
|
||||
<%= range.network %>
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
|
||||
<%= range.source.gsub('bot_import_', '').titleize %>
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
|
||||
<%= range.company || 'Unknown' %>
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
|
||||
<%= range.created_at.strftime('%Y-%m-%d %H:%M') %>
|
||||
</td>
|
||||
<td class="px-6 py-4 text-sm text-gray-500">
|
||||
<% if range.additional_data.present? %>
|
||||
<% data = JSON.parse(range.additional_data) rescue {} %>
|
||||
<% if data['crawler_type'] %>
|
||||
<span class="px-2 py-1 text-xs font-medium rounded bg-blue-100 text-blue-800">
|
||||
<%= data['crawler_type'].titleize %>
|
||||
</span>
|
||||
<% end %>
|
||||
<% if data['aws_service'] %>
|
||||
<span class="px-2 py-1 text-xs font-medium rounded bg-orange-100 text-orange-800">
|
||||
<%= data['aws_service'] %>
|
||||
</span>
|
||||
<% end %>
|
||||
<% end %>
|
||||
</td>
|
||||
</tr>
|
||||
<% end %>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
<!-- Real-time updates via Turbo Streams -->
|
||||
<turbo-stream-source src="/cable" channel="BotImportsChannel"></turbo-stream-source>
|
||||
175
app/views/bot_network_ranges/show.html.erb
Normal file
175
app/views/bot_network_ranges/show.html.erb
Normal file
@@ -0,0 +1,175 @@
|
||||
<% content_for :title, "#{@source_name} Network Ranges" %>
|
||||
|
||||
<div class="max-w-7xl mx-auto px-4 py-8">
|
||||
<!-- Header -->
|
||||
<div class="mb-8">
|
||||
<div class="flex items-center justify-between">
|
||||
<div>
|
||||
<h1 class="text-3xl font-bold text-gray-900 mb-2"><%= @source_name %> Network Ranges</h1>
|
||||
<p class="text-gray-600">Network ranges imported from <%= @source_name %> official sources</p>
|
||||
</div>
|
||||
<div class="flex space-x-3">
|
||||
<%= link_to "Back to Sources", bot_network_ranges_path,
|
||||
class: "px-4 py-2 text-sm font-medium text-gray-700 bg-gray-200 rounded hover:bg-gray-300 transition-colors" %>
|
||||
|
||||
<%= form_with url: bot_network_range_path(params[:source]), method: :delete, class: "inline" do |f| %>
|
||||
<%= f.submit "Delete All Ranges",
|
||||
class: "px-4 py-2 text-sm font-medium text-white bg-red-600 rounded hover:bg-red-700 transition-colors",
|
||||
confirm: "Are you sure you want to delete all #{@source_name} network ranges? This action cannot be undone." %>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Statistics -->
|
||||
<% if @import_stats.any? %>
|
||||
<div class="bg-white shadow rounded-lg mb-8">
|
||||
<div class="px-6 py-4 border-b border-gray-200">
|
||||
<h2 class="text-lg font-semibold text-gray-900">Import Statistics</h2>
|
||||
</div>
|
||||
<div class="p-6">
|
||||
<div class="grid grid-cols-1 md:grid-cols-3 gap-6">
|
||||
<% @import_stats.each do |source, count| %>
|
||||
<div class="text-center">
|
||||
<div class="text-3xl font-bold text-blue-600"><%= count %></div>
|
||||
<div class="text-sm text-gray-600 mt-1"><%= source.gsub('bot_import_', '').titleize %></div>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
<!-- Network Ranges Table -->
|
||||
<div class="bg-white shadow rounded-lg">
|
||||
<div class="px-6 py-4 border-b border-gray-200">
|
||||
<div class="flex items-center justify-between">
|
||||
<h2 class="text-lg font-semibold text-gray-900">Network Ranges</h2>
|
||||
<div class="text-sm text-gray-500">
|
||||
Showing <%= @network_ranges.offset_value + 1 %> to <%= [@network_ranges.offset_value + @network_ranges.current_page_count, @network_ranges.total_count].min %>
|
||||
of <%= @network_ranges.total_count %> ranges
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="overflow-x-auto">
|
||||
<table class="min-w-full divide-y divide-gray-200">
|
||||
<thead class="bg-gray-50">
|
||||
<tr>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Network</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Source</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Company</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Country</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Created</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Details</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="bg-white divide-y divide-gray-200">
|
||||
<% @network_ranges.each do |range| %>
|
||||
<tr class="hover:bg-gray-50">
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900">
|
||||
<%= link_to range.network, network_range_path(range), class: "text-blue-600 hover:text-blue-800" %>
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
|
||||
<%= range.source.gsub('bot_import_', '').titleize %>
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
|
||||
<%= range.company || 'Unknown' %>
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
|
||||
<%= range.country || 'Unknown' %>
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
|
||||
<%= range.created_at.strftime('%Y-%m-%d %H:%M') %>
|
||||
</td>
|
||||
<td class="px-6 py-4 text-sm text-gray-500">
|
||||
<% if range.additional_data.present? %>
|
||||
<% data = JSON.parse(range.additional_data) rescue {} %>
|
||||
<div class="flex flex-wrap gap-1">
|
||||
<% if data['crawler_type'] %>
|
||||
<span class="px-2 py-1 text-xs font-medium rounded bg-blue-100 text-blue-800">
|
||||
<%= data['crawler_type'].titleize %>
|
||||
</span>
|
||||
<% end %>
|
||||
<% if data['crawler_purpose'] %>
|
||||
<span class="px-2 py-1 text-xs font-medium rounded bg-purple-100 text-purple-800" title="<%= data['crawler_purpose'] %>">
|
||||
Purpose
|
||||
</span>
|
||||
<% end %>
|
||||
<% if data['aws_service'] %>
|
||||
<span class="px-2 py-1 text-xs font-medium rounded bg-orange-100 text-orange-800">
|
||||
<%= data['aws_service'] %>
|
||||
</span>
|
||||
<% end %>
|
||||
<% if data['aws_region'] %>
|
||||
<span class="px-2 py-1 text-xs font-medium rounded bg-green-100 text-green-800">
|
||||
<%= data['aws_region'] %>
|
||||
</span>
|
||||
<% end %>
|
||||
<% if data['ip_version'] %>
|
||||
<span class="px-2 py-1 text-xs font-medium rounded bg-gray-100 text-gray-800">
|
||||
IPv<%= data['ip_version'] %>
|
||||
</span>
|
||||
<% end %>
|
||||
</div>
|
||||
<% end %>
|
||||
</td>
|
||||
</tr>
|
||||
<% end %>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<!-- Pagination -->
|
||||
<% if @network_ranges.total_pages > 1 %>
|
||||
<div class="px-6 py-4 border-t border-gray-200">
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="text-sm text-gray-700">
|
||||
Page <%= @network_ranges.current_page %> of <%= @network_ranges.total_pages %>
|
||||
</div>
|
||||
<div class="flex space-x-2">
|
||||
<% if @network_ranges.prev_page %>
|
||||
<%= link_to "Previous", bot_network_range_path(params[:source], page: @network_ranges.prev_page),
|
||||
class: "px-3 py-1 text-sm font-medium text-gray-700 bg-white border border-gray-300 rounded hover:bg-gray-50" %>
|
||||
<% end %>
|
||||
|
||||
<%# Show page numbers %>
|
||||
<% (1..@network_ranges.total_pages).select { |p| p == 1 || p == @network_ranges.total_pages || (p - @network_ranges.current_page).abs <= 2 }.each do |page| %>
|
||||
<% if page == @network_ranges.current_page %>
|
||||
<span class="px-3 py-1 text-sm font-medium text-white bg-blue-600 rounded">
|
||||
<%= page %>
|
||||
</span>
|
||||
<% else %>
|
||||
<%= link_to page, bot_network_range_path(params[:source], page: page),
|
||||
class: "px-3 py-1 text-sm font-medium text-gray-700 bg-white border border-gray-300 rounded hover:bg-gray-50" %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
|
||||
<% if @network_ranges.next_page %>
|
||||
<%= link_to "Next", bot_network_range_path(params[:source], page: @network_ranges.next_page),
|
||||
class: "px-3 py-1 text-sm font-medium text-gray-700 bg-white border border-gray-300 rounded hover:bg-gray-50" %>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
<% if @network_ranges.empty? %>
|
||||
<div class="bg-white shadow rounded-lg">
|
||||
<div class="px-6 py-12 text-center">
|
||||
<div class="text-gray-400 mb-4">
|
||||
<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="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z" />
|
||||
</svg>
|
||||
</div>
|
||||
<h3 class="text-lg font-medium text-gray-900 mb-2">No network ranges found</h3>
|
||||
<p class="text-gray-600 mb-6">
|
||||
No <%= @source_name %> network ranges have been imported yet.
|
||||
</p>
|
||||
<%= link_to "Import #{@source_name} Ranges", bot_network_ranges_path,
|
||||
class: "inline-flex items-center px-4 py-2 border border-transparent text-sm font-medium rounded-md shadow-sm text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500" %>
|
||||
</div>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
@@ -25,7 +25,7 @@
|
||||
<div>
|
||||
<%= form.label :waf_action, "Action", class: "block text-sm font-medium text-gray-700" %>
|
||||
<%= form.select :waf_action,
|
||||
options_for_select([['All', ''], ['Allow', 'allow'], ['Deny', 'deny'], ['Redirect', 'redirect'], ['Challenge', 'challenge']], params[:waf_action]),
|
||||
options_for_select([['All', ''], ['Allow', 'allow'], ['Deny', 'deny'], ['Redirect', 'redirect'], ['Challenge', 'challenge'], ['Add Header', 'add_header']], params[:waf_action]),
|
||||
{ }, { 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>
|
||||
@@ -178,9 +178,10 @@
|
||||
when 'deny' then 'bg-red-100 text-red-800'
|
||||
when 'redirect' then 'bg-blue-100 text-blue-800'
|
||||
when 'challenge' then 'bg-yellow-100 text-yellow-800'
|
||||
when 'add_header' then 'bg-purple-100 text-purple-800'
|
||||
else 'bg-gray-100 text-gray-800'
|
||||
end %>">
|
||||
<%= event.waf_action %>
|
||||
<%= event.waf_action.humanize %>
|
||||
</span>
|
||||
</td>
|
||||
<td class="px-6 py-4 text-sm font-mono text-gray-900">
|
||||
|
||||
@@ -75,6 +75,8 @@
|
||||
class: nav_link_class(network_ranges_path) %>
|
||||
|
||||
<% if user_signed_in? && current_user_admin? %>
|
||||
<%= link_to "🤖 Bot Ranges", bot_network_ranges_path,
|
||||
class: nav_link_class(bot_network_ranges_path) %>
|
||||
<%= link_to "📊 Data Imports", data_imports_path,
|
||||
class: nav_link_class(data_imports_path) %>
|
||||
<%= link_to "🔗 DSNs", dsns_path,
|
||||
@@ -172,6 +174,8 @@
|
||||
class: mobile_nav_link_class(network_ranges_path) %>
|
||||
|
||||
<% if user_signed_in? && current_user_admin? %>
|
||||
<%= link_to "🤖 Bot Ranges", bot_network_ranges_path,
|
||||
class: mobile_nav_link_class(bot_network_ranges_path) %>
|
||||
<%= link_to "📊 Data Imports", data_imports_path,
|
||||
class: mobile_nav_link_class(data_imports_path) %>
|
||||
<%= link_to "🔗 DSNs", dsns_path,
|
||||
|
||||
@@ -225,14 +225,16 @@
|
||||
<td class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium">
|
||||
<%= link_to "View", rule_path(rule), class: "text-blue-600 hover:text-blue-900 mr-3" %>
|
||||
<% if rule.enabled? %>
|
||||
<%= link_to "Disable", disable_rule_path(rule),
|
||||
<%= button_to "Disable", disable_rule_path(rule),
|
||||
method: :post,
|
||||
data: { confirm: "Are you sure you want to disable this rule?" },
|
||||
class: "text-yellow-600 hover:text-yellow-900 mr-3" %>
|
||||
form: { style: "display: inline;" },
|
||||
data: { turbo_confirm: "Are you sure you want to disable this rule?" },
|
||||
class: "text-yellow-600 hover:text-yellow-900 mr-3 bg-transparent border-0 p-0 cursor-pointer" %>
|
||||
<% else %>
|
||||
<%= link_to "Enable", enable_rule_path(rule),
|
||||
<%= button_to "Enable", enable_rule_path(rule),
|
||||
method: :post,
|
||||
class: "text-green-600 hover:text-green-900 mr-3" %>
|
||||
form: { style: "display: inline;" },
|
||||
class: "text-green-600 hover:text-green-900 mr-3 bg-transparent border-0 p-0 cursor-pointer" %>
|
||||
<% end %>
|
||||
<%= link_to "Edit", edit_rule_path(rule), class: "text-indigo-600 hover:text-indigo-900" %>
|
||||
</td>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<p class="mt-2 text-gray-600">Create a WAF rule to allow, block, or rate limit traffic</p>
|
||||
</div>
|
||||
|
||||
<div class="bg-white shadow rounded-lg">
|
||||
<div class="bg-white shadow rounded-lg" data-controller="rule-form">
|
||||
<%= form_with(model: @rule, local: true, class: "space-y-6") do |form| %>
|
||||
<% if @rule.errors.any? %>
|
||||
<div class="rounded-md bg-red-50 p-4">
|
||||
@@ -54,7 +54,8 @@
|
||||
<%= form.select :waf_action,
|
||||
options_for_select(@waf_actions.map { |action, _| [action.humanize, action] }, @rule.waf_action),
|
||||
{ prompt: "Select action" },
|
||||
{ class: "mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 sm:text-sm" } %>
|
||||
{ class: "mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 sm:text-sm",
|
||||
data: { rule_form_target: "actionSelect", action: "change->rule-form#updateActionSections" } } %>
|
||||
<p class="mt-2 text-sm text-gray-500">What action to take when this rule matches</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -158,6 +159,27 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Add Header Fields (shown for add_header action) -->
|
||||
<div id="add_header_section" class="hidden space-y-4" data-rule-form-target="addHeaderSection">
|
||||
<div>
|
||||
<%= label_tag :header_name, "Header Name", class: "block text-sm font-medium text-gray-700" %>
|
||||
<%= text_field_tag :header_name, "",
|
||||
class: "mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 sm:text-sm",
|
||||
placeholder: "X-Bot-Agent",
|
||||
id: "header_name_input" %>
|
||||
<p class="mt-2 text-sm text-gray-500">The HTTP header name to add (e.g., X-Bot-Agent, X-Network-Type)</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<%= label_tag :header_value, "Header Value", class: "block text-sm font-medium text-gray-700" %>
|
||||
<%= text_field_tag :header_value, "",
|
||||
class: "mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 sm:text-sm",
|
||||
placeholder: "BingBot",
|
||||
id: "header_value_input" %>
|
||||
<p class="mt-2 text-sm text-gray-500">The value for the header (e.g., BingBot, GoogleBot, Unknown)</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Metadata -->
|
||||
<div data-controller="json-validator" data-json-validator-valid-class="json-valid" data-json-validator-invalid-class="json-invalid" data-json-validator-valid-status-class="json-valid-status" data-json-validator-invalid-status-class="json-invalid-status">
|
||||
<%= form.label :metadata, "Metadata", class: "block text-sm font-medium text-gray-700" %>
|
||||
@@ -197,10 +219,18 @@
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<%= form.label :expires_at, "Expires At", class: "block text-sm font-medium text-gray-700" %>
|
||||
<%= form.datetime_local_field :expires_at,
|
||||
class: "mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 sm:text-sm" %>
|
||||
<p class="mt-2 text-sm text-gray-500">Leave blank for permanent rule</p>
|
||||
<div class="flex items-center mb-2">
|
||||
<%= check_box_tag :set_expiration, "1", false,
|
||||
class: "h-4 w-4 rounded border-gray-300 text-blue-600 focus:ring-blue-500",
|
||||
data: { rule_form_target: "expirationCheckbox", action: "change->rule-form#toggleExpiration" } %>
|
||||
<%= label_tag :set_expiration, "Set expiration", class: "ml-2 block text-sm font-medium text-gray-700" %>
|
||||
</div>
|
||||
<div class="hidden" data-rule-form-target="expirationField">
|
||||
<%= form.label :expires_at, "Expires At", class: "block text-sm font-medium text-gray-700" %>
|
||||
<%= form.datetime_local_field :expires_at,
|
||||
class: "mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 sm:text-sm" %>
|
||||
<p class="mt-2 text-sm text-gray-500">When this rule should automatically expire</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center pt-6">
|
||||
|
||||
@@ -39,12 +39,12 @@
|
||||
<div class="flex space-x-3">
|
||||
<%= link_to "Edit", edit_rule_path(@rule), 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" %>
|
||||
<% if @rule.enabled? %>
|
||||
<%= link_to "Disable", disable_rule_path(@rule),
|
||||
<%= button_to "Disable", disable_rule_path(@rule),
|
||||
method: :post,
|
||||
data: { confirm: "Are you sure you want to disable this rule?" },
|
||||
data: { turbo_confirm: "Are you sure you want to disable this rule?" },
|
||||
class: "inline-flex items-center px-4 py-2 border border-yellow-300 rounded-md shadow-sm text-sm font-medium text-yellow-700 bg-yellow-50 hover:bg-yellow-100" %>
|
||||
<% else %>
|
||||
<%= link_to "Enable", enable_rule_path(@rule),
|
||||
<%= button_to "Enable", enable_rule_path(@rule),
|
||||
method: :post,
|
||||
class: "inline-flex items-center px-4 py-2 border border-green-300 rounded-md shadow-sm text-sm font-medium text-green-700 bg-green-50 hover:bg-green-100" %>
|
||||
<% end %>
|
||||
|
||||
Reference in New Issue
Block a user