Add DeviceDetector and postres_cursor

This commit is contained in:
Dan Milne
2025-11-13 08:35:00 +11:00
parent cc8213f87a
commit 2c7b801ed5
15 changed files with 472 additions and 158 deletions

View File

@@ -0,0 +1,112 @@
<div id="ipapi_data_section" class="bg-white shadow rounded-lg mb-6">
<div class="px-6 py-4 border-b border-gray-200">
<h3 class="text-lg font-medium text-gray-900">IPAPI Enrichment Data</h3>
</div>
<% if ipapi_loading %>
<div class="px-6 py-8 text-center">
<div class="inline-block animate-spin rounded-full h-8 w-8 border-b-2 border-blue-600"></div>
<p class="mt-2 text-sm text-gray-500">Fetching enrichment data...</p>
</div>
<% elsif ipapi_data.present? %>
<div class="px-6 py-4">
<% if parent_with_ipapi %>
<div class="mb-4 p-3 bg-blue-50 border border-blue-200 rounded-md">
<div class="flex items-center">
<svg class="w-5 h-5 text-blue-600 mr-2" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a1 1 0 000 2v3a1 1 0 001 1h1a1 1 0 100-2v-3a1 1 0 00-1-1H9z" clip-rule="evenodd" />
</svg>
<span class="text-sm text-blue-800">
Data inherited from parent network <%= link_to parent_with_ipapi.cidr, network_range_path(parent_with_ipapi), class: "font-mono font-medium hover:underline" %>
</span>
</div>
</div>
<% end %>
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
<% if ipapi_data['asn'].present? %>
<div>
<dt class="text-sm font-medium text-gray-500">ASN (IPAPI)</dt>
<dd class="mt-1 text-sm text-gray-900">
AS<%= ipapi_data['asn']['asn'] %>
<% if ipapi_data['asn']['org'].present? %>
<div class="text-xs text-gray-600"><%= ipapi_data['asn']['org'] %></div>
<% end %>
<% if ipapi_data['asn']['route'].present? %>
<div class="text-xs text-gray-500 font-mono"><%= ipapi_data['asn']['route'] %></div>
<% end %>
</dd>
</div>
<% end %>
<% if ipapi_data['location'].present? %>
<div>
<dt class="text-sm font-medium text-gray-500">Location</dt>
<dd class="mt-1 text-sm text-gray-900">
<%= [ipapi_data['location']['city'], ipapi_data['location']['state'], ipapi_data['location']['country']].compact.join(', ') %>
<% if ipapi_data['location']['country_code'].present? %>
<span class="ml-2 text-lg"><%= country_flag(ipapi_data['location']['country_code']) %></span>
<% end %>
</dd>
</div>
<% end %>
<% if ipapi_data['company'].present? %>
<div>
<dt class="text-sm font-medium text-gray-500">Company (IPAPI)</dt>
<dd class="mt-1 text-sm text-gray-900">
<%= ipapi_data['company']['name'] %>
<% if ipapi_data['company']['type'].present? %>
<div class="text-xs text-gray-600"><%= ipapi_data['company']['type'].humanize %></div>
<% end %>
</dd>
</div>
<% end %>
<% if ipapi_data['is_datacenter'] || ipapi_data['is_vpn'] || ipapi_data['is_proxy'] || ipapi_data['is_tor'] %>
<div class="md:col-span-2 lg:col-span-3">
<dt class="text-sm font-medium text-gray-500 mb-2">IPAPI Flags</dt>
<dd class="flex flex-wrap gap-2">
<% if ipapi_data['is_datacenter'] %>
<span class="inline-flex items-center px-3 py-1 rounded-full text-sm font-medium bg-orange-100 text-orange-800">Datacenter</span>
<% end %>
<% if ipapi_data['is_vpn'] %>
<span class="inline-flex items-center px-3 py-1 rounded-full text-sm font-medium bg-purple-100 text-purple-800">VPN</span>
<% end %>
<% if ipapi_data['is_proxy'] %>
<span class="inline-flex items-center px-3 py-1 rounded-full text-sm font-medium bg-red-100 text-red-800">Proxy</span>
<% end %>
<% if ipapi_data['is_tor'] %>
<span class="inline-flex items-center px-3 py-1 rounded-full text-sm font-medium bg-gray-800 text-white">Tor</span>
<% end %>
<% if ipapi_data['is_abuser'] %>
<span class="inline-flex items-center px-3 py-1 rounded-full text-sm font-medium bg-red-600 text-white">Abuser</span>
<% end %>
<% if ipapi_data['is_bogon'] %>
<span class="inline-flex items-center px-3 py-1 rounded-full text-sm font-medium bg-yellow-100 text-yellow-800">Bogon</span>
<% end %>
</dd>
</div>
<% end %>
</div>
<!-- Raw IPAPI Data (collapsible) -->
<details class="mt-6 pt-6 border-t border-gray-200">
<summary class="cursor-pointer text-sm font-medium text-gray-700 hover:text-gray-900">
Show Raw IPAPI Data
</summary>
<div class="mt-3">
<pre class="bg-gray-50 p-3 rounded-md text-xs overflow-x-auto"><%= JSON.pretty_generate(ipapi_data) %></pre>
</div>
</details>
</div>
<% else %>
<div class="px-6 py-8 text-center text-gray-500">
<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="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
<h3 class="mt-2 text-sm font-medium text-gray-900">No IPAPI data available</h3>
<p class="mt-1 text-sm text-gray-500">Enrichment data will be fetched automatically.</p>
</div>
<% end %>
</div>