Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 7cd7709b4a | |||
| 9cd50d1257 | |||
| 646d61f72e | |||
| 5f7a98f21c | |||
| 19ed231a31 |
@@ -12,6 +12,20 @@ alerts_collection = db["alerts"]
|
|||||||
logger = structlog.get_logger("aoc.database")
|
logger = structlog.get_logger("aoc.database")
|
||||||
|
|
||||||
|
|
||||||
|
def _dedupe_alert_rules():
|
||||||
|
"""Remove duplicate alert_rules by name, keeping the oldest document."""
|
||||||
|
try:
|
||||||
|
pipeline = [
|
||||||
|
{"$sort": {"_id": ASCENDING}},
|
||||||
|
{"$group": {"_id": "$name", "first_id": {"$first": "$_id"}}},
|
||||||
|
]
|
||||||
|
seen = {doc["_id"]: doc["first_id"] for doc in db["alert_rules"].aggregate(pipeline)}
|
||||||
|
for name, keep_id in seen.items():
|
||||||
|
db["alert_rules"].delete_many({"name": name, "_id": {"$ne": keep_id}})
|
||||||
|
except Exception:
|
||||||
|
pass # Collection may not exist yet
|
||||||
|
|
||||||
|
|
||||||
def setup_indexes(max_retries: int = 5, delay: float = 2.0):
|
def setup_indexes(max_retries: int = 5, delay: float = 2.0):
|
||||||
"""Ensure MongoDB indexes exist. Retries on connection errors."""
|
"""Ensure MongoDB indexes exist. Retries on connection errors."""
|
||||||
from time import sleep
|
from time import sleep
|
||||||
@@ -23,6 +37,8 @@ def setup_indexes(max_retries: int = 5, delay: float = 2.0):
|
|||||||
events_collection.create_index([("service", ASCENDING), ("timestamp", DESCENDING)])
|
events_collection.create_index([("service", ASCENDING), ("timestamp", DESCENDING)])
|
||||||
events_collection.create_index("id")
|
events_collection.create_index("id")
|
||||||
saved_searches_collection.create_index([("created_by", ASCENDING), ("created_at", DESCENDING)])
|
saved_searches_collection.create_index([("created_by", ASCENDING), ("created_at", DESCENDING)])
|
||||||
|
_dedupe_alert_rules()
|
||||||
|
db["alert_rules"].create_index("name", unique=True)
|
||||||
events_collection.create_index(
|
events_collection.create_index(
|
||||||
[("actor_display", TEXT), ("raw_text", TEXT), ("operation", TEXT)],
|
[("actor_display", TEXT), ("raw_text", TEXT), ("operation", TEXT)],
|
||||||
name="text_search_index",
|
name="text_search_index",
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ from datetime import UTC, datetime, timedelta
|
|||||||
import structlog
|
import structlog
|
||||||
from config import ALERT_DEDUPE_MINUTES, ALERT_WEBHOOK_FORMAT, ALERT_WEBHOOK_URL
|
from config import ALERT_DEDUPE_MINUTES, ALERT_WEBHOOK_FORMAT, ALERT_WEBHOOK_URL
|
||||||
from database import db
|
from database import db
|
||||||
|
from pymongo import ASCENDING
|
||||||
|
|
||||||
logger = structlog.get_logger("aoc.rules")
|
logger = structlog.get_logger("aoc.rules")
|
||||||
rules_collection = db["alert_rules"]
|
rules_collection = db["alert_rules"]
|
||||||
@@ -136,9 +137,15 @@ def _create_alert(rule: dict, event: dict):
|
|||||||
|
|
||||||
|
|
||||||
def seed_default_rules():
|
def seed_default_rules():
|
||||||
"""Insert pre-built admin-ops rule templates if the collection is empty."""
|
"""Upsert pre-built admin-ops rule templates. Safe for concurrent startup."""
|
||||||
if rules_collection.count_documents({}) > 0:
|
# One-time cleanup: remove duplicates by name, keep the oldest (_id ascending)
|
||||||
return
|
pipeline = [
|
||||||
|
{"$sort": {"_id": ASCENDING}},
|
||||||
|
{"$group": {"_id": "$name", "first_id": {"$first": "$_id"}}},
|
||||||
|
]
|
||||||
|
seen = {doc["_id"]: doc["first_id"] for doc in rules_collection.aggregate(pipeline)}
|
||||||
|
for name, keep_id in seen.items():
|
||||||
|
rules_collection.delete_many({"name": name, "_id": {"$ne": keep_id}})
|
||||||
|
|
||||||
defaults = [
|
defaults = [
|
||||||
{
|
{
|
||||||
@@ -261,8 +268,17 @@ def seed_default_rules():
|
|||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
try:
|
inserted = 0
|
||||||
rules_collection.insert_many(defaults)
|
for rule in defaults:
|
||||||
logger.info("Default admin-ops rules seeded", count=len(defaults))
|
try:
|
||||||
except Exception as exc:
|
result = rules_collection.replace_one(
|
||||||
logger.warning("Failed to seed default rules", error=str(exc))
|
{"name": rule["name"]},
|
||||||
|
rule,
|
||||||
|
upsert=True,
|
||||||
|
)
|
||||||
|
if result.upserted_id:
|
||||||
|
inserted += 1
|
||||||
|
except Exception as exc:
|
||||||
|
logger.warning("Failed to seed rule", rule=rule["name"], error=str(exc))
|
||||||
|
if inserted:
|
||||||
|
logger.info("Default admin-ops rules seeded", inserted=inserted, total=len(defaults))
|
||||||
|
|||||||
Reference in New Issue
Block a user