Açıklama Yok

mvp_repo.py 4.9KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154
  1. from __future__ import annotations
  2. from datetime import datetime, timezone
  3. from typing import Any
  4. from psycopg.types.json import Json
  5. from app.db import get_conn
  6. def utc_now() -> datetime:
  7. return datetime.now(timezone.utc)
  8. DEFAULT_POLICY: dict[str, Any] = {
  9. "escalate_severities": ["high", "critical"],
  10. "vpn": {
  11. "allowed_country": "TH",
  12. "exception_users": [],
  13. },
  14. "risk": {
  15. "weights": {
  16. "outside_thailand": 50,
  17. "admin": 20,
  18. "off_hours": 15,
  19. "first_seen_country": 15,
  20. },
  21. "thresholds": {
  22. "high": 70,
  23. "medium": 40,
  24. },
  25. },
  26. "shuffle": {
  27. "ioc_workflow_id": "",
  28. },
  29. }
  30. class MvpRepository:
  31. def has_event(self, source: str, event_id: str) -> bool:
  32. with get_conn() as conn, conn.cursor() as cur:
  33. cur.execute(
  34. "SELECT 1 FROM incident_events WHERE source = %s AND event_id = %s LIMIT 1",
  35. (source, event_id),
  36. )
  37. return cur.fetchone() is not None
  38. def ensure_policy(self) -> None:
  39. with get_conn() as conn, conn.cursor() as cur:
  40. cur.execute("SELECT id FROM policy_config WHERE id = 1")
  41. found = cur.fetchone()
  42. if not found:
  43. cur.execute(
  44. "INSERT INTO policy_config(id, data) VALUES (1, %s)",
  45. (Json(DEFAULT_POLICY),),
  46. )
  47. def get_policy(self) -> dict[str, Any]:
  48. self.ensure_policy()
  49. with get_conn() as conn, conn.cursor() as cur:
  50. cur.execute("SELECT data FROM policy_config WHERE id = 1")
  51. row = cur.fetchone()
  52. return dict(row["data"]) if row else dict(DEFAULT_POLICY)
  53. def update_policy(self, data: dict[str, Any]) -> dict[str, Any]:
  54. with get_conn() as conn, conn.cursor() as cur:
  55. cur.execute(
  56. """
  57. INSERT INTO policy_config(id, data, updated_at)
  58. VALUES (1, %s, NOW())
  59. ON CONFLICT (id)
  60. DO UPDATE SET data = EXCLUDED.data, updated_at = NOW()
  61. RETURNING data
  62. """,
  63. (Json(data),),
  64. )
  65. row = cur.fetchone()
  66. return dict(row["data"])
  67. def get_incident(self, incident_key: str) -> dict[str, Any] | None:
  68. with get_conn() as conn, conn.cursor() as cur:
  69. cur.execute(
  70. "SELECT incident_key, iris_case_id, status, severity, first_seen, last_seen FROM incident_index WHERE incident_key = %s",
  71. (incident_key,),
  72. )
  73. row = cur.fetchone()
  74. return dict(row) if row else None
  75. def upsert_incident(
  76. self,
  77. incident_key: str,
  78. severity: str,
  79. status: str,
  80. iris_case_id: str | None,
  81. ) -> dict[str, Any]:
  82. now = utc_now()
  83. with get_conn() as conn, conn.cursor() as cur:
  84. cur.execute(
  85. """
  86. INSERT INTO incident_index(incident_key, iris_case_id, status, severity, first_seen, last_seen)
  87. VALUES (%s, %s, %s, %s, %s, %s)
  88. ON CONFLICT (incident_key)
  89. DO UPDATE SET
  90. iris_case_id = COALESCE(EXCLUDED.iris_case_id, incident_index.iris_case_id),
  91. status = EXCLUDED.status,
  92. severity = EXCLUDED.severity,
  93. last_seen = EXCLUDED.last_seen
  94. RETURNING incident_key, iris_case_id, status, severity, first_seen, last_seen
  95. """,
  96. (incident_key, iris_case_id, status, severity, now, now),
  97. )
  98. return dict(cur.fetchone())
  99. def add_event(
  100. self,
  101. incident_key: str,
  102. event_id: str | None,
  103. source: str,
  104. event_type: str,
  105. raw_payload: dict[str, Any],
  106. decision_trace: dict[str, Any],
  107. ) -> None:
  108. with get_conn() as conn, conn.cursor() as cur:
  109. cur.execute(
  110. """
  111. INSERT INTO incident_events(incident_key, event_id, source, event_type, raw_payload, decision_trace)
  112. VALUES (%s, %s, %s, %s, %s, %s)
  113. """,
  114. (
  115. incident_key,
  116. event_id,
  117. source,
  118. event_type,
  119. Json(raw_payload),
  120. Json(decision_trace),
  121. ),
  122. )
  123. def add_escalation_audit(
  124. self,
  125. incident_key: str,
  126. status_code: int | None,
  127. success: bool,
  128. response_excerpt: str | None,
  129. ) -> None:
  130. with get_conn() as conn, conn.cursor() as cur:
  131. cur.execute(
  132. """
  133. INSERT INTO escalation_audit(incident_key, status_code, success, response_excerpt)
  134. VALUES (%s, %s, %s, %s)
  135. """,
  136. (incident_key, status_code, success, response_excerpt),
  137. )