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(
|
async def get_current_user(
|
||||||
credentials: HTTPAuthorizationCredentials = Depends(security),
|
credentials: HTTPAuthorizationCredentials = Depends(security),
|
||||||
) -> dict:
|
) -> dict:
|
||||||
@@ -69,6 +104,7 @@ async def get_current_user(
|
|||||||
detail="Nicht authentifiziert",
|
detail="Nicht authentifiziert",
|
||||||
)
|
)
|
||||||
payload = decode_token(credentials.credentials)
|
payload = decode_token(credentials.credentials)
|
||||||
|
await _track_activity(int(payload["sub"]), payload.get("tenant_id"))
|
||||||
return {
|
return {
|
||||||
"id": int(payload["sub"]),
|
"id": int(payload["sub"]),
|
||||||
"username": payload["username"],
|
"username": payload["username"],
|
||||||
|
|||||||
@@ -920,6 +920,24 @@ async def init_db():
|
|||||||
await db.commit()
|
await db.commit()
|
||||||
logger.info("Migration: billing_tariff angelegt und mit Konfigurationswerten befuellt")
|
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
|
# Migration: Token-Usage-Monatstabelle
|
||||||
cursor = await db.execute("SELECT name FROM sqlite_master WHERE type='table' AND name='token_usage_monthly'")
|
cursor = await db.execute("SELECT name FROM sqlite_master WHERE type='table' AND name='token_usage_monthly'")
|
||||||
if not await cursor.fetchone():
|
if not await cursor.fetchone():
|
||||||
|
|||||||
In neuem Issue referenzieren
Einen Benutzer sperren