Statistik: anonymes Impression-Tracking mit ASN-Einordnung

Seitenaufrufe werden serverseitig pro (Tag, anonymisiertes Netz, Pfad)
gezaehlt -- nur auf den HTML-Seiten-Routen, nicht auf API/Assets. Die
Client-IP wird sofort maskiert (IPv4 /16, IPv6 /32); die vollstaendige
IP wird nie gespeichert oder weiterverwendet.

Optional loest eine lokale GeoLite2-ASN-DB (Env KVER_GEOIP_ASN) den
Netzbetreiber aus dem bereits anonymisierten Netz auf -- auch dafuer wird
nicht die volle IP herangezogen. Fehlt die DB, bleibt das Feld leer.

Neuer Endpunkt GET /stats/detail liefert die Aggregate; die Tabelle
impression speichert nur Zaehlwerte (kein Eventlog).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-17 23:21:17 +02:00
parent bbd4bc27ba
commit 662285c8b5
5 changed files with 248 additions and 2 deletions
+15
View File
@@ -49,6 +49,21 @@ CREATE TABLE IF NOT EXISTS vote (
UNIQUE(uid, pid)
);
-- Seitenaufrufe, aggregiert pro (Tag, anonymisiertes Netz, Pfad). Es wird nie
-- eine volle IP gespeichert: anon_ip ist auf /16 (IPv4) bzw. /32 (IPv6) gekürzt
-- (siehe maskIP). asn ist das autonome System (Netzbetreiber), aufgelöst aus dem
-- bereits anonymisierten Netz (siehe lookupASN) -- nicht im PK, da pro Netz
-- deterministisch. Aggregation statt Eventlog -> die Tabelle wächst nur mit
-- Tag × Netz × Pfad, nicht pro Request.
CREATE TABLE IF NOT EXISTS impression (
day TEXT NOT NULL,
anon_ip TEXT NOT NULL,
path TEXT NOT NULL DEFAULT '',
asn TEXT NOT NULL DEFAULT '',
hits INTEGER NOT NULL DEFAULT 0,
PRIMARY KEY (day, anon_ip, path)
);
-- Indizes für die häufigen Zugriffspfade; ohne sie werden Feed (ORDER BY
-- last_activity), Thread (WHERE reply_to) und Vote-Zähler (WHERE pid) mit
-- wachsender Tabelle zu Full-Table-Scans. username/session.value/vote(uid,pid)