harden deploy reports and admin alerts

This commit is contained in:
VPN SaaS Dev
2026-05-18 18:17:53 +09:00
parent 2d5695fdce
commit 22b9b40d78
12 changed files with 549 additions and 31 deletions

View File

@@ -41,7 +41,13 @@ async def check_rate_limit(
if settings.redis_url:
allowed = await check_redis_rate_limit(scope, identifiers, limit, window_seconds)
if not allowed:
await log_rate_limit_event(session, scope=scope, identifier="redis")
await log_rate_limit_event(
session,
scope=scope,
identifier="redis",
user=user,
request=request,
)
raise_rate_limit(scope, window_seconds)
return
@@ -52,7 +58,13 @@ async def check_rate_limit(
while bucket and now - bucket[0] > window_seconds:
bucket.popleft()
if len(bucket) >= limit:
await log_rate_limit_event(session, scope=scope, identifier=str(identifier))
await log_rate_limit_event(
session,
scope=scope,
identifier=str(identifier),
user=user,
request=request,
)
raise_rate_limit(scope, window_seconds)
for identifier in identifiers:
_buckets[(scope, identifier)].append(now)
@@ -107,18 +119,82 @@ async def log_rate_limit_event(
*,
scope: str,
identifier: str,
user: User | None = None,
request: Request | None = None,
) -> None:
if session is None:
return
from app.models.car import AuditLog
client_host = request.client.host if request and request.client else None
user_agent = request.headers.get("user-agent") if request else None
metadata = {
"scope": scope,
"identifier": identifier,
"telegram_id": user.telegram_id if user else None,
"user_id": user.id if user else None,
"ip": client_host,
}
session.add(
AuditLog(
actor_user_id=None,
actor_role="system",
action="rate_limit.exceeded",
target_type=scope,
target_id=identifier[:80],
metadata_json={"scope": scope, "identifier": identifier},
)
if session is None:
from app.db.session import async_session_factory
async with async_session_factory() as event_session:
await persist_rate_limit_event(
event_session,
scope=scope,
identifier=identifier,
user=user,
client_host=client_host,
user_agent=user_agent,
metadata=metadata,
)
return
await persist_rate_limit_event(
session,
scope=scope,
identifier=identifier,
user=user,
client_host=client_host,
user_agent=user_agent,
metadata=metadata,
)
async def persist_rate_limit_event(
event_session: AsyncSession,
*,
scope: str,
identifier: str,
user: User | None,
client_host: str | None,
user_agent: str | None,
metadata: dict,
) -> None:
from app.models.car import AuditLog
from app.services.admin_notifications import create_admin_notification
try:
event_session.add(
AuditLog(
actor_user_id=user.id if user else None,
actor_role=user.platform_role if user else "system",
action="rate_limit.exceeded",
target_type=scope,
target_id=identifier[:80],
metadata_json=metadata,
ip=client_host,
user_agent=user_agent[:256] if user_agent else None,
)
)
await create_admin_notification(
event_session,
event_type="rate_limit_exceeded",
title="Rate limit exceeded",
body=f"Scope: {scope}\nIdentifier: {identifier}",
entity_type="user" if user else "system",
entity_id=user.id if user else scope,
severity="warning",
idempotency_key=f"rate_limit:{scope}:{identifier}:{int(time.time() // max(60, 1))}",
metadata=metadata,
)
await event_session.commit()
except Exception:
await event_session.rollback()