@router.get("/audit") async def list_audit(action: str = Query(""), page: int = Query(1, ge=1), page_size: int = Query(50, ge=1, le=200), db: AsyncSession = Depends(get_db), user: User = Depends(get_current_user)): """Audit log (own actions only). Secrets are redacted at write time.""" q = select(AuditLog).where(AuditLog.user_id == user.id) if action: q = q.where(AuditLog.action == action) total = (await db.execute( select(func.count()).select_from(q.subquery()))).scalar() or 0 rows = (await db.execute( q.order_by(AuditLog.id.desc()).offset((page - 1) * page_size).limit(page_size) )).scalars().all() out = [] for r in rows: out.append({ "id": r.id, "action": r.action, "entity": r.entity, "entity_id": r.entity_id, "detail": r.detail if isinstance(r.detail, dict) else {}, "ip_address": r.ip_address, "created_at": r.created_at.isoformat() if r.created_at else None, }) return {"audit": out, "total": total, "page": page, "page_size": page_size} @router.get("/dlq") async def list_dlq(page: int = Query(1, ge=1), page_size: int = Query(50, ge=1, le=200), user: User = Depends(get_current_user)): """Dead-letter queue (Redis list dlq:send): jobs that exhausted retries.""" import redis as redis_lib from app.core.config import settings r = redis_lib.Redis.from_url(settings.REDIS_URL, decode_responses=True) try: items = r.lrange("dlq:send", 0, -1) except Exception as e: # Redis down — don't 500 the whole logs page return {"dlq": [], "total": 0, "error": f"Redis unavailable: {e}"} import json as _json out = [] for raw in items: try: out.append(_json.loads(raw)) except Exception: out.append({"raw": raw[:200]}) total = len(out)