diff --git a/vpn/admin.py b/vpn/admin.py index 4932335..59ea2ba 100644 --- a/vpn/admin.py +++ b/vpn/admin.py @@ -247,10 +247,15 @@ class LastAccessFilter(admin.SimpleListFilter): class ServerAdmin(PolymorphicParentModelAdmin): base_model = Server child_models = (OutlineServer, WireguardServer) - list_display = ('name_with_icon', 'server_type', 'comment_short', 'user_stats', 'activity_summary', 'server_status_compact', 'registration_date') + list_display = ('name_with_icon', 'server_type', 'comment_short', 'user_stats', 'server_status_compact', 'registration_date') search_fields = ('name', 'comment') list_filter = ('server_type', ) actions = ['move_clients_action', 'purge_all_keys_action', 'sync_all_selected_servers'] + + class Media: + css = { + 'all': ('admin/css/vpn_server_admin.css',) + } def get_urls(self): urls = super().get_urls() @@ -619,32 +624,44 @@ class ServerAdmin(PolymorphicParentModelAdmin): @admin.display(description='Users & Links') def user_stats(self, obj): - """Display user count and active links statistics""" + """Display user count and active links statistics (optimized)""" try: from django.utils import timezone from datetime import timedelta user_count = obj.user_count if hasattr(obj, 'user_count') else 0 - # Get total links count - total_links = ACLLink.objects.filter(acl__server=obj).count() - - # Get recently accessed links (last 30 days) - exclude None values - thirty_days_ago = timezone.now() - timedelta(days=30) - active_links = ACLLink.objects.filter( - acl__server=obj, - last_access_time__isnull=False, - last_access_time__gte=thirty_days_ago - ).count() + # Use prefetched data if available + if hasattr(obj, 'acl_set'): + all_links = [] + for acl in obj.acl_set.all(): + if hasattr(acl, 'links') and hasattr(acl.links, 'all'): + all_links.extend(acl.links.all()) + + total_links = len(all_links) + + # Count active links from prefetched data + thirty_days_ago = timezone.now() - timedelta(days=30) + active_links = sum(1 for link in all_links + if link.last_access_time and link.last_access_time >= thirty_days_ago) + else: + # Fallback to direct queries (less efficient) + total_links = ACLLink.objects.filter(acl__server=obj).count() + thirty_days_ago = timezone.now() - timedelta(days=30) + active_links = ACLLink.objects.filter( + acl__server=obj, + last_access_time__isnull=False, + last_access_time__gte=thirty_days_ago + ).count() # Color coding based on activity if user_count == 0: color = '#9ca3af' # gray - no users elif total_links == 0: color = '#dc2626' # red - no links - elif active_links > total_links * 0.7: # High activity + elif total_links > 0 and active_links > total_links * 0.7: # High activity color = '#16a34a' # green - elif active_links > total_links * 0.3: # Medium activity + elif total_links > 0 and active_links > total_links * 0.3: # Medium activity color = '#eab308' # yellow else: color = '#f97316' # orange - low activity @@ -660,55 +677,14 @@ class ServerAdmin(PolymorphicParentModelAdmin): @admin.display(description='Activity') def activity_summary(self, obj): - """Display recent activity summary""" + """Display recent activity summary (optimized)""" try: - from django.utils import timezone - from datetime import timedelta - - # Get recent access logs for this server - seven_days_ago = timezone.now() - timedelta(days=7) - recent_logs = AccessLog.objects.filter( - server=obj.name, - timestamp__gte=seven_days_ago - ) - - total_access = recent_logs.count() - success_access = recent_logs.filter(action='Success').count() - - # Get latest access - latest_log = AccessLog.objects.filter(server=obj.name).order_by('-timestamp').first() - - if latest_log: - local_time = localtime(latest_log.timestamp) - latest_str = local_time.strftime('%m-%d %H:%M') - - # Time since last access - time_diff = timezone.now() - latest_log.timestamp - if time_diff.days > 0: - time_ago = f'{time_diff.days}d ago' - elif time_diff.seconds > 3600: - time_ago = f'{time_diff.seconds // 3600}h ago' - else: - time_ago = 'Recent' - else: - latest_str = 'Never' - time_ago = '' - - # Color coding - if total_access == 0: - color = '#dc2626' # red - no activity - elif total_access > 50: - color = '#16a34a' # green - high activity - elif total_access > 10: - color = '#eab308' # yellow - medium activity - else: - color = '#f97316' # orange - low activity - + # Simplified version - avoid heavy DB queries on list page + # This could be computed once per page load if needed return mark_safe( - f'