暂无描述

mvp_repo.py 4.6KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146
  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 ensure_policy(self) -> None:
  32. with get_conn() as conn, conn.cursor() as cur:
  33. cur.execute("SELECT id FROM policy_config WHERE id = 1")
  34. found = cur.fetchone()
  35. if not found:
  36. cur.execute(
  37. "INSERT INTO policy_config(id, data) VALUES (1, %s)",
  38. (Json(DEFAULT_POLICY),),
  39. )
  40. def get_policy(self) -> dict[str, Any]:
  41. self.ensure_policy()
  42. with get_conn() as conn, conn.cursor() as cur:
  43. cur.execute("SELECT data FROM policy_config WHERE id = 1")
  44. row = cur.fetchone()
  45. return dict(row["data"]) if row else dict(DEFAULT_POLICY)
  46. def update_policy(self, data: dict[str, Any]) -> dict[str, Any]:
  47. with get_conn() as conn, conn.cursor() as cur:
  48. cur.execute(
  49. """
  50. INSERT INTO policy_config(id, data, updated_at)
  51. VALUES (1, %s, NOW())
  52. ON CONFLICT (id)
  53. DO UPDATE SET data = EXCLUDED.data, updated_at = NOW()
  54. RETURNING data
  55. """,
  56. (Json(data),),
  57. )
  58. row = cur.fetchone()
  59. return dict(row["data"])
  60. def get_incident(self, incident_key: str) -> dict[str, Any] | None:
  61. with get_conn() as conn, conn.cursor() as cur:
  62. cur.execute(
  63. "SELECT incident_key, iris_case_id, status, severity, first_seen, last_seen FROM incident_index WHERE incident_key = %s",
  64. (incident_key,),
  65. )
  66. row = cur.fetchone()
  67. return dict(row) if row else None
  68. def upsert_incident(
  69. self,
  70. incident_key: str,
  71. severity: str,
  72. status: str,
  73. iris_case_id: str | None,
  74. ) -> dict[str, Any]:
  75. now = utc_now()
  76. with get_conn() as conn, conn.cursor() as cur:
  77. cur.execute(
  78. """
  79. INSERT INTO incident_index(incident_key, iris_case_id, status, severity, first_seen, last_seen)
  80. VALUES (%s, %s, %s, %s, %s, %s)
  81. ON CONFLICT (incident_key)
  82. DO UPDATE SET
  83. iris_case_id = COALESCE(EXCLUDED.iris_case_id, incident_index.iris_case_id),
  84. status = EXCLUDED.status,
  85. severity = EXCLUDED.severity,
  86. last_seen = EXCLUDED.last_seen
  87. RETURNING incident_key, iris_case_id, status, severity, first_seen, last_seen
  88. """,
  89. (incident_key, iris_case_id, status, severity, now, now),
  90. )
  91. return dict(cur.fetchone())
  92. def add_event(
  93. self,
  94. incident_key: str,
  95. event_id: str | None,
  96. source: str,
  97. event_type: str,
  98. raw_payload: dict[str, Any],
  99. decision_trace: dict[str, Any],
  100. ) -> None:
  101. with get_conn() as conn, conn.cursor() as cur:
  102. cur.execute(
  103. """
  104. INSERT INTO incident_events(incident_key, event_id, source, event_type, raw_payload, decision_trace)
  105. VALUES (%s, %s, %s, %s, %s, %s)
  106. """,
  107. (
  108. incident_key,
  109. event_id,
  110. source,
  111. event_type,
  112. Json(raw_payload),
  113. Json(decision_trace),
  114. ),
  115. )
  116. def add_escalation_audit(
  117. self,
  118. incident_key: str,
  119. status_code: int | None,
  120. success: bool,
  121. response_excerpt: str | None,
  122. ) -> None:
  123. with get_conn() as conn, conn.cursor() as cur:
  124. cur.execute(
  125. """
  126. INSERT INTO escalation_audit(incident_key, status_code, success, response_excerpt)
  127. VALUES (%s, %s, %s, %s)
  128. """,
  129. (incident_key, status_code, success, response_excerpt),
  130. )