Promote develop → main (2026-07-25 23:31 UTC) #53
36
src/auth.py
36
src/auth.py
@@ -59,6 +59,41 @@ def decode_token(token: str) -> dict:
|
||||
)
|
||||
|
||||
|
||||
# --- Aktivitaets-Erfassung fuer MAU/DAU ---------------------------------------
|
||||
# Je Nutzer und Kalendertag genau ein Eintrag in user_activity_days. Der
|
||||
# In-Memory-Merker haelt die DB-Last bei einem INSERT je Nutzer und Tag.
|
||||
# Ein Fehler hier darf NIEMALS eine Anfrage blockieren.
|
||||
_activity_seen: set = set()
|
||||
|
||||
|
||||
async def _track_activity(user_id: int, tenant_id) -> None:
|
||||
day = datetime.now(TIMEZONE).strftime("%Y-%m-%d")
|
||||
key = (user_id, day)
|
||||
if key in _activity_seen:
|
||||
return
|
||||
_activity_seen.add(key)
|
||||
if len(_activity_seen) > 20000:
|
||||
# Speicher begrenzen, alte Tage interessieren den Merker nicht mehr
|
||||
_activity_seen.clear()
|
||||
_activity_seen.add(key)
|
||||
try:
|
||||
import aiosqlite
|
||||
from config import DB_PATH
|
||||
db = await aiosqlite.connect(DB_PATH)
|
||||
try:
|
||||
await db.execute(
|
||||
"INSERT OR IGNORE INTO user_activity_days (user_id, day, tenant_id) VALUES (?, ?, ?)",
|
||||
(user_id, day, tenant_id),
|
||||
)
|
||||
await db.commit()
|
||||
finally:
|
||||
await db.close()
|
||||
except Exception:
|
||||
# Bewusst schlucken (z.B. Tabelle fehlt noch). Der Merker behaelt den
|
||||
# Schluessel, damit ein Dauerfehler nicht jede Anfrage erneut trifft.
|
||||
pass
|
||||
|
||||
|
||||
async def get_current_user(
|
||||
credentials: HTTPAuthorizationCredentials = Depends(security),
|
||||
) -> dict:
|
||||
@@ -69,6 +104,7 @@ async def get_current_user(
|
||||
detail="Nicht authentifiziert",
|
||||
)
|
||||
payload = decode_token(credentials.credentials)
|
||||
await _track_activity(int(payload["sub"]), payload.get("tenant_id"))
|
||||
return {
|
||||
"id": int(payload["sub"]),
|
||||
"username": payload["username"],
|
||||
|
||||
@@ -920,6 +920,24 @@ async def init_db():
|
||||
await db.commit()
|
||||
logger.info("Migration: billing_tariff angelegt und mit Konfigurationswerten befuellt")
|
||||
|
||||
# Migration: Aktivitaets-Tage je Nutzer fuer die MAU/DAU-Statistik im
|
||||
# Verwaltungsportal. Geschrieben von auth.get_current_user (ein Eintrag
|
||||
# je Nutzer und Tag), gelesen nur vom Portal.
|
||||
cursor = await db.execute(
|
||||
"SELECT name FROM sqlite_master WHERE type='table' AND name='user_activity_days'"
|
||||
)
|
||||
if not await cursor.fetchone():
|
||||
await db.execute("""
|
||||
CREATE TABLE user_activity_days (
|
||||
user_id INTEGER NOT NULL,
|
||||
day TEXT NOT NULL,
|
||||
tenant_id INTEGER,
|
||||
PRIMARY KEY (user_id, day)
|
||||
)
|
||||
""")
|
||||
await db.commit()
|
||||
logger.info("Migration: user_activity_days angelegt (MAU/DAU-Erfassung)")
|
||||
|
||||
# Migration: Token-Usage-Monatstabelle
|
||||
cursor = await db.execute("SELECT name FROM sqlite_master WHERE type='table' AND name='token_usage_monthly'")
|
||||
if not await cursor.fetchone():
|
||||
|
||||
In neuem Issue referenzieren
Einen Benutzer sperren