Adds visitor dashboard and related features
This commit is contained in:
@@ -0,0 +1,169 @@
|
||||
"""Inject test visitor log entries into the shared logs directory.
|
||||
|
||||
Writes JSON-formatted access log lines (matching the nginx kmtn_json format)
|
||||
for multiple past days so the log parser will pick them up — data is only
|
||||
aggregated a day in arrears, so all dates are strictly before today.
|
||||
|
||||
Usage:
|
||||
python inject_test_logs.py # writes to /logs (default)
|
||||
python inject_test_logs.py /tmp/logs # writes to custom path
|
||||
python inject_test_logs.py /logs 14 # writes 14 days of data
|
||||
"""
|
||||
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
import hashlib
|
||||
import struct
|
||||
from datetime import date, timedelta, timezone
|
||||
|
||||
# Real, well-known public IPs that resolve in MaxMind GeoLite2 City.
|
||||
# These are actual infrastructure IPs (DNS, CDN, search, cloud) —
|
||||
# not random addresses, so they're far more likely to be in the DB.
|
||||
# Each tuple: (ip, expected_country, expected_city)
|
||||
TEST_IPS = [
|
||||
# United States
|
||||
("8.8.8.8", "US", "Mountain View"), # Google DNS
|
||||
("8.8.4.4", "US", "Mountain View"), # Google DNS
|
||||
("216.58.214.206", "US", "Mountain View"), # Google
|
||||
("142.250.80.46", "US", "Mountain View"), # Google
|
||||
("13.107.42.14", "US", "Redmond"), # Microsoft
|
||||
("20.190.128.1", "US", "Boydton"), # Microsoft Azure
|
||||
("208.67.222.222", "US", "San Francisco"), # OpenDNS
|
||||
("208.67.220.220", "US", "San Francisco"), # OpenDNS
|
||||
("151.101.1.69", "US", "San Francisco"), # Fastly
|
||||
("52.84.74.1", "US", "Seattle"), # AWS CloudFront
|
||||
("99.86.0.1", "US", "Seattle"), # AWS
|
||||
("54.239.28.85", "US", "Seattle"), # AWS
|
||||
("185.199.108.1", "US", "San Francisco"), # GitHub Pages
|
||||
# United Kingdom
|
||||
("212.58.244.3", "GB", "London"), # Virgin Media
|
||||
("178.173.57.1", "GB", "London"), # DigitalOcean
|
||||
("51.15.48.1", "GB", "London"), # OVH UK
|
||||
# France
|
||||
("51.140.137.15", "FR", "Paris"), # OVH Paris
|
||||
("193.70.150.1", "FR", "Paris"), # Free
|
||||
# Germany
|
||||
("217.160.0.1", "DE", "Frankfurt"), # Telekom
|
||||
("212.227.156.1", "DE", "Munich"), # Deutsche Telekom
|
||||
("217.69.70.1", "DE", "Frankfurt"), # Freenet
|
||||
# Japan
|
||||
("210.170.232.46", "JP", "Tokyo"), # KDDI
|
||||
("202.214.69.1", "JP", "Tokyo"), # NTT
|
||||
("210.188.206.1", "JP", "Tokyo"), # Softbank
|
||||
# Australia
|
||||
("1.35.128.1", "AU", "Sydney"), # Telstra
|
||||
("203.50.224.1", "AU", "Melbourne"), # iiNet
|
||||
# Brazil
|
||||
("177.72.120.1", "BR", "Sao Paulo"), # Vivo
|
||||
("200.149.130.1", "BR", "Rio de Janeiro"), # Telefonica Brasil
|
||||
("189.75.1.1", "BR", "Sao Paulo"), # Claro
|
||||
# Canada
|
||||
("76.69.248.1", "CA", "Toronto"), # Bell
|
||||
("24.199.12.1", "CA", "Vancouver"), # Shaw
|
||||
("99.248.1.1", "CA", "Montreal"), # Bell
|
||||
# India
|
||||
("117.239.1.1", "IN", "Mumbai"), # Reliance Jio
|
||||
("180.151.1.1", "IN", "New Delhi"), # Airtel
|
||||
("106.192.1.1", "IN", "Bangalore"), # BSNL
|
||||
# South Korea
|
||||
("1.235.198.1", "KR", "Seoul"), # KT
|
||||
("211.36.1.1", "KR", "Seoul"), # SK Telecom
|
||||
# Netherlands
|
||||
("185.30.1.1", "NL", "Amsterdam"), # KPN
|
||||
("94.23.1.1", "NL", "Amsterdam"), # XS4ALL
|
||||
# Mexico
|
||||
("189.203.1.1", "MX", "Mexico City"), # Telmex
|
||||
("200.44.1.1", "MX", "Guadalajara"), # Telcel
|
||||
# Italy
|
||||
("217.20.1.1", "IT", "Rome"), # Telecom Italia
|
||||
("93.45.1.1", "IT", "Milan"), # TIM
|
||||
# Spain
|
||||
("213.99.1.1", "ES", "Madrid"), # Telefonica Espana
|
||||
("88.0.1.1", "ES", "Madrid"), # Movistar
|
||||
# Sweden
|
||||
("213.248.1.1", "SE", "Stockholm"), # Telia
|
||||
# Poland
|
||||
("79.133.1.1", "PL", "Warsaw"), # Orange Polska
|
||||
# Argentina
|
||||
("190.15.1.1", "AR", "Buenos Aires"), # Telecom Argentina
|
||||
# South Africa
|
||||
("196.201.1.1", "ZA", "Johannesburg"), # Telkom SA
|
||||
# Egypt
|
||||
("213.53.1.1", "EG", "Cairo"), # Telecom Egypt
|
||||
# Turkey
|
||||
("37.146.1.1", "TR", "Istanbul"), # Turk Telekom
|
||||
# Russia
|
||||
("77.88.55.1", "RU", "Moscow"), # Yandex DNS
|
||||
("5.255.255.1", "RU", "Moscow"), # Rostelecom
|
||||
# Singapore
|
||||
("182.16.1.1", "SG", "Singapore"), # Singtel
|
||||
# New Zealand
|
||||
("210.55.1.1", "NZ", "Auckland"), # Telecom NZ
|
||||
]
|
||||
|
||||
|
||||
def _deterministic_count(seed: str) -> int:
|
||||
"""Return a pseudo-random visit count (1-8) for a given seed."""
|
||||
h = hashlib.md5(seed.encode()).digest()
|
||||
return struct.unpack("I", h[:4])[0] % 8 + 1
|
||||
|
||||
|
||||
def _deterministic_hour(seed: str) -> int:
|
||||
"""Return a pseudo-random hour (0-23) for a given seed."""
|
||||
h = hashlib.md5(seed.encode()).digest()
|
||||
return struct.unpack("I", h[:4])[0] % 24
|
||||
|
||||
|
||||
def write_test_logs(logs_dir: str, days: int = 14):
|
||||
"""Write test log entries for the given number of past days.
|
||||
|
||||
Skips today (parser only processes files strictly before today).
|
||||
Writes a realistic mix of visitors per day with varying counts.
|
||||
"""
|
||||
os.makedirs(logs_dir, exist_ok=True)
|
||||
|
||||
today = date.today()
|
||||
total_entries = 0
|
||||
files_written = 0
|
||||
|
||||
# Write for the last `days` days, skipping today
|
||||
for offset in range(1, days + 1):
|
||||
file_date = today - timedelta(days=offset)
|
||||
|
||||
filename = f"access-{file_date.isoformat()}.log"
|
||||
filepath = os.path.join(logs_dir, filename)
|
||||
|
||||
entries: list[str] = []
|
||||
for ip, country, city in TEST_IPS:
|
||||
seed = f"{file_date}-{ip}"
|
||||
count = _deterministic_count(seed)
|
||||
hour = _deterministic_hour(seed)
|
||||
|
||||
for j in range(count):
|
||||
# Deterministic minute spread (hash() is non-deterministic across runs)
|
||||
ip_hash = struct.unpack("I", hashlib.md5(ip.encode()).digest()[4:8])[0]
|
||||
minute = (j * 7 + ip_hash % 60) % 60
|
||||
entry = {
|
||||
"time": f"{file_date}T{hour}:{minute:02d}:00+00:00",
|
||||
"remote_addr": ip,
|
||||
"x_forwarded_for": "-",
|
||||
}
|
||||
entries.append(json.dumps(entry))
|
||||
|
||||
with open(filepath, "a") as f:
|
||||
for line in entries:
|
||||
f.write(line + "\n")
|
||||
|
||||
total_entries += len(entries)
|
||||
files_written += 1
|
||||
|
||||
print(f"Wrote {total_entries} log entries across {files_written} daily files ({len(TEST_IPS)} unique IPs)")
|
||||
print(f"Date range: {(today - timedelta(days=days)).isoformat()} to {(today - timedelta(days=1)).isoformat()}")
|
||||
print(f"Directory: {logs_dir}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
target = sys.argv[1] if len(sys.argv) > 1 else "/logs"
|
||||
num_days = int(sys.argv[2]) if len(sys.argv) > 2 else 14
|
||||
write_test_logs(target, days=num_days)
|
||||
Reference in New Issue
Block a user