feat: The Other Dude v9.0.1 — full-featured email system
ci: add GitHub Pages deployment workflow for docs site Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
70
backend/app/services/push_tracker.py
Normal file
70
backend/app/services/push_tracker.py
Normal file
@@ -0,0 +1,70 @@
|
||||
"""Track recent config pushes in Redis for poller-aware rollback.
|
||||
|
||||
When a device goes offline shortly after a push, the poller checks these
|
||||
keys and triggers rollback (template/restore) or alert (editor).
|
||||
|
||||
Redis key format: push:recent:{device_id}
|
||||
TTL: 300 seconds (5 minutes)
|
||||
"""
|
||||
|
||||
import json
|
||||
import logging
|
||||
from typing import Optional
|
||||
|
||||
import redis.asyncio as redis
|
||||
|
||||
from app.config import settings
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
PUSH_TTL_SECONDS = 300 # 5 minutes
|
||||
|
||||
_redis: Optional[redis.Redis] = None
|
||||
|
||||
|
||||
async def _get_redis() -> redis.Redis:
|
||||
global _redis
|
||||
if _redis is None:
|
||||
_redis = redis.from_url(settings.REDIS_URL)
|
||||
return _redis
|
||||
|
||||
|
||||
async def record_push(
|
||||
device_id: str,
|
||||
tenant_id: str,
|
||||
push_type: str,
|
||||
push_operation_id: str = "",
|
||||
pre_push_commit_sha: str = "",
|
||||
) -> None:
|
||||
"""Record a recent config push in Redis.
|
||||
|
||||
Args:
|
||||
device_id: UUID of the device.
|
||||
tenant_id: UUID of the tenant.
|
||||
push_type: 'template' (auto-rollback) or 'editor' (alert only) or 'restore'.
|
||||
push_operation_id: ID of the ConfigPushOperation row.
|
||||
pre_push_commit_sha: Git SHA of the pre-push backup (for rollback).
|
||||
"""
|
||||
r = await _get_redis()
|
||||
key = f"push:recent:{device_id}"
|
||||
value = json.dumps({
|
||||
"device_id": device_id,
|
||||
"tenant_id": tenant_id,
|
||||
"push_type": push_type,
|
||||
"push_operation_id": push_operation_id,
|
||||
"pre_push_commit_sha": pre_push_commit_sha,
|
||||
})
|
||||
await r.set(key, value, ex=PUSH_TTL_SECONDS)
|
||||
logger.debug(
|
||||
"Recorded push for device %s (type=%s, TTL=%ds)",
|
||||
device_id,
|
||||
push_type,
|
||||
PUSH_TTL_SECONDS,
|
||||
)
|
||||
|
||||
|
||||
async def clear_push(device_id: str) -> None:
|
||||
"""Clear the push tracking key (e.g., after successful commit)."""
|
||||
r = await _get_redis()
|
||||
await r.delete(f"push:recent:{device_id}")
|
||||
logger.debug("Cleared push tracking for device %s", device_id)
|
||||
Reference in New Issue
Block a user