from django.shortcuts import render, redirect import json import logging from django.contrib.auth.decorators import login_required from django.http import HttpResponseForbidden from django.contrib import messages from django.db import models from django.db.models import Count, Sum from django.views.decorators.http import require_POST from django.contrib.auth import get_user_model from django.db import transaction from django.urls import reverse from core.models import SystemSettings from plans.models import Plan from domains.models import Domain from billing.models import Invoice from .forms import SystemSettingsForm, PlanForm, DomainPlanSwitchForm, DomainGrantTrafficForm, InvoiceAdjustmentForm from billing.models import Invoice, InvoiceItem from decimal import Decimal from django.utils import timezone from core.goedge_client import GoEdgeClient from core.models import OperationLog def _staff_only(request): if not request.user.is_authenticated: return False return request.user.is_staff @login_required def dashboard(request): if not _staff_only(request): return HttpResponseForbidden('需要运营权限(staff)') users_count = request.user.__class__.objects.count() domains_count = Domain.objects.count() plans_count = Plan.objects.count() unpaid_invoices = Invoice.objects.filter(status='unpaid').count() if hasattr(Invoice, 'status') else 0 return render(request, 'admin_panel/dashboard.html', { 'users_count': users_count, 'domains_count': domains_count, 'plans_count': plans_count, 'unpaid_invoices': unpaid_invoices, }) @login_required def operation_logs(request): if not _staff_only(request): return HttpResponseForbidden('需要运营权限(staff)') q_actor = request.GET.get('actor', '').strip() q_action = request.GET.get('action', '').strip() q_target = request.GET.get('target', '').strip() start = request.GET.get('start') or '' end = request.GET.get('end') or '' export = request.GET.get('export') or '' qs = OperationLog.objects.select_related('actor').all() if q_actor: qs = qs.filter(actor__username__icontains=q_actor) if q_action: qs = qs.filter(action__icontains=q_action) if q_target: qs = qs.filter(target__icontains=q_target) if start: try: from django.utils.dateparse import parse_datetime, parse_date dt = parse_datetime(start) or parse_date(start) if dt: qs = qs.filter(created_at__gte=dt) except Exception: pass if end: try: from django.utils.dateparse import parse_datetime, parse_date dt = parse_datetime(end) or parse_date(end) if dt: qs = qs.filter(created_at__lte=dt) except Exception: pass qs = qs.order_by('-created_at') if export == 'csv': import csv from django.http import HttpResponse resp = HttpResponse(content_type='text/csv; charset=utf-8') resp['Content-Disposition'] = 'attachment; filename="operation_logs.csv"' writer = csv.writer(resp) writer.writerow(['时间', '操作人', '动作', '目标', '详情']) for row in qs[:5000]: writer.writerow([ row.created_at, (row.actor.username if row.actor else ''), row.action, row.target, row.detail, ]) return resp from django.core.paginator import Paginator paginator = Paginator(qs, 50) page = request.GET.get('page') or 1 try: page_obj = paginator.get_page(page) except Exception: page_obj = paginator.get_page(1) return render(request, 'admin_panel/operation_logs.html', { 'page_obj': page_obj, 'actor': q_actor, 'action': q_action, 'target': q_target, 'start': start, 'end': end, }) @login_required def settings(request): if not _staff_only(request): return HttpResponseForbidden('需要运营权限(staff)') settings_obj = SystemSettings.objects.order_by('id').first() if not settings_obj: settings_obj = SystemSettings.objects.create() if request.method == 'POST': action = request.POST.get('action') or 'save' if action == 'save': form = SystemSettingsForm(request.POST, instance=settings_obj) if form.is_valid(): form.save() messages.success(request, '系统设置已保存') return redirect('admin_panel:settings') else: messages.error(request, '保存失败,请检查表单字段') elif action == 'get_token': form = SystemSettingsForm(instance=settings_obj) try: client = GoEdgeClient() token = client._ensure_token() # 触发获取/续期 messages.success(request, f'已获取 AccessToken(已缓存):{token[:8]}...') except Exception as e: messages.error(request, f'获取 AccessToken 失败:{e}') elif action == 'test_connection': form = SystemSettingsForm(instance=settings_obj) try: client = GoEdgeClient() # 构造时即验证 base_url _ = client._headers() # 触发令牌确保 messages.success(request, '与 GoEdge API 连接正常(令牌有效)') except Exception as e: messages.error(request, f'连接测试失败:{e}') elif action == 'validate_access_log_policy': form = SystemSettingsForm(request.POST, instance=settings_obj) if form.is_valid(): pid = form.cleaned_data.get('default_http_access_log_policy_id') if not pid: messages.error(request, '请先填写访问日志策略ID') else: try: client = GoEdgeClient() exists = client.check_access_log_policy_exists(int(pid)) messages.success(request, f'访问日志策略ID {pid} 校验结果:{"存在" if exists else "不存在"}') # 操作日志 try: OperationLog.objects.create( actor=request.user, action='validate_policy_id', target='SystemSettings', detail=json.dumps({'type': 'access_log', 'policyId': int(pid), 'exists': bool(exists)}, ensure_ascii=False) ) except Exception: logger.exception('operation log write failed') except Exception as e: messages.error(request, f'访问日志策略校验失败:{e}') else: messages.error(request, '策略ID格式不正确,请检查输入') elif action == 'validate_firewall_policy': form = SystemSettingsForm(request.POST, instance=settings_obj) if form.is_valid(): pid = form.cleaned_data.get('default_http_firewall_policy_id') if not pid: messages.error(request, '请先填写WAF策略ID') else: try: client = GoEdgeClient() exists = client.check_firewall_policy_exists(int(pid)) messages.success(request, f'WAF策略ID {pid} 校验结果:{"存在" if exists else "不存在"}') # 操作日志 try: OperationLog.objects.create( actor=request.user, action='validate_policy_id', target='SystemSettings', detail=json.dumps({'type': 'firewall', 'policyId': int(pid), 'exists': bool(exists)}, ensure_ascii=False) ) except Exception: logger.exception('operation log write failed') except Exception as e: messages.error(request, f'WAF策略校验失败:{e}') else: messages.error(request, '策略ID格式不正确,请检查输入') else: form = SystemSettingsForm(instance=settings_obj) messages.error(request, '未知操作') else: form = SystemSettingsForm(instance=settings_obj) return render(request, 'admin_panel/settings.html', { 'form': form, 'settings_obj': settings_obj, }) @login_required def plans_list(request): if not _staff_only(request): return HttpResponseForbidden('需要运营权限(staff)') plans = Plan.objects.all().order_by('-is_active', 'base_price_per_domain') return render(request, 'admin_panel/plans_list.html', { 'plans': plans, }) @login_required def plan_create(request): if not _staff_only(request): return HttpResponseForbidden('需要运营权限(staff)') if request.method == 'POST': form = PlanForm(request.POST) if form.is_valid(): plan = form.save() messages.success(request, f'套餐已创建:{plan.name}') return redirect('admin_panel:plans') else: messages.error(request, '创建失败,请检查表单字段') else: form = PlanForm(initial={'billing_mode': 'per_domain_monthly'}) return render(request, 'admin_panel/plan_form.html', { 'form': form, 'is_edit': False, }) @login_required def plan_edit(request, plan_id: int): if not _staff_only(request): return HttpResponseForbidden('需要运营权限(staff)') plan = Plan.objects.filter(id=plan_id).first() if not plan: messages.error(request, '套餐不存在') return redirect('admin_panel:plans') if request.method == 'POST': form = PlanForm(request.POST, instance=plan) if form.is_valid(): form.save() messages.success(request, f'套餐已更新:{plan.name}') return redirect('admin_panel:plans') else: messages.error(request, '更新失败,请检查表单字段') else: form = PlanForm(instance=plan) return render(request, 'admin_panel/plan_form.html', { 'form': form, 'is_edit': True, 'plan': plan, }) @login_required @require_POST def plan_toggle_active(request, plan_id: int): if not _staff_only(request): return HttpResponseForbidden('需要运营权限(staff)') plan = Plan.objects.filter(id=plan_id).first() if not plan: messages.error(request, '套餐不存在') return redirect('admin_panel:plans') plan.is_active = not plan.is_active plan.save(update_fields=['is_active', 'updated_at']) messages.success(request, f'已{"启用" if plan.is_active else "禁用"}套餐:{plan.name}') return redirect('admin_panel:plans') @login_required @require_POST def plan_toggle_public(request, plan_id: int): if not _staff_only(request): return HttpResponseForbidden('需要运营权限(staff)') plan = Plan.objects.filter(id=plan_id).first() if not plan: messages.error(request, '套餐不存在') return redirect('admin_panel:plans') plan.is_public = not plan.is_public plan.save(update_fields=['is_public', 'updated_at']) messages.success(request, f'已设置公开状态({"公开" if plan.is_public else "隐藏"}):{plan.name}') return redirect('admin_panel:plans') @login_required @require_POST def plan_toggle_allow_new(request, plan_id: int): if not _staff_only(request): return HttpResponseForbidden('需要运营权限(staff)') plan = Plan.objects.filter(id=plan_id).first() if not plan: messages.error(request, '套餐不存在') return redirect('admin_panel:plans') plan.allow_new_purchase = not plan.allow_new_purchase plan.save(update_fields=['allow_new_purchase', 'updated_at']) messages.success(request, f'已切换允许新购:{plan.name}') return redirect('admin_panel:plans') @login_required @require_POST def plan_toggle_allow_renew(request, plan_id: int): if not _staff_only(request): return HttpResponseForbidden('需要运营权限(staff)') plan = Plan.objects.filter(id=plan_id).first() if not plan: messages.error(request, '套餐不存在') return redirect('admin_panel:plans') plan.allow_renew = not plan.allow_renew plan.save(update_fields=['allow_renew', 'updated_at']) messages.success(request, f'已切换允许续费:{plan.name}') return redirect('admin_panel:plans') # Users management @login_required def users_list(request): if not _staff_only(request): return HttpResponseForbidden('需要运营权限(staff)') User = get_user_model() q = request.GET.get('q', '').strip() users_qs = User.objects.all() if q: users_qs = users_qs.filter(models.Q(username__icontains=q) | models.Q(email__icontains=q)) users = users_qs.annotate(domain_count=Count('domains')).order_by('-is_staff', '-domain_count', '-date_joined') return render(request, 'admin_panel/users_list.html', { 'users': users, 'q': q, }) # Domains management @login_required def domains_list(request): if not _staff_only(request): return HttpResponseForbidden('需要运营权限(staff)') q = request.GET.get('q', '').strip() user_id = request.GET.get('user_id') status = request.GET.get('status') domains = Domain.objects.select_related('user', 'current_plan').all() if q: domains = domains.filter(name__icontains=q) if user_id: domains = domains.filter(user_id=user_id) if status: domains = domains.filter(status=status) domains = domains.order_by('-updated_at') # 汇总用于筛选的状态列表 status_choices = Domain.STATUS_CHOICES return render(request, 'admin_panel/domains_list.html', { 'domains': domains, 'q': q, 'user_id': user_id, 'status': status, 'status_choices': status_choices, }) @login_required @require_POST @transaction.atomic def domain_toggle_suspend(request, domain_id: int): if not _staff_only(request): return HttpResponseForbidden('需要运营权限(staff)') domain = Domain.objects.filter(id=domain_id).first() if not domain: messages.error(request, '域名不存在') return redirect('admin_panel:domains') if (request.POST.get('confirm') or '').strip().upper() != 'CONFIRM': messages.error(request, '请在确认框中输入 CONFIRM 以继续') return redirect('admin_panel:domains') if domain.status == Domain.STATUS_ACTIVE: domain.status = Domain.STATUS_SUSPENDED elif domain.status == Domain.STATUS_SUSPENDED: domain.status = Domain.STATUS_ACTIVE else: messages.warning(request, '当前状态不支持暂停/恢复切换') return redirect('admin_panel:domains') domain.save(update_fields=['status', 'updated_at']) try: OperationLog.objects.create( actor=request.user, action='domain_toggle_suspend', target=domain.name, detail=f"new_status={domain.status}" ) except Exception: logger.exception('operation log write failed') messages.success(request, f'域名状态已切换为:{dict(Domain.STATUS_CHOICES).get(domain.status, domain.status)}') return redirect('admin_panel:domains') @login_required @transaction.atomic def domain_switch_plan(request, domain_id: int): if not _staff_only(request): return HttpResponseForbidden('需要运营权限(staff)') domain = Domain.objects.filter(id=domain_id).select_related('current_plan').first() if not domain: messages.error(request, '域名不存在') return redirect('admin_panel:domains') if request.method == 'POST': form = DomainPlanSwitchForm(request.POST, instance=domain) if form.is_valid(): old_plan = domain.current_plan form.save() try: OperationLog.objects.create( actor=request.user, action='domain_switch_plan', target=domain.name, detail=f"{old_plan.name if old_plan else '-'} → {domain.current_plan.name if domain.current_plan else '-'}" ) except Exception: logger.exception('operation log write failed') messages.success(request, f'已切换套餐:{old_plan.name if old_plan else "-"} → {domain.current_plan.name if domain.current_plan else "-"}') return redirect('admin_panel:domains') else: messages.error(request, '提交失败,请检查表单字段') else: form = DomainPlanSwitchForm(instance=domain) return render(request, 'admin_panel/domain_plan_switch.html', { 'domain': domain, 'form': form, }) # Domain: grant extra free traffic for current cycle @login_required @transaction.atomic def domain_grant_traffic(request, domain_id: int): if not _staff_only(request): return HttpResponseForbidden('需要运营权限(staff)') domain = Domain.objects.filter(id=domain_id).first() if not domain: messages.error(request, '域名不存在') return redirect('admin_panel:domains') if request.method == 'POST': form = DomainGrantTrafficForm(request.POST, instance=domain) if form.is_valid(): form.save() try: OperationLog.objects.create( actor=request.user, action='domain_grant_traffic', target=domain.name, detail=f"extra_free_traffic_gb_current_cycle={domain.extra_free_traffic_gb_current_cycle}" ) except Exception: logger.exception('operation log write failed') messages.success(request, f'已更新本周期额外赠送流量(GB):{domain.extra_free_traffic_gb_current_cycle}') return redirect('admin_panel:domains') else: messages.error(request, '提交失败,请检查表单字段') else: form = DomainGrantTrafficForm(instance=domain) return render(request, 'admin_panel/domain_grant_traffic.html', { 'domain': domain, 'form': form, }) @login_required @require_POST @transaction.atomic def domain_delete(request, domain_id: int): if not _staff_only(request): return HttpResponseForbidden('需要运营权限(staff)') domain = Domain.objects.filter(id=domain_id).first() if not domain: messages.error(request, '域名不存在') return redirect('admin_panel:domains') if (request.POST.get('confirm') or '').strip().upper() != 'DELETE': messages.error(request, '请在确认框中输入 DELETE 以继续') return redirect('admin_panel:domains') domain.status = Domain.STATUS_DELETED if hasattr(Domain, 'STATUS_DELETED') else 'deleted' domain.save(update_fields=['status', 'updated_at']) try: OperationLog.objects.create( actor=request.user, action='domain_delete', target=domain.name, detail='soft-delete' ) except Exception: logger.exception('operation log write failed') messages.success(request, '域名已标记为删除') return redirect('admin_panel:domains') # Billing management (admin) @login_required def invoices_list(request): if not _staff_only(request): return HttpResponseForbidden('需要运营权限(staff)') status = request.GET.get('status') user_id = request.GET.get('user_id') qs = Invoice.objects.select_related('user').all() if status: qs = qs.filter(status=status) if user_id: qs = qs.filter(user_id=user_id) qs = qs.order_by('-period_end') totals = qs.aggregate(total_amount=Sum('amount_total')) return render(request, 'admin_panel/invoices_list.html', { 'invoices': qs, 'status': status, 'user_id': user_id, 'total_amount': totals.get('total_amount') or Decimal('0.00'), }) @login_required @transaction.atomic def invoice_detail_admin(request, invoice_id: int): if not _staff_only(request): return HttpResponseForbidden('需要运营权限(staff)') invoice = Invoice.objects.select_related('user').filter(id=invoice_id).first() if not invoice: messages.error(request, '账单不存在') return redirect('admin_panel:billing_list') adj_form = InvoiceAdjustmentForm() if request.method == 'POST': action = request.POST.get('action') if action == 'mark_paid' and invoice.status == Invoice.STATUS_UNPAID: invoice.status = Invoice.STATUS_PAID invoice.paid_at = timezone.now() invoice.save(update_fields=['status', 'paid_at']) try: OperationLog.objects.create( actor=request.user, action='invoice_mark_paid_admin', target=f"Invoice#{invoice.id}", detail=f"period={invoice.period_start}→{invoice.period_end}; amount_total={invoice.amount_total}" ) except Exception: logger.exception('operation log write failed') try: client = GoEdgeClient() for it in invoice.items.select_related('domain').all(): if not it.domain or not it.domain.edge_server_id: continue web_id = client.find_server_web_id(int(it.domain.edge_server_id)) if web_id: # 关闭停服与限速 client.update_http_web_shutdown(web_id, {'isOn': False}) client.update_http_web_request_limit(web_id, {'isOn': False}) # 如域名被暂停,恢复为 active if it.domain.status == 'suspended': it.domain.status = 'active' it.domain.save(update_fields=['status', 'updated_at']) except Exception: logger.exception('auto recover after paid failed') messages.success(request, '账单已标记为已支付') return redirect('admin_panel:billing_detail', invoice_id=invoice.id) elif action == 'add_adjustment': if (request.POST.get('confirm') or '').strip().upper() != 'CONFIRM': messages.error(request, '请在确认框中输入 CONFIRM 以继续') return redirect('admin_panel:billing_detail', invoice_id=invoice.id) adj_form = InvoiceAdjustmentForm(request.POST) if adj_form.is_valid(): adj_form.apply(invoice) try: OperationLog.objects.create( actor=request.user, action='invoice_add_adjustment', target=f"Invoice#{invoice.id}", detail=f"adjustment={invoice.amount_adjustment}; total={invoice.amount_total}" ) except Exception: logger.exception('operation log write failed') messages.success(request, '已添加人工调整并更新总金额') return redirect('admin_panel:billing_detail', invoice_id=invoice.id) else: messages.error(request, '调整提交失败,请检查表单字段') elif action == 'cancel' and invoice.status != Invoice.STATUS_CANCELLED: if (request.POST.get('confirm') or '').strip().upper() != 'CONFIRM': messages.error(request, '请在确认框中输入 CONFIRM 以继续') return redirect('admin_panel:billing_detail', invoice_id=invoice.id) invoice.status = Invoice.STATUS_CANCELLED invoice.save(update_fields=['status']) try: OperationLog.objects.create( actor=request.user, action='invoice_cancel', target=f"Invoice#{invoice.id}", detail=f"period={invoice.period_start}→{invoice.period_end}" ) except Exception: logger.exception('operation log write failed') messages.success(request, '账单已取消') return redirect('admin_panel:billing_detail', invoice_id=invoice.id) elif action == 'apply_overage_policy' and invoice.status == Invoice.STATUS_UNPAID: if (request.POST.get('confirm') or '').strip().upper() != 'CONFIRM': messages.error(request, '请在确认框中输入 CONFIRM 以继续') return redirect('admin_panel:billing_detail', invoice_id=invoice.id) sys = SystemSettings.objects.order_by('id').first() policy = getattr(sys, 'default_overage_policy', {}) or {} action_type = (policy.get('action') or '').lower() limit_bps = int(policy.get('limit_bps') or 0) applied = [] try: client = GoEdgeClient() for it in invoice.items.select_related('domain').all(): d = it.domain if not d or not d.edge_server_id: continue web_id = client.find_server_web_id(int(d.edge_server_id)) if not web_id: continue if action_type == 'limit' and limit_bps > 0: # 最小限速配置(简化形态):开启限速并设定速率 client.update_http_web_request_limit(web_id, {'isOn': True, 'rateBytes': int(limit_bps)}) applied.append({'domain': d.name, 'action': 'limit', 'rateBytes': limit_bps}) else: # 默认执行停服 client.update_http_web_shutdown(web_id, {'isOn': True}) # 域名状态置为暂停 if d.status != 'suspended': d.status = 'suspended' d.save(update_fields=['status', 'updated_at']) applied.append({'domain': d.name, 'action': 'shutdown'}) try: OperationLog.objects.create( actor=request.user, action='apply_overage_policy', target=f"Invoice#{invoice.id}", detail=json.dumps({'applied': applied}, ensure_ascii=False) ) except Exception: logger.exception('operation log write failed') messages.success(request, '已执行未支付策略(参考系统默认策略配置)') except Exception as e: messages.error(request, f'策略执行失败:{e}') return redirect('admin_panel:billing_detail', invoice_id=invoice.id) items = invoice.items.select_related('domain').all() return render(request, 'admin_panel/invoice_detail.html', { 'invoice': invoice, 'items': items, 'adj_form': adj_form, }) # Quotas view: show composed quotas (global/plan/user override/domain extra) @login_required def quotas_view(request): if not _staff_only(request): return HttpResponseForbidden('需要运营权限(staff)') user_id = request.GET.get('user_id') domains = Domain.objects.select_related('user', 'current_plan').all() if user_id: domains = domains.filter(user_id=user_id) sys = SystemSettings.objects.order_by('id').first() rows = [] for d in domains: plan_quota = int(getattr(d.current_plan, 'included_traffic_gb_per_domain', 0) or 0) user_override = 0 profile = getattr(d.user, 'profile', None) if profile and profile.default_free_traffic_gb_per_domain_override is not None: user_override = int(profile.default_free_traffic_gb_per_domain_override) global_default = int(sys.default_free_traffic_gb_per_domain if sys else 0) base_quota = plan_quota if plan_quota > 0 else (user_override if user_override > 0 else global_default) domain_extra = int(d.extra_free_traffic_gb_current_cycle or 0) total_quota = base_quota + domain_extra rows.append({ 'domain': d, 'plan_quota': plan_quota, 'user_override': user_override, 'global_default': global_default, 'domain_extra': domain_extra, 'total_quota': total_quota, }) rows = sorted(rows, key=lambda r: (-(r['total_quota']), r['domain'].name)) return render(request, 'admin_panel/quotas.html', { 'rows': rows, 'user_id': user_id, }) # Monitoring: platform daily totals and top-N domains @login_required def monitoring_view(request): if not _staff_only(request): return HttpResponseForbidden('需要运营权限(staff)') from domains.models import DomainTrafficDaily today = timezone.now().date() days = int(request.GET.get('days') or 14) start = today - timezone.timedelta(days=days-1) sys = SystemSettings.objects.order_by('id').first() anomaly_enabled = bool(getattr(sys, 'anomaly_detection_enabled', True)) threshold_mult = float(getattr(sys, 'anomaly_threshold_multiplier', 3.0) or 3.0) window_days = int(getattr(sys, 'anomaly_window_days', 7) or 7) min_gb = float(getattr(sys, 'anomaly_min_gb', 1.0) or 1.0) # 平台每日总流量 daily = (DomainTrafficDaily.objects .filter(day__gte=start, day__lte=today) .values('day') .annotate(total_bytes=Sum('bytes')) .order_by('day')) daily_rows = [] for row in daily: gb = round((row['total_bytes'] or 0) / (1024 ** 3), 3) daily_rows.append({'day': row['day'], 'gb': gb}) # Top N 域名(本月总量) month_start = today.replace(day=1) domains = Domain.objects.all() top = [] anomalies = [] for d in domains: bytes_sum = DomainTrafficDaily.objects.filter(domain=d, day__gte=month_start, day__lte=today).aggregate(b=Sum('bytes'))['b'] or 0 gb = round(bytes_sum / (1024 ** 3), 3) top.append({'domain': d, 'gb': gb}) # 异常检测:今日 vs 过去窗口均值 if anomaly_enabled: window_start = today - timezone.timedelta(days=window_days) past = (DomainTrafficDaily.objects .filter(domain=d, day__gte=window_start, day__lt=today) .aggregate(b=Sum('bytes'))['b'] or 0) past_days_count = DomainTrafficDaily.objects.filter(domain=d, day__gte=window_start, day__lt=today).count() past_avg_gb = 0.0 if past_days_count > 0: past_avg_gb = round((past / (1024 ** 3)) / past_days_count, 3) today_bytes = DomainTrafficDaily.objects.filter(domain=d, day=today).aggregate(b=Sum('bytes'))['b'] or 0 today_gb = round(today_bytes / (1024 ** 3), 3) trigger = False if past_avg_gb > 0: trigger = today_gb >= max(min_gb, past_avg_gb * threshold_mult) else: trigger = today_gb >= min_gb if trigger and today_gb > 0: anomalies.append({ 'domain': d, 'today_gb': today_gb, 'past_avg_gb': past_avg_gb, 'ratio': round((today_gb / past_avg_gb), 2) if past_avg_gb > 0 else None, }) top_sorted = sorted(top, key=lambda x: -x['gb'])[:20] return render(request, 'admin_panel/monitoring.html', { 'daily_rows': daily_rows, 'top_domains': top_sorted, 'days': days, 'anomalies': anomalies, 'anomaly_config': { 'enabled': anomaly_enabled, 'multiplier': threshold_mult, 'window_days': window_days, 'min_gb': min_gb, }, }) logger = logging.getLogger(__name__)