Add 'tags' to event model. Add a dataimport system - currently for MaxMind zip files
This commit is contained in:
151
app/services/country_helper.rb
Normal file
151
app/services/country_helper.rb
Normal file
@@ -0,0 +1,151 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# CountryHelper - Service for country display utilities
|
||||
#
|
||||
# Provides methods to convert ISO country codes to display names,
|
||||
# generate country flags, and format country data for UI components.
|
||||
class CountryHelper
|
||||
# Convert ISO code to display name
|
||||
def self.display_name(iso_code)
|
||||
return iso_code if iso_code.blank?
|
||||
|
||||
country = ISO3166::Country[iso_code]
|
||||
country.local_name
|
||||
rescue
|
||||
iso_code
|
||||
end
|
||||
|
||||
# Convert ISO code to flag emoji
|
||||
def self.flag_emoji(iso_code)
|
||||
return "" if iso_code.blank? || iso_code.length != 2
|
||||
|
||||
# Convert each letter to regional indicator symbol (A + 0x1F1E5)
|
||||
iso_code.upcase.codepoints.map { |code| (code + 0x1F1E5).chr }.join
|
||||
rescue
|
||||
""
|
||||
end
|
||||
|
||||
# Display name with flag
|
||||
def self.display_with_flag(iso_code)
|
||||
return iso_code if iso_code.blank?
|
||||
|
||||
"#{display_name(iso_code)} (#{iso_code})"
|
||||
end
|
||||
|
||||
# Check if ISO code is valid
|
||||
def self.valid_iso_code?(iso_code)
|
||||
return false if iso_code.blank?
|
||||
|
||||
ISO3166::Country[iso_code].present?
|
||||
rescue
|
||||
false
|
||||
end
|
||||
|
||||
# Get all countries for select dropdowns
|
||||
# Returns array of [display_name, iso_code] pairs
|
||||
def self.all_for_select
|
||||
ISO3166::Country.all.map do |country|
|
||||
# Try different name sources in order of preference
|
||||
# Use the proper countries gem methods
|
||||
name = country.local_name.presence ||
|
||||
country.iso_short_name.presence ||
|
||||
country.common_name.presence ||
|
||||
country.alpha2
|
||||
display_name = "#{name} (#{country.alpha2})"
|
||||
[display_name, country.alpha2]
|
||||
end.sort_by { |name, _| name }
|
||||
rescue => e
|
||||
puts "Error in CountryHelper.all_for_select: #{e.message}"
|
||||
puts e.backtrace
|
||||
[]
|
||||
end
|
||||
|
||||
|
||||
# Get countries by common regions for quick selection
|
||||
def self.by_region
|
||||
{
|
||||
'Americas' => [
|
||||
'US', 'CA', 'MX', 'BR', 'AR', 'CL', 'CO', 'PE', 'VE'
|
||||
],
|
||||
'Europe' => [
|
||||
'GB', 'DE', 'FR', 'IT', 'ES', 'NL', 'BE', 'CH', 'AT', 'SE',
|
||||
'NO', 'DK', 'FI', 'PL', 'CZ', 'HU', 'RO', 'GR', 'PT'
|
||||
],
|
||||
'Asia Pacific' => [
|
||||
'CN', 'JP', 'KR', 'IN', 'SG', 'AU', 'NZ', 'TH', 'MY', 'ID',
|
||||
'PH', 'VN', 'HK', 'TW'
|
||||
],
|
||||
'Middle East & Africa' => [
|
||||
'ZA', 'EG', 'NG', 'KE', 'SA', 'AE', 'IL', 'TR', 'IR'
|
||||
]
|
||||
}
|
||||
rescue
|
||||
{}
|
||||
end
|
||||
|
||||
# Get countries for specific region with display names
|
||||
def self.countries_for_region(region_name)
|
||||
country_codes = by_region[region_name] || []
|
||||
|
||||
country_codes.map do |code|
|
||||
{
|
||||
code: code,
|
||||
name: display_name(code),
|
||||
display: display_with_flag(code)
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
# Format multiple country targets for display
|
||||
def self.format_targets(targets)
|
||||
return [] if targets.blank?
|
||||
|
||||
targets.map do |target|
|
||||
{
|
||||
code: target,
|
||||
name: display_name(target),
|
||||
display: display_with_flag(target)
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
# Get popular countries for quick blocking (common threat sources)
|
||||
def self.popular_for_blocking
|
||||
[
|
||||
{ code: 'CN', name: 'China', display: '🇨🇳 China', reason: 'High bot/scanner activity' },
|
||||
{ code: 'RU', name: 'Russia', display: '🇷🇺 Russia', reason: 'State-sponsored attacks' },
|
||||
{ code: 'IN', name: 'India', display: '🇮🇳 India', reason: 'High spam volume' },
|
||||
{ code: 'BR', name: 'Brazil', display: '🇧🇷 Brazil', reason: 'Scanner activity' },
|
||||
{ code: 'IR', name: 'Iran', display: '🇮🇷 Iran', reason: 'Attacks on critical infrastructure' },
|
||||
{ code: 'KP', name: 'North Korea', display: '🇰🇵 North Korea', reason: 'State-sponsored hacking' }
|
||||
]
|
||||
end
|
||||
|
||||
# Search countries by name or code
|
||||
def self.search(query)
|
||||
return [] if query.blank?
|
||||
|
||||
query = query.downcase
|
||||
ISO3166::Country.all.select do |country|
|
||||
country.alpha2.downcase.include?(query) ||
|
||||
country.local_name.downcase.include?(query)
|
||||
end.first(20).map { |c| [display_with_flag(c.alpha2), c.alpha2] }
|
||||
rescue
|
||||
[]
|
||||
end
|
||||
|
||||
# Country statistics for analytics
|
||||
def self.usage_statistics(country_codes)
|
||||
return {} if country_codes.blank?
|
||||
|
||||
stats = {}
|
||||
country_codes.each do |code|
|
||||
stats[code] = {
|
||||
name: display_name(code),
|
||||
flag: flag_emoji(code),
|
||||
display: display_with_flag(code)
|
||||
}
|
||||
end
|
||||
stats
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user