- proxy: asyncio TCP proxy with handshake parser, domain whitelist, transparent backend tunneling, SQLite logging, mtime hot reload - api: FastAPI routes for config/domains/logs/status + restart trigger - frontend: React + Vite NPM-style dashboard (dashboard/domains/logs/settings) - nginx: reverse proxy for /api -> api:8000 and / -> frontend:3000 - docker-compose: full stack with shared data volume - replace spec mc-domain-filter.md with README.md
39 lines
1.0 KiB
Python
39 lines
1.0 KiB
Python
"""접속 로그 조회."""
|
|
from __future__ import annotations
|
|
|
|
import sqlite3
|
|
|
|
from fastapi import APIRouter, Query
|
|
|
|
from config_io import LOG_DB
|
|
|
|
router = APIRouter()
|
|
|
|
|
|
@router.get("/logs")
|
|
def list_logs(
|
|
limit: int = Query(50, ge=1, le=500),
|
|
offset: int = Query(0, ge=0),
|
|
action: str | None = Query(None),
|
|
) -> dict:
|
|
if not LOG_DB.exists():
|
|
return {"total": 0, "items": []}
|
|
con = sqlite3.connect(LOG_DB)
|
|
try:
|
|
con.row_factory = sqlite3.Row
|
|
where, params = "", []
|
|
if action:
|
|
where = "WHERE action = ?"
|
|
params.append(action)
|
|
total = con.execute(
|
|
f"SELECT COUNT(*) FROM connections {where}", params
|
|
).fetchone()[0]
|
|
rows = con.execute(
|
|
f"SELECT id, ts, client_ip, domain, next_state, action, reason "
|
|
f"FROM connections {where} ORDER BY id DESC LIMIT ? OFFSET ?",
|
|
[*params, limit, offset],
|
|
).fetchall()
|
|
finally:
|
|
con.close()
|
|
return {"total": total, "items": [dict(r) for r in rows]}
|