dedc3b4183
- Full FastAPI backend with JWT auth, file management, processing pipeline, memory CRUD, barcode mappings, config management, cloud sync - Vue 3 + Element Plus frontend with dashboard, task history, HTTP logs, memory editor, barcode editor, config editor, sync page - HTTP request logging middleware with SQLite persistence - Task history tracking with progress and retry support - File metadata recording for upload/download operations - WebAuth section in config.ini for bcrypt password storage - Bug fix: logs.py count query returns tuple not dict Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
104 lines
2.9 KiB
Python
104 lines
2.9 KiB
Python
"""HTTP log query endpoints."""
|
|
|
|
import logging
|
|
import sqlite3
|
|
from pathlib import Path
|
|
from typing import Optional
|
|
|
|
from fastapi import APIRouter, Depends, Query
|
|
|
|
from ..auth.dependencies import get_current_user
|
|
from ..services.db_schema import query_http_logs, query_http_log_stats
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
router = APIRouter(prefix="/api/logs", tags=["logs"])
|
|
|
|
_db_path = Path(__file__).resolve().parent.parent.parent.parent / "data" / "web_data.db"
|
|
|
|
|
|
def _count_http_logs(
|
|
method: str = None,
|
|
path: str = None,
|
|
status_code: int = None,
|
|
start_time: str = None,
|
|
end_time: str = None,
|
|
) -> int:
|
|
"""Count total matching HTTP log rows for pagination."""
|
|
conn = sqlite3.connect(_db_path)
|
|
try:
|
|
clauses = []
|
|
params = []
|
|
if method:
|
|
clauses.append("method = ?")
|
|
params.append(method)
|
|
if path:
|
|
clauses.append("path LIKE ?")
|
|
params.append(f"%{path}%")
|
|
if status_code is not None:
|
|
clauses.append("status_code = ?")
|
|
params.append(status_code)
|
|
if start_time:
|
|
clauses.append("timestamp >= ?")
|
|
params.append(start_time)
|
|
if end_time:
|
|
clauses.append("timestamp <= ?")
|
|
params.append(end_time)
|
|
|
|
where = (" WHERE " + " AND ".join(clauses)) if clauses else ""
|
|
row = conn.execute(
|
|
f"SELECT COUNT(*) as cnt FROM http_logs{where}", params
|
|
).fetchone()
|
|
return row[0] if row else 0
|
|
finally:
|
|
conn.close()
|
|
|
|
|
|
@router.get("")
|
|
async def list_logs(
|
|
page: int = Query(1, ge=1),
|
|
page_size: int = Query(50, ge=1, le=200),
|
|
method: Optional[str] = None,
|
|
status_code: Optional[int] = None,
|
|
path: Optional[str] = None,
|
|
start_date: Optional[str] = None,
|
|
end_date: Optional[str] = None,
|
|
current_user: dict = Depends(get_current_user),
|
|
):
|
|
"""List HTTP logs with filters and pagination."""
|
|
offset = (page - 1) * page_size
|
|
items = query_http_logs(
|
|
method=method,
|
|
path=path,
|
|
status_code=status_code,
|
|
start_time=start_date,
|
|
end_time=end_date,
|
|
limit=page_size,
|
|
offset=offset,
|
|
)
|
|
total = _count_http_logs(
|
|
method=method,
|
|
path=path,
|
|
status_code=status_code,
|
|
start_time=start_date,
|
|
end_time=end_date,
|
|
)
|
|
return {"items": items, "total": total}
|
|
|
|
|
|
@router.get("/stats")
|
|
async def log_stats(
|
|
current_user: dict = Depends(get_current_user),
|
|
):
|
|
"""Get today's HTTP log statistics."""
|
|
raw = query_http_log_stats()
|
|
total = raw.get("total", 0)
|
|
errors = raw.get("errors", 0)
|
|
avg_duration = raw.get("avg_duration")
|
|
return {
|
|
"today_count": total,
|
|
"error_count": errors,
|
|
"avg_duration_ms": round(avg_duration, 2) if avg_duration else 0.0,
|
|
"error_rate": round(errors / total, 4) if total > 0 else 0.0,
|
|
}
|