feat: 주요 CLI, 인증, 서비스 로직 및 문서 개선
This commit is contained in:
@@ -345,6 +345,30 @@ class PostgresPortfolioRepository:
|
||||
ON portfolio_tags (tag_value, portfolio_id)
|
||||
"""
|
||||
)
|
||||
cur.execute(
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS auth_audit_logs (
|
||||
id TEXT PRIMARY KEY,
|
||||
event_type TEXT NOT NULL,
|
||||
action TEXT NOT NULL,
|
||||
actor_id TEXT NOT NULL,
|
||||
bot_id TEXT NOT NULL,
|
||||
token_id_masked TEXT NOT NULL,
|
||||
token_ref_masked TEXT NOT NULL,
|
||||
result TEXT NOT NULL,
|
||||
detail TEXT,
|
||||
payload JSONB NOT NULL,
|
||||
occurred_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
)
|
||||
"""
|
||||
)
|
||||
cur.execute(
|
||||
"""
|
||||
CREATE INDEX IF NOT EXISTS idx_auth_audit_logs_bot_occurred
|
||||
ON auth_audit_logs (bot_id, occurred_at DESC)
|
||||
"""
|
||||
)
|
||||
cur.execute("SELECT EXISTS (SELECT 1 FROM pg_extension WHERE extname = 'timescaledb')")
|
||||
if cur.fetchone()[0]:
|
||||
cur.execute(
|
||||
@@ -821,6 +845,41 @@ class PostgresPortfolioRepository:
|
||||
bot_principal_id=row[4],
|
||||
)
|
||||
|
||||
def record_audit_event(self, payload: dict[str, Any]) -> None:
|
||||
self.ensure_schema()
|
||||
event_id = str(uuid.uuid4())
|
||||
occurred_at_raw = payload.get("occurred_at")
|
||||
occurred_at = datetime.now(timezone.utc)
|
||||
if isinstance(occurred_at_raw, str):
|
||||
try:
|
||||
occurred_at = datetime.fromisoformat(occurred_at_raw)
|
||||
except ValueError:
|
||||
occurred_at = datetime.now(timezone.utc)
|
||||
|
||||
with self._connect().cursor() as cur:
|
||||
cur.execute(
|
||||
"""
|
||||
INSERT INTO auth_audit_logs (
|
||||
id, event_type, action, actor_id, bot_id,
|
||||
token_id_masked, token_ref_masked, result, detail, payload, occurred_at
|
||||
)
|
||||
VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s::jsonb, %s)
|
||||
""",
|
||||
(
|
||||
event_id,
|
||||
str(payload.get("event_type", "auth.bot_token")),
|
||||
str(payload.get("action", "")),
|
||||
str(payload.get("actor_id", "unknown")),
|
||||
str(payload.get("bot_id", "unknown")),
|
||||
str(payload.get("token_id", "")),
|
||||
str(payload.get("token_ref", "")),
|
||||
str(payload.get("result", "unknown")),
|
||||
payload.get("detail"),
|
||||
json.dumps(payload, ensure_ascii=True),
|
||||
occurred_at,
|
||||
),
|
||||
)
|
||||
|
||||
def get(self, portfolio_id: str) -> Portfolio:
|
||||
self.ensure_schema()
|
||||
with self._connect().cursor() as cur:
|
||||
|
||||
Reference in New Issue
Block a user