- P0/P1 审计修复: 滑块验证码不再下发 x_position/成败即销毁 key、 user-login 补失败计数+滑块门控、限流标识改 X-Real-IP、 百度翻译 appkey 环境化、ChangeEmail/ChangePhone 补调 avalidate、 logs/tasks.py Count(filter=Q) 修复、chat 收藏 SSRF 内网黑名单 - P1 #6/7: token_blacklist + ROTATE_REFRESH_TOKENS 开启, /user/token/refresh/ 挂载 - #2 JWT HttpOnly Cookie 双模认证: user/cookie_auth.py 种/清 Cookie, user/authentication.py CookieOrHeaderJWTAuthentication(Bearer 优先/_COOKIE 兜底), user/views/token.py CookieTokenRefreshView + UserLogoutAPIView(/user/logout/), create_standardized_response 自动对含 token 的响应种 Cookie, 异步视图内 RefreshToken.for_user 全部 sync_to_async 包裹(修 SynchronousOnlyOperation 500), WS ChatConsumer 优先读 Cookie token - P2 #11 限流: utils/rate_limit.py 固定窗口频控, shorturl 生成 匿名10次/分+登录60次/分, 邮箱验证码 同邮箱60s1次+同IP10次/10min, 登录/注册验证码 错5次作废+成功即销毁防重放, 换绑邮箱/手机 同步落地, urls.py 补挂 shorturl 路由(此前 404)
63 lines
2.1 KiB
Python
63 lines
2.1 KiB
Python
from celery import shared_task
|
||
from django.utils import timezone
|
||
from datetime import timedelta
|
||
|
||
|
||
@shared_task
|
||
def cleanup_old_request_logs():
|
||
threshold = timezone.now() - timedelta(days=30)
|
||
from .models import ApiRequestLog
|
||
deleted_count, _ = ApiRequestLog.objects.filter(timestamp__lt=threshold).delete()
|
||
return f'已清理 {deleted_count} 条过期请求日志'
|
||
|
||
|
||
@shared_task
|
||
def cleanup_old_error_logs():
|
||
threshold = timezone.now() - timedelta(days=90)
|
||
from .models import ErrorLog
|
||
deleted_count, _ = ErrorLog.objects.filter(
|
||
timestamp__lt=threshold,
|
||
is_resolved=True
|
||
).delete()
|
||
return f'已清理 {deleted_count} 条已处理的过期错误日志'
|
||
|
||
|
||
@shared_task
|
||
def cleanup_old_event_logs():
|
||
threshold = timezone.now() - timedelta(days=60)
|
||
from .models import SystemEventLog
|
||
deleted_count, _ = SystemEventLog.objects.filter(timestamp__lt=threshold).delete()
|
||
return f'已清理 {deleted_count} 条过期事件日志'
|
||
|
||
|
||
@shared_task
|
||
def generate_daily_stats():
|
||
from .models import ApiRequestLog, ErrorLog
|
||
from django.db.models import Count, Avg, Q
|
||
|
||
today_start = timezone.now().replace(hour=0, minute=0, second=0, microsecond=0)
|
||
yesterday_start = today_start - timedelta(days=1)
|
||
|
||
yesterday_stats = ApiRequestLog.objects.filter(
|
||
timestamp__gte=yesterday_start,
|
||
timestamp__lt=today_start
|
||
).aggregate(
|
||
total_requests=Count('id'),
|
||
# 修复:Count 的 filter 参数必须为 Q 对象(传 dict 会在查询编译时抛 AttributeError)
|
||
error_count=Count('id', filter=Q(is_error=True)),
|
||
avg_duration=Avg('duration_ms'),
|
||
)
|
||
|
||
error_count = ErrorLog.objects.filter(
|
||
timestamp__gte=yesterday_start,
|
||
timestamp__lt=today_start
|
||
).count()
|
||
|
||
return {
|
||
'date': yesterday_start.date().isoformat(),
|
||
'total_requests': yesterday_stats['total_requests'],
|
||
'error_requests': yesterday_stats['error_count'],
|
||
'error_logs': error_count,
|
||
'avg_duration_ms': round(yesterday_stats['avg_duration'] or 0, 2),
|
||
}
|