feat(logs): date filter + clear log endpoints

- API GET /api/logs now accepts from_ts / to_ts (unix epoch, half-open
  [from, to)) so callers can scope by arbitrary time range.
- API DELETE /api/logs added. Same from_ts / to_ts semantics. No params
  = wipe everything and reset the AUTOINCREMENT counter.
- Dashboard Logs page: date picker that scopes both the view and the
  delete button to the selected day in the user's local timezone. The
  clear button is red and confirms before deleting; label switches
  between "전체 로그 초기화" and "<날짜> 하루치 삭제".
This commit is contained in:
2026-05-23 17:55:58 +09:00
parent 8312cfe861
commit 7362b45846
4 changed files with 116 additions and 5 deletions

View File

@@ -1,4 +1,4 @@
"""접속 로그 조회."""
"""접속 로그 조회 및 초기화."""
from __future__ import annotations
import sqlite3
@@ -15,16 +15,26 @@ def list_logs(
limit: int = Query(50, ge=1, le=500),
offset: int = Query(0, ge=0),
action: str | None = Query(None),
from_ts: float | None = Query(None, description="unix epoch seconds, inclusive"),
to_ts: float | None = Query(None, description="unix epoch seconds, exclusive"),
) -> dict:
if not LOG_DB.exists():
return {"total": 0, "items": []}
con = sqlite3.connect(LOG_DB)
try:
con.row_factory = sqlite3.Row
where, params = "", []
conds: list[str] = []
params: list = []
if action:
where = "WHERE action = ?"
conds.append("action = ?")
params.append(action)
if from_ts is not None:
conds.append("ts >= ?")
params.append(from_ts)
if to_ts is not None:
conds.append("ts < ?")
params.append(to_ts)
where = ("WHERE " + " AND ".join(conds)) if conds else ""
total = con.execute(
f"SELECT COUNT(*) FROM connections {where}", params
).fetchone()[0]
@@ -36,3 +46,40 @@ def list_logs(
finally:
con.close()
return {"total": total, "items": [dict(r) for r in rows]}
@router.delete("/logs")
def clear_logs(
from_ts: float | None = Query(None, description="ts >= from_ts 만 삭제"),
to_ts: float | None = Query(None, description="ts < to_ts 만 삭제"),
) -> dict:
"""접속 로그를 삭제한다.
- from_ts/to_ts 둘 다 없으면 전체 삭제 (AUTOINCREMENT 카운터까지 리셋)
- 둘 중 하나만 있으면 그 조건만 적용
- 둘 다 있으면 [from_ts, to_ts) 범위 삭제 — 날짜 선택 삭제 용도
"""
if not LOG_DB.exists():
return {"deleted": 0}
con = sqlite3.connect(LOG_DB, timeout=5)
try:
conds: list[str] = []
params: list = []
if from_ts is not None:
conds.append("ts >= ?")
params.append(from_ts)
if to_ts is not None:
conds.append("ts < ?")
params.append(to_ts)
if conds:
where = "WHERE " + " AND ".join(conds)
cur = con.execute(f"DELETE FROM connections {where}", params)
deleted = cur.rowcount
else:
cur = con.execute("DELETE FROM connections")
deleted = cur.rowcount
con.execute("DELETE FROM sqlite_sequence WHERE name='connections'")
con.commit()
finally:
con.close()
return {"deleted": deleted}