import json import os import time import uuid import hashlib from pathlib import Path from nats.aio.client import Client as NATS import asyncio NATS_URL = os.getenv('NATS_URL', 'nats://localhost:4222') # Default: /app/logs (mounted rw volume) або /tmp як fallback AUDIT_FILE = os.getenv('AGX_AUDIT_FILE', '/app/logs/stepan_audit.log.jsonl') def _hash(text: str): return hashlib.sha256(text.encode()).hexdigest()[:16] async def _publish_nats(subject: str, payload: dict): nc = NATS() await nc.connect(servers=[NATS_URL], connect_timeout=2) await nc.publish(subject, json.dumps(payload).encode()) await nc.flush(1) await nc.drain() def audit_event(event: dict): try: Path(AUDIT_FILE).parent.mkdir(parents=True, exist_ok=True) with open(AUDIT_FILE, 'a', encoding='utf-8') as f: f.write(json.dumps(event, ensure_ascii=False) + '\n') except Exception: pass try: asyncio.run(_publish_nats('agx.audit.delegation', event)) except Exception: pass def new_trace(user_request: str): return { 'trace_id': str(uuid.uuid4()), 'user_request_hash': _hash(user_request), 'ts': int(time.time() * 1000) }