Show device country next to its IP address, resolved via ip-api.com
This commit is contained in:
@@ -31,6 +31,7 @@ import pathlib
|
||||
from datetime import datetime
|
||||
from bson import ObjectId
|
||||
import urllib.request
|
||||
import ipaddress
|
||||
|
||||
|
||||
|
||||
@@ -75,6 +76,8 @@ settings_collection = db_iptv['settings']
|
||||
devices_db = db_iptv['devices']
|
||||
devices = []
|
||||
active_xtream_imports = {}
|
||||
sio_session_ips = {}
|
||||
ip_country_cache = {}
|
||||
|
||||
# Configure Socket IO
|
||||
sio = socketio.AsyncServer(logger=True, engineio_logger=True, cors_allowed_origins='*')
|
||||
@@ -1417,6 +1420,34 @@ app.router.add_post('/gemini_search', gemini_search)
|
||||
app.router.add_get('/channels_epg', channels_epg)
|
||||
app.router.add_get('/epg', epg_programs)
|
||||
|
||||
def resolve_ip_country(ip_address):
|
||||
if not ip_address:
|
||||
return ''
|
||||
if ip_address in ip_country_cache:
|
||||
return ip_country_cache[ip_address]
|
||||
try:
|
||||
ip_obj = ipaddress.ip_address(ip_address)
|
||||
if ip_obj.is_private or ip_obj.is_loopback or ip_obj.is_link_local:
|
||||
ip_country_cache[ip_address] = ''
|
||||
return ''
|
||||
except ValueError:
|
||||
return ''
|
||||
country = ''
|
||||
try:
|
||||
response = requests.get(
|
||||
f'http://ip-api.com/json/{ip_address}',
|
||||
params={'fields': 'status,country'},
|
||||
timeout=5,
|
||||
)
|
||||
data = response.json()
|
||||
if data.get('status') == 'success':
|
||||
country = data.get('country', '') or ''
|
||||
except requests.RequestException as exc:
|
||||
logger.warning("IP country lookup failed for %s: %s", ip_address, exc)
|
||||
ip_country_cache[ip_address] = country
|
||||
return country
|
||||
|
||||
|
||||
# SIO FUNCTIONS
|
||||
|
||||
def messageReceived():
|
||||
@@ -1424,7 +1455,12 @@ def messageReceived():
|
||||
|
||||
@sio.event
|
||||
def connect(sid, environ):
|
||||
print("connect ", sid, flush=True)
|
||||
# engineio's aiohttp integration hardcodes REMOTE_ADDR to 127.0.0.1;
|
||||
# the real peer address is only available via the underlying aiohttp request.
|
||||
aiohttp_request = environ.get('aiohttp.request')
|
||||
ip_address = aiohttp_request.remote if aiohttp_request else ''
|
||||
sio_session_ips[sid] = ip_address
|
||||
print("connect ", sid, ip_address, flush=True)
|
||||
|
||||
|
||||
@sio.event
|
||||
@@ -1432,11 +1468,12 @@ async def communication(fromSid, data):
|
||||
print("search request from ", fromSid, flush=True)
|
||||
toSidDevice = data['toSidDevice']
|
||||
print("send signal to ", toSidDevice, flush=True)
|
||||
await sio.emit('communication', data, room=toSidDevice ,callback=messageReceived)
|
||||
await sio.emit('communication', data, room=toSidDevice ,callback=messageReceived)
|
||||
|
||||
|
||||
@sio.event
|
||||
def disconnect(sid):
|
||||
sio_session_ips.pop(sid, None)
|
||||
print('disconnect ', sid, flush=True)
|
||||
|
||||
|
||||
@@ -1455,14 +1492,21 @@ async def polling(sid, data):
|
||||
try:
|
||||
vpn_status = data['vpn_status']
|
||||
except:
|
||||
vpn_status = 'unknown'
|
||||
device_data = devices_db.find_one({ 'id_device':IdDevice})
|
||||
vpn_status = 'unknown'
|
||||
|
||||
ip_address = sio_session_ips.get(sid, '')
|
||||
if ip_address in ip_country_cache:
|
||||
ip_country = ip_country_cache[ip_address]
|
||||
else:
|
||||
ip_country = await asyncio.get_event_loop().run_in_executor(None, resolve_ip_country, ip_address)
|
||||
|
||||
device_data = devices_db.find_one({ 'id_device':IdDevice})
|
||||
if device_data:
|
||||
query = { 'id_device':IdDevice}
|
||||
update = { "$set": { "last_datetime": device_last_datetime ,"name": device_name, "sid":sid, "mediaRunning":mediaRunning, 'vpn_status':vpn_status, 'data_running':data_running, 'auto_snapshot_enabled': auto_snapshot_enabled, 'snapshot_interval_seconds': snapshot_interval_seconds} }
|
||||
update = { "$set": { "last_datetime": device_last_datetime ,"name": device_name, "sid":sid, "mediaRunning":mediaRunning, 'vpn_status':vpn_status, 'data_running':data_running, 'auto_snapshot_enabled': auto_snapshot_enabled, 'snapshot_interval_seconds': snapshot_interval_seconds, 'ip_address': ip_address, 'ip_country': ip_country} }
|
||||
devices_db.update_one(query, update)
|
||||
else:
|
||||
new_device = { 'id_device': IdDevice, 'name' : device_name, 'last_datetime' : device_last_datetime, 'sid':sid ,'mediaRunning' : mediaRunning, 'vpn_status':vpn_status, 'data_running':data_running, 'auto_snapshot_enabled': auto_snapshot_enabled, 'snapshot_interval_seconds': snapshot_interval_seconds}
|
||||
new_device = { 'id_device': IdDevice, 'name' : device_name, 'last_datetime' : device_last_datetime, 'sid':sid ,'mediaRunning' : mediaRunning, 'vpn_status':vpn_status, 'data_running':data_running, 'auto_snapshot_enabled': auto_snapshot_enabled, 'snapshot_interval_seconds': snapshot_interval_seconds, 'ip_address': ip_address, 'ip_country': ip_country}
|
||||
devices_db.insert_one(new_device)
|
||||
|
||||
if data_running:
|
||||
|
||||
+10
-1
@@ -180,6 +180,11 @@
|
||||
font-weight: 500;
|
||||
min-width: 0;
|
||||
}
|
||||
.device-ip {
|
||||
font-size: 0.75rem;
|
||||
font-weight: 400;
|
||||
color: #9ca3af;
|
||||
}
|
||||
.device-status {
|
||||
color: var(--success);
|
||||
font-size: 0.95rem;
|
||||
@@ -2618,10 +2623,14 @@
|
||||
status2 = '#ffe6e6'
|
||||
}
|
||||
|
||||
var ipAddress = dataJSON[i].ip_address || '';
|
||||
var ipCountry = dataJSON[i].ip_country || '';
|
||||
var ipLine = ipAddress ? `<div class="device-ip text-truncate">${ipAddress}${ipCountry ? ' · ' + ipCountry : ''}</div>` : '';
|
||||
|
||||
var activeClass = (selectedDeviceId && selectedDeviceId === dataJSON[i].id_device) ? 'active' : '';
|
||||
html += `<div class="device-card ${activeClass}" id="btn_select_device_${dataJSON[i].id_device}" data-param1="${dataJSON[i].id_device}" data-param2="${dataJSON[i].name}" data-param3="${dataJSON[i].sid}" data-param4="${dataJSON[i].vpn_status}">
|
||||
<div class="device-card-header">
|
||||
<div class="device-name text-truncate">${dataJSON[i].name}</div>
|
||||
<div class="device-name text-truncate">${dataJSON[i].name}${ipLine}</div>
|
||||
<div class="device-actions">
|
||||
<div class="device-status">${status}</div>
|
||||
<div>${vpn_status_icon}</div>
|
||||
|
||||
Reference in New Issue
Block a user