import hmac import platform import random import socket import time from datetime import timedelta from django import get_version as django_version from django.conf import settings from django.contrib.auth import get_user_model from django.contrib.auth.decorators import login_required from django.contrib.sessions.models import Session from django.core.cache import cache from django.db.models import Count, Sum from django.http import HttpResponseForbidden, JsonResponse from django.shortcuts import redirect, render from django.utils import timezone as tz from common.git_utils import get_git_commit_hash from common.middleware import gate_whitelist from library.models import ( Comment, FileIndex, Project, ProjectAsset, ProjectDraft, Rating, Tag, TempUpload, Version, VersionFile, ) def _under_lockout(request): lock_until = request.session.get('lock_until') if lock_until and time.time() < lock_until: return True return False def _start_lockout(request): duration = random.randint(30, 300) request.session['lock_until'] = time.time() + duration @gate_whitelist def gate(request): if request.method == 'POST': if _under_lockout(request): return render(request, 'landing/gate.html', { 'error': 'Incorrect password.', }) submitted = request.POST.get('password', '') master = settings.PASSWORD or '' if hmac.compare_digest(submitted, master): request.session['authorized'] = True request.session.pop('lock_until', None) return redirect('landing:home') else: _start_lockout(request) return render(request, 'landing/gate.html', { 'error': 'Incorrect password.', }) if request.session.get('authorized'): return redirect('landing:home') return render(request, 'landing/gate.html') def home(request): latest_projects = ( Project.objects.select_related('owner', 'thumbnail') .prefetch_related('tag_links__tag__category', 'versions') .order_by('-created_at')[:4] ) total_downloads = Version.objects.aggregate(total=Sum('downloads'))['total'] or 0 creators = ( get_user_model().objects.filter(projects__isnull=False).distinct().count() ) context = { 'latest_projects': latest_projects, 'stats': { 'creators': creators, 'packs': Project.objects.count(), 'downloads': total_downloads, 'versions': Version.objects.count(), }, } return render(request, 'landing/home.html', context) def _fmt_bytes(n): """Human-readable byte count.""" if n is None: return '—' size = float(n) for unit in ('B', 'KB', 'MB', 'GB', 'TB'): if size < 1024 or unit == 'TB': return f'{size:.1f} {unit}' size /= 1024 def _system_snapshot(): """Collect a one-shot system/performance snapshot via psutil.""" try: import psutil except ImportError: return None snapshot = {} try: boot_ts = psutil.boot_time() snapshot['boot_time'] = tz.datetime.fromtimestamp( boot_ts, tz.get_current_timezone() ).isoformat(timespec='seconds') snapshot['uptime'] = round(time.time() - boot_ts) try: snapshot['load_avg'] = [round(x, 2) for x in psutil.getloadavg()] except Exception: snapshot['load_avg'] = None snapshot['cpus'] = { 'logical': psutil.cpu_count(logical=True) or 0, 'physical': psutil.cpu_count(logical=False) or 0, 'percent': psutil.cpu_percent(interval=0.1), } vm = psutil.virtual_memory() snapshot['memory'] = { 'total': vm.total, 'used': vm.used, 'available': vm.available, 'percent': vm.percent, } sw = psutil.swap_memory() snapshot['swap'] = {'total': sw.total, 'used': sw.used, 'percent': sw.percent} snapshot['disks'] = {} for label, path in (('Root /', '/'), ('Code', str(settings.BASE_DIR)), ('Media', str(settings.MEDIA_ROOT))): try: d = psutil.disk_usage(path) snapshot['disks'][label] = { 'path': path, 'total': d.total, 'used': d.used, 'free': d.free, 'percent': d.percent, } except OSError: snapshot['disks'][label] = None try: snapshot['net'] = { 'sent': psutil.net_io_counters().bytes_sent, 'recv': psutil.net_io_counters().bytes_recv, } except Exception: snapshot['net'] = None running = 0 top_cpu = [] top_mem = [] gunicorn = 0 for proc in psutil.process_iter(['pid', 'name', 'cmdline', 'cpu_percent', 'memory_percent']): try: running += 1 info = proc.info cmd = ' '.join(info.get('cmdline') or []) if 'gunicorn' in cmd: gunicorn += 1 top_cpu.append({ 'name': info.get('name') or '?', 'pid': info.get('pid'), 'value': round(info.get('cpu_percent') or 0, 1), }) top_mem.append({ 'name': info.get('name') or '?', 'pid': info.get('pid'), 'value': round(info.get('memory_percent') or 0, 1), }) except (psutil.NoSuchProcess, psutil.AccessDenied): continue snapshot['processes'] = running snapshot['gunicorn'] = gunicorn top_cpu.sort(key=lambda x: x['value'], reverse=True) top_mem.sort(key=lambda x: x['value'], reverse=True) snapshot['top_cpu'] = top_cpu[:6] snapshot['top_mem'] = top_mem[:6] except Exception: return None return snapshot @login_required def server_stats(request): """Staff-only stats page. Renders a lightweight shell; the snapshot data is fetched from the JSON endpoint so page loads stay cheap.""" if not request.user.is_staff: return HttpResponseForbidden('Only staff can view server stats.') return render(request, 'landing/server_stats.html', { 'disk_labels': ['Root /', 'Code', 'Media'], }) STATS_CACHE_KEY = 'stats:payload' STATS_CACHE_TTL = 20 @login_required def server_stats_api(request): """JSON snapshot of server state / performance / recent activity. The payload is cached server-side for STATS_CACHE_TTL seconds so polling clients share one computation per interval instead of hammering the DB.""" if not request.user.is_staff: return JsonResponse({'detail': 'Only staff can view server stats.'}, status=403) payload = cache.get(STATS_CACHE_KEY) if payload is None: start = time.time() payload = _collect_stats() payload['app']['page_gen_ms'] = round((time.time() - start) * 1000, 1) cache.set(STATS_CACHE_KEY, payload, STATS_CACHE_TTL) return JsonResponse(payload) def _collect_stats(page_gen_ms=None): """One-shot, JSON-safe snapshot of server state and recent activity.""" now = tz.now() users = get_user_model().objects category_breakdown = dict( Project.objects.values_list('category').annotate(n=Count('pk')).order_by() ) project_categories = [c for c, _ in Project.CATEGORY_CHOICES] categories_list = [ [c, category_breakdown.get(c, 0)] for c in project_categories ] try: import importlib.metadata as _md gunicorn_version = _md.version('gunicorn') except Exception: gunicorn_version = 'unknown' content = { 'users': users.count(), 'staff': users.filter(is_staff=True).count(), 'superusers': users.filter(is_superuser=True).count(), 'active_24h': users.filter(last_login__gte=now - timedelta(hours=24)).count(), 'active_7d': users.filter(last_login__gte=now - timedelta(days=7)).count(), 'projects': sum(category_breakdown.get(c, 0) for c in project_categories), 'categories': category_breakdown, 'versions': Version.objects.count(), 'files': FileIndex.objects.count(), 'storage': _fmt_bytes( FileIndex.objects.aggregate(s=Sum('size'))['s'] or 0 ), 'assets': ProjectAsset.objects.count(), 'downloads': Version.objects.aggregate(t=Sum('downloads'))['t'] or 0, 'ratings': Rating.objects.count(), 'ratings_positive': Rating.objects.filter(value=True).count(), 'ratings_negative': Rating.objects.filter(value=False).count(), 'comments': Comment.objects.count(), 'tags': Tag.objects.count(), 'sessions': Session.objects.filter(expire_date__gte=now).count(), 'pending_uploads': TempUpload.objects.filter(status='pending').count(), 'drafts': ProjectDraft.objects.count(), } def _iso(dt): return dt.isoformat(timespec='seconds') recent = { 'versions': [ { 'project': v.project.title, 'slug': v.project.slug, 'version': v.version_name, 'created_at': _iso(v.created_at), } for v in Version.objects.select_related('project').order_by('-created_at')[:8] ], 'comments': [ { 'user': c.user.username, 'project': c.project.title, 'slug': c.project.slug, 'body': c.body, 'created_at': _iso(c.created_at), } for c in Comment.objects.select_related('user', 'project').order_by('-created_at')[:8] ], 'projects': [ { 'title': p.title, 'slug': p.slug, 'category': p.category, 'owner': p.owner.username, 'created_at': _iso(p.created_at), } for p in Project.objects.select_related('owner').order_by('-created_at')[:5] ], 'ratings': [ { 'user': r.user.username, 'project': r.project.title, 'slug': r.project.slug, 'value': r.value, 'created_at': _iso(r.created_at), } for r in Rating.objects.select_related('user', 'project').order_by('-created_at')[:5] ], } app = { 'django': django_version(), 'python': platform.python_version(), 'hostname': socket.gethostname(), 'app_env': 'dev' if settings.DEBUG else 'prod', 'debug': settings.DEBUG, 'git': get_git_commit_hash(), 'db': settings.DATABASES['default']['ENGINE'].rsplit('.', 1)[-1], 'tz': settings.TIME_ZONE, 'media_root': str(settings.MEDIA_ROOT), 'gunicorn': gunicorn_version, 'page_gen_ms': page_gen_ms, } return { 'generated_at': _iso(now), 'system': _system_snapshot(), 'app': app, 'content': content, 'categories_list': categories_list, 'project_categories': project_categories, 'recent': recent, }