diff --git a/.djlintrc b/.djlintrc new file mode 100644 index 0000000..d768d32 --- /dev/null +++ b/.djlintrc @@ -0,0 +1,3 @@ +{ + "ignore": "H021,D018,H037" +} \ No newline at end of file diff --git a/.env.example b/.env.example index 581be93..1bdc6d2 100644 --- a/.env.example +++ b/.env.example @@ -1,9 +1,147 @@ -API_Authorization= -SENTRY_DSN= -EMAIL_HOST_USER= -EMAIL_HOST_PASSWORD= -DISCORD_LOGGING_WEBHOOK= -HCAPTCHA_SITEKEY= -HCAPTCHA_SECRET= -MAINTENANCE_MODE= -BOT_HOST= \ No newline at end of file +#============================================================================== +# Server Configuration +#============================================================================== + +# Enable Django debug mode (True/False) +# Default: False +# WARNING: Never set to True in production +# DEBUG=False + +# Django secret key for cryptographic signing +# REQUIRED: Generate a new one using `python -c 'from django.core.management.utils import get_random_secret_key; print(get_random_secret_key())'` +# SECRET_KEY=your-secret-key-here + +# Comma-separated list of allowed hosts +# Default: "localhost,127.0.0.1" +# Example: "example.com,subdomain.example.com,localhost" +# ALLOWED_HOSTS=localhost,127.0.0.1 + +# Comma-separated list of trusted origins for CSRF +# Default: "http://localhost:8000" +# Example: "https://example.com,https://subdomain.example.com" +# CSRF_TRUSTED_ORIGINS=http://localhost:8000 + +#============================================================================== +# Database Configuration (PostgreSQL) +#============================================================================== + +# PostgreSQL database name +# REQUIRED: Name of your database +# DB_NAME=redcrypt + +# PostgreSQL database user +# REQUIRED: Username with access to the database +# DB_USER=your_db_user + +# PostgreSQL database password +# REQUIRED: Password for the database user +# DB_PASSWORD=your_db_password + +# PostgreSQL host address +# Default: localhost +# Example: "db.example.com" or "postgresql.service" +# DB_HOST=localhost + +# PostgreSQL port number +# Default: 5432 +# DB_PORT=5432 + +#============================================================================== +# Email Configuration (SMTP) +#============================================================================== + +# SMTP server hostname +# Example: "smtp.gmail.com" for Gmail +# EMAIL_HOST=smtp.gmail.com + +# SMTP server port +# Common values: 587 (TLS) or 465 (SSL) +# EMAIL_PORT=587 + +# Enable TLS for SMTP +# Default: True +# EMAIL_USE_TLS=True + +# SMTP authentication username +# Usually your email address +# EMAIL_HOST_USER=your-email@gmail.com + +# SMTP authentication password +# For Gmail: Use App Password, not account password +# EMAIL_HOST_PASSWORD=your-app-specific-password + +# Email sender address +# Default: Same as EMAIL_HOST_USER +# Example: "Re-Dcrypt " +# EMAIL_SENDER=your-email@gmail.com + +#============================================================================== +# Authentication & Security +#============================================================================== + +# hCaptcha site key (public key) +# Get from: https://www.hcaptcha.com/ +# HCAPTCHA_SITEKEY=your-hcaptcha-site-key + +# hCaptcha secret key +# Get from: https://www.hcaptcha.com/ +# HCAPTCHA_SECRET=your-hcaptcha-secret-key + +# Google OAuth credentials +# Get from: https://console.cloud.google.com/ +# GOOGLE_CLIENT_ID=your-google-client-id +# GOOGLE_CLIENT_SECRET=your-google-client-secret + +#============================================================================== +# Discord Integration +#============================================================================== + +# Discord bot host URL +# Example: "https://bot.example.com" +# BOT_HOST=https://your-discord-bot-host + +# API authorization token for bot communication +# REQUIRED: Generate a secure random string +# API_Authorization=your-api-authorization-token + +# Discord webhook URL for backups +# Get from Discord channel settings +# Discord_backup_webhook=your-discord-webhook-url + +#============================================================================== +# Application Control +#============================================================================== + +# Maintenance mode toggle (true/false) +# Default: false +# MAINTENANCE_MODE=false + +# API authentication for cron jobs +# REQUIRED: Generate a secure random string +# API_CRON=your-cron-api-key + +#============================================================================== +# Backup Configuration +#============================================================================== + +# Password for encrypting backup ZIP files +# REQUIRED: Choose a strong password +# PWD_zip=your-backup-zip-password + +#============================================================================== +# Monitoring (Optional) +#============================================================================== + +# Sentry DSN for error tracking +# Get from: https://sentry.io/ +# Optional: Leave empty to disable Sentry +# SENTRY_DSN=your-sentry-dsn + +#============================================================================== +# Site Configuration +#============================================================================== + +# Django site ID for social auth +# Default: 3 +# WARNING: Change this if you have different site configurations +# SITE_ID=3 diff --git a/.gitignore b/.gitignore index 798128b..f506ce6 100644 --- a/.gitignore +++ b/.gitignore @@ -150,3 +150,7 @@ replit.nix poetry.lock pyproject.toml db.zip +.aider* + +# Avatars file +media/avatars/* diff --git a/accounts/adapter.py b/accounts/adapter.py new file mode 100644 index 0000000..9789b96 --- /dev/null +++ b/accounts/adapter.py @@ -0,0 +1,71 @@ +from allauth.account.adapter import DefaultAccountAdapter +from django.conf import settings +from django.core.mail import EmailMultiAlternatives +from django.template.loader import render_to_string +import logging + +logger = logging.getLogger(__name__) + +class CustomAccountAdapter(DefaultAccountAdapter): + def send_mail(self, template_prefix, email, context): + required_settings = [ + 'EMAIL_HOST', + 'EMAIL_PORT', + 'EMAIL_HOST_USER', + 'EMAIL_HOST_PASSWORD', + 'EMAIL_SENDER' + ] + + smtp_configured = all(hasattr(settings, setting) and getattr(settings, setting) + for setting in required_settings) + + if not smtp_configured: + logger.warning( + "SMTP credentials not configured. Skipping email send. " + "Please configure EMAIL_HOST, EMAIL_PORT, EMAIL_HOST_USER, EMAIL_HOST_PASSWORD, and EMAIL_SENDER" + ) + return + + try: + from_email = f"Re-Dcrypt <{settings.EMAIL_SENDER}>" + + # Remove duplication prior to building final paths + if template_prefix.startswith('account/email/'): + template_prefix = template_prefix.replace('account/email/', '') + + html_template = f"account/email/{template_prefix}_message.html" + text_template = f"account/email/{template_prefix}_message.txt" + + try: + context['activate_url'] = context.get('activate_url', '') + context['current_site'] = context.get('current_site', '') + html_body = render_to_string(html_template, context) + text_body = render_to_string(text_template, context) + except Exception as e: + logger.warning(f"Template rendering failed: {str(e)}") + # Fallback to basic text email + text_body = ( + f"Welcome to Re-Dcrypt!\n\n" + f"Please verify your email by clicking this link: {context.get('activate_url', '')}\n\n" + f"If you did not request this email, please ignore it." + ) + html_body = text_body.replace('\n', '
') + + # Create email message + msg = EmailMultiAlternatives( + subject=f"Re-Dcrypt - {context.get('subject', 'Verify your email')}", + body=text_body, + from_email=from_email, + to=[email], + headers={'From': from_email} + ) + + if html_body: + msg.attach_alternative(html_body, "text/html") + + msg.send() + logger.info(f"Successfully sent verification email to {email}") + + except Exception as e: + logger.error(f"Failed to send email: {str(e)}") + logger.exception(e) diff --git a/accounts/admin.py b/accounts/admin.py index 0ccdfd0..97bd23d 100644 --- a/accounts/admin.py +++ b/accounts/admin.py @@ -20,6 +20,7 @@ class ProfileAdmin(admin.ModelAdmin): list_display = [ 'user', 'score', + 'organization', 'current_level', 'ip_address_count' ] @@ -31,7 +32,7 @@ class ProfileAdmin(admin.ModelAdmin): ('name', 'is_public_name'), ('organization', 'is_public_organization'), ('discord_id'), - ('avatar_url') + ('avatar') ]}), ('Hunt', {'fields': [ @@ -48,13 +49,13 @@ class ProfileAdmin(admin.ModelAdmin): def reset_profile_pic(self, request, queryset): for i in queryset: - i.avatar_url=f"https://source.boringavatars.com/beam/512/{i.user.username}?colors=00D2D2,006D6D,002A2A,055D5D,074848&square" + i.avatar_url=f"https://api.dicebear.com/9.x/fun-emoji/svg?seed={i.user.username}&backgroundColor=059ff2,71cf62,d84be5,d9915b,f6d594,fcbc34,b6e3f4,c0aede,d1d4f9,ffd5dc,ffdfbf&backgroundRotation[]" i.save() self.message_user( request, "Updated URLs" ) - reset_profile_pic.short_description="Reset Avatar URL" + reset_profile_pic.short_description = "Reset Avatar" actions=[reset_profile_pic] diff --git a/accounts/forms.py b/accounts/forms.py index a17b60b..4bb2876 100644 --- a/accounts/forms.py +++ b/accounts/forms.py @@ -1,15 +1,50 @@ from django import forms -from allauth.account.forms import SignupForm, ResetPasswordForm +from allauth.account.forms import SignupForm, ResetPasswordForm, LoginForm +from allauth.socialaccount.forms import SignupForm as SocialSignupForm from accounts.models import Profile from hcaptcha.fields import hCaptchaField from datetime import datetime +from allauth.socialaccount.adapter import get_adapter + import pytz +class MyCustomSocialSignupForm(SocialSignupForm): + name = forms.CharField(required=False, label="Name [Optional]") + organization = forms.CharField(required=False, label='College/Organization [Optional]') + email = forms.EmailField(required=False) + + def save(self, request): + adapter = get_adapter(request) + user = adapter.save_user(request, self.sociallogin, form=self) + self.custom_signup(request, user) + try: + avatar_url = self.sociallogin.account.get_avatar_url() + except: + avatar_url = f"https://api.dicebear.com/9.x/fun-emoji/svg?seed={user.username}&backgroundColor=059ff2,71cf62,d84be5,d9915b,f6d594,fcbc34,b6e3f4,c0aede,d1d4f9,ffd5dc,ffdfbf&backgroundRotation[]" + + current_time = datetime.now(pytz.timezone('Asia/Kolkata')) + profile = Profile.objects.create( + user=user, + name=self.cleaned_data['name'], + organization=self.cleaned_data['organization'], + avatar=avatar_url, + last_completed_time=current_time) + profile.save() + return user + + def validate_unique_email(self, value): + try: + return super(MyCustomSocialSignupForm, self).validate_unique_email(value) + except forms.ValidationError: + raise forms.ValidationError( + get_adapter().error_messages["email_taken"] + % self.sociallogin.account.get_provider().name + ) + class MyCustomSignupForm(SignupForm): name = forms.CharField(required=False, label="Name [Optional]") - organization = forms.CharField(required=False, - label='School/Organization [Optional]') + organization = forms.CharField(required=False, label='College/Organization [Optional]') hcaptcha = hCaptchaField(theme='dark') field_order = [ @@ -18,23 +53,18 @@ class MyCustomSignupForm(SignupForm): ] def save(self, request): - # Ensure you call the parent class's save. - # .save() returns a User object. user = super(MyCustomSignupForm, self).save(request) profile = Profile.objects.create( user=user, name=self.cleaned_data['name'], organization=self.cleaned_data['organization'], - avatar_url= - f"https://source.boringavatars.com/beam/512/{user.username}?colors=00D2D2,006D6D,002A2A,055D5D,074848&square", + avatar=f"https://api.dicebear.com/9.x/fun-emoji/svg?seed={user.username}&backgroundColor=059ff2,71cf62,d84be5,d9915b,f6d594,fcbc34,b6e3f4,c0aede,d1d4f9,ffd5dc,ffdfbf&backgroundRotation[]", last_completed_time=datetime.now(pytz.timezone('Asia/Kolkata'))) profile.save() + return user - # Add your own processing here. - # You must return the original result. - return user class CustomForgetPassword(ResetPasswordForm): @@ -45,8 +75,19 @@ class ContactForm(forms.Form): subject = forms.CharField( required=True, max_length=150, - widget=forms.TextInput(attrs={'class': 'text-nblue p-2'})) + label="Subject", + widget=forms.TextInput(attrs={ + 'class': 'form-input', + 'placeholder': 'Enter subject', + }) + ) body = forms.CharField( required=True, - widget=forms.Textarea(attrs={'class': 'text-nblue p-2'})) + label="Message", + widget=forms.Textarea(attrs={ + 'class': 'form-input', + 'placeholder': 'Type your message here...', + 'rows': 4, + }) + ) hCaptcha = hCaptchaField(theme='dark') diff --git a/accounts/migrations/0001_initial.py b/accounts/migrations/0001_initial.py index 1f6ce01..69efd55 100644 --- a/accounts/migrations/0001_initial.py +++ b/accounts/migrations/0001_initial.py @@ -1,8 +1,8 @@ -# Generated by Django 4.1 on 2022-09-01 17:40 +# Generated by Django 5.0.1 on 2025-01-02 18:13 +import django.db.models.deletion from django.conf import settings from django.db import migrations, models -import django.db.models.deletion class Migration(migrations.Migration): @@ -14,13 +14,34 @@ class Migration(migrations.Migration): ] operations = [ + migrations.CreateModel( + name='contact_form', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('subject', models.CharField(max_length=150)), + ('body', models.TextField()), + ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ], + ), + migrations.CreateModel( + name='IPs', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('ip_address', models.CharField(max_length=150)), + ('count', models.IntegerField(default=0)), + ('users_from_ip', models.ManyToManyField(to=settings.AUTH_USER_MODEL)), + ], + options={ + 'verbose_name_plural': 'IP addresses', + }, + ), migrations.CreateModel( name='Profile', fields=[ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('name', models.CharField(blank=True, max_length=150)), ('score', models.IntegerField(default=0)), - ('last_completed_time', models.DateTimeField(auto_now=True)), + ('last_completed_time', models.DateTimeField(blank=True, null=True)), ('is_public_name', models.BooleanField(default=False)), ('current_level', models.IntegerField(default=0)), ('discord_id', models.IntegerField(default=0)), @@ -30,34 +51,15 @@ class Migration(migrations.Migration): ('banned_reason', models.CharField(blank=True, max_length=150)), ('ip_address', models.JSONField(default=list)), ('ip_address_count', models.IntegerField(default=0)), - ('avatar_url', models.CharField(default='https://source.boringavatars.com/beam/512/redcrypt?colors=00D2D2,006D6D,002A2A,055D5D,074848&square', max_length=150)), + ('avatar', models.URLField(default='https://api.dicebear.com/9.x/fun-emoji/svg?seed=Easton&backgroundColor=059ff2,71cf62,d84be5,d9915b,f6d594,fcbc34,b6e3f4,c0aede,d1d4f9,ffd5dc,ffdfbf')), ('stats', models.JSONField(blank=True, default=dict)), ('rank', models.CharField(blank=True, default='0', max_length=5)), + ('last_verification_email_sent', models.DateTimeField(blank=True, null=True)), + ('verification_emails_sent', models.IntegerField(default=0)), ('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), ], options={ 'verbose_name_plural': 'User Profiles', }, ), - migrations.CreateModel( - name='IPs', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('ip_address', models.CharField(max_length=150)), - ('count', models.IntegerField(default=0)), - ('users_from_ip', models.ManyToManyField(to=settings.AUTH_USER_MODEL)), - ], - options={ - 'verbose_name_plural': 'IP addresses', - }, - ), - migrations.CreateModel( - name='contact_form', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('subject', models.CharField(max_length=150)), - ('body', models.TextField()), - ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), - ], - ), ] diff --git a/accounts/migrations/0002_alter_profile_last_completed_time.py b/accounts/migrations/0002_alter_profile_last_completed_time.py deleted file mode 100644 index cce8d66..0000000 --- a/accounts/migrations/0002_alter_profile_last_completed_time.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 4.0.7 on 2022-09-20 10:03 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('accounts', '0001_initial'), - ] - - operations = [ - migrations.AlterField( - model_name='profile', - name='last_completed_time', - field=models.DateTimeField(), - ), - ] diff --git a/accounts/models.py b/accounts/models.py index 3daff4e..a518a70 100644 --- a/accounts/models.py +++ b/accounts/models.py @@ -17,7 +17,7 @@ class Profile(models.Model): user = models.OneToOneField(User, unique=True, on_delete=models.CASCADE) name = models.CharField(max_length=150, blank=True) score = models.IntegerField(default=0) - last_completed_time = models.DateTimeField() + last_completed_time = models.DateTimeField(null=True, blank=True) is_public_name = models.BooleanField(default=False) current_level = models.IntegerField(default=0) discord_id = models.IntegerField(default=0) @@ -27,12 +27,17 @@ class Profile(models.Model): banned_reason = models.CharField(max_length=150, blank=True) ip_address = models.JSONField(default=list) ip_address_count = models.IntegerField(default=0) - avatar_url = models.CharField( - max_length=150, - default="https://source.boringavatars.com/beam/512/redcrypt?colors=00D2D2,006D6D,002A2A,055D5D,074848&square" - ) + avatar = models.URLField(default="https://api.dicebear.com/9.x/fun-emoji/svg?seed=Easton&backgroundColor=059ff2,71cf62,d84be5,d9915b,f6d594,fcbc34,b6e3f4,c0aede,d1d4f9,ffd5dc,ffdfbf") stats = models.JSONField(default=dict, blank=True) rank = models.CharField(default='0', max_length=5, blank=True) + last_verification_email_sent = models.DateTimeField(null=True, blank=True) + verification_emails_sent = models.IntegerField(default=0) + + @property + def avatar_url(self): + if self.avatar: + return self.avatar + return '' def __str__(self): return str(self.user) @@ -60,34 +65,6 @@ class contact_form(models.Model): body = models.TextField() -@receiver(user_signed_up) -def user_signed_up_(request, user, **kwargs): - profile = Profile.objects.get(user=user) - "Send discord webhook after user signup" - name = profile.name if profile.name else "None" - organization = profile.organization if profile.organization else "None" - json_data = {"content": "New Signup", "embeds": [{ - "title": "New User Signup", "color": 53970, "fields": [ - { - "name": "Username", - "value": str(user.username) - }, - { - "name": "Name", - "value": name - }, - { - "name": "Email", - "value": str(user.email) - }, - { - "name": "School/Organisation", - "value": organization - }]}]} - url = os.getenv("DISCORD_LOGGING_WEBHOOK") - requests.post(url, json=json_data) - - @receiver(social_account_added) def social_account_added_(request, **kwargs): profile = Profile.objects.get(user=request.user) diff --git a/accounts/templatetags/__init__.py b/accounts/templatetags/__init__.py index e69de29..3016adf 100644 --- a/accounts/templatetags/__init__.py +++ b/accounts/templatetags/__init__.py @@ -0,0 +1 @@ +# Empty file to make the directory a Python package diff --git a/accounts/templatetags/form_filters.py b/accounts/templatetags/form_filters.py new file mode 100644 index 0000000..61261cc --- /dev/null +++ b/accounts/templatetags/form_filters.py @@ -0,0 +1,7 @@ +from django import template + +register = template.Library() + +@register.filter(name='addclass') +def addclass(field, css_class): + return field.as_widget(attrs={'class': css_class}) diff --git a/accounts/utils.py b/accounts/utils.py new file mode 100644 index 0000000..1d54000 --- /dev/null +++ b/accounts/utils.py @@ -0,0 +1,28 @@ +from datetime import timedelta +from django.utils import timezone + + +def can_send_verification_email(user): + """Check if user can send another verification email""" + profile = user.profile + + # Allow 2 additional sends after signup (total 3 including signup email) + if profile.verification_emails_sent < 3: + return True + + if not profile.last_verification_email_sent: + return True + + cooldown_period = timedelta(hours=3) + time_since_last = timezone.now() - profile.last_verification_email_sent + + return time_since_last > cooldown_period + +def get_next_available_time(user): + """Get timestamp when user can send next verification email""" + profile = user.profile + if not profile.last_verification_email_sent: + return None + + next_time = profile.last_verification_email_sent + timedelta(hours=3) + return next_time diff --git a/accounts/views.py b/accounts/views.py index e3592e6..c0d0a02 100644 --- a/accounts/views.py +++ b/accounts/views.py @@ -1,6 +1,15 @@ +import json +import logging +from django.http import HttpResponse +import json +import logging +from django.http import HttpResponse from django.shortcuts import redirect, render from django.http import JsonResponse from accounts.models import Profile, contact_form +from django.contrib import messages + +logger = logging.getLogger(__name__) from allauth.socialaccount.models import SocialAccount from django.contrib.auth.models import User from django.contrib.auth.decorators import login_required @@ -8,16 +17,52 @@ from allauth.account.utils import send_email_confirmation from django.core.exceptions import ObjectDoesNotExist from hunt.utils import get_rank -from sentry_sdk import capture_exception +# from sentry_sdk import capture_exception from accounts.forms import ContactForm +import requests +from django.conf import settings +from django.utils import timezone +from .utils import can_send_verification_email, get_next_available_time @login_required @not_banned +def verify_captcha(request): + if request.method == "POST": + try: + # Get hCaptcha response token from form + captcha_response = request.POST.get('h-captcha-response') + + # Verify with hCaptcha API + data = { + 'secret': settings.HCAPTCHA_SECRET, + 'response': captcha_response + } + response = requests.post('https://hcaptcha.com/siteverify', data=data) + result = response.json() + print(type(result.get('success'))) + return JsonResponse({ + 'captchaValid': result.get('success') + }) + + except Exception as e: + logger.error(f"Captcha verification error: {str(e)}") + return JsonResponse({ + 'captchaValid': False, + 'error': 'Verification failed' + }, status=400) + + return JsonResponse({ + 'captchaValid': False, + 'error': 'Invalid request method' + }, status=405) + +@login_required(login_url='account_login') +@not_banned def profile(request): user = request.user profile = Profile.objects.get(user=user) - k = SocialAccount.objects.filter(user=request.user) + k = SocialAccount.objects.filter(user=request.user).filter(provider='discord') rank = get_rank(request.user) if len(k) > 0: connected = True @@ -37,7 +82,7 @@ def profile(request): }) -@login_required +@login_required(login_url='account_login') @not_banned def edit_profile(request): user = request.user @@ -51,7 +96,7 @@ def edit_profile(request): }) -@login_required +@login_required(login_url='account_login') @not_banned def save_profile(request): if request.method == "POST": @@ -77,7 +122,7 @@ def save_profile(request): profile.save() return JsonResponse({'saved': True}, status=200) except Exception as e: - capture_exception(e) + # capture_exception(e) return JsonResponse({'saved': False}, status=400) @@ -107,14 +152,29 @@ def connect(request): @login_required @not_banned -def send_confirmation_email(request): - send_email_confirmation(request, request.user) +def send_confirmation_email(request): + send_email_confirmation(request, request.user, signup=False) + messages.success(request, f"Verification email sent successfully!") return redirect('verification-sent') @login_required def verification_sent(request): - return render(request, 'verification_sent.html') + # Check if email is already verified + if request.user.emailaddress_set.filter(verified=True).exists(): + messages.info(request, "Your email is already verified!") + return redirect('profile') + + context = {} + if not can_send_verification_email(request.user): + next_time = get_next_available_time(request.user) + remaining = next_time - timezone.now() + hours = remaining.seconds // 3600 + minutes = (remaining.seconds % 3600) // 60 + context['cooldown'] = True + context['remaining_time'] = f"{hours}h {minutes}m" + + return render(request, 'verification_sent.html', context) def contact_form_view(request): @@ -137,16 +197,68 @@ def submit_contact_form(request): form_model.save() return JsonResponse({'saved': True}, status=200) except Exception as e: - capture_exception(e) - return JsonResponse( - {'saved': False, 'message': str(e)}, - status=400) + # capture_exception(e) + return JsonResponse({'saved': False, 'message': str(e)}, status=400) - else: - return JsonResponse( - {'saved': False, 'message': "Some Error Occured"}, - status=400) +def check_unique(request): + if request.method == 'POST': + data = json.loads(request.body) + email = data.get('email') + username = data.get('username') + email_exists = User.objects.filter(email=email).exists() + username_exists = User.objects.filter(username=username).exists() + + return JsonResponse({'emailExists': email_exists, 'usernameExists': username_exists}) + else: + return HttpResponse(status=405) def e500(request): return render(request, '500.html', status=500) + +def verify_captcha(request): + if request.method == "POST": + try: + # Get hCaptcha response token from form + captcha_response = request.POST.get('h-captcha-response') + + # Verify with hCaptcha API + data = { + 'secret': settings.HCAPTCHA_SECRET, + 'response': captcha_response + } + response = requests.post('https://hcaptcha.com/siteverify', data=data) + result = response.json() + print(type(result.get('success'))) + return JsonResponse({ + 'captchaValid': result.get('success') + }) + + except Exception as e: + logger.error(f"Captcha verification error: {str(e)}") + return JsonResponse({ + 'captchaValid': False, + 'error': 'Verification failed' + }, status=400) + + return JsonResponse({ + 'captchaValid': False, + 'error': 'Invalid request method' + }, status=405) + +def check_email(request): + if request.method == 'POST': + data = json.loads(request.body) + email = data.get('email') + exists = User.objects.filter(email=email).exists() + return JsonResponse({'exists': exists}) + return HttpResponse(status=405) + +def check_username(request): + if request.method == 'POST': + data = json.loads(request.body) + username = data.get('username') + # Using iexact for case-insensitive comparison + exists = User.objects.filter(username__iexact=username).exists() + return JsonResponse({'exists': exists}) + return HttpResponse(status=405) diff --git a/admin_honeypot/migrations/0001_initial.py b/admin_honeypot/migrations/0001_initial.py index 99d4d22..44cf539 100644 --- a/admin_honeypot/migrations/0001_initial.py +++ b/admin_honeypot/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 4.1 on 2022-09-01 17:40 +# Generated by Django 5.0.1 on 2025-01-02 18:13 from django.db import migrations, models diff --git a/allauth1/account/views.py b/allauth1/account/views.py deleted file mode 100644 index cc4842f..0000000 --- a/allauth1/account/views.py +++ /dev/null @@ -1,892 +0,0 @@ -from django.contrib import messages -from django.contrib.auth.decorators import login_required -from hunt.decorators import not_banned -from django.contrib.sites.shortcuts import get_current_site -from django.http import ( - Http404, - HttpResponsePermanentRedirect, - HttpResponseRedirect, -) -from django.shortcuts import redirect -from django.urls import reverse, reverse_lazy -from django.utils.decorators import method_decorator -from django.views.decorators.debug import sensitive_post_parameters -from django.views.generic.base import TemplateResponseMixin, TemplateView, View -from django.views.generic.edit import FormView - -from allauth import ratelimit -from allauth.decorators import rate_limit -from allauth.exceptions import ImmediateHttpResponse -from allauth.utils import get_form_class, get_request_param - -from allauth.account import app_settings, signals -from allauth.account.adapter import get_adapter -from allauth.account.forms import ( - AddEmailForm, - ChangePasswordForm, - LoginForm, - ResetPasswordForm, - ResetPasswordKeyForm, - SetPasswordForm, - SignupForm, - UserTokenForm, -) -from allauth.account.models import EmailAddress, EmailConfirmation, EmailConfirmationHMAC -from allauth.account.utils import ( - complete_signup, - get_login_redirect_url, - get_next_redirect_url, - logout_on_password_change, - passthrough_next_redirect_url, - perform_login, - send_email_confirmation, - sync_user_email_addresses, - url_str_to_user_pk, -) - - -INTERNAL_RESET_SESSION_KEY = "_password_reset_key" - - -sensitive_post_parameters_m = method_decorator( - sensitive_post_parameters("oldpassword", "password", "password1", "password2") -) - - -def _ajax_response(request, response, form=None, data=None): - adapter = get_adapter(request) - if adapter.is_ajax(request): - if isinstance(response, HttpResponseRedirect) or isinstance( - response, HttpResponsePermanentRedirect - ): - redirect_to = response["Location"] - else: - redirect_to = None - response = adapter.ajax_response( - request, response, form=form, data=data, redirect_to=redirect_to - ) - return response - - -class RedirectAuthenticatedUserMixin(object): - def dispatch(self, request, *args, **kwargs): - if request.user.is_authenticated and app_settings.AUTHENTICATED_LOGIN_REDIRECTS: - redirect_to = self.get_authenticated_redirect_url() - response = HttpResponseRedirect(redirect_to) - return _ajax_response(request, response) - else: - response = super(RedirectAuthenticatedUserMixin, self).dispatch( - request, *args, **kwargs - ) - return response - - def get_authenticated_redirect_url(self): - redirect_field_name = self.redirect_field_name - return get_login_redirect_url( - self.request, - url=self.get_success_url(), - redirect_field_name=redirect_field_name, - ) - - -class AjaxCapableProcessFormViewMixin(object): - def get(self, request, *args, **kwargs): - response = super(AjaxCapableProcessFormViewMixin, self).get( - request, *args, **kwargs - ) - form = self.get_form() - return _ajax_response( - self.request, response, form=form, data=self._get_ajax_data_if() - ) - - def post(self, request, *args, **kwargs): - form_class = self.get_form_class() - form = self.get_form(form_class) - if form.is_valid(): - response = self.form_valid(form) - else: - response = self.form_invalid(form) - return _ajax_response( - self.request, response, form=form, data=self._get_ajax_data_if() - ) - - def get_form(self, form_class=None): - form = getattr(self, "_cached_form", None) - if form is None: - form = super(AjaxCapableProcessFormViewMixin, self).get_form(form_class) - self._cached_form = form - return form - - def _get_ajax_data_if(self): - return ( - self.get_ajax_data() - if get_adapter(self.request).is_ajax(self.request) - else None - ) - - def get_ajax_data(self): - return None - - -class LogoutFunctionalityMixin(object): - def logout(self): - adapter = get_adapter(self.request) - adapter.add_message( - self.request, messages.SUCCESS, "account/messages/logged_out.txt" - ) - adapter.logout(self.request) - - -class LoginView( - RedirectAuthenticatedUserMixin, AjaxCapableProcessFormViewMixin, FormView -): - form_class = LoginForm - template_name = "account/login." + app_settings.TEMPLATE_EXTENSION - success_url = None - redirect_field_name = "next" - - @sensitive_post_parameters_m - def dispatch(self, request, *args, **kwargs): - return super(LoginView, self).dispatch(request, *args, **kwargs) - - def get_form_kwargs(self): - kwargs = super(LoginView, self).get_form_kwargs() - kwargs["request"] = self.request - return kwargs - - def get_form_class(self): - return get_form_class(app_settings.FORMS, "login", self.form_class) - - def form_valid(self, form): - success_url = self.get_success_url() - try: - return form.login(self.request, redirect_url=success_url) - except ImmediateHttpResponse as e: - return e.response - - def get_success_url(self): - # Explicitly passed ?next= URL takes precedence - ret = ( - get_next_redirect_url(self.request, self.redirect_field_name) - or self.success_url - ) - return ret - - def get_context_data(self, **kwargs): - ret = super(LoginView, self).get_context_data(**kwargs) - signup_url = passthrough_next_redirect_url( - self.request, reverse("account_signup"), self.redirect_field_name - ) - redirect_field_value = get_request_param(self.request, self.redirect_field_name) - site = get_current_site(self.request) - - ret.update( - { - "signup_url": signup_url, - "site": site, - "redirect_field_name": self.redirect_field_name, - "redirect_field_value": redirect_field_value, - } - ) - return ret - - -login = LoginView.as_view() - - -class CloseableSignupMixin(object): - template_name_signup_closed = ( - "account/signup_closed." + app_settings.TEMPLATE_EXTENSION - ) - - def dispatch(self, request, *args, **kwargs): - try: - if not self.is_open(): - return self.closed() - except ImmediateHttpResponse as e: - return e.response - return super(CloseableSignupMixin, self).dispatch(request, *args, **kwargs) - - def is_open(self): - return get_adapter(self.request).is_open_for_signup(self.request) - - def closed(self): - response_kwargs = { - "request": self.request, - "template": self.template_name_signup_closed, - } - return self.response_class(**response_kwargs) - - -@method_decorator(rate_limit(action="signup"), name="dispatch") -class SignupView( - RedirectAuthenticatedUserMixin, - CloseableSignupMixin, - AjaxCapableProcessFormViewMixin, - FormView, -): - template_name = "account/signup." + app_settings.TEMPLATE_EXTENSION - form_class = SignupForm - redirect_field_name = "next" - success_url = None - - @sensitive_post_parameters_m - def dispatch(self, request, *args, **kwargs): - return super(SignupView, self).dispatch(request, *args, **kwargs) - - def get_form_class(self): - return get_form_class(app_settings.FORMS, "signup", self.form_class) - - def get_success_url(self): - # Explicitly passed ?next= URL takes precedence - ret = ( - get_next_redirect_url(self.request, self.redirect_field_name) - or self.success_url - ) - return ret - - def form_valid(self, form): - # By assigning the User to a property on the view, we allow subclasses - # of SignupView to access the newly created User instance - self.user = form.save(self.request) - try: - return complete_signup( - self.request, - self.user, - app_settings.EMAIL_VERIFICATION, - self.get_success_url(), - ) - except ImmediateHttpResponse as e: - return e.response - - def get_context_data(self, **kwargs): - ret = super(SignupView, self).get_context_data(**kwargs) - form = ret["form"] - email = self.request.session.get("account_verified_email") - if email: - email_keys = ["email"] - if app_settings.SIGNUP_EMAIL_ENTER_TWICE: - email_keys.append("email2") - for email_key in email_keys: - form.fields[email_key].initial = email - login_url = passthrough_next_redirect_url( - self.request, reverse("account_login"), self.redirect_field_name - ) - redirect_field_name = self.redirect_field_name - site = get_current_site(self.request) - redirect_field_value = get_request_param(self.request, redirect_field_name) - ret.update( - { - "login_url": login_url, - "redirect_field_name": redirect_field_name, - "redirect_field_value": redirect_field_value, - "site": site, - } - ) - return ret - - -signup = SignupView.as_view() - - -class ConfirmEmailView(TemplateResponseMixin, LogoutFunctionalityMixin, View): - - template_name = "account/email_confirm." + app_settings.TEMPLATE_EXTENSION - - def get(self, *args, **kwargs): - try: - self.object = self.get_object() - if app_settings.CONFIRM_EMAIL_ON_GET: - return self.post(*args, **kwargs) - except Http404: - self.object = None - ctx = self.get_context_data() - return self.render_to_response(ctx) - - def post(self, *args, **kwargs): - self.object = confirmation = self.get_object() - confirmation.confirm(self.request) - - # In the event someone clicks on an email confirmation link - # for one account while logged into another account, - # logout of the currently logged in account. - if ( - self.request.user.is_authenticated - and self.request.user.pk != confirmation.email_address.user_id - ): - self.logout() - - get_adapter(self.request).add_message( - self.request, - messages.SUCCESS, - "account/messages/email_confirmed.txt", - {"email": confirmation.email_address.email}, - ) - if app_settings.LOGIN_ON_EMAIL_CONFIRMATION: - resp = self.login_on_confirm(confirmation) - if resp is not None: - return resp - # Don't -- allauth doesn't touch is_active so that sys admin can - # use it to block users et al - # - # user = confirmation.email_address.user - # user.is_active = True - # user.save() - redirect_url = self.get_redirect_url() - if not redirect_url: - ctx = self.get_context_data() - return self.render_to_response(ctx) - return redirect(redirect_url) - - def login_on_confirm(self, confirmation): - """ - Simply logging in the user may become a security issue. If you - do not take proper care (e.g. don't purge used email - confirmations), a malicious person that got hold of the link - will be able to login over and over again and the user is - unable to do anything about it. Even restoring their own mailbox - security will not help, as the links will still work. For - password reset this is different, this mechanism works only as - long as the attacker has access to the mailbox. If they no - longer has access they cannot issue a password request and - intercept it. Furthermore, all places where the links are - listed (log files, but even Google Analytics) all of a sudden - need to be secured. Purging the email confirmation once - confirmed changes the behavior -- users will not be able to - repeatedly confirm (in case they forgot that they already - clicked the mail). - - All in all, opted for storing the user that is in the process - of signing up in the session to avoid all of the above. This - may not 100% work in case the user closes the browser (and the - session gets lost), but at least we're secure. - """ - user_pk = None - user_pk_str = get_adapter(self.request).unstash_user(self.request) - if user_pk_str: - user_pk = url_str_to_user_pk(user_pk_str) - user = confirmation.email_address.user - if user_pk == user.pk and self.request.user.is_anonymous: - return perform_login( - self.request, - user, - app_settings.EmailVerificationMethod.NONE, - # passed as callable, as this method - # depends on the authenticated state - redirect_url=self.get_redirect_url, - ) - - return None - - def get_object(self, queryset=None): - key = self.kwargs["key"] - emailconfirmation = EmailConfirmationHMAC.from_key(key) - if not emailconfirmation: - if queryset is None: - queryset = self.get_queryset() - try: - emailconfirmation = queryset.get(key=key.lower()) - except EmailConfirmation.DoesNotExist: - raise Http404() - return emailconfirmation - - def get_queryset(self): - qs = EmailConfirmation.objects.all_valid() - qs = qs.select_related("email_address__user") - return qs - - def get_context_data(self, **kwargs): - ctx = kwargs - ctx["confirmation"] = self.object - site = get_current_site(self.request) - ctx.update({"site": site}) - return ctx - - def get_redirect_url(self): - return get_adapter(self.request).get_email_confirmation_redirect_url( - self.request - ) - - -confirm_email = ConfirmEmailView.as_view() - - -@method_decorator(rate_limit(action="manage_email"), name="dispatch") -class EmailView(AjaxCapableProcessFormViewMixin, FormView): - template_name = "account/email." + app_settings.TEMPLATE_EXTENSION - form_class = AddEmailForm - success_url = reverse_lazy("account_email") - - def get_form_class(self): - return get_form_class(app_settings.FORMS, "add_email", self.form_class) - - def dispatch(self, request, *args, **kwargs): - sync_user_email_addresses(request.user) - return super(EmailView, self).dispatch(request, *args, **kwargs) - - def get_form_kwargs(self): - kwargs = super(EmailView, self).get_form_kwargs() - kwargs["user"] = self.request.user - return kwargs - - def form_valid(self, form): - email_address = form.save(self.request) - get_adapter(self.request).add_message( - self.request, - messages.INFO, - "account/messages/email_confirmation_sent.txt", - {"email": form.cleaned_data["email"]}, - ) - signals.email_added.send( - sender=self.request.user.__class__, - request=self.request, - user=self.request.user, - email_address=email_address, - ) - return super(EmailView, self).form_valid(form) - - def post(self, request, *args, **kwargs): - res = None - if "action_add" in request.POST: - res = super(EmailView, self).post(request, *args, **kwargs) - elif request.POST.get("email"): - if "action_send" in request.POST: - res = self._action_send(request) - elif "action_remove" in request.POST: - res = self._action_remove(request) - elif "action_primary" in request.POST: - res = self._action_primary(request) - res = res or HttpResponseRedirect(self.get_success_url()) - # Given that we bypassed AjaxCapableProcessFormViewMixin, - # we'll have to call invoke it manually... - res = _ajax_response(request, res, data=self._get_ajax_data_if()) - else: - # No email address selected - res = HttpResponseRedirect(self.success_url) - res = _ajax_response(request, res, data=self._get_ajax_data_if()) - return res - - def _get_email_address(self, request): - email = request.POST["email"] - try: - return EmailAddress.objects.get_for_user(user=request.user, email=email) - except EmailAddress.DoesNotExist: - pass - - def _action_send(self, request, *args, **kwargs): - email_address = self._get_email_address(request) - if email_address: - send_email_confirmation( - self.request, request.user, email=email_address.email - ) - - def _action_remove(self, request, *args, **kwargs): - email_address = self._get_email_address(request) - if email_address: - if email_address.primary: - get_adapter(request).add_message( - request, - messages.ERROR, - "account/messages/cannot_delete_primary_email.txt", - {"email": email_address.email}, - ) - else: - email_address.delete() - signals.email_removed.send( - sender=request.user.__class__, - request=request, - user=request.user, - email_address=email_address, - ) - get_adapter(request).add_message( - request, - messages.SUCCESS, - "account/messages/email_deleted.txt", - {"email": email_address.email}, - ) - return HttpResponseRedirect(self.get_success_url()) - - def _action_primary(self, request, *args, **kwargs): - email_address = self._get_email_address(request) - if email_address: - # Not primary=True -- Slightly different variation, don't - # require verified unless moving from a verified - # address. Ignore constraint if previous primary email - # address is not verified. - if ( - not email_address.verified - and EmailAddress.objects.filter( - user=request.user, verified=True - ).exists() - ): - get_adapter(request).add_message( - request, - messages.ERROR, - "account/messages/unverified_primary_email.txt", - ) - else: - # Sending the old primary address to the signal - # adds a db query. - try: - from_email_address = EmailAddress.objects.get( - user=request.user, primary=True - ) - except EmailAddress.DoesNotExist: - from_email_address = None - email_address.set_as_primary() - get_adapter(request).add_message( - request, - messages.SUCCESS, - "account/messages/primary_email_set.txt", - ) - signals.email_changed.send( - sender=request.user.__class__, - request=request, - user=request.user, - from_email_address=from_email_address, - to_email_address=email_address, - ) - return HttpResponseRedirect(self.get_success_url()) - - def get_context_data(self, **kwargs): - ret = super(EmailView, self).get_context_data(**kwargs) - # NOTE: For backwards compatibility - ret["add_email_form"] = ret.get("form") - # (end NOTE) - ret["can_add_email"] = EmailAddress.objects.can_add_email(self.request.user) - return ret - - def get_ajax_data(self): - data = [] - for emailaddress in self.request.user.emailaddress_set.all(): - data.append( - { - "id": emailaddress.pk, - "email": emailaddress.email, - "verified": emailaddress.verified, - "primary": emailaddress.primary, - } - ) - return data - - -email = login_required(EmailView.as_view()) - - -@method_decorator(rate_limit(action="change_password"), name="dispatch") -class PasswordChangeView(AjaxCapableProcessFormViewMixin, FormView): - template_name = "account/password_change." + app_settings.TEMPLATE_EXTENSION - form_class = ChangePasswordForm - success_url = reverse_lazy("account_change_password") - - def get_form_class(self): - return get_form_class(app_settings.FORMS, "change_password", self.form_class) - - @sensitive_post_parameters_m - def dispatch(self, request, *args, **kwargs): - return super(PasswordChangeView, self).dispatch(request, *args, **kwargs) - - def render_to_response(self, context, **response_kwargs): - if not self.request.user.has_usable_password(): - return HttpResponseRedirect(reverse("account_set_password")) - return super(PasswordChangeView, self).render_to_response( - context, **response_kwargs - ) - - def get_form_kwargs(self): - kwargs = super(PasswordChangeView, self).get_form_kwargs() - kwargs["user"] = self.request.user - return kwargs - - def form_valid(self, form): - form.save() - logout_on_password_change(self.request, form.user) - get_adapter(self.request).add_message( - self.request, - messages.SUCCESS, - "account/messages/password_changed.txt", - ) - signals.password_changed.send( - sender=self.request.user.__class__, - request=self.request, - user=self.request.user, - ) - return super(PasswordChangeView, self).form_valid(form) - - def get_context_data(self, **kwargs): - ret = super(PasswordChangeView, self).get_context_data(**kwargs) - # NOTE: For backwards compatibility - ret["password_change_form"] = ret.get("form") - # (end NOTE) - return ret - - -password_change = login_required(not_banned(PasswordChangeView.as_view())) - - -@method_decorator( - # NOTE: 'change_password' (iso 'set_') is intentional, there is no need to - # differentiate between set and change. - rate_limit(action="change_password"), - name="dispatch", -) -class PasswordSetView(AjaxCapableProcessFormViewMixin, FormView): - template_name = "account/password_set." + app_settings.TEMPLATE_EXTENSION - form_class = SetPasswordForm - success_url = reverse_lazy("account_set_password") - - def get_form_class(self): - return get_form_class(app_settings.FORMS, "set_password", self.form_class) - - @sensitive_post_parameters_m - def dispatch(self, request, *args, **kwargs): - if self.request.user.has_usable_password(): - return HttpResponseRedirect(reverse("account_change_password")) - return super(PasswordSetView, self).dispatch(request, *args, **kwargs) - - def render_to_response(self, context, **response_kwargs): - return super(PasswordSetView, self).render_to_response( - context, **response_kwargs - ) - - def get_form_kwargs(self): - kwargs = super(PasswordSetView, self).get_form_kwargs() - kwargs["user"] = self.request.user - return kwargs - - def form_valid(self, form): - form.save() - logout_on_password_change(self.request, form.user) - get_adapter(self.request).add_message( - self.request, messages.SUCCESS, "account/messages/password_set.txt" - ) - signals.password_set.send( - sender=self.request.user.__class__, - request=self.request, - user=self.request.user, - ) - return super(PasswordSetView, self).form_valid(form) - - def get_context_data(self, **kwargs): - ret = super(PasswordSetView, self).get_context_data(**kwargs) - # NOTE: For backwards compatibility - ret["password_set_form"] = ret.get("form") - # (end NOTE) - return ret - - -password_set = login_required(PasswordSetView.as_view()) - - -@method_decorator(rate_limit(action="reset_password"), name="dispatch") -class PasswordResetView(AjaxCapableProcessFormViewMixin, FormView): - template_name = "account/password_reset." + app_settings.TEMPLATE_EXTENSION - form_class = ResetPasswordForm - success_url = reverse_lazy("account_reset_password_done") - redirect_field_name = "next" - - def get_form_class(self): - return get_form_class(app_settings.FORMS, "reset_password", self.form_class) - - def form_valid(self, form): - r429 = ratelimit.consume_or_429( - self.request, - action="reset_password_email", - key=form.cleaned_data["email"].lower(), - ) - if r429: - return r429 - form.save(self.request) - return super(PasswordResetView, self).form_valid(form) - - def get_context_data(self, **kwargs): - ret = super(PasswordResetView, self).get_context_data(**kwargs) - login_url = passthrough_next_redirect_url( - self.request, reverse("account_login"), self.redirect_field_name - ) - # NOTE: For backwards compatibility - ret["password_reset_form"] = ret.get("form") - # (end NOTE) - ret.update({"login_url": login_url}) - return ret - - -password_reset = PasswordResetView.as_view() - - -class PasswordResetDoneView(TemplateView): - template_name = "account/password_reset_done." + app_settings.TEMPLATE_EXTENSION - - -password_reset_done = PasswordResetDoneView.as_view() - - -@method_decorator(rate_limit(action="reset_password_from_key"), name="dispatch") -class PasswordResetFromKeyView( - AjaxCapableProcessFormViewMixin, LogoutFunctionalityMixin, FormView -): - template_name = "account/password_reset_from_key." + app_settings.TEMPLATE_EXTENSION - form_class = ResetPasswordKeyForm - success_url = reverse_lazy("account_reset_password_from_key_done") - reset_url_key = "set-password" - - def get_form_class(self): - return get_form_class( - app_settings.FORMS, "reset_password_from_key", self.form_class - ) - - def dispatch(self, request, uidb36, key, **kwargs): - self.request = request - self.key = key - - if self.key == self.reset_url_key: - self.key = self.request.session.get(INTERNAL_RESET_SESSION_KEY, "") - # (Ab)using forms here to be able to handle errors in XHR #890 - token_form = UserTokenForm(data={"uidb36": uidb36, "key": self.key}) - if token_form.is_valid(): - self.reset_user = token_form.reset_user - - # In the event someone clicks on a password reset link - # for one account while logged into another account, - # logout of the currently logged in account. - if ( - self.request.user.is_authenticated - and self.request.user.pk != self.reset_user.pk - ): - self.logout() - self.request.session[INTERNAL_RESET_SESSION_KEY] = self.key - - return super(PasswordResetFromKeyView, self).dispatch( - request, uidb36, self.key, **kwargs - ) - else: - token_form = UserTokenForm(data={"uidb36": uidb36, "key": self.key}) - if token_form.is_valid(): - # Store the key in the session and redirect to the - # password reset form at a URL without the key. That - # avoids the possibility of leaking the key in the - # HTTP Referer header. - self.request.session[INTERNAL_RESET_SESSION_KEY] = self.key - redirect_url = self.request.path.replace(self.key, self.reset_url_key) - return redirect(redirect_url) - - self.reset_user = None - response = self.render_to_response(self.get_context_data(token_fail=True)) - return _ajax_response(self.request, response, form=token_form) - - def get_context_data(self, **kwargs): - ret = super(PasswordResetFromKeyView, self).get_context_data(**kwargs) - ret["action_url"] = reverse( - "account_reset_password_from_key", - kwargs={ - "uidb36": self.kwargs["uidb36"], - "key": self.kwargs["key"], - }, - ) - return ret - - def get_form_kwargs(self): - kwargs = super(PasswordResetFromKeyView, self).get_form_kwargs() - kwargs["user"] = self.reset_user - kwargs["temp_key"] = self.key - return kwargs - - def form_valid(self, form): - form.save() - adapter = get_adapter(self.request) - - if self.reset_user and app_settings.LOGIN_ATTEMPTS_LIMIT: - # User successfully reset the password, clear any - # possible cache entries for all email addresses. - for email in self.reset_user.emailaddress_set.all(): - adapter._delete_login_attempts_cached_email( - self.request, email=email.email - ) - - adapter.add_message( - self.request, - messages.SUCCESS, - "account/messages/password_changed.txt", - ) - signals.password_reset.send( - sender=self.reset_user.__class__, - request=self.request, - user=self.reset_user, - ) - - if app_settings.LOGIN_ON_PASSWORD_RESET: - return perform_login( - self.request, - self.reset_user, - email_verification=app_settings.EMAIL_VERIFICATION, - ) - - return super(PasswordResetFromKeyView, self).form_valid(form) - - -password_reset_from_key = PasswordResetFromKeyView.as_view() - - -class PasswordResetFromKeyDoneView(TemplateView): - template_name = ( - "account/password_reset_from_key_done." + app_settings.TEMPLATE_EXTENSION - ) - - -password_reset_from_key_done = PasswordResetFromKeyDoneView.as_view() - - -class LogoutView(TemplateResponseMixin, LogoutFunctionalityMixin, View): - - template_name = "account/logout." + app_settings.TEMPLATE_EXTENSION - redirect_field_name = "next" - - def get(self, *args, **kwargs): - if app_settings.LOGOUT_ON_GET: - return self.post(*args, **kwargs) - if not self.request.user.is_authenticated: - response = redirect(self.get_redirect_url()) - return _ajax_response(self.request, response) - ctx = self.get_context_data() - response = self.render_to_response(ctx) - return _ajax_response(self.request, response) - - def post(self, *args, **kwargs): - url = self.get_redirect_url() - if self.request.user.is_authenticated: - self.logout() - response = redirect(url) - return _ajax_response(self.request, response) - - def get_context_data(self, **kwargs): - ctx = kwargs - redirect_field_value = get_request_param(self.request, self.redirect_field_name) - ctx.update( - { - "redirect_field_name": self.redirect_field_name, - "redirect_field_value": redirect_field_value, - } - ) - return ctx - - def get_redirect_url(self): - return get_next_redirect_url( - self.request, self.redirect_field_name - ) or get_adapter(self.request).get_logout_redirect_url(self.request) - - -logout = LogoutView.as_view() - - -class AccountInactiveView(TemplateView): - template_name = "account/account_inactive." + app_settings.TEMPLATE_EXTENSION - - -account_inactive = AccountInactiveView.as_view() - - -class EmailVerificationSentView(TemplateView): - template_name = "account/verification_sent." + app_settings.TEMPLATE_EXTENSION - - -email_verification_sent = EmailVerificationSentView.as_view() diff --git a/apis/views.py b/apis/views.py index 94f09c6..53290da 100644 --- a/apis/views.py +++ b/apis/views.py @@ -7,7 +7,7 @@ from hunt.models import EasterEgg from django.core.exceptions import ObjectDoesNotExist from hunt.utils import get_rank, update_rank_all, backup_db -from sentry_sdk import capture_exception +# from sentry_sdk import capture_exception from django.views.decorators.csrf import csrf_exempt from extra_settings.models import Setting @@ -54,7 +54,7 @@ def verify_discord_id(request, discord_id): except ObjectDoesNotExist: return HttpResponse(status=404) except Exception as e: - capture_exception(e) + # capture_exception(e) return HttpResponse(status=500) @@ -85,7 +85,7 @@ def leaderboard(request): 'score': user.score, 'rank': rank}) return HttpResponse(json.dumps(data_dict)) except Exception as e: - capture_exception(e) + # capture_exception(e) return HttpResponse(status=500) @@ -105,7 +105,7 @@ def stats(request, discord_id): except ObjectDoesNotExist: return HttpResponse(status=404) except Exception as e: - capture_exception(e) + # capture_exception(e) return HttpResponse(status=500) @@ -123,7 +123,7 @@ def ban(request, discord_id, reason): except ObjectDoesNotExist: return HttpResponse(status=404) except Exception as e: - capture_exception(e) + # capture_exception(e) return HttpResponse(status=500) @@ -140,7 +140,7 @@ def unban(request, discord_id): except ObjectDoesNotExist: return HttpResponse(status=404) except Exception as e: - capture_exception(e) + # capture_exception(e) return HttpResponse(status=500) @@ -174,12 +174,12 @@ def easteregg(request, discord_id, egg): 'code': 'not_found', 'response': 'Wrong! Try again!'}) except Exception as e: - capture_exception(e) + # capture_exception(e) return HttpResponse(status=500) except ObjectDoesNotExist: return HttpResponse(status=404) except Exception as e: - capture_exception(e) + # capture_exception(e) return HttpResponse(status=500) diff --git a/data.json b/data.json deleted file mode 100644 index cc2f165..0000000 --- a/data.json +++ /dev/null @@ -1 +0,0 @@ -[{"model": "admin_interface.theme", "pk": 1, "fields": {"name": "Django", "active": false, "title": "Re-Dcrypt", "title_color": "#39FF14", "title_visible": true, "logo": "", "logo_color": "#000000", "logo_max_width": 400, "logo_max_height": 100, "logo_visible": true, "favicon": "", "env_name": "Re-Dcrypt", "env_color": "#0CE7E7", "env_visible_in_header": true, "env_visible_in_favicon": true, "language_chooser_active": true, "language_chooser_display": "code", "css_header_background_color": "#0C4B33", "css_header_text_color": "#44B78B", "css_header_link_color": "#FFFFFF", "css_header_link_hover_color": "#C9F0DD", "css_module_background_color": "#44B78B", "css_module_background_selected_color": "#FFFFCC", "css_module_text_color": "#FFFFFF", "css_module_link_color": "#FFFFFF", "css_module_link_selected_color": "#FFFFFF", "css_module_link_hover_color": "#C9F0DD", "css_module_rounded_corners": true, "css_generic_link_color": "#0C3C26", "css_generic_link_hover_color": "#156641", "css_save_button_background_color": "#0C4B33", "css_save_button_background_hover_color": "#0C3C26", "css_save_button_text_color": "#FFFFFF", "css_delete_button_background_color": "#BA2121", "css_delete_button_background_hover_color": "#A41515", "css_delete_button_text_color": "#FFFFFF", "related_modal_active": true, "related_modal_background_color": "#000000", "related_modal_background_opacity": "0.3", "related_modal_rounded_corners": true, "related_modal_close_button_visible": true, "list_filter_dropdown": true, "list_filter_sticky": true, "foldable_apps": true, "recent_actions_visible": true, "form_submit_sticky": false, "form_pagination_sticky": false}}, {"model": "admin_interface.theme", "pk": 2, "fields": {"name": "Bootstrap", "active": false, "title": "Django administration", "title_color": "#503873", "title_visible": false, "logo": "", "logo_color": "#503873", "logo_max_width": 400, "logo_max_height": 100, "logo_visible": true, "favicon": "", "env_name": "", "env_color": "#E74C3C", "env_visible_in_header": true, "env_visible_in_favicon": true, "language_chooser_active": true, "language_chooser_display": "code", "css_header_background_color": "#FFFFFF", "css_header_text_color": "#463265", "css_header_link_color": "#463265", "css_header_link_hover_color": "#7351A6", "css_module_background_color": "#7351A6", "css_module_background_selected_color": "#FFFFCC", "css_module_text_color": "#FFFFFF", "css_module_link_color": "#CDBFE3", "css_module_link_selected_color": "#FFFFFF", "css_module_link_hover_color": "#FFFFFF", "css_module_rounded_corners": true, "css_generic_link_color": "#463265", "css_generic_link_hover_color": "#7351A6", "css_save_button_background_color": "#5CB85C", "css_save_button_background_hover_color": "#449D44", "css_save_button_text_color": "#FFFFFF", "css_delete_button_background_color": "#D9534F", "css_delete_button_background_hover_color": "#C9302C", "css_delete_button_text_color": "#FFFFFF", "related_modal_active": true, "related_modal_background_color": "#503873", "related_modal_background_opacity": "0.2", "related_modal_rounded_corners": true, "related_modal_close_button_visible": true, "list_filter_dropdown": false, "list_filter_sticky": true, "foldable_apps": true, "recent_actions_visible": true, "form_submit_sticky": false, "form_pagination_sticky": false}}, {"model": "admin_interface.theme", "pk": 3, "fields": {"name": "Foundation", "active": false, "title": "Django administration", "title_color": "#DDDDDD", "title_visible": false, "logo": "", "logo_color": "#CCCCCC", "logo_max_width": 400, "logo_max_height": 100, "logo_visible": true, "favicon": "", "env_name": "", "env_color": "#E74C3C", "env_visible_in_header": true, "env_visible_in_favicon": true, "language_chooser_active": true, "language_chooser_display": "code", "css_header_background_color": "#2C3840", "css_header_text_color": "#FFFFFF", "css_header_link_color": "#FFFFFF", "css_header_link_hover_color": "#DDDDDD", "css_module_background_color": "#074E68", "css_module_background_selected_color": "#FFFFCC", "css_module_text_color": "#FFFFFF", "css_module_link_color": "#FFFFFF", "css_module_link_selected_color": "#FFFFFF", "css_module_link_hover_color": "#DDDDDD", "css_module_rounded_corners": true, "css_generic_link_color": "#000000", "css_generic_link_hover_color": "#074E68", "css_save_button_background_color": "#2199E8", "css_save_button_background_hover_color": "#1585CF", "css_save_button_text_color": "#FFFFFF", "css_delete_button_background_color": "#CC4B37", "css_delete_button_background_hover_color": "#BF4634", "css_delete_button_text_color": "#FFFFFF", "related_modal_active": true, "related_modal_background_color": "#000000", "related_modal_background_opacity": "0.2", "related_modal_rounded_corners": true, "related_modal_close_button_visible": true, "list_filter_dropdown": false, "list_filter_sticky": true, "foldable_apps": true, "recent_actions_visible": true, "form_submit_sticky": false, "form_pagination_sticky": false}}, {"model": "admin_interface.theme", "pk": 4, "fields": {"name": "USWDS", "active": true, "title": "Re-Dcrypt", "title_color": "#39FF14", "title_visible": true, "logo": "", "logo_color": "#12FF00", "logo_max_width": 400, "logo_max_height": 100, "logo_visible": true, "favicon": "", "env_name": "Re-Dcrypt", "env_color": "#39FF14", "env_visible_in_header": true, "env_visible_in_favicon": true, "language_chooser_active": true, "language_chooser_display": "code", "css_header_background_color": "#0D1451", "css_header_text_color": "#FFFFFF", "css_header_link_color": "#FFFFFF", "css_header_link_hover_color": "#E1F3F8", "css_module_background_color": "#205493", "css_module_background_selected_color": "#FFFFCC", "css_module_text_color": "#FFFFFF", "css_module_link_color": "#FFFFFF", "css_module_link_selected_color": "#FFFFFF", "css_module_link_hover_color": "#E1F3F8", "css_module_rounded_corners": true, "css_generic_link_color": "#205493", "css_generic_link_hover_color": "#0071BC", "css_save_button_background_color": "#205493", "css_save_button_background_hover_color": "#112E51", "css_save_button_text_color": "#FFFFFF", "css_delete_button_background_color": "#CD2026", "css_delete_button_background_hover_color": "#981B1E", "css_delete_button_text_color": "#FFFFFF", "related_modal_active": true, "related_modal_background_color": "#000000", "related_modal_background_opacity": "0.3", "related_modal_rounded_corners": true, "related_modal_close_button_visible": true, "list_filter_dropdown": false, "list_filter_sticky": true, "foldable_apps": true, "recent_actions_visible": true, "form_submit_sticky": false, "form_pagination_sticky": false}}] \ No newline at end of file diff --git a/hunt/decorators.py b/hunt/decorators.py index 832e80f..177a04d 100644 --- a/hunt/decorators.py +++ b/hunt/decorators.py @@ -1,6 +1,6 @@ from extra_settings.models import Setting from django.shortcuts import redirect, render -from sentry_sdk import capture_exception +# from sentry_sdk import capture_exception def hunt_status(function): @@ -33,7 +33,7 @@ def wrap(request, *args, **kwargs): elif status == 'ended': return render(request, 'ended.html') except Exception as e: - capture_exception(e) + # capture_exception(e) return redirect('index') wrap.__doc__ = function.__doc__ @@ -51,7 +51,7 @@ def wrap(request, *args, **kwargs): return function(request, *args, **kwargs) except Exception as e: - capture_exception(e) + # capture_exception(e) return redirect('index') wrap.__doc__ = function.__doc__ diff --git a/hunt/migrations/0001_initial.py b/hunt/migrations/0001_initial.py index b651fbf..fc1a73a 100644 --- a/hunt/migrations/0001_initial.py +++ b/hunt/migrations/0001_initial.py @@ -1,8 +1,8 @@ -# Generated by Django 4.1 on 2022-09-01 17:40 +# Generated by Django 5.0.1 on 2025-01-02 18:13 +import django.db.models.deletion from django.conf import settings from django.db import migrations, models -import django.db.models.deletion class Migration(migrations.Migration): @@ -28,30 +28,16 @@ class Migration(migrations.Migration): ], ), migrations.CreateModel( - name='SampleQuestion', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('question', models.TextField()), - ('answer', models.CharField(max_length=255)), - ('img_url', models.URLField(blank=True, null=True)), - ('level', models.IntegerField(default=0)), - ('points', models.IntegerField(default=0)), - ('completed_by', models.ManyToManyField(blank=True, to=settings.AUTH_USER_MODEL)), - ], - options={ - 'verbose_name_plural': 'Sample Questions', - }, - ), - migrations.CreateModel( - name='LevelTracking', + name='AnswerAttempt', fields=[ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('level', models.IntegerField()), + ('answer', models.CharField(max_length=1024)), ('time', models.DateTimeField(auto_now=True)), ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), ], options={ - 'verbose_name_plural': 'Level Completion Tracking', + 'verbose_name_plural': 'Answer Attempts', }, ), migrations.CreateModel( @@ -68,16 +54,15 @@ class Migration(migrations.Migration): }, ), migrations.CreateModel( - name='AnswerAttempt', + name='LevelTracking', fields=[ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('level', models.IntegerField()), - ('answer', models.CharField(max_length=1024)), ('time', models.DateTimeField(auto_now=True)), ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), ], options={ - 'verbose_name_plural': 'Answer Attempts', + 'verbose_name_plural': 'Level Completion Tracking', }, ), migrations.CreateModel( @@ -88,4 +73,19 @@ class Migration(migrations.Migration): ('question', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='hunt.question')), ], ), + migrations.CreateModel( + name='SampleQuestion', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('question', models.TextField()), + ('answer', models.CharField(max_length=255)), + ('img_url', models.URLField(blank=True, null=True)), + ('level', models.IntegerField(default=0)), + ('points', models.IntegerField(default=0)), + ('completed_by', models.ManyToManyField(blank=True, to=settings.AUTH_USER_MODEL)), + ], + options={ + 'verbose_name_plural': 'Sample Questions', + }, + ), ] diff --git a/hunt/utils.py b/hunt/utils.py index 0c7eb8c..17a5b17 100644 --- a/hunt/utils.py +++ b/hunt/utils.py @@ -5,14 +5,16 @@ from discord_webhook import DiscordWebhook def simplify(text): - import unicodedata - text = unicodedata.normalize('NFD', text).encode('ascii', 'ignore').decode("utf-8") - return str(text) + import unicodedata + text = unicodedata.normalize('NFD', text).encode('ascii', 'ignore').decode("utf-8") + return str(text) def match_answer(actual_answer, submitted_answer): submitted_answer = simplify(submitted_answer) + actual_answer = simplify(actual_answer) submitted_answer_filtered = re.sub('[\W_]+', '', submitted_answer.lower().replace(' ', '').strip()) - return submitted_answer_filtered == actual_answer + actual_answer_filtered = re.sub('[\W_]+', '', actual_answer.lower().replace(' ', '').strip()) + return submitted_answer_filtered == actual_answer_filtered def get_rank(user): @@ -22,6 +24,9 @@ def get_rank(user): elif user_profile.user.is_staff: return '🛡' else: + if user_profile.last_completed_time is None: + user_profile.last_completed_time = user_profile.user.date_joined + user_profile.save() above_players = Profile.objects.filter(score__gt=user_profile.score).exclude(is_banned=True).exclude(user__is_staff=True) | Profile.objects.filter(score=user_profile.score, last_completed_time__lt=user_profile.last_completed_time).exclude(is_banned=True).exclude(user__is_staff=True) above = above_players.count() return above+1 @@ -40,16 +45,16 @@ def update_rank(user): def backup_db(): - inpt = "db.sqlite3" - oupt = "db.zip" - pwd = os.getenv('PWD_zip') - webhook_url = os.getenv('Discord_backup_webhook') - pyminizip.compress( - inpt, - None, - oupt, + inpt = "db.sqlite3" + oupt = "db.zip" + pwd = os.getenv('PWD_zip') + webhook_url = os.getenv('Discord_backup_webhook') + pyminizip.compress( + inpt, + None, + oupt, pwd,1) - webhook = DiscordWebhook(url=webhook_url, rate_limit_retry=True) - with open("db.zip", "rb") as f: - webhook.add_file(file=f.read(), filename='db.zip') - webhook.execute() + webhook = DiscordWebhook(url=webhook_url, rate_limit_retry=True) + with open("db.zip", "rb") as f: + webhook.add_file(file=f.read(), filename='db.zip') + webhook.execute() diff --git a/hunt/views.py b/hunt/views.py index 3eb8088..37821ec 100644 --- a/hunt/views.py +++ b/hunt/views.py @@ -7,7 +7,7 @@ from hunt.models import LevelTracking, AnswerAttempt, SampleQuestion from accounts.models import Profile from hunt.utils import match_answer, get_rank, update_rank_all -from sentry_sdk import capture_exception +# from sentry_sdk import capture_exception import os import requests from datetime import datetime @@ -34,7 +34,7 @@ def guidelines(request): def play(request): user = request.user profile = Profile.objects.get(user=user) - if profile.current_level > 21: + if profile.current_level == Question.objects.count(): return render(request, 'complete.html') else: question = Question.objects.get(level=profile.current_level) @@ -51,6 +51,8 @@ def check_ans(request): if request.method == "POST": user = request.user profile = Profile.objects.get(user=user) + if profile.is_banned: + return JsonResponse({'banned': True}, status=403) question = Question.objects.get(level=profile.current_level) answer = request.POST['answer'] try: @@ -59,13 +61,15 @@ def check_ans(request): level=profile.current_level, answer=answer) except Exception as e: - capture_exception(e) + # capture_exception(e) + print(e) try: profile.stats[str(question.level)] += 1 except KeyError: profile.stats[str(question.level)] = 1 except Exception as e: - capture_exception(e) + # capture_exception(e) + print(e) if match_answer(question.answer, answer): profile.current_level += 1 profile.score += question.points @@ -76,10 +80,11 @@ def check_ans(request): user=request.user, level=profile.current_level) except Exception as e: - capture_exception(e) - url = f"{os.getenv('BOT_HOST')}/level/complete/{profile.discord_id}/{int(profile.current_level)-1}" - headers = {"Authorization": os.getenv("API_Authorization")} - request = requests.post(url, headers=headers) + # capture_exception(e) + print(e) + # url = f"{os.getenv('BOT_HOST')}/level/complete/{profile.discord_id}/{int(profile.current_level)-1}" + # headers = {"Authorization": os.getenv("API_Authorization")} + # request = requests.post(url, headers=headers) return JsonResponse({'correct': True}, status=200) else: profile.save() @@ -131,46 +136,8 @@ def privacy_policy(request): return render(request, 'privacy-policy.html', {'url_name': 'privacy_policy'}) -@login_required -def sample_questions_play(request): - user = request.user - sample_questions = SampleQuestion.objects.all() - if len(sample_questions) == 0: - return render( - request, - 'shortner.html', - { - 'content': "No sample questions available", - 'urlname': "Sample"}) - for i in sample_questions: - if user in i.completed_by.all(): - pass - else: - return render( - request, - 'sample_questions.html', - {'question': i}) - return render( - request, - 'shortner.html', - { - 'content': "You have completed all sample questions", - 'urlname': "Sample"}) - - -@login_required -def sample_checkans(request): - if request.method == "POST": - user = request.user - sample_question = SampleQuestion.objects.get( - level=request.POST['level']) - answer = request.POST['answer'] - if match_answer(sample_question.answer, answer): - sample_question.completed_by.add(user) - sample_question.save() - return JsonResponse({'correct': True}, status=200) - else: - return JsonResponse({'correct': False}, status=400) +def help_center(request): + return render(request, 'help.html') @login_required @@ -180,4 +147,4 @@ def update_rank(request): t.start() return JsonResponse({'status': "Updating"}, status=200) else: - return JsonResponse({'status': "Unauthenticated"}, status=403) + return JsonResponse({'status': "Unauthenticated"}, status=403) \ No newline at end of file diff --git a/output.json b/output.json deleted file mode 100644 index fddf48a..0000000 --- a/output.json +++ /dev/null @@ -1 +0,0 @@ -[{"model": "admin_interface.theme", "pk": 1, "fields": {"name": "Django", "active": false, "title": "Re-Dcrypt", "title_color": "#39FF14", "title_visible": true, "logo": "", "logo_color": "#000000", "logo_max_width": 400, "logo_max_height": 100, "logo_visible": true, "favicon": "", "env_name": "Re-Dcrypt", "env_color": "#0CE7E7", "env_visible_in_header": true, "env_visible_in_favicon": true, "language_chooser_active": true, "language_chooser_display": "code", "css_header_background_color": "#0C4B33", "css_header_text_color": "#44B78B", "css_header_link_color": "#FFFFFF", "css_header_link_hover_color": "#C9F0DD", "css_module_background_color": "#44B78B", "css_module_background_selected_color": "#FFFFCC", "css_module_text_color": "#FFFFFF", "css_module_link_color": "#FFFFFF", "css_module_link_selected_color": "#FFFFFF", "css_module_link_hover_color": "#C9F0DD", "css_module_rounded_corners": true, "css_generic_link_color": "#0C3C26", "css_generic_link_hover_color": "#156641", "css_save_button_background_color": "#0C4B33", "css_save_button_background_hover_color": "#0C3C26", "css_save_button_text_color": "#FFFFFF", "css_delete_button_background_color": "#BA2121", "css_delete_button_background_hover_color": "#A41515", "css_delete_button_text_color": "#FFFFFF", "related_modal_active": true, "related_modal_background_color": "#000000", "related_modal_background_opacity": "0.3", "related_modal_rounded_corners": true, "related_modal_close_button_visible": true, "list_filter_dropdown": true, "list_filter_sticky": true, "foldable_apps": true, "recent_actions_visible": true, "form_submit_sticky": false, "form_pagination_sticky": false}}, {"model": "admin_interface.theme", "pk": 2, "fields": {"name": "Bootstrap", "active": false, "title": "Django administration", "title_color": "#503873", "title_visible": false, "logo": "", "logo_color": "#503873", "logo_max_width": 400, "logo_max_height": 100, "logo_visible": true, "favicon": "", "env_name": "", "env_color": "#E74C3C", "env_visible_in_header": true, "env_visible_in_favicon": true, "language_chooser_active": true, "language_chooser_display": "code", "css_header_background_color": "#FFFFFF", "css_header_text_color": "#463265", "css_header_link_color": "#463265", "css_header_link_hover_color": "#7351A6", "css_module_background_color": "#7351A6", "css_module_background_selected_color": "#FFFFCC", "css_module_text_color": "#FFFFFF", "css_module_link_color": "#CDBFE3", "css_module_link_selected_color": "#FFFFFF", "css_module_link_hover_color": "#FFFFFF", "css_module_rounded_corners": true, "css_generic_link_color": "#463265", "css_generic_link_hover_color": "#7351A6", "css_save_button_background_color": "#5CB85C", "css_save_button_background_hover_color": "#449D44", "css_save_button_text_color": "#FFFFFF", "css_delete_button_background_color": "#D9534F", "css_delete_button_background_hover_color": "#C9302C", "css_delete_button_text_color": "#FFFFFF", "related_modal_active": true, "related_modal_background_color": "#503873", "related_modal_background_opacity": "0.2", "related_modal_rounded_corners": true, "related_modal_close_button_visible": true, "list_filter_dropdown": false, "list_filter_sticky": true, "foldable_apps": true, "recent_actions_visible": true, "form_submit_sticky": false, "form_pagination_sticky": false}}, {"model": "admin_interface.theme", "pk": 3, "fields": {"name": "Foundation", "active": false, "title": "Django administration", "title_color": "#DDDDDD", "title_visible": false, "logo": "", "logo_color": "#CCCCCC", "logo_max_width": 400, "logo_max_height": 100, "logo_visible": true, "favicon": "", "env_name": "", "env_color": "#E74C3C", "env_visible_in_header": true, "env_visible_in_favicon": true, "language_chooser_active": true, "language_chooser_display": "code", "css_header_background_color": "#2C3840", "css_header_text_color": "#FFFFFF", "css_header_link_color": "#FFFFFF", "css_header_link_hover_color": "#DDDDDD", "css_module_background_color": "#074E68", "css_module_background_selected_color": "#FFFFCC", "css_module_text_color": "#FFFFFF", "css_module_link_color": "#FFFFFF", "css_module_link_selected_color": "#FFFFFF", "css_module_link_hover_color": "#DDDDDD", "css_module_rounded_corners": true, "css_generic_link_color": "#000000", "css_generic_link_hover_color": "#074E68", "css_save_button_background_color": "#2199E8", "css_save_button_background_hover_color": "#1585CF", "css_save_button_text_color": "#FFFFFF", "css_delete_button_background_color": "#CC4B37", "css_delete_button_background_hover_color": "#BF4634", "css_delete_button_text_color": "#FFFFFF", "related_modal_active": true, "related_modal_background_color": "#000000", "related_modal_background_opacity": "0.2", "related_modal_rounded_corners": true, "related_modal_close_button_visible": true, "list_filter_dropdown": false, "list_filter_sticky": true, "foldable_apps": true, "recent_actions_visible": true, "form_submit_sticky": false, "form_pagination_sticky": false}}, {"model": "admin_interface.theme", "pk": 4, "fields": {"name": "USWDS", "active": true, "title": "Re-Dcrypt", "title_color": "#00D2D2", "title_visible": true, "logo": "", "logo_color": "#00D2D2", "logo_max_width": 400, "logo_max_height": 100, "logo_visible": true, "favicon": "", "env_name": "Re-Dcrypt", "env_color": "#00D2D2", "env_visible_in_header": true, "env_visible_in_favicon": true, "language_chooser_active": true, "language_chooser_display": "code", "css_header_background_color": "#002A2A", "css_header_text_color": "#00D2D2", "css_header_link_color": "#FFFFFF", "css_header_link_hover_color": "#E1F3F8", "css_module_background_color": "#002A2A", "css_module_background_selected_color": "#FFFFCC", "css_module_text_color": "#00D2D2", "css_module_link_color": "#FFFFFF", "css_module_link_selected_color": "#FFFFFF", "css_module_link_hover_color": "#E1F3F8", "css_module_rounded_corners": true, "css_generic_link_color": "#205493", "css_generic_link_hover_color": "#0071BC", "css_save_button_background_color": "#002A2A", "css_save_button_background_hover_color": "#000000", "css_save_button_text_color": "#00D2D2", "css_delete_button_background_color": "#CD2026", "css_delete_button_background_hover_color": "#981B1E", "css_delete_button_text_color": "#FFFFFF", "related_modal_active": true, "related_modal_background_color": "#000000", "related_modal_background_opacity": "0.3", "related_modal_rounded_corners": true, "related_modal_close_button_visible": true, "list_filter_dropdown": false, "list_filter_sticky": true, "foldable_apps": true, "recent_actions_visible": true, "form_submit_sticky": false, "form_pagination_sticky": false}}, {"model": "admin.logentry", "pk": 1, "fields": {"action_time": "2022-06-07T11:32:37.290Z", "user": 1, "content_type": 8, "object_id": "1", "object_repr": "Question object (1)", "action_flag": 1, "change_message": "[{\"added\": {}}]"}}, {"model": "admin.logentry", "pk": 2, "fields": {"action_time": "2022-06-07T11:52:59.928Z", "user": 1, "content_type": 12, "object_id": "1", "object_repr": "rachit", "action_flag": 2, "change_message": "[{\"changed\": {\"fields\": [\"Name\"]}}]"}}, {"model": "admin.logentry", "pk": 3, "fields": {"action_time": "2022-06-07T12:10:30.008Z", "user": 1, "content_type": 11, "object_id": "1", "object_repr": "hello", "action_flag": 1, "change_message": "[{\"added\": {}}]"}}, {"model": "admin.logentry", "pk": 4, "fields": {"action_time": "2022-06-07T12:13:11.246Z", "user": 1, "content_type": 16, "object_id": "1", "object_repr": "hello", "action_flag": 1, "change_message": "[{\"added\": {}}]"}}, {"model": "admin.logentry", "pk": 5, "fields": {"action_time": "2022-06-07T13:02:10.783Z", "user": 1, "content_type": 9, "object_id": "1", "object_repr": "AdditionalHint object (1)", "action_flag": 1, "change_message": "[{\"added\": {}}]"}}, {"model": "admin.logentry", "pk": 6, "fields": {"action_time": "2022-06-10T19:52:55.483Z", "user": 1, "content_type": 5, "object_id": "2", "object_repr": "dscs", "action_flag": 3, "change_message": ""}}, {"model": "admin.logentry", "pk": 7, "fields": {"action_time": "2022-06-10T19:52:55.495Z", "user": 1, "content_type": 5, "object_id": "4", "object_repr": "dscssa", "action_flag": 3, "change_message": ""}}, {"model": "admin.logentry", "pk": 8, "fields": {"action_time": "2022-06-10T19:52:55.506Z", "user": 1, "content_type": 5, "object_id": "3", "object_repr": "dvsavsscs", "action_flag": 3, "change_message": ""}}, {"model": "admin.logentry", "pk": 9, "fields": {"action_time": "2022-06-10T19:55:36.310Z", "user": 1, "content_type": 5, "object_id": "5", "object_repr": "dscssa", "action_flag": 3, "change_message": ""}}, {"model": "admin.logentry", "pk": 10, "fields": {"action_time": "2022-06-10T19:58:44.743Z", "user": 1, "content_type": 5, "object_id": "6", "object_repr": "dscs", "action_flag": 3, "change_message": ""}}, {"model": "admin.logentry", "pk": 11, "fields": {"action_time": "2022-06-11T03:26:21.306Z", "user": 1, "content_type": 5, "object_id": "7", "object_repr": "rachita", "action_flag": 3, "change_message": ""}}, {"model": "admin.logentry", "pk": 12, "fields": {"action_time": "2022-06-11T04:06:07.330Z", "user": 1, "content_type": 5, "object_id": "8", "object_repr": "dilutewater", "action_flag": 3, "change_message": ""}}, {"model": "admin.logentry", "pk": 13, "fields": {"action_time": "2022-06-11T04:17:19.766Z", "user": 1, "content_type": 5, "object_id": "9", "object_repr": "dilutewater", "action_flag": 3, "change_message": ""}}, {"model": "admin.logentry", "pk": 14, "fields": {"action_time": "2022-06-11T04:18:50.684Z", "user": 1, "content_type": 5, "object_id": "10", "object_repr": "dilutewater", "action_flag": 3, "change_message": ""}}, {"model": "admin.logentry", "pk": 15, "fields": {"action_time": "2022-06-11T04:25:05.570Z", "user": 1, "content_type": 5, "object_id": "11", "object_repr": "dilutewater", "action_flag": 3, "change_message": ""}}, {"model": "admin.logentry", "pk": 16, "fields": {"action_time": "2022-06-11T08:20:25.690Z", "user": 1, "content_type": 12, "object_id": "1", "object_repr": "rachit", "action_flag": 2, "change_message": "[{\"changed\": {\"fields\": [\"Organization\"]}}]"}}, {"model": "admin.logentry", "pk": 17, "fields": {"action_time": "2022-06-11T08:20:42.608Z", "user": 1, "content_type": 5, "object_id": "1", "object_repr": "rachit", "action_flag": 2, "change_message": "[{\"changed\": {\"fields\": [\"Email address\"]}}]"}}, {"model": "admin.logentry", "pk": 18, "fields": {"action_time": "2022-06-11T08:49:13.303Z", "user": 1, "content_type": 20, "object_id": "1", "object_repr": "Discord", "action_flag": 1, "change_message": "[{\"added\": {}}]"}}, {"model": "admin.logentry", "pk": 19, "fields": {"action_time": "2022-06-11T08:55:11.228Z", "user": 1, "content_type": 20, "object_id": "1", "object_repr": "discord", "action_flag": 2, "change_message": "[{\"changed\": {\"fields\": [\"Name\"]}}]"}}, {"model": "admin.logentry", "pk": 20, "fields": {"action_time": "2022-06-11T11:25:12.242Z", "user": 1, "content_type": 19, "object_id": "1", "object_repr": "rachit", "action_flag": 3, "change_message": ""}}, {"model": "admin.logentry", "pk": 21, "fields": {"action_time": "2022-06-11T14:22:18.565Z", "user": 1, "content_type": 19, "object_id": "3", "object_repr": "rachit", "action_flag": 3, "change_message": ""}}, {"model": "admin.logentry", "pk": 22, "fields": {"action_time": "2022-06-11T14:39:39.320Z", "user": 1, "content_type": 19, "object_id": "5", "object_repr": "rachit", "action_flag": 3, "change_message": ""}}, {"model": "admin.logentry", "pk": 23, "fields": {"action_time": "2022-06-11T14:53:12.444Z", "user": 1, "content_type": 19, "object_id": "6", "object_repr": "rachit", "action_flag": 3, "change_message": ""}}, {"model": "admin.logentry", "pk": 24, "fields": {"action_time": "2022-06-11T15:26:21.319Z", "user": 1, "content_type": 19, "object_id": "7", "object_repr": "rachit", "action_flag": 3, "change_message": ""}}, {"model": "admin.logentry", "pk": 25, "fields": {"action_time": "2022-06-12T08:49:19.329Z", "user": 1, "content_type": 19, "object_id": "8", "object_repr": "rachit", "action_flag": 3, "change_message": ""}}, {"model": "admin.logentry", "pk": 26, "fields": {"action_time": "2022-06-12T09:20:48.348Z", "user": 1, "content_type": 12, "object_id": "1", "object_repr": "rachit", "action_flag": 2, "change_message": "[{\"changed\": {\"fields\": [\"Is public name\", \"Is public organization\"]}}]"}}, {"model": "admin.logentry", "pk": 27, "fields": {"action_time": "2022-06-14T15:00:26.163Z", "user": 1, "content_type": 5, "object_id": "1", "object_repr": "rachit", "action_flag": 2, "change_message": "[{\"changed\": {\"fields\": [\"Email address\"]}}]"}}, {"model": "admin.logentry", "pk": 28, "fields": {"action_time": "2022-06-17T19:11:33.269Z", "user": 1, "content_type": 5, "object_id": "12", "object_repr": "dilutewater", "action_flag": 3, "change_message": ""}}, {"model": "admin.logentry", "pk": 29, "fields": {"action_time": "2022-06-17T19:12:49.777Z", "user": 1, "content_type": 17, "object_id": "12", "object_repr": "rachitkhurana40@gmail.com", "action_flag": 3, "change_message": ""}}, {"model": "admin.logentry", "pk": 30, "fields": {"action_time": "2022-06-19T20:05:49.869Z", "user": 1, "content_type": 17, "object_id": "14", "object_repr": "hey@rachitkhurana.xyz", "action_flag": 2, "change_message": "[{\"changed\": {\"fields\": [\"Primary\"]}}]"}}, {"model": "admin.logentry", "pk": 31, "fields": {"action_time": "2022-06-20T04:22:33.396Z", "user": 1, "content_type": 5, "object_id": "13", "object_repr": "dilutewater", "action_flag": 3, "change_message": ""}}, {"model": "admin.logentry", "pk": 32, "fields": {"action_time": "2022-06-20T04:45:00.890Z", "user": 1, "content_type": 16, "object_id": "2", "object_repr": "wow", "action_flag": 1, "change_message": "[{\"added\": {}}]"}}, {"model": "admin.logentry", "pk": 33, "fields": {"action_time": "2022-06-20T05:06:48.898Z", "user": 1, "content_type": 16, "object_id": "3", "object_repr": "hello1", "action_flag": 1, "change_message": "[{\"added\": {}}]"}}, {"model": "admin.logentry", "pk": 34, "fields": {"action_time": "2022-06-20T06:12:35.127Z", "user": 1, "content_type": 5, "object_id": "17", "object_repr": "dilutewater", "action_flag": 3, "change_message": ""}}, {"model": "admin.logentry", "pk": 35, "fields": {"action_time": "2022-06-20T06:14:51.871Z", "user": 1, "content_type": 5, "object_id": "18", "object_repr": "dilutewater", "action_flag": 3, "change_message": ""}}, {"model": "admin.logentry", "pk": 36, "fields": {"action_time": "2022-06-20T06:17:50.087Z", "user": 1, "content_type": 5, "object_id": "19", "object_repr": "dilutewater", "action_flag": 3, "change_message": ""}}, {"model": "admin.logentry", "pk": 37, "fields": {"action_time": "2022-06-20T06:21:53.053Z", "user": 1, "content_type": 5, "object_id": "20", "object_repr": "dilutewater", "action_flag": 3, "change_message": ""}}, {"model": "admin.logentry", "pk": 38, "fields": {"action_time": "2022-06-20T06:32:58.519Z", "user": 1, "content_type": 5, "object_id": "21", "object_repr": "dilutewater", "action_flag": 3, "change_message": ""}}, {"model": "admin.logentry", "pk": 39, "fields": {"action_time": "2022-06-20T06:36:09.026Z", "user": 1, "content_type": 5, "object_id": "22", "object_repr": "dilutewater", "action_flag": 3, "change_message": ""}}, {"model": "admin.logentry", "pk": 40, "fields": {"action_time": "2022-06-20T06:37:39.754Z", "user": 1, "content_type": 5, "object_id": "23", "object_repr": "dilutewater", "action_flag": 3, "change_message": ""}}, {"model": "admin.logentry", "pk": 41, "fields": {"action_time": "2022-06-20T06:39:23.997Z", "user": 1, "content_type": 5, "object_id": "24", "object_repr": "dilutewater", "action_flag": 3, "change_message": ""}}, {"model": "admin.logentry", "pk": 42, "fields": {"action_time": "2022-06-20T06:40:42.341Z", "user": 1, "content_type": 5, "object_id": "25", "object_repr": "dilutewater", "action_flag": 3, "change_message": ""}}, {"model": "admin.logentry", "pk": 43, "fields": {"action_time": "2022-06-20T06:43:24.131Z", "user": 1, "content_type": 5, "object_id": "26", "object_repr": "dw", "action_flag": 3, "change_message": ""}}, {"model": "admin.logentry", "pk": 44, "fields": {"action_time": "2022-06-20T07:15:33.447Z", "user": 1, "content_type": 5, "object_id": "27", "object_repr": "dw2", "action_flag": 3, "change_message": ""}}, {"model": "admin.logentry", "pk": 45, "fields": {"action_time": "2022-06-20T07:15:33.460Z", "user": 1, "content_type": 5, "object_id": "28", "object_repr": "dw3", "action_flag": 3, "change_message": ""}}, {"model": "admin.logentry", "pk": 46, "fields": {"action_time": "2022-06-20T07:15:33.470Z", "user": 1, "content_type": 5, "object_id": "29", "object_repr": "dw4", "action_flag": 3, "change_message": ""}}, {"model": "admin.logentry", "pk": 47, "fields": {"action_time": "2022-06-20T08:59:58.166Z", "user": 1, "content_type": 17, "object_id": "14", "object_repr": "hey@rachitkhurana.xyz", "action_flag": 2, "change_message": "[{\"changed\": {\"fields\": [\"Verified\"]}}]"}}, {"model": "admin.logentry", "pk": 48, "fields": {"action_time": "2022-06-26T06:14:40.815Z", "user": 1, "content_type": 16, "object_id": "4", "object_repr": "another", "action_flag": 1, "change_message": "[{\"added\": {}}]"}}, {"model": "admin.logentry", "pk": 49, "fields": {"action_time": "2022-06-26T06:18:25.633Z", "user": 1, "content_type": 16, "object_id": "4", "object_repr": "another", "action_flag": 2, "change_message": "[{\"changed\": {\"fields\": [\"Is content\"]}}]"}}, {"model": "admin.logentry", "pk": 50, "fields": {"action_time": "2022-06-26T06:28:42.678Z", "user": 1, "content_type": 16, "object_id": "4", "object_repr": "another", "action_flag": 2, "change_message": "[{\"changed\": {\"fields\": [\"Content\"]}}]"}}, {"model": "admin.logentry", "pk": 51, "fields": {"action_time": "2022-06-26T06:35:15.057Z", "user": 1, "content_type": 16, "object_id": "4", "object_repr": "another", "action_flag": 2, "change_message": "[{\"changed\": {\"fields\": [\"Content\"]}}]"}}, {"model": "admin.logentry", "pk": 52, "fields": {"action_time": "2022-06-26T06:52:45.947Z", "user": 1, "content_type": 16, "object_id": "4", "object_repr": "another", "action_flag": 2, "change_message": "[{\"changed\": {\"fields\": [\"Content\", \"Click counts\"]}}]"}}, {"model": "admin.logentry", "pk": 53, "fields": {"action_time": "2022-06-26T06:56:45.912Z", "user": 1, "content_type": 16, "object_id": "4", "object_repr": "another", "action_flag": 2, "change_message": "[{\"changed\": {\"fields\": [\"Content\"]}}]"}}, {"model": "admin.logentry", "pk": 54, "fields": {"action_time": "2022-06-26T06:58:26.065Z", "user": 1, "content_type": 16, "object_id": "4", "object_repr": "another", "action_flag": 2, "change_message": "[{\"changed\": {\"fields\": [\"Content\"]}}]"}}, {"model": "admin.logentry", "pk": 55, "fields": {"action_time": "2022-06-27T11:36:54.854Z", "user": 1, "content_type": 1, "object_id": "4", "object_repr": "USWDS", "action_flag": 2, "change_message": "[{\"changed\": {\"fields\": [\"Logo\"]}}]"}}, {"model": "admin.logentry", "pk": 56, "fields": {"action_time": "2022-06-27T11:57:24.051Z", "user": 1, "content_type": 1, "object_id": "4", "object_repr": "USWDS", "action_flag": 2, "change_message": "[{\"changed\": {\"fields\": [\"Logo\"]}}]"}}, {"model": "admin.logentry", "pk": 57, "fields": {"action_time": "2022-06-27T11:57:38.184Z", "user": 1, "content_type": 1, "object_id": "4", "object_repr": "USWDS", "action_flag": 2, "change_message": "[{\"changed\": {\"fields\": [\"Favicon\"]}}]"}}, {"model": "admin.logentry", "pk": 58, "fields": {"action_time": "2022-06-27T11:58:08.187Z", "user": 1, "content_type": 1, "object_id": "4", "object_repr": "USWDS", "action_flag": 2, "change_message": "[{\"changed\": {\"fields\": [\"Favicon\"]}}]"}}, {"model": "admin.logentry", "pk": 59, "fields": {"action_time": "2022-06-27T11:58:32.517Z", "user": 1, "content_type": 1, "object_id": "4", "object_repr": "USWDS", "action_flag": 2, "change_message": "[{\"changed\": {\"fields\": [\"Favicon\"]}}]"}}, {"model": "admin.logentry", "pk": 60, "fields": {"action_time": "2022-06-27T11:59:05.743Z", "user": 1, "content_type": 1, "object_id": "4", "object_repr": "USWDS", "action_flag": 2, "change_message": "[{\"changed\": {\"fields\": [\"Color\", \"Color\", \"Color\"]}}]"}}, {"model": "admin.logentry", "pk": 61, "fields": {"action_time": "2022-06-27T12:00:18.656Z", "user": 1, "content_type": 1, "object_id": "4", "object_repr": "USWDS", "action_flag": 2, "change_message": "[{\"changed\": {\"fields\": [\"Background color\", \"Text color\", \"Background color\", \"Text color\", \"Background color\", \"Background hover color\", \"Text color\"]}}]"}}, {"model": "admin.logentry", "pk": 62, "fields": {"action_time": "2022-06-30T09:14:34.195Z", "user": 1, "content_type": 8, "object_id": "2", "object_repr": "0: hello", "action_flag": 1, "change_message": "[{\"added\": {}}]"}}, {"model": "admin.logentry", "pk": 63, "fields": {"action_time": "2022-06-30T09:17:07.828Z", "user": 1, "content_type": 8, "object_id": "2", "object_repr": "0: hello", "action_flag": 2, "change_message": "[{\"changed\": {\"fields\": [\"Points\"]}}]"}}, {"model": "admin.logentry", "pk": 64, "fields": {"action_time": "2022-06-30T09:27:12.194Z", "user": 1, "content_type": 8, "object_id": "2", "object_repr": "0: hello", "action_flag": 2, "change_message": "[{\"changed\": {\"fields\": [\"Question text\"]}}]"}}, {"model": "admin.logentry", "pk": 65, "fields": {"action_time": "2022-06-30T11:02:47.417Z", "user": 1, "content_type": 8, "object_id": "2", "object_repr": "0: hello", "action_flag": 2, "change_message": "[{\"changed\": {\"fields\": [\"Img url\"]}}]"}}, {"model": "admin.logentry", "pk": 66, "fields": {"action_time": "2022-06-30T19:25:50.799Z", "user": 1, "content_type": 12, "object_id": "1", "object_repr": "rachit", "action_flag": 2, "change_message": "[{\"changed\": {\"fields\": [\"Current level\"]}}]"}}, {"model": "admin.logentry", "pk": 67, "fields": {"action_time": "2022-06-30T19:28:53.389Z", "user": 1, "content_type": 8, "object_id": "2", "object_repr": "0: hello", "action_flag": 2, "change_message": "[{\"changed\": {\"fields\": [\"Question text\"]}}]"}}, {"model": "admin.logentry", "pk": 68, "fields": {"action_time": "2022-06-30T19:38:26.473Z", "user": 1, "content_type": 12, "object_id": "1", "object_repr": "rachit", "action_flag": 2, "change_message": "[{\"changed\": {\"fields\": [\"Current level\"]}}]"}}, {"model": "admin.logentry", "pk": 69, "fields": {"action_time": "2022-07-01T06:49:08.992Z", "user": 1, "content_type": 12, "object_id": "1", "object_repr": "rachit", "action_flag": 2, "change_message": "[{\"changed\": {\"fields\": [\"Current level\"]}}]"}}, {"model": "admin.logentry", "pk": 70, "fields": {"action_time": "2022-07-01T06:56:22.171Z", "user": 1, "content_type": 8, "object_id": "2", "object_repr": "0: hello", "action_flag": 2, "change_message": "[{\"changed\": {\"fields\": [\"Img url\"]}}]"}}, {"model": "admin.logentry", "pk": 71, "fields": {"action_time": "2022-07-02T06:30:15.483Z", "user": 1, "content_type": 9, "object_id": "2", "object_repr": "bdbdb", "action_flag": 1, "change_message": "[{\"added\": {}}]"}}, {"model": "admin.logentry", "pk": 72, "fields": {"action_time": "2022-07-02T06:34:42.995Z", "user": 1, "content_type": 9, "object_id": "3", "object_repr": "vssd", "action_flag": 1, "change_message": "[{\"added\": {}}]"}}, {"model": "admin.logentry", "pk": 73, "fields": {"action_time": "2022-07-02T06:59:10.084Z", "user": 1, "content_type": 12, "object_id": "1", "object_repr": "rachit", "action_flag": 2, "change_message": "[{\"changed\": {\"fields\": [\"Current level\"]}}]"}}, {"model": "admin.logentry", "pk": 74, "fields": {"action_time": "2022-07-02T09:00:33.401Z", "user": 1, "content_type": 14, "object_id": "1", "object_repr": "HUNT_STATUS [string]", "action_flag": 1, "change_message": "[{\"added\": {}}]"}}, {"model": "admin.logentry", "pk": 75, "fields": {"action_time": "2022-07-02T09:00:52.653Z", "user": 1, "content_type": 14, "object_id": "1", "object_repr": "HUNT_STATUS [string]", "action_flag": 2, "change_message": "[{\"changed\": {\"fields\": [\"Value\"]}}]"}}, {"model": "admin.logentry", "pk": 76, "fields": {"action_time": "2022-07-02T09:01:18.471Z", "user": 1, "content_type": 14, "object_id": "2", "object_repr": "HUNT_START_TIME [datetime]", "action_flag": 1, "change_message": "[{\"added\": {}}]"}}, {"model": "admin.logentry", "pk": 77, "fields": {"action_time": "2022-07-02T09:01:30.083Z", "user": 1, "content_type": 14, "object_id": "2", "object_repr": "HUNT_START_TIME [datetime]", "action_flag": 2, "change_message": "[{\"changed\": {\"fields\": [\"Value\"]}}]"}}, {"model": "admin.logentry", "pk": 78, "fields": {"action_time": "2022-07-02T09:02:55.993Z", "user": 1, "content_type": 14, "object_id": "2", "object_repr": "HUNT_START_TIME [datetime]", "action_flag": 3, "change_message": ""}}, {"model": "admin.logentry", "pk": 79, "fields": {"action_time": "2022-07-02T09:12:49.270Z", "user": 1, "content_type": 14, "object_id": "3", "object_repr": "HUNT_START_TIME [datetime]", "action_flag": 1, "change_message": "[{\"added\": {}}]"}}, {"model": "admin.logentry", "pk": 80, "fields": {"action_time": "2022-07-02T09:13:01.618Z", "user": 1, "content_type": 14, "object_id": "3", "object_repr": "HUNT_START_TIME [datetime]", "action_flag": 2, "change_message": "[{\"changed\": {\"fields\": [\"Value\"]}}]"}}, {"model": "admin.logentry", "pk": 81, "fields": {"action_time": "2022-07-02T09:16:25.050Z", "user": 1, "content_type": 14, "object_id": "3", "object_repr": "HUNT_START_TIME [datetime]", "action_flag": 2, "change_message": "[{\"changed\": {\"fields\": [\"Name\"]}}]"}}, {"model": "admin.logentry", "pk": 82, "fields": {"action_time": "2022-07-02T10:05:37.822Z", "user": 1, "content_type": 14, "object_id": "4", "object_repr": "HUNT_RESUME_TIME [datetime]", "action_flag": 1, "change_message": "[{\"added\": {}}]"}}, {"model": "admin.logentry", "pk": 83, "fields": {"action_time": "2022-07-02T10:05:56.436Z", "user": 1, "content_type": 14, "object_id": "4", "object_repr": "HUNT_RESUME_TIME [datetime]", "action_flag": 2, "change_message": "[{\"changed\": {\"fields\": [\"Value\"]}}]"}}, {"model": "admin.logentry", "pk": 84, "fields": {"action_time": "2022-07-02T10:05:56.454Z", "user": 1, "content_type": 14, "object_id": "1", "object_repr": "HUNT_STATUS [string]", "action_flag": 2, "change_message": "[{\"changed\": {\"fields\": [\"Value\"]}}]"}}, {"model": "admin.logentry", "pk": 85, "fields": {"action_time": "2022-07-02T10:12:48.713Z", "user": 1, "content_type": 14, "object_id": "1", "object_repr": "HUNT_STATUS [string]", "action_flag": 2, "change_message": "[{\"changed\": {\"fields\": [\"Value\", \"Description\"]}}]"}}, {"model": "admin.logentry", "pk": 86, "fields": {"action_time": "2022-07-02T10:16:13.735Z", "user": 1, "content_type": 14, "object_id": "1", "object_repr": "HUNT_STATUS [string]", "action_flag": 2, "change_message": "[{\"changed\": {\"fields\": [\"Value\"]}}]"}}, {"model": "admin.logentry", "pk": 87, "fields": {"action_time": "2022-07-02T10:18:18.880Z", "user": 1, "content_type": 14, "object_id": "1", "object_repr": "HUNT_STATUS [string]", "action_flag": 2, "change_message": "[{\"changed\": {\"fields\": [\"Value\"]}}]"}}, {"model": "admin.logentry", "pk": 88, "fields": {"action_time": "2022-07-02T10:36:40.248Z", "user": 1, "content_type": 14, "object_id": "1", "object_repr": "HUNT_STATUS [string]", "action_flag": 2, "change_message": "[{\"changed\": {\"fields\": [\"Value\"]}}]"}}, {"model": "admin.logentry", "pk": 89, "fields": {"action_time": "2022-07-02T10:38:54.187Z", "user": 1, "content_type": 12, "object_id": "1", "object_repr": "rachit", "action_flag": 2, "change_message": "[{\"changed\": {\"fields\": [\"Current level\"]}}]"}}, {"model": "admin.logentry", "pk": 90, "fields": {"action_time": "2022-07-02T15:38:41.910Z", "user": 1, "content_type": 5, "object_id": "14", "object_repr": "test", "action_flag": 2, "change_message": "[{\"changed\": {\"fields\": [\"Email address\"]}}]"}}, {"model": "admin.logentry", "pk": 91, "fields": {"action_time": "2022-07-02T15:39:06.303Z", "user": 1, "content_type": 5, "object_id": "14", "object_repr": "test", "action_flag": 2, "change_message": "[{\"changed\": {\"fields\": [\"password\"]}}]"}}, {"model": "admin.logentry", "pk": 92, "fields": {"action_time": "2022-07-02T18:00:05.915Z", "user": 1, "content_type": 12, "object_id": "4", "object_repr": "test", "action_flag": 2, "change_message": "[{\"changed\": {\"fields\": [\"Score\"]}}]"}}, {"model": "admin.logentry", "pk": 93, "fields": {"action_time": "2022-07-02T18:00:18.146Z", "user": 1, "content_type": 12, "object_id": "5", "object_repr": "test2", "action_flag": 2, "change_message": "[{\"changed\": {\"fields\": [\"Score\"]}}]"}}, {"model": "admin.logentry", "pk": 94, "fields": {"action_time": "2022-07-02T18:00:47.333Z", "user": 1, "content_type": 12, "object_id": "6", "object_repr": "testing", "action_flag": 2, "change_message": "[{\"changed\": {\"fields\": [\"Score\"]}}]"}}, {"model": "admin.logentry", "pk": 95, "fields": {"action_time": "2022-07-02T18:01:14.846Z", "user": 1, "content_type": 12, "object_id": "20", "object_repr": "dr", "action_flag": 2, "change_message": "[{\"changed\": {\"fields\": [\"Score\"]}}]"}}, {"model": "admin.logentry", "pk": 96, "fields": {"action_time": "2022-07-03T07:17:30.088Z", "user": 1, "content_type": 12, "object_id": "4", "object_repr": "test", "action_flag": 2, "change_message": "[{\"changed\": {\"fields\": [\"Is banned\"]}}]"}}, {"model": "admin.logentry", "pk": 97, "fields": {"action_time": "2022-07-03T07:20:38.996Z", "user": 1, "content_type": 12, "object_id": "4", "object_repr": "test", "action_flag": 2, "change_message": "[{\"changed\": {\"fields\": [\"Banned reason\"]}}]"}}, {"model": "admin.logentry", "pk": 98, "fields": {"action_time": "2022-07-05T17:05:23.372Z", "user": 1, "content_type": 17, "object_id": "14", "object_repr": "hey@rachitkhurana.xyz", "action_flag": 2, "change_message": "[{\"changed\": {\"fields\": [\"Verified\"]}}]"}}, {"model": "admin.logentry", "pk": 99, "fields": {"action_time": "2022-07-09T08:37:26.458Z", "user": 1, "content_type": 5, "object_id": "35", "object_repr": "admin", "action_flag": 3, "change_message": ""}}, {"model": "admin.logentry", "pk": 100, "fields": {"action_time": "2022-07-09T08:37:26.559Z", "user": 1, "content_type": 5, "object_id": "35", "object_repr": "admin", "action_flag": 3, "change_message": ""}}, {"model": "admin.logentry", "pk": 101, "fields": {"action_time": "2022-07-16T13:06:18.916Z", "user": 1, "content_type": 12, "object_id": "1", "object_repr": "rachit", "action_flag": 2, "change_message": "[{\"changed\": {\"fields\": [\"Current level\"]}}]"}}, {"model": "admin.logentry", "pk": 102, "fields": {"action_time": "2022-07-29T15:17:56.486Z", "user": 1, "content_type": 27, "object_id": "1", "object_repr": "0: Another", "action_flag": 1, "change_message": "[{\"added\": {}}]"}}, {"model": "admin.logentry", "pk": 103, "fields": {"action_time": "2022-07-29T17:12:28.592Z", "user": 1, "content_type": 27, "object_id": "2", "object_repr": "1: aedhtryh", "action_flag": 1, "change_message": "[{\"added\": {}}]"}}, {"model": "admin.logentry", "pk": 104, "fields": {"action_time": "2022-07-29T17:56:15.107Z", "user": 1, "content_type": 27, "object_id": "2", "object_repr": "1: aedhtryh", "action_flag": 3, "change_message": ""}}, {"model": "admin.logentry", "pk": 105, "fields": {"action_time": "2022-07-29T17:56:15.127Z", "user": 1, "content_type": 27, "object_id": "1", "object_repr": "0: Another", "action_flag": 3, "change_message": ""}}, {"model": "admin.logentry", "pk": 106, "fields": {"action_time": "2022-07-30T09:01:03.422Z", "user": 1, "content_type": 12, "object_id": "1", "object_repr": "rachit", "action_flag": 2, "change_message": "[{\"changed\": {\"fields\": [\"Current level\"]}}]"}}, {"model": "admin.logentry", "pk": 107, "fields": {"action_time": "2022-08-02T06:12:08.855Z", "user": 1, "content_type": 12, "object_id": "1", "object_repr": "rachit", "action_flag": 2, "change_message": "[{\"changed\": {\"fields\": [\"Current level\"]}}]"}}, {"model": "admin.logentry", "pk": 108, "fields": {"action_time": "2022-08-02T06:12:30.308Z", "user": 1, "content_type": 8, "object_id": "2", "object_repr": "0: hello", "action_flag": 2, "change_message": "[{\"changed\": {\"fields\": [\"Img url\"]}}]"}}, {"model": "admin.logentry", "pk": 109, "fields": {"action_time": "2022-08-02T06:13:53.888Z", "user": 1, "content_type": 9, "object_id": "2", "object_repr": "\r\nPrint", "level": 0, "points": 100}}, {"model": "hunt.additionalhint", "pk": 1, "fields": {"question": 1, "hint_text": "hufuew"}}, {"model": "hunt.additionalhint", "pk": 2, "fields": {"question": 2, "hint_text": ""}}, {"model": "hunt.additionalhint", "pk": 3, "fields": {"question": 2, "hint_text": "vssd"}}, {"model": "hunt.leveltracking", "pk": 1, "fields": {"user": 1, "level": 1, "time": "2022-07-02T06:59:48.763Z"}}, {"model": "hunt.leveltracking", "pk": 2, "fields": {"user": 1, "level": 2, "time": "2022-07-02T10:36:57.408Z"}}, {"model": "hunt.leveltracking", "pk": 3, "fields": {"user": 1, "level": 1, "time": "2022-07-05T08:03:20.802Z"}}, {"model": "hunt.leveltracking", "pk": 4, "fields": {"user": 1, "level": 2, "time": "2022-07-05T08:03:28.628Z"}}, {"model": "hunt.leveltracking", "pk": 5, "fields": {"user": 1, "level": 1, "time": "2022-08-03T11:15:13.819Z"}}, {"model": "hunt.leveltracking", "pk": 6, "fields": {"user": 1, "level": 1, "time": "2022-08-11T04:10:03.970Z"}}, {"model": "hunt.leveltracking", "pk": 7, "fields": {"user": 1, "level": 1, "time": "2022-08-11T04:21:44.248Z"}}, {"model": "hunt.leveltracking", "pk": 8, "fields": {"user": 1, "level": 1, "time": "2022-08-11T04:22:35.182Z"}}, {"model": "hunt.leveltracking", "pk": 9, "fields": {"user": 1, "level": 2, "time": "2022-08-11T04:33:51.116Z"}}, {"model": "hunt.leveltracking", "pk": 10, "fields": {"user": 1, "level": 1, "time": "2022-08-11T11:01:03.267Z"}}, {"model": "hunt.leveltracking", "pk": 11, "fields": {"user": 38, "level": 1, "time": "2022-08-16T12:22:01.954Z"}}, {"model": "hunt.leveltracking", "pk": 12, "fields": {"user": 38, "level": 1, "time": "2022-08-16T12:44:27.702Z"}}, {"model": "hunt.leveltracking", "pk": 13, "fields": {"user": 38, "level": 1, "time": "2022-08-16T12:49:00.314Z"}}, {"model": "hunt.leveltracking", "pk": 14, "fields": {"user": 38, "level": 2, "time": "2022-08-16T12:49:18.006Z"}}, {"model": "hunt.answerattempt", "pk": 1, "fields": {"user": 1, "level": 1, "answer": "hello", "time": "2022-06-07T12:10:30.005Z"}}, {"model": "hunt.answerattempt", "pk": 2, "fields": {"user": 1, "level": 1, "answer": "ynt", "time": "2022-07-02T06:58:41.528Z"}}, {"model": "hunt.answerattempt", "pk": 3, "fields": {"user": 1, "level": 0, "answer": "hello", "time": "2022-07-02T06:59:28.971Z"}}, {"model": "hunt.answerattempt", "pk": 4, "fields": {"user": 1, "level": 0, "answer": "hi", "time": "2022-07-02T06:59:48.745Z"}}, {"model": "hunt.answerattempt", "pk": 5, "fields": {"user": 1, "level": 1, "answer": "hi", "time": "2022-07-02T10:36:54.579Z"}}, {"model": "hunt.answerattempt", "pk": 6, "fields": {"user": 1, "level": 1, "answer": "hello", "time": "2022-07-02T10:36:57.386Z"}}, {"model": "hunt.answerattempt", "pk": 7, "fields": {"user": 1, "level": 0, "answer": "hello", "time": "2022-07-05T08:03:17.022Z"}}, {"model": "hunt.answerattempt", "pk": 8, "fields": {"user": 1, "level": 0, "answer": "hi", "time": "2022-07-05T08:03:20.751Z"}}, {"model": "hunt.answerattempt", "pk": 9, "fields": {"user": 1, "level": 1, "answer": "hello", "time": "2022-07-05T08:03:28.601Z"}}, {"model": "hunt.answerattempt", "pk": 10, "fields": {"user": 1, "level": 0, "answer": "fgvfg", "time": "2022-07-16T13:06:29.255Z"}}, {"model": "hunt.answerattempt", "pk": 11, "fields": {"user": 1, "level": 0, "answer": "erere", "time": "2022-07-16T13:06:34.500Z"}}, {"model": "hunt.answerattempt", "pk": 12, "fields": {"user": 1, "level": 0, "answer": "fefer", "time": "2022-07-16T13:06:39.284Z"}}, {"model": "hunt.answerattempt", "pk": 13, "fields": {"user": 1, "level": 0, "answer": "vevr", "time": "2022-07-16T13:06:43.793Z"}}, {"model": "hunt.answerattempt", "pk": 14, "fields": {"user": 1, "level": 0, "answer": "helloworld", "time": "2022-07-25T16:08:42.978Z"}}, {"model": "hunt.answerattempt", "pk": 15, "fields": {"user": 1, "level": 0, "answer": "dil", "time": "2022-07-29T17:12:36.710Z"}}, {"model": "hunt.answerattempt", "pk": 16, "fields": {"user": 1, "level": 0, "answer": "cgmh", "time": "2022-07-29T17:12:41.486Z"}}, {"model": "hunt.answerattempt", "pk": 17, "fields": {"user": 1, "level": 0, "answer": "sd", "time": "2022-07-29T17:12:45.506Z"}}, {"model": "hunt.answerattempt", "pk": 18, "fields": {"user": 1, "level": 0, "answer": "ok", "time": "2022-07-29T17:13:04.565Z"}}, {"model": "hunt.answerattempt", "pk": 19, "fields": {"user": 1, "level": 0, "answer": "hgjchf", "time": "2022-07-29T17:19:58.133Z"}}, {"model": "hunt.answerattempt", "pk": 20, "fields": {"user": 1, "level": 0, "answer": "f", "time": "2022-07-29T17:20:03.080Z"}}, {"model": "hunt.answerattempt", "pk": 21, "fields": {"user": 1, "level": 0, "answer": "xgdheyt", "time": "2022-07-29T17:20:06.306Z"}}, {"model": "hunt.answerattempt", "pk": 22, "fields": {"user": 1, "level": 0, "answer": " vjgh", "time": "2022-07-29T17:20:13.474Z"}}, {"model": "hunt.answerattempt", "pk": 23, "fields": {"user": 1, "level": 0, "answer": " vgjg", "time": "2022-07-29T17:20:18.909Z"}}, {"model": "hunt.answerattempt", "pk": 24, "fields": {"user": 1, "level": 0, "answer": " fcj", "time": "2022-07-29T17:20:24.806Z"}}, {"model": "hunt.answerattempt", "pk": 25, "fields": {"user": 1, "level": 0, "answer": "ttcjhrdj", "time": "2022-07-29T17:20:36.628Z"}}, {"model": "hunt.answerattempt", "pk": 26, "fields": {"user": 1, "level": 0, "answer": "chjfg", "time": "2022-07-29T17:20:45.775Z"}}, {"model": "hunt.answerattempt", "pk": 27, "fields": {"user": 1, "level": 0, "answer": "jmcxfy", "time": "2022-07-29T17:20:53.517Z"}}, {"model": "hunt.answerattempt", "pk": 28, "fields": {"user": 1, "level": 0, "answer": "hello", "time": "2022-08-03T11:15:10.134Z"}}, {"model": "hunt.answerattempt", "pk": 29, "fields": {"user": 1, "level": 0, "answer": "hi", "time": "2022-08-03T11:15:13.803Z"}}, {"model": "hunt.answerattempt", "pk": 30, "fields": {"user": 1, "level": 1, "answer": "xcewc", "time": "2022-08-11T04:03:59.615Z"}}, {"model": "hunt.answerattempt", "pk": 31, "fields": {"user": 1, "level": 1, "answer": "cerccec", "time": "2022-08-11T04:04:05.552Z"}}, {"model": "hunt.answerattempt", "pk": 32, "fields": {"user": 1, "level": 1, "answer": "xwecew", "time": "2022-08-11T04:04:20.065Z"}}, {"model": "hunt.answerattempt", "pk": 33, "fields": {"user": 1, "level": 1, "answer": "cecw", "time": "2022-08-11T04:06:49.529Z"}}, {"model": "hunt.answerattempt", "pk": 34, "fields": {"user": 1, "level": 0, "answer": "hello", "time": "2022-08-11T04:09:59.465Z"}}, {"model": "hunt.answerattempt", "pk": 35, "fields": {"user": 1, "level": 0, "answer": "hi", "time": "2022-08-11T04:10:03.919Z"}}, {"model": "hunt.answerattempt", "pk": 36, "fields": {"user": 1, "level": 1, "answer": "vevrever", "time": "2022-08-11T04:10:37.248Z"}}, {"model": "hunt.answerattempt", "pk": 37, "fields": {"user": 1, "level": 1, "answer": "cewcew", "time": "2022-08-11T04:12:04.657Z"}}, {"model": "hunt.answerattempt", "pk": 38, "fields": {"user": 1, "level": 1, "answer": "gg45g", "time": "2022-08-11T04:12:54.312Z"}}, {"model": "hunt.answerattempt", "pk": 39, "fields": {"user": 1, "level": 1, "answer": "cccer", "time": "2022-08-11T04:13:41.691Z"}}, {"model": "hunt.answerattempt", "pk": 40, "fields": {"user": 1, "level": 1, "answer": "vdsvs", "time": "2022-08-11T04:16:57.160Z"}}, {"model": "hunt.answerattempt", "pk": 41, "fields": {"user": 1, "level": 1, "answer": "bgber", "time": "2022-08-11T04:17:05.792Z"}}, {"model": "hunt.answerattempt", "pk": 42, "fields": {"user": 1, "level": 1, "answer": "cecw", "time": "2022-08-11T04:17:44.230Z"}}, {"model": "hunt.answerattempt", "pk": 43, "fields": {"user": 1, "level": 1, "answer": "dqwd", "time": "2022-08-11T04:17:53.096Z"}}, {"model": "hunt.answerattempt", "pk": 44, "fields": {"user": 1, "level": 1, "answer": "dewdew", "time": "2022-08-11T04:18:48.127Z"}}, {"model": "hunt.answerattempt", "pk": 45, "fields": {"user": 1, "level": 1, "answer": "cwew", "time": "2022-08-11T04:19:34.556Z"}}, {"model": "hunt.answerattempt", "pk": 46, "fields": {"user": 1, "level": 1, "answer": "xewwe", "time": "2022-08-11T04:20:37.540Z"}}, {"model": "hunt.answerattempt", "pk": 47, "fields": {"user": 1, "level": 1, "answer": "ccxcw", "time": "2022-08-11T04:20:41.947Z"}}, {"model": "hunt.answerattempt", "pk": 48, "fields": {"user": 1, "level": 1, "answer": "ttrg", "time": "2022-08-11T04:20:49.752Z"}}, {"model": "hunt.answerattempt", "pk": 49, "fields": {"user": 1, "level": 1, "answer": "rttgr", "time": "2022-08-11T04:20:53.280Z"}}, {"model": "hunt.answerattempt", "pk": 50, "fields": {"user": 1, "level": 0, "answer": "cscds", "time": "2022-08-11T04:21:36.560Z"}}, {"model": "hunt.answerattempt", "pk": 51, "fields": {"user": 1, "level": 0, "answer": "hello", "time": "2022-08-11T04:21:40.970Z"}}, {"model": "hunt.answerattempt", "pk": 52, "fields": {"user": 1, "level": 0, "answer": "hi", "time": "2022-08-11T04:21:44.220Z"}}, {"model": "hunt.answerattempt", "pk": 53, "fields": {"user": 1, "level": 0, "answer": "dewwdew", "time": "2022-08-11T04:22:29.153Z"}}, {"model": "hunt.answerattempt", "pk": 54, "fields": {"user": 1, "level": 0, "answer": "hi", "time": "2022-08-11T04:22:35.144Z"}}, {"model": "hunt.answerattempt", "pk": 55, "fields": {"user": 1, "level": 1, "answer": "verve", "time": "2022-08-11T04:33:36.061Z"}}, {"model": "hunt.answerattempt", "pk": 56, "fields": {"user": 1, "level": 1, "answer": "vervre", "time": "2022-08-11T04:33:44.121Z"}}, {"model": "hunt.answerattempt", "pk": 57, "fields": {"user": 1, "level": 1, "answer": "hi", "time": "2022-08-11T04:33:47.931Z"}}, {"model": "hunt.answerattempt", "pk": 58, "fields": {"user": 1, "level": 1, "answer": "hello", "time": "2022-08-11T04:33:51.091Z"}}, {"model": "hunt.answerattempt", "pk": 59, "fields": {"user": 1, "level": 0, "answer": "ccwecw", "time": "2022-08-11T10:48:40.018Z"}}, {"model": "hunt.answerattempt", "pk": 60, "fields": {"user": 1, "level": 0, "answer": " hjgjv", "time": "2022-08-11T10:51:26.992Z"}}, {"model": "hunt.answerattempt", "pk": 61, "fields": {"user": 1, "level": 0, "answer": " df d", "time": "2022-08-11T10:51:34.090Z"}}, {"model": "hunt.answerattempt", "pk": 62, "fields": {"user": 1, "level": 0, "answer": "bvdfvvdf", "time": "2022-08-11T10:52:18.682Z"}}, {"model": "hunt.answerattempt", "pk": 63, "fields": {"user": 1, "level": 0, "answer": "cxcew", "time": "2022-08-11T10:53:35.847Z"}}, {"model": "hunt.answerattempt", "pk": 64, "fields": {"user": 1, "level": 0, "answer": "cercercer", "time": "2022-08-11T10:54:32.276Z"}}, {"model": "hunt.answerattempt", "pk": 65, "fields": {"user": 1, "level": 0, "answer": "deew", "time": "2022-08-11T10:54:47.875Z"}}, {"model": "hunt.answerattempt", "pk": 66, "fields": {"user": 1, "level": 0, "answer": "dwed", "time": "2022-08-11T10:55:26.798Z"}}, {"model": "hunt.answerattempt", "pk": 67, "fields": {"user": 1, "level": 0, "answer": "cdcds", "time": "2022-08-11T10:55:31.524Z"}}, {"model": "hunt.answerattempt", "pk": 68, "fields": {"user": 1, "level": 0, "answer": "axxsa", "time": "2022-08-11T10:56:42.110Z"}}, {"model": "hunt.answerattempt", "pk": 69, "fields": {"user": 1, "level": 0, "answer": "xaxas", "time": "2022-08-11T10:56:52.343Z"}}, {"model": "hunt.answerattempt", "pk": 70, "fields": {"user": 1, "level": 0, "answer": "vrvre", "time": "2022-08-11T10:58:11.997Z"}}, {"model": "hunt.answerattempt", "pk": 71, "fields": {"user": 1, "level": 0, "answer": "xewx", "time": "2022-08-11T10:58:57.523Z"}}, {"model": "hunt.answerattempt", "pk": 72, "fields": {"user": 1, "level": 0, "answer": "ferfer", "time": "2022-08-11T10:59:52.209Z"}}, {"model": "hunt.answerattempt", "pk": 73, "fields": {"user": 1, "level": 0, "answer": "ccdscds", "time": "2022-08-11T11:00:31.835Z"}}, {"model": "hunt.answerattempt", "pk": 74, "fields": {"user": 1, "level": 0, "answer": "hi", "time": "2022-08-11T11:01:03.215Z"}}, {"model": "hunt.answerattempt", "pk": 75, "fields": {"user": 1, "level": 1, "answer": "dewdwe", "time": "2022-08-11T11:01:16.837Z"}}, {"model": "hunt.answerattempt", "pk": 76, "fields": {"user": 1, "level": 1, "answer": "hrthtr", "time": "2022-08-11T11:01:25.068Z"}}, {"model": "hunt.answerattempt", "pk": 77, "fields": {"user": 38, "level": 0, "answer": "hi", "time": "2022-08-16T12:22:01.909Z"}}, {"model": "hunt.answerattempt", "pk": 78, "fields": {"user": 38, "level": 0, "answer": "hi", "time": "2022-08-16T12:44:27.679Z"}}, {"model": "hunt.answerattempt", "pk": 79, "fields": {"user": 38, "level": 0, "answer": "hi", "time": "2022-08-16T12:49:00.273Z"}}, {"model": "hunt.answerattempt", "pk": 80, "fields": {"user": 38, "level": 1, "answer": "hello", "time": "2022-08-16T12:49:17.954Z"}}, {"model": "hunt.answerattempt", "pk": 81, "fields": {"user": 1, "level": 1, "answer": "fgrv", "time": "2022-08-29T19:47:22.196Z"}}, {"model": "hunt.answerattempt", "pk": 82, "fields": {"user": 1, "level": 1, "answer": "cdscds", "time": "2022-08-29T19:47:25.081Z"}}, {"model": "hunt.answerattempt", "pk": 83, "fields": {"user": 1, "level": 1, "answer": "cdscd", "time": "2022-08-29T19:47:28.492Z"}}, {"model": "hunt.answerattempt", "pk": 84, "fields": {"user": 1, "level": 1, "answer": "cdscs", "time": "2022-08-29T19:47:31.932Z"}}, {"model": "hunt.answerattempt", "pk": 85, "fields": {"user": 1, "level": 1, "answer": "cdscsd", "time": "2022-08-29T19:47:36.447Z"}}, {"model": "hunt.answerattempt", "pk": 86, "fields": {"user": 1, "level": 1, "answer": "c", "time": "2022-08-29T19:47:47.951Z"}}, {"model": "hunt.answerattempt", "pk": 87, "fields": {"user": 1, "level": 1, "answer": "csdcsd", "time": "2022-08-29T19:47:51.586Z"}}, {"model": "hunt.answerattempt", "pk": 88, "fields": {"user": 1, "level": 1, "answer": "vfdvd", "time": "2022-08-30T11:55:04.313Z"}}, {"model": "hunt.answerattempt", "pk": 89, "fields": {"user": 1, "level": 1, "answer": "vsdcsdc", "time": "2022-08-30T11:55:07.978Z"}}, {"model": "hunt.answerattempt", "pk": 90, "fields": {"user": 1, "level": 1, "answer": "ok", "time": "2022-08-30T11:55:12.801Z"}}, {"model": "hunt.answerattempt", "pk": 91, "fields": {"user": 38, "level": 0, "answer": "gergre", "time": "2022-09-01T12:14:26.513Z"}}, {"model": "hunt.answerattempt", "pk": 92, "fields": {"user": 38, "level": 0, "answer": "hello world", "time": "2022-09-01T14:07:03.057Z"}}, {"model": "hunt.answerattempt", "pk": 93, "fields": {"user": 38, "level": 0, "answer": "visualstudio", "time": "2022-09-01T14:07:26.253Z"}}, {"model": "hunt.answerattempt", "pk": 94, "fields": {"user": 38, "level": 0, "answer": "sdsdsd", "time": "2022-09-01T14:07:42.095Z"}}, {"model": "hunt.answerattempt", "pk": 95, "fields": {"user": 38, "level": 0, "answer": "helloworld", "time": "2022-09-01T14:08:08.900Z"}}, {"model": "hunt.easteregg", "pk": 1, "fields": {"egg": "ok", "claimed_by": null, "points": 10, "claimed": false}}, {"model": "hunt.easteregg", "pk": 2, "fields": {"egg": "nice", "claimed_by": null, "points": 10, "claimed": false}}, {"model": "hunt.easteregg", "pk": 3, "fields": {"egg": "redcrypt", "claimed_by": null, "points": 10, "claimed": false}}, {"model": "accounts.profile", "pk": 1, "fields": {"user": 1, "name": "Rachit Khuranaa", "score": 900, "last_completed_time": "2022-09-01T12:08:28.049Z", "is_public_name": true, "current_level": 1, "discord_id": 744224056966119565, "organization": "SPV", "is_public_organization": true, "is_banned": false, "banned_reason": "", "ip_address": ["", "127.0.0.1", "122.161.51.173", "122.161.48.44", "162.158.227.187", "162.158.48.127", "162.158.48.63", "162.158.227.151", "162.158.235.30", "162.158.235.42", "162.158.48.121", "162.158.227.135", "162.158.235.36", "2401:4900:1f38:be:f55b:66f1:2e56:fc7f", "2401:4900:1f38:be:bd51:eb29:8ced:807d", "2401:4900:1f38:be:3210:2b31:a590:b414", "2401:4900:1f38:be:cd6:9c6d:8581:de36", "2401:4900:1f38:be:df81:d18c:109:e3d8", "2401:4900:1f38:be:1c2e:48f3:cb76:a32d", "2401:4900:1f39:4e18:9153:c7f9:3781:26fb", "2401:4900:1c5d:8e18:f02b:77a1:5889:2111", "2401:4900:1f39:5b51:e3ac:41ec:e806:5fdf", "2401:4900:1f39:64f2:125b:ccfa:8610:8e3f", "172.17.0.1", "2401:4900:1f39:64f2:797e:5d67:51b:c322", "2401:4900:1f39:64f2:4930:eb4:3c6b:9854", "122.161.72.73", "2401:4900:1f39:64f2:73b7:9fba:d099:2326", "2401:4900:1f39:64f2:7d7d:1c81:9b0f:fb44", "2401:4900:1c5a:9c25:ca6c:76ab:7d4a:6a3a", "2401:4900:1c5c:4a3:a14f:d1ce:fbcf:a620", "2401:4900:1c5c:4a3:814:1a1a:9af0:57e4", "2401:4900:1c5c:986f:c975:6356:e86f:37a7", "122.161.49.144"], "ip_address_count": 33, "avatar_url": "https://cdn.discordapp.com/icons/933037692340551700/4ad0fcb29185882181c8b49d2141ddc1.webp?size=512", "stats": {"0": 3, "1": 12}, "rank": "0"}}, {"model": "accounts.profile", "pk": 4, "fields": {"user": 14, "name": "", "score": 700, "last_completed_time": "2022-08-17T10:48:31.777Z", "is_public_name": false, "current_level": 0, "discord_id": 770552570762493964, "organization": "", "is_public_organization": false, "is_banned": false, "banned_reason": "fun", "ip_address": ["", "127.0.0.1"], "ip_address_count": 1, "avatar_url": "https://cdn.discordapp.com/avatars/770552570762493964/e2f28359f286095f01952e0c77d52faf.png", "stats": {}, "rank": "0"}}, {"model": "accounts.profile", "pk": 5, "fields": {"user": 15, "name": "", "score": 500, "last_completed_time": "2022-08-26T19:39:29.823Z", "is_public_name": false, "current_level": 0, "discord_id": 0, "organization": "", "is_public_organization": false, "is_banned": false, "banned_reason": "", "ip_address": ["", "127.0.0.1"], "ip_address_count": 1, "avatar_url": "https://source.boringavatars.com/beam/512/test2?colors=00D2D2,006D6D,002A2A,055D5D,074848&square", "stats": {}, "rank": "0"}}, {"model": "accounts.profile", "pk": 6, "fields": {"user": 16, "name": "", "score": 500, "last_completed_time": "2022-08-26T19:39:52.967Z", "is_public_name": false, "current_level": 0, "discord_id": 0, "organization": "", "is_public_organization": false, "is_banned": false, "banned_reason": "", "ip_address": ["", "127.0.0.1"], "ip_address_count": 1, "avatar_url": "https://source.boringavatars.com/beam/512/testing?colors=00D2D2,006D6D,002A2A,055D5D,074848&square", "stats": {}, "rank": "0"}}, {"model": "accounts.profile", "pk": 20, "fields": {"user": 30, "name": "", "score": 500, "last_completed_time": "2022-08-26T19:40:15.374Z", "is_public_name": false, "current_level": 0, "discord_id": 0, "organization": "", "is_public_organization": false, "is_banned": false, "banned_reason": "", "ip_address": ["", "127.0.0.1"], "ip_address_count": 1, "avatar_url": "https://source.boringavatars.com/beam/512/dr?colors=00D2D2,006D6D,002A2A,055D5D,074848&square", "stats": {}, "rank": "0"}}, {"model": "accounts.profile", "pk": 21, "fields": {"user": 31, "name": "", "score": 0, "last_completed_time": "2022-08-26T19:40:26.279Z", "is_public_name": false, "current_level": 0, "discord_id": 0, "organization": "", "is_public_organization": false, "is_banned": false, "banned_reason": "", "ip_address": ["", "127.0.0.1"], "ip_address_count": 1, "avatar_url": "https://source.boringavatars.com/beam/512/dr1?colors=00D2D2,006D6D,002A2A,055D5D,074848&square", "stats": {}, "rank": "0"}}, {"model": "accounts.profile", "pk": 22, "fields": {"user": 32, "name": "", "score": 0, "last_completed_time": "2022-08-26T19:42:24.421Z", "is_public_name": false, "current_level": 0, "discord_id": 0, "organization": "", "is_public_organization": false, "is_banned": false, "banned_reason": "", "ip_address": ["", "127.0.0.1"], "ip_address_count": 1, "avatar_url": "https://source.boringavatars.com/beam/512/dr2?colors=00D2D2,006D6D,002A2A,055D5D,074848&square", "stats": {}, "rank": "0"}}, {"model": "accounts.profile", "pk": 23, "fields": {"user": 33, "name": "", "score": 0, "last_completed_time": "2022-08-26T19:41:08.941Z", "is_public_name": false, "current_level": 0, "discord_id": 0, "organization": "", "is_public_organization": false, "is_banned": false, "banned_reason": "", "ip_address": ["", "162.158.48.129", "162.158.227.151"], "ip_address_count": 2, "avatar_url": "https://source.boringavatars.com/beam/512/rk?colors=00D2D2,006D6D,002A2A,055D5D,074848&square", "stats": {}, "rank": "0"}}, {"model": "accounts.profile", "pk": 24, "fields": {"user": 34, "name": "", "score": 0, "last_completed_time": "2022-08-26T19:41:24.608Z", "is_public_name": false, "current_level": 0, "discord_id": 0, "organization": "", "is_public_organization": false, "is_banned": false, "banned_reason": "", "ip_address": ["", "2401:4900:1f38:be:cd6:9c6d:8581:de36", "2401:4900:1f38:be:df81:d18c:109:e3d8"], "ip_address_count": 2, "avatar_url": "https://source.boringavatars.com/beam/512/wow?colors=00D2D2,006D6D,002A2A,055D5D,074848&square", "stats": {}, "rank": "0"}}, {"model": "accounts.profile", "pk": 25, "fields": {"user": 36, "name": "", "score": 0, "last_completed_time": "2022-08-03T09:12:04.003Z", "is_public_name": false, "current_level": 0, "discord_id": 0, "organization": "", "is_public_organization": false, "is_banned": true, "banned_reason": "I wish", "ip_address": ["127.0.0.1", "20.219.192.211"], "ip_address_count": 2, "avatar_url": "https://source.boringavatars.com/beam/512/ dilutewater123456?colors=00D2D2,006D6D,002A2A,055D5D,074848&square", "stats": {}, "rank": "0"}}, {"model": "accounts.profile", "pk": 26, "fields": {"user": 37, "name": "", "score": 0, "last_completed_time": "2022-08-03T08:32:14.252Z", "is_public_name": false, "current_level": 0, "discord_id": 0, "organization": "", "is_public_organization": false, "is_banned": false, "banned_reason": "", "ip_address": ["127.0.0.1"], "ip_address_count": 1, "avatar_url": "https://source.boringavatars.com/beam/512/rachit16?colors=00D2D2,006D6D,002A2A,055D5D,074848&square", "stats": {}, "rank": "0"}}, {"model": "accounts.profile", "pk": 27, "fields": {"user": 38, "name": "", "score": 300, "last_completed_time": "2022-09-01T15:40:24.970Z", "is_public_name": false, "current_level": 0, "discord_id": 0, "organization": "", "is_public_organization": false, "is_banned": false, "banned_reason": "", "ip_address": ["127.0.0.1", "2401:4900:1c5a:9c25:ca6c:76ab:7d4a:6a3a", "103.203.254.159", "2401:4900:1c5c:4a3:a14f:d1ce:fbcf:a620", "122.161.49.144", "106.51.24.198", "122.162.144.13"], "ip_address_count": 7, "avatar_url": "https://source.boringavatars.com/beam/512/demo?colors=00D2D2,006D6D,002A2A,055D5D,074848&square", "stats": {"0": 8, "1": 1}, "rank": "0"}}, {"model": "accounts.profile", "pk": 28, "fields": {"user": 39, "name": "nishit", "score": 0, "last_completed_time": "2022-09-01T16:15:42.215Z", "is_public_name": false, "current_level": 0, "discord_id": 0, "organization": "Delhi Public School, Vasant Kunj", "is_public_organization": false, "is_banned": false, "banned_reason": "", "ip_address": ["122.162.144.13"], "ip_address_count": 1, "avatar_url": "https://source.boringavatars.com/beam/512/nishit?colors=00D2D2,006D6D,002A2A,055D5D,074848&square", "stats": {}, "rank": "0"}}, {"model": "accounts.ips", "pk": 1, "fields": {"ip_address": "127.0.0.1", "count": 27, "users_from_ip": [1, 14, 15, 16, 30, 31, 32, 36, 37, 38]}}, {"model": "accounts.ips", "pk": 2, "fields": {"ip_address": "122.161.51.173", "count": 1, "users_from_ip": [1]}}, {"model": "accounts.ips", "pk": 3, "fields": {"ip_address": "122.161.48.44", "count": 1, "users_from_ip": [1]}}, {"model": "accounts.ips", "pk": 4, "fields": {"ip_address": "162.158.227.187", "count": 1, "users_from_ip": [1]}}, {"model": "accounts.ips", "pk": 5, "fields": {"ip_address": "162.158.48.127", "count": 1, "users_from_ip": [1]}}, {"model": "accounts.ips", "pk": 6, "fields": {"ip_address": "162.158.48.63", "count": 1, "users_from_ip": [1]}}, {"model": "accounts.ips", "pk": 7, "fields": {"ip_address": "162.158.227.151", "count": 2, "users_from_ip": [1, 33]}}, {"model": "accounts.ips", "pk": 8, "fields": {"ip_address": "162.158.235.30", "count": 1, "users_from_ip": [1]}}, {"model": "accounts.ips", "pk": 9, "fields": {"ip_address": "162.158.235.42", "count": 1, "users_from_ip": [1]}}, {"model": "accounts.ips", "pk": 10, "fields": {"ip_address": "162.158.48.129", "count": 1, "users_from_ip": [33]}}, {"model": "accounts.ips", "pk": 11, "fields": {"ip_address": "162.158.48.121", "count": 1, "users_from_ip": [1]}}, {"model": "accounts.ips", "pk": 12, "fields": {"ip_address": "162.158.227.135", "count": 1, "users_from_ip": [1]}}, {"model": "accounts.ips", "pk": 13, "fields": {"ip_address": "162.158.235.36", "count": 1, "users_from_ip": [1]}}, {"model": "accounts.ips", "pk": 14, "fields": {"ip_address": "2401:4900:1f38:be:f55b:66f1:2e56:fc7f", "count": 1, "users_from_ip": [1]}}, {"model": "accounts.ips", "pk": 15, "fields": {"ip_address": "2401:4900:1f38:be:bd51:eb29:8ced:807d", "count": 1, "users_from_ip": [1]}}, {"model": "accounts.ips", "pk": 16, "fields": {"ip_address": "2401:4900:1f38:be:3210:2b31:a590:b414", "count": 1, "users_from_ip": [1]}}, {"model": "accounts.ips", "pk": 17, "fields": {"ip_address": "2401:4900:1f38:be:cd6:9c6d:8581:de36", "count": 2, "users_from_ip": [1, 34]}}, {"model": "accounts.ips", "pk": 18, "fields": {"ip_address": "2401:4900:1f38:be:df81:d18c:109:e3d8", "count": 2, "users_from_ip": [1, 34]}}, {"model": "accounts.ips", "pk": 19, "fields": {"ip_address": "2401:4900:1f38:be:1c2e:48f3:cb76:a32d", "count": 1, "users_from_ip": [1]}}, {"model": "accounts.ips", "pk": 20, "fields": {"ip_address": "2401:4900:1f39:4e18:9153:c7f9:3781:26fb", "count": 1, "users_from_ip": [1]}}, {"model": "accounts.ips", "pk": 21, "fields": {"ip_address": "2401:4900:1c5d:8e18:f02b:77a1:5889:2111", "count": 1, "users_from_ip": [1]}}, {"model": "accounts.ips", "pk": 22, "fields": {"ip_address": "2401:4900:1f39:5b51:e3ac:41ec:e806:5fdf", "count": 1, "users_from_ip": [1]}}, {"model": "accounts.ips", "pk": 23, "fields": {"ip_address": "2401:4900:1f39:64f2:125b:ccfa:8610:8e3f", "count": 1, "users_from_ip": [1]}}, {"model": "accounts.ips", "pk": 24, "fields": {"ip_address": "172.17.0.1", "count": 1, "users_from_ip": [1]}}, {"model": "accounts.ips", "pk": 25, "fields": {"ip_address": "2401:4900:1f39:64f2:797e:5d67:51b:c322", "count": 1, "users_from_ip": [1]}}, {"model": "accounts.ips", "pk": 26, "fields": {"ip_address": "2401:4900:1f39:64f2:4930:eb4:3c6b:9854", "count": 1, "users_from_ip": [1]}}, {"model": "accounts.ips", "pk": 27, "fields": {"ip_address": "122.161.72.73", "count": 1, "users_from_ip": [1]}}, {"model": "accounts.ips", "pk": 28, "fields": {"ip_address": "2401:4900:1f39:64f2:73b7:9fba:d099:2326", "count": 1, "users_from_ip": [1]}}, {"model": "accounts.ips", "pk": 29, "fields": {"ip_address": "2401:4900:1f39:64f2:7d7d:1c81:9b0f:fb44", "count": 1, "users_from_ip": [1]}}, {"model": "accounts.ips", "pk": 30, "fields": {"ip_address": "20.219.192.211", "count": 1, "users_from_ip": [36]}}, {"model": "accounts.ips", "pk": 31, "fields": {"ip_address": "2401:4900:1c5a:9c25:ca6c:76ab:7d4a:6a3a", "count": 2, "users_from_ip": [1, 38]}}, {"model": "accounts.ips", "pk": 32, "fields": {"ip_address": "103.203.254.159", "count": 1, "users_from_ip": [38]}}, {"model": "accounts.ips", "pk": 33, "fields": {"ip_address": "2401:4900:1c5c:4a3:a14f:d1ce:fbcf:a620", "count": 2, "users_from_ip": [1, 38]}}, {"model": "accounts.ips", "pk": 34, "fields": {"ip_address": "2401:4900:1c5c:4a3:814:1a1a:9af0:57e4", "count": 1, "users_from_ip": [1]}}, {"model": "accounts.ips", "pk": 35, "fields": {"ip_address": "2401:4900:1c5c:986f:c975:6356:e86f:37a7", "count": 1, "users_from_ip": [1]}}, {"model": "accounts.ips", "pk": 36, "fields": {"ip_address": "122.161.49.144", "count": 2, "users_from_ip": [1, 38]}}, {"model": "accounts.ips", "pk": 37, "fields": {"ip_address": "106.51.24.198", "count": 1, "users_from_ip": [38]}}, {"model": "accounts.ips", "pk": 38, "fields": {"ip_address": "122.162.144.13", "count": 2, "users_from_ip": [38, 39]}}, {"model": "accounts.contact_form", "pk": 1, "fields": {"user": 1, "subject": "saasx", "body": "xsaxas"}}, {"model": "accounts.contact_form", "pk": 2, "fields": {"user": 1, "subject": "sa", "body": "xsa"}}, {"model": "accounts.contact_form", "pk": 3, "fields": {"user": 1, "subject": "ddds", "body": "fddf"}}, {"model": "extra_settings.setting", "pk": 1, "fields": {"name": "HUNT_STATUS", "value_type": "string", "description": "<'active', 'not started', 'paused', 'ended'>", "value_bool": false, "value_date": null, "value_datetime": null, "value_decimal": "0E-10", "value_duration": null, "value_email": "", "value_file": "", "value_float": 0.0, "value_image": "", "value_int": 0, "value_json": "{}", "value_string": "active", "value_text": "", "value_time": null, "value_url": ""}}, {"model": "extra_settings.setting", "pk": 3, "fields": {"name": "HUNT_START_TIME", "value_type": "datetime", "description": "", "value_bool": false, "value_date": null, "value_datetime": "2022-07-03T06:30:00Z", "value_decimal": "0E-10", "value_duration": null, "value_email": "", "value_file": "", "value_float": 0.0, "value_image": "", "value_int": 0, "value_json": "{}", "value_string": "", "value_text": "", "value_time": null, "value_url": ""}}, {"model": "extra_settings.setting", "pk": 4, "fields": {"name": "HUNT_RESUME_TIME", "value_type": "datetime", "description": null, "value_bool": false, "value_date": null, "value_datetime": "2022-07-14T18:30:00Z", "value_decimal": "0E-10", "value_duration": null, "value_email": "", "value_file": "", "value_float": 0.0, "value_image": "", "value_int": 0, "value_json": "{}", "value_string": "", "value_text": "", "value_time": null, "value_url": ""}}, {"model": "admin_honeypot.loginattempt", "pk": 1, "fields": {"username": "rachit", "ip_address": "127.0.0.1", "session_key": "4pkz9b1rnyllbmwdr9sxftxleedqcq77", "user_agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/102.0.5005.115 Safari/537.36", "timestamp": "2022-06-12T09:58:52.332Z", "path": "/admin/login/?next=/admin/"}}, {"model": "admin_honeypot.loginattempt", "pk": 2, "fields": {"username": "rachit", "ip_address": "127.0.0.1", "session_key": "4d39vrnwivapmked87k81mt09486mq0i", "user_agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/102.0.5005.115 Safari/537.36", "timestamp": "2022-06-13T13:38:31.649Z", "path": "/admin/login/?next=/admin/"}}] \ No newline at end of file diff --git a/pwa1/urls.py b/pwa1/urls.py deleted file mode 100644 index 13b5f82..0000000 --- a/pwa1/urls.py +++ /dev/null @@ -1,8 +0,0 @@ -from django.urls import re_path as url - -from pwa.views import manifest, service_worker, offline - -# Serve up serviceworker.js and manifest.json at the root -urlpatterns = [ - url('manifest.json', manifest, name='manifest'), -] diff --git a/redcrypt/middleware.py b/redcrypt/middleware.py index 2c346c4..d09cec8 100644 --- a/redcrypt/middleware.py +++ b/redcrypt/middleware.py @@ -10,16 +10,15 @@ def __init__(self, get_response): self.get_response = get_response def __call__(self, request): - response = self.get_response(request) if request.user.is_authenticated: + user, created = Profile.objects.get_or_create(user=request.user) + client_ip, is_routable = get_client_ip(request) - if request.user not in Profile.objects.all(): - user = Profile.objects.get_or_create(user=request.user) - user = Profile.objects.get(user=request.user) if client_ip not in user.ip_address: user.ip_address.append(client_ip) user.ip_address_count += 1 user.save() + if client_ip not in IPs.objects.all(): IPs.objects.get_or_create(ip_address=client_ip) ip_object = IPs.objects.get(ip_address=client_ip) @@ -27,4 +26,6 @@ def __call__(self, request): ip_object.users_from_ip.add(request.user) ip_object.count += 1 ip_object.save() + + response = self.get_response(request) return response diff --git a/redcrypt/settings.py b/redcrypt/settings.py index 73b5e61..0fab536 100644 --- a/redcrypt/settings.py +++ b/redcrypt/settings.py @@ -13,8 +13,8 @@ from pathlib import Path import os from dotenv import load_dotenv -import sentry_sdk -from sentry_sdk.integrations.django import DjangoIntegration +# import sentry_sdk +# from sentry_sdk.integrations.django import DjangoIntegration load_dotenv() @@ -26,32 +26,35 @@ # See https://docs.djangoproject.com/en/4.0/howto/deployment/checklist/ # SECURITY WARNING: keep the secret key used in production secret! -SECRET_KEY = os.environ.get("SECRET_KEY") +SECRET_KEY = os.environ.get("SECRET_KEY", '%^7w6=-k)c*od9w1ci*dj-3$+yg45(@g_+kxw==(3t3z+^s7gd') # SECURITY WARNING: don't run with debug turned on in production! -try: - if os.environ.get("DEBUG") == "True": - DEBUG = True - else: - DEBUG = False -except: - DEBUG = False +DEBUG = os.environ.get("DEBUG", "False").lower() == "true" ALLOWED_HOSTS = ['*'] -CSRF_TRUSTED_ORIGINS = [ - 'https://*.rachitkhurana.xyz', - 'https://*.127.0.0.1', - 'https://redcrypt.rachitkhurana.repl.co', - 'https://*.redcrypt.xyz/'] +CSRF_TRUSTED_ORIGINS = [] + +ALLOWED_HOSTS = os.getenv("ALLOWED_HOSTS", "localhost,127.0.0.1").split(",") +CSRF_TRUSTED_ORIGINS = os.getenv("CSRF_TRUSTED_ORIGINS", "http://localhost:8000").split(",") + + +if DEBUG: + CSRF_COOKIE_SECURE = False + SESSION_COOKIE_SECURE = False + SECURE_SSL_REDIRECT = False + +else: + CSRF_COOKIE_SECURE = True + SESSION_COOKIE_SECURE = True + ACCOUNT_DEFAULT_HTTP_PROTOCOL = 'https' + + INTERNAL_IPS = [ "127.0.0.1", ] -SECURE_SSL_REDIRECT = False -SESSION_COOKIE_SECURE = True -CSRF_COOKIE_SECURE = True -ACCOUNT_DEFAULT_HTTP_PROTOCOL = 'https' -# Application definition +SESSION_ENGINE = "django.contrib.sessions.backends.db" +SESSION_COOKIE_AGE = 86400 INSTALLED_APPS = [ 'admin_interface', @@ -59,7 +62,7 @@ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.sites', - 'django.contrib.contenttypes', + 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'whitenoise.runserver_nostatic', @@ -68,6 +71,7 @@ 'allauth.account', 'allauth.socialaccount', 'allauth.socialaccount.providers.discord', + 'allauth.socialaccount.providers.google', 'hunt', 'accounts', 'extra_settings', @@ -76,7 +80,7 @@ 'hcaptcha', 'maintenance_mode', 'pwa', - 'debug_toolbar', + 'django_extensions' ] MIDDLEWARE = [ @@ -90,7 +94,8 @@ 'django.middleware.clickjacking.XFrameOptionsMiddleware', 'redcrypt.middleware.CustomMiddleware', 'maintenance_mode.middleware.MaintenanceModeMiddleware', - 'debug_toolbar.middleware.DebugToolbarMiddleware', + "allauth.account.middleware.AccountMiddleware", + ] ROOT_URLCONF = 'redcrypt.urls' @@ -116,32 +121,39 @@ WSGI_APPLICATION = 'redcrypt.wsgi.application' - -# Database -# https://docs.djangoproject.com/en/4.0/ref/settings/#databases - -DATABASES = { - 'default': { - 'ENGINE': 'django.db.backends.sqlite3', - 'NAME': BASE_DIR / 'db.sqlite3', +if DEBUG: + DATABASES = { + 'default': { + 'ENGINE': 'django.db.backends.sqlite3', + 'NAME': BASE_DIR / 'db.sqlite3', + } + } +else: + DATABASES = { + 'default': { + 'ENGINE': 'django.db.backends.postgresql', + 'NAME': os.getenv('DB_NAME'), + 'USER': os.getenv('DB_USER'), + 'PASSWORD': os.getenv('DB_PASSWORD'), + 'HOST': os.getenv('DB_HOST'), + 'PORT': os.getenv('DB_PORT'), + } } -} -sentry_sdk.init( - dsn=os.getenv('SENTRY_DSN'), - integrations=[DjangoIntegration()], - # Set traces_sample_rate to 1.0 to capture 100% - # of transactions for performance monitoring. - # We recommend adjusting this value in production. - traces_sample_rate=1.0, - # If you wish to associate users to errors (assuming you are using - # django.contrib.auth) you may enable sending PII data. - send_default_pii=True -) +# sentry_sdk.init( +# dsn=os.getenv('SENTRY_DSN'), +# integrations=[DjangoIntegration()], -# Password validation -# https://docs.djangoproject.com/en/4.0/ref/settings/#auth-password-validators +# # Set traces_sample_rate to 1.0 to capture 100% +# # of transactions for performance monitoring. +# # We recommend adjusting this value in production. +# traces_sample_rate=1.0, + +# # If you wish to associate users to errors (assuming you are using +# # django.contrib.auth) you may enable sending PII data. +# send_default_pii=True +# ) SITE_ID = 3 @@ -165,10 +177,7 @@ ] AUTHENTICATION_BACKENDS = [ - # Needed to login by username in Django admin, regardless of `allauth` 'django.contrib.auth.backends.ModelBackend', - - # `allauth` specific authentication methods, such as login by e-mail 'allauth.account.auth_backends.AuthenticationBackend', ] @@ -176,7 +185,9 @@ 'signup': 'accounts.forms.MyCustomSignupForm', 'reset_password': 'accounts.forms.CustomForgetPassword'} -EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend' +SOCIALACCOUNT_FORMS = { + 'signup': 'accounts.forms.MyCustomSocialSignupForm', +} ACCOUNT_EMAIL_REQUIRED = True @@ -184,10 +195,16 @@ ACCOUNT_MAX_EMAIL_ADDRESSES = 1 ACCOUNT_AUTHENTICATED_LOGIN_REDIRECTS = True +ACCOUNT_SIGNUP_PASSWORD_ENTER_TWICE = False LOGIN_REDIRECT_URL = '/profile/' -# Internationalization -# https://docs.djangoproject.com/en/4.0/topics/i18n/ +ACCOUNT_LOGOUT_REDIRECT_URL = '/accounts/login/' + +SOCIALACCOUNT_AUTO_SIGNUP = False + +ACCOUNT_ADAPTER = 'accounts.adapter.CustomAccountAdapter' + +ACCOUNT_EMAIL_VERIFICATION = 'optional' # Change to 'mandatory' if you want to enforce it when SMTP is configured LANGUAGE_CODE = 'en-us' @@ -199,9 +216,13 @@ APPEND_SLASH = True -# Static files (CSS, JavaScript, Images) -# https://docs.djangoproject.com/en/4.0/howto/static-files/ -STATICFILES_STORAGE = "whitenoise.storage.CompressedManifestStaticFilesStorage" +STORAGES = { + "staticfiles": { + "BACKEND": "whitenoise.storage.CompressedManifestStaticFilesStorage", + }, +} + + WHITENOISE_MANIFEST_STRICT = True WHITENOISE_ROOT = "static" STATIC_URL = 'static/' @@ -210,85 +231,62 @@ STATICFILES_DIRS = [ BASE_DIR / "static", ] -# Default primary key field type -# https://docs.djangoproject.com/en/4.0/ref/settings/#default-auto-field + +# Media files configuration +MEDIA_URL = '/media/' +MEDIA_ROOT = os.path.join(BASE_DIR, 'media') DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' +# Email Configuration EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend' -EMAIL_HOST = 'smtp-pulse.com' -EMAIL_USE_TLS = True -EMAIL_PORT = 587 -EMAIL_HOST_USER = os.getenv('EMAIL_HOST_USER') +EMAIL_HOST = os.getenv("EMAIL_HOST") +EMAIL_USE_TLS = os.getenv("EMAIL_USE_TLS", default=True) +EMAIL_PORT = os.getenv("EMAIL_PORT", default=587) +EMAIL_HOST_USER = os.getenv("EMAIL_HOST_USER") EMAIL_HOST_PASSWORD = os.getenv('EMAIL_HOST_PASSWORD') -DEFAULT_FROM_EMAIL = "Re-Dcrypt " +EMAIL_SENDER = os.getenv('EMAIL_SENDER', EMAIL_HOST_USER) +DEFAULT_FROM_EMAIL = f"Re-Dcrypt <{EMAIL_SENDER}>" ACCOUNT_EMAIL_SUBJECT_PREFIX = "Re-Dcrypt - " -ACCOUNT_EMAIL_CONFIRMATION_COOLDOWN = 600 -SOCIALACCOUNT_AUTO_SIGNUP = False +ACCOUNT_RATE_LIMITS = { + "confirm_email": 600 +} HCAPTCHA_SITEKEY = os.getenv('HCAPTCHA_SITEKEY') HCAPTCHA_SECRET = os.getenv('HCAPTCHA_SECRET') -try: - if os.getenv("MAINTENANCE_MODE").lower() == "true": - MAINTENANCE_MODE = True - else: - MAINTENANCE_MODE = False -except: - MAINTENANCE_MODE = False +MAINTENANCE_MODE = os.getenv("MAINTENANCE_MODE", "false").lower() == "true" MAINTENANCE_MODE_IGNORE_ADMIN_SITE = True MAINTENANCE_MODE_IGNORE_SUPERUSER = True +LOGIN_URL = 'account_login' +ACCOUNT_LOGIN_ON_GET = True -PWA_SERVICE_WORKER_PATH = os.path.join(BASE_DIR, 'serviceworker.js') -PWA_APP_NAME = 'Re-Dcrypt' -PWA_APP_DESCRIPTION = 'Re-Dcrypt Cryptic Hunt.' -PWA_APP_THEME_COLOR = '#00d2d2' -PWA_APP_BACKGROUND_COLOR = '#002a2a' -PWA_APP_DISPLAY = 'standalone' -PWA_APP_SCOPE = '/' -PWA_APP_START_URL = '/' -PWA_APP_HOME_PATH = '/' -PWA_APP_STATUS_BAR_COLOR = 'default' -PWA_APP_DEBUG_MODE = False -PWA_APP_ICONS = [ - { - 'src': '/icons/maskable_icon_x192.png', - 'sizes': '192x192', - 'type': 'image/png', - "purpose": "maskable" - }, - { - 'src': '/icons/maskable_icon_x192.png', - 'sizes': '192x192', - 'type': 'image/png', - "purpose": "any" +SOCIALACCOUNT_PROVIDERS = { + 'google': { + 'APP': { + 'client_id': os.getenv('GOOGLE_CLIENT_ID'), + 'secret': os.getenv('GOOGLE_CLIENT_SECRET'), + }, + 'FETCH_USERINFO' : True }, - { - 'src': '/icons/maskable_icon_x512.png', - 'sizes': '512x512', - 'type': 'image/png', - "purpose": "maskable" - } -] -PWA_APP_ICONS_APPLE = [ - { - 'src': '/icons/maskable_icon_x192.png', - 'sizes': '192x192', - 'type': 'image/png', - "purpose": "maskable", +} + + +SOCIALACCOUNT_EMAIL_AUTHENTICATION = True + +LOGGING = { + 'version': 1, + 'disable_existing_loggers': False, + 'handlers': { + 'console': { + 'class': 'logging.StreamHandler', + }, }, - { - 'src': '/icons/maskable_icon_x192.png', - 'sizes': '192x192', - 'type': 'image/png', - "purpose": "any" + 'loggers': { + 'accounts.adapter': { + 'handlers': ['console'], + 'level': 'WARNING', + }, }, - { - 'src': '/icons/maskable_icon_x512.png', - 'sizes': '512x512', - 'type': 'image/png', - "purpose": "maskable" - } -] -PWA_APP_LANG = 'en-US' +} diff --git a/redcrypt/urls.py b/redcrypt/urls.py index c46ad94..28ee19e 100644 --- a/redcrypt/urls.py +++ b/redcrypt/urls.py @@ -16,75 +16,60 @@ from django.contrib import admin from django.urls import path, include from hunt.views import index, offline, play, check_ans, leaderboard, rules -from hunt.views import faqs, sample_questions_play, update_rank -from hunt.views import sample_checkans, about, guidelines -from hunt.views import privacy_policy, terms_and_conditions +from hunt.views import faqs, update_rank +from hunt.views import about, guidelines +from hunt.views import privacy_policy, terms_and_conditions, help_center from accounts import views as accounts_views -from allauth1.account.views import password_change - +from django.conf import settings +from django.conf.urls.static import static +from accounts import views urlpatterns = [ - path('', index, name='index'), - path('', include('pwa1.urls')), - path('offline/', offline, name="offline"), - path('offline', offline, name="offline"), - path('admin/', include('admin_honeypot.urls', namespace='admin_honeypot')), - path('honeypot/', admin.site.urls), - path('api/', include(('apis.urls', 'apis'), namespace='api')), - path( - 'accounts/social/connections/', - accounts_views.connect, name='connect'), + path("", index, name="index"), + path("offline/", offline, name="offline"), + path("offline", offline, name="offline"), + path("admin/", include("admin_honeypot.urls", namespace="admin_honeypot")), + path("honeypot/", admin.site.urls), + path("api/", include(("apis.urls", "apis"), namespace="api")), + path("accounts/social/connections/", accounts_views.connect, name="connect"), + path("accounts/", include("allauth.urls")), + path("profile/", accounts_views.profile, name="profile"), + path("profile/edit/", accounts_views.edit_profile, name="edit_profile"), + path("profile/edit/save_profile", accounts_views.save_profile, name="save_profile"), path( - "accounts/password/change/", - password_change, - name="account_change_password", + "profile//", accounts_views.public_profile, name="public-profile" ), - path('accounts/', include('allauth.urls')), - - path('profile/', accounts_views.profile, name='profile'), - path('profile/edit/', accounts_views.edit_profile, name='edit_profile'), - path( - 'profile/edit/save_profile', - accounts_views.save_profile, - name='save_profile'), - path( - 'profile//', - accounts_views.public_profile, - name='public-profile'), - path( - 'profile/', - accounts_views.public_profile), + path("profile/", accounts_views.public_profile), path( - 'send_confirmation_email', + "send_confirmation_email", accounts_views.send_confirmation_email, - name='send_confirmation_email'), - path( - 'verification-sent', - accounts_views.verification_sent, - name='verification-sent'), - path('play/', play, name='play'), + name="send_confirmation_email", + ), path( - 'play/sample_questions', - sample_questions_play, - name='sample_questions_play'), - path('play/sample_check_ans', sample_checkans, name='sample_checkans'), - path('leaderboard/', leaderboard, name='leaderboard'), - path('faqs/', faqs, name='faqs'), - path('rules/', rules, name='rules'), - path('guidelines/', guidelines, name='guidelines'), - path('about/', about, name='about'), - path('privacy-policy/', privacy_policy, name='privacy_policy'), - path('terms-and-conditions/', terms_and_conditions, name='terms_and_conditions'), - path('check/', check_ans, name='check_ans'), - path('contact/', accounts_views.contact_form_view, name='contact'), - path('update_rank/', update_rank, name='update_rank'), - path('500/', accounts_views.e500, name='500'), + "verification-sent", accounts_views.verification_sent, name="verification-sent" + ), + path("play/", play, name="play"), + path("leaderboard/", leaderboard, name="leaderboard"), + path("faqs/", faqs, name="faqs"), + path("rules/", rules, name="rules"), + path("guidelines/", guidelines, name="guidelines"), + path("about/", about, name="about"), + path("privacy-policy/", privacy_policy, name="privacy_policy"), + path("terms-and-conditions/", terms_and_conditions, name="terms_and_conditions"), + path("check/", check_ans, name="check_ans"), + path("contact/", accounts_views.contact_form_view, name="contact"), + path("update_rank/", update_rank, name="update_rank"), + path("500/", accounts_views.e500, name="500"), + path("check-email/", views.check_email, name="check_email"), + path("check-username/", views.check_username, name="check_username"), path( - 'contact_form_submit', + "contact_form_submit", accounts_views.submit_contact_form, - name='contact_form_submit'), - path('', include(( - 'url_shortner.urls', 'url_shortner'), - namespace='url_shortner')), - path('__debug__/', include('debug_toolbar.urls')), -] + name="contact_form_submit", + ), + path( + "account/verify-captcha/", accounts_views.verify_captcha, name="verify_captcha" + ), + path("", include(("url_shortner.urls", "url_shortner"), namespace="url_shortner")), + path("help/", help_center, name="help"), +] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) diff --git a/requirements.txt b/requirements.txt index 8364939..7c772d3 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,16 +1,22 @@ -Django==4.0.8 -django-admin-interface==0.19.1 +django==5.0.1 +django-admin-interface==0.29 django-admin-honeypot==1.1.0 django-ipware==4.0.2 -python-dotenv==0.20.0 -sentry-sdk==1.6.0 +python-dotenv==1.0.1 +sentry-sdk==2.19 django-extra-settings -django-allauth==0.51.0 +django-allauth==65.3.1 django-hCaptcha==0.2.0 whitenoise==6.2.0 django-maintenance-mode==0.16.3 -django-debug-toolbar==3.5.0 django-pwa==1.0.10 -django-admin-interface==0.20.0 pyminizip discord-webhook +pytz +djlint +django-extensions +Werkzeug +PyJWT +gunicorn +cryptography +psycopg2-binary \ No newline at end of file diff --git a/static/404.svg b/static/404.svg deleted file mode 100644 index 8aea301..0000000 --- a/static/404.svg +++ /dev/null @@ -1,893 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/static/banned.png b/static/banned.png new file mode 100644 index 0000000..eca9fbf Binary files /dev/null and b/static/banned.png differ diff --git a/static/banned.svg b/static/banned.svg deleted file mode 100644 index 20622b2..0000000 --- a/static/banned.svg +++ /dev/null @@ -1,402 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/static/bg.svg b/static/bg.svg deleted file mode 100644 index ebaac14..0000000 --- a/static/bg.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/static/congrats.svg b/static/congrats.svg deleted file mode 100644 index 8079fec..0000000 --- a/static/congrats.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/static/error.png b/static/error.png new file mode 100644 index 0000000..19c398b Binary files /dev/null and b/static/error.png differ diff --git a/static/favicon.ico b/static/favicon.ico index 620142a..6293edf 100644 Binary files a/static/favicon.ico and b/static/favicon.ico differ diff --git a/static/foss-bu-dark.png b/static/foss-bu-dark.png new file mode 100644 index 0000000..c69fc16 Binary files /dev/null and b/static/foss-bu-dark.png differ diff --git a/static/logo.png b/static/logo.png new file mode 100644 index 0000000..8a81934 Binary files /dev/null and b/static/logo.png differ diff --git a/static/logo.svg b/static/logo.svg index b0b3a74..4a2445c 100644 --- a/static/logo.svg +++ b/static/logo.svg @@ -1,108 +1,7 @@ - - - - + + + + + + + diff --git a/static/logo_banner.svg b/static/logo_banner.svg index 658ab08..ef7de21 100644 --- a/static/logo_banner.svg +++ b/static/logo_banner.svg @@ -1,131 +1,8 @@ - - - - + + + + + + + + diff --git a/static/logo_color.svg b/static/logo_color.svg new file mode 100644 index 0000000..a1d7f34 --- /dev/null +++ b/static/logo_color.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/static/manifest.json b/static/manifest.json deleted file mode 100644 index d5b2740..0000000 --- a/static/manifest.json +++ /dev/null @@ -1,63 +0,0 @@ -{ - "name": "Re-Dcrypt", - "short_name": "Re-Dcrypt", - "description": "Re-Dcrypt Cryptic Hunt.", - "start_url": "/", - "display": "standalone", - "scope": "/", - "background_color": "#002a2a", - "theme_color": "#00d2d2", - "orientation": "portrait-primary", - "shortcuts": [ - { - "name": "Play Re-Dcrypt", - "short_name": "Play", - "description": "Play the cryptic hunt", - "url": "/play/", - "icons": [{ "src": "/icons/maskable_icon_x192.png", "sizes": "192x192" }] - }, - { - "name": "Leaderboard", - "short_name": "Leaderboard", - "description": "View leaderboard", - "url": "/leaderboard/", - "icons": [{ "src": "/icons/maskable_icon_x192.png", "sizes": "192x192" }] - }, - { - "name": "Profile", - "short_name": "Profile", - "description": "View your profile", - "url": "/profile/", - "icons": [{ "src": "/icons/maskable_icon_x192.png", "sizes": "192x192" }] - } - ], - "status_bar": "default", - "icons": [{"src": "/icons/maskable_icon_x192.png", "sizes": "192x192", "type": "image/png", "purpose": "maskable"}, {"src": "/icons/maskable_icon_x192.png", "sizes": "192x192", "type": "image/png", "purpose": "any"}, {"src": "/icons/maskable_icon_x512.png", "sizes": "512x512", "type": "image/png", "purpose": "maskable"}], - "screenshots":[ - { - "src": "/pic1.png", - "type": "image/png", - "sizes": "910x1618" - }, - { - "src": "/pic2.png", - "type": "image/png", - "sizes": "910x1618" - }, - { - "src": "/pic3.png", - "type": "image/png", - "sizes": "910x1618" - }, - { - "src": "/pic4.png", - "type": "image/png", - "sizes": "910x1618" - }, - { - "src": "/pic5.png", - "type": "image/png", - "sizes": "910x1618" - } - ] -} \ No newline at end of file diff --git a/static/offline.png b/static/offline.png new file mode 100644 index 0000000..6e4708a Binary files /dev/null and b/static/offline.png differ diff --git a/static/offline.svg b/static/offline.svg deleted file mode 100644 index 3aa918c..0000000 --- a/static/offline.svg +++ /dev/null @@ -1,772 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/static/poster.png b/static/poster.png index ac52369..8a81934 100644 Binary files a/static/poster.png and b/static/poster.png differ diff --git a/static/robots.txt b/static/robots.txt index d761fc8..e04a272 100644 --- a/static/robots.txt +++ b/static/robots.txt @@ -1,6 +1,4 @@ # I'm pretty sure you are not a robot. So go somewhere else User-agent: * -Disallow: /play - -🥚: sophia \ No newline at end of file +Disallow: /play \ No newline at end of file diff --git a/static/scene.glb b/static/scene.glb index dc7100b..3d9febf 100644 Binary files a/static/scene.glb and b/static/scene.glb differ diff --git a/static/videos/404.webm b/static/videos/404.webm new file mode 100644 index 0000000..515badf Binary files /dev/null and b/static/videos/404.webm differ diff --git a/static/videos/503.webm b/static/videos/503.webm new file mode 100644 index 0000000..1cb0836 Binary files /dev/null and b/static/videos/503.webm differ diff --git a/static/videos/star.webm b/static/videos/star.webm new file mode 100644 index 0000000..f56d83c Binary files /dev/null and b/static/videos/star.webm differ diff --git a/tailwind.config.js b/tailwind.config.js new file mode 100644 index 0000000..81345b5 --- /dev/null +++ b/tailwind.config.js @@ -0,0 +1 @@ +// This file doesnt mean anything. It is here just for the sole purpose of making visual studio code to recognize the tailwindcss classes in the html file. \ No newline at end of file diff --git a/templates/403_csrf.html b/templates/403_csrf.html index d6aac62..190ccaa 100644 --- a/templates/403_csrf.html +++ b/templates/403_csrf.html @@ -1,110 +1,68 @@ +{% extends "base.html" %} +{% load static %} - +{% block head_title %}403 - CSRF Verification Failed{% endblock head_title %} - +{% block content %} +
+
+
+
+ CSRF Error +
- - - Server Error 500 | Re-Dcrypt - - - - - - - - - - +
+

+ 403 Error +

+

CSRF Verification Failed

- - -
- +
+
+ +

This error occurs for your security. Please try refreshing the page.

-
- -
-
-

-

- CRSF Error. Try again after refreshing, if problem persist, contact DiluteWater#3149 on discord. - 500
- Internet illustrations by Storyset -
-
+
+ + +
+
+
+
+
- - \ No newline at end of file + +{% endblock content %} \ No newline at end of file diff --git a/templates/404.html b/templates/404.html index b2576ad..d057aa8 100644 --- a/templates/404.html +++ b/templates/404.html @@ -1,274 +1,60 @@ -{% extends 'base.html' %} +{% extends "base.html" %} {% load static %} -{% block head_title %}404{% endblock %} +{% block head_title %} + 404 - Page Not Found +{% endblock head_title %} {% block content %} -
-

404
Looks like you went a little out of the box
-


-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- Web illustrations by Storyset -
+
+
+
+
+ +
+
+

Oops!

+

Looks like you went a little too far out of the box

+
+
+ +

Lost in the cryptic maze?

+
+
+ +

Maybe it's another puzzle?

+
+
+
+ +
+
+
+
+
+ {% endblock content %} diff --git a/templates/500.html b/templates/500.html index 07b0fb2..d4de6f5 100644 --- a/templates/500.html +++ b/templates/500.html @@ -1,110 +1,64 @@ - - - - - - - - Server Error 500 | Re-Dcrypt - - - - - - - - - - - - - -
-
- - - - - +{% extends "base.html" %} +{% load static %} +{% block head_title %} + 500 - Server Error +{% endblock head_title %} +{% block content %} +
+
+
+
+ Server Error +
+
+

+ 500 Error +

+

Oops! Something went wrong on our servers

+
+
+ +

Please try again later

+
+ + +
-
- -
-
- - - \ No newline at end of file +
+ + + +{% endblock content %} diff --git a/templates/503.html b/templates/503.html index 21f4d49..0382b8e 100644 --- a/templates/503.html +++ b/templates/503.html @@ -1,139 +1,63 @@ - - - - - - - - Server Error 503 | Re-Dcrypt - - - - - - - - - - - - - -
-
- - - - - +{% extends "base.html" %} +{% load static %} +{% block head_title %} + 503 - Service Unavailable +{% endblock head_title %} +{% block content %} +
+
+
+
+ +
+
+

+ 503 Error +

+

Server Under Maintenance

+
+
+ +

We're working on making things better

+
+ + +
-
- -
-
-

-

-

Server Under Maintainence

-
- 503
- Internet illustrations by Storyset -
- -
- - - \ No newline at end of file + + + + +{% endblock content %} diff --git a/templates/about.html b/templates/about.html index d3473d9..7b5e6e1 100644 --- a/templates/about.html +++ b/templates/about.html @@ -1,139 +1,160 @@ -{% extends 'base.html' %} +{% extends "base.html" %} {% load static %} -{% block head_title %}About{% endblock %} +{% block head_title %} + About +{% endblock head_title %} {% block head %} - - - -{% endblock %} -{% block content %} -
-

- About -

+ + +{% endblock head %} +{% block content %} +
+
+
+
+ +
+

About

+

+ A cryptic hunt organized by passionate students pushing the boundaries of problem-solving and digital exploration.

-
- - - - - - - - - - - - - - - - - -
-
-

- Prizes - - - - - - - - - - - - - - - - - - - - - - - - - - -
RankPrizes
Top 3 -
    -
  • Taskade Unlimited for Lifetime
  • -
  • 1 year .xyz domain
  • -
  • Certificate of appreciation
  • -
-
Top 10 -
    -
  • Taskade Unlimited for Lifetime
  • -
  • 1 year .xyz domain
  • -
  • Certificate of participation
  • -
-
Top 40 -
    -
  • Taskade Unlimited for 5 years
  • -
  • 1 year .xyz domain
  • -
  • Certificate of participation
  • -
-
Everyone -
    -
  • Taskade Unlimited for 5 years
  • -
  • Certificate of participation
  • -
-
-

- -
-

- Sponsors -

- - taskade - - - gen.xyz - + +
+ +
+ + +
+
+
+
+
+ +
+

Our Mission

+
+

+ To create an engaging platform that challenges minds, encourages creative thinking, and brings together a community of problem solvers. +

+ +
+
+
+
+ +
+

Join Our Community

+
+

+ Connect with fellow cryptic hunters, share experiences, and be part of an amazing journey. +

+ +
+
+
+
+
+

Connect With Us

+ + +
+
+
+ + +
-
- -
- - -
+
{% endblock content %} {% block scripts %} - - -{% endblock %} + +{% endblock scripts %} diff --git a/templates/account/base.html b/templates/account/base.html index 7393137..89a5009 100644 --- a/templates/account/base.html +++ b/templates/account/base.html @@ -1,6 +1,6 @@ {% extends "base.html" %} {% block content %} -{% endblock %} +{% endblock content %} {% block extra_body %} -{% endblock %} \ No newline at end of file +{% endblock extra_body %} \ No newline at end of file diff --git a/templates/account/email/base_message.html b/templates/account/email/base_message.html index 237498f..ac4b004 100644 --- a/templates/account/email/base_message.html +++ b/templates/account/email/base_message.html @@ -1,208 +1,87 @@ {% load account %} - - - - - + + + Re-Dcrypt + - - - - - - - - + +
+
+ +
+
+ {% block content %}{% endblock %} +
+ +
diff --git a/templates/account/email/email_confirmation_message.html b/templates/account/email/email_confirmation_message.html index dc6c703..8324a2f 100644 --- a/templates/account/email/email_confirmation_message.html +++ b/templates/account/email/email_confirmation_message.html @@ -2,14 +2,20 @@ {% load account %} {% load i18n %} -{% block content %}{% user_display user as user_display %}You're receiving this e-mail because user {{ user_display }} has given your e-mail address to register an account on {{ site_domain }}. -
+{% block content %} +{% user_display user as user_display %} +

Verify Your Email

-
- -
Activate Email
-
+

+ You're receiving this email because {{ user_display }} has registered this email address on {{ site_domain }}. +

+ + -
-If the above button isn't working then try copying and pasting the following link into your browser: {{ activate_url }} -{% endblock %} \ No newline at end of file + +

+ If the button above doesn't work, copy and paste this link into your browser:
+ {{ activate_url }} +

+{% endblock %} diff --git a/templates/account/email/email_confirmation_signup_message.html b/templates/account/email/email_confirmation_signup_message.html index afd377b..6c127c0 100644 --- a/templates/account/email/email_confirmation_signup_message.html +++ b/templates/account/email/email_confirmation_signup_message.html @@ -2,16 +2,24 @@ {% load account %} {% load i18n %} -{% block content %}{% user_display user as user_display %}Welcome to Re-Dcrypt, {{ user_display }}. -
Hope you will have fun solving questions. Best of luck!!
-Please confirm your email below.
-
+{% block content %} +{% user_display user as user_display %} +

Welcome to Re-Dcrypt!

-
- -
Activate Email
-
+

+ Hey {{ user_display }}, we're excited to have you on board! Just one more step to get started - please confirm your email address. +

+ + -
-If the above button isn't working then try copying and pasting the following link into your browser: {{ activate_url }} -{% endblock %} \ No newline at end of file + +

+ If the button above doesn't work, copy and paste this link into your browser:
+ {{ activate_url }} +

+ +

+ Get ready for some exciting challenges! 🚀 +

+{% endblock %} diff --git a/templates/account/email/password_reset_key_message.html b/templates/account/email/password_reset_key_message.html index 0626367..c1b0821 100644 --- a/templates/account/email/password_reset_key_message.html +++ b/templates/account/email/password_reset_key_message.html @@ -1,15 +1,25 @@ {% extends "account/email/base_message.html" %} {% load i18n %} -{% block content %}{% blocktrans %}You're receiving this e-mail because you or someone else has requested a password for your user account. -It can be safely ignored if you did not request a password reset. Click the link below to reset your password.{% endblocktrans %} -
- -
Reset Password
-
+{% block content %} +

Password Reset Request

+ +

+ You're receiving this email because you or someone else has requested a password reset for your Re-Dcrypt account. If you didn't request this, you can safely ignore this email. +

+ + -
-If the above button isn't working then try copying and pasting the following link into your browser:
{{ password_reset_url }}
-{{ password_reset_url }}

{% if username %} -{% blocktrans %}In case you forgot, your username is {{ username }}.{% endblocktrans %}{% endif %}{% endblock %} +

+ If the button above doesn't work, copy and paste this link into your browser:
+ {{ password_reset_url }} +

+ +{% if username %} +

+ Your username: {{ username }} +

+{% endif %} +{% endblock %} diff --git a/templates/account/email_confirm.html b/templates/account/email_confirm.html index 664810a..11365eb 100644 --- a/templates/account/email_confirm.html +++ b/templates/account/email_confirm.html @@ -1,52 +1,53 @@ {% extends "base.html" %} - {% load i18n %} {% load account %} -{% block head_title %}Confirm Email{% endblock %} -{% block style %} - -{% endblock %} - -{% block content %} -
-

{% trans "Confirm E-mail Address" %}

-
-{% if confirmation %} - -{% user_display confirmation.email_address.user as user_display %} - -

{% blocktrans with confirmation.email_address.email as email %}Please confirm that {{ email }} is an e-mail address for user {{ user_display }}.{% endblocktrans %}

+{% load static %} -
-{% csrf_token %} - -
+{% block head_title %}{% trans "Confirm Email" %}{% endblock head_title %} -{% else %} - -{% url 'account_email' as email_url %} - -

{% blocktrans %}This e-mail confirmation link expired or is invalid. Please issue a new e-mail confirmation request.{% endblocktrans %}

- -{% endif %} +{% block content %} +
+
+
+
+
+ +
+ +

+ {% trans "Confirm Email Address" %} +

+ + {% if confirmation %} + {% user_display confirmation.email_address.user as user_display %} +

+ {% blocktrans with confirmation.email_address.email as email %} + Please confirm that {{ email }} is the email address for user {{ user_display }}. + {% endblocktrans %} +

+ +
+ {% csrf_token %} + +
+ + {% else %} + {% url 'account_email' as email_url %} + + {% endif %} +
+
+
-{% endblock %} +{% endblock content %} diff --git a/templates/account/login.html b/templates/account/login.html index 072fcb8..74da11e 100644 --- a/templates/account/login.html +++ b/templates/account/login.html @@ -1,64 +1,263 @@ {% extends "base.html" %} -{% block head_title %}Login{% endblock %} -{% block head %} - -{% endblock%} + +{% endblock head %} {% block content %} -
- {% csrf_token %} -
- -
-

Login to your account

-

Dont have account?
Sign up here

-
-
-
- {{ form.as_p }}
-
- +
+
+
+
+ +
+ + + + + + + + + + + +
+
+ FOSS BU + Re-Dcrypt +
+
+

+ Let's Log You In +

+
+
+
+
+
+ + {% csrf_token %} +
+
+ + +
+

Welcome back!

+

+ Don't have an account? + Sign up +

+
+
+
+
+ +
+ +
+
+
+
+ + Forgot Password? +
+
+ + +
+
+
+
+
+ +
+
+ OR +
+
+
+ +
{% include "components/google_onetap.html" %}
+

+ By logging in, you agree to Re-Dcrypt's + Terms of Service and + Privacy Policy +

+
+
- Forgot Password? +
+
+
+ + {% if form.errors %} + + {% for field in form %} + {% for error in field.errors %} + + {% endfor %} + {% endfor %} + {% endif %} +{% endblock content %} diff --git a/templates/account/password_change.html b/templates/account/password_change.html index a1bd7ef..e23476a 100644 --- a/templates/account/password_change.html +++ b/templates/account/password_change.html @@ -1,51 +1,283 @@ {% extends "base.html" %} - {% load i18n %} - -{% block head_title %}{% trans "Change Password" %}{% endblock %} +{% block head_title %} + {% trans "Change Password" %} +{% endblock head_title %} {% block head %} - -{% endblock %} + +.strength-ring.strength-strong circle { + stroke: rgb(16 185 129); +} + +{% endblock head %} {% block content %} -
-

{% trans "Change Password" %}

-
-
- {% csrf_token %} - {{ form.as_p }} - - {% trans "Forgot Password?" %} -
-
-{% endblock %} +
+
+
+
+ +
+

{% trans "Change Password" %}

+
+
+
+ {% csrf_token %} +
+
+ + +
+
+ + +
+
+
+
+ + +
+
+ +
+
+ + + + +
+
+

Password Requirements

+
+
+
+
+
+ +
+
+
+
+ + +
+
+ + + +
+
+
+ + + + +
+
+
+
+
+ + {% if form.errors %} + + {% for field in form %} + {% for error in field.errors %} + + {% endfor %} + {% endfor %} + {% endif %} +{% endblock content %} diff --git a/templates/account/password_reset.html b/templates/account/password_reset.html index 4d6e3f3..224026d 100644 --- a/templates/account/password_reset.html +++ b/templates/account/password_reset.html @@ -1,64 +1,72 @@ {% extends "base.html" %} - {% load i18n %} {% load account %} -{% block head_title %}Password Reset{% endblock %} -{% block head %} - -{% endblock %} + +{% block head_title %}Password Reset{% endblock head_title %} + {% block content %} -
-

{% trans "Password Reset" %}

-
- {% if user.is_authenticated %} - {% include "account/snippets/already_logged_in.html" %} - {% endif %} +
+
+
+
+ +
+

{% trans "Password Reset" %}

+
-

{% trans "Forgotten your password? Enter your e-mail address below, and we'll send you an e-mail allowing you to reset it." %}

+
+ + {% if user.is_authenticated %} + {% include "account/snippets/already_logged_in.html" %} + {% endif %} -
+
+

{% trans "Forgot your password? Enter your e-mail address below, and we'll send you an e-mail allowing you to reset it." %}

+
+ + {% csrf_token %} - {{ form.as_p }} - -
-

{% blocktrans %}Please contact us if you have any trouble resetting your password.{% endblocktrans %}

+
+
+ + +
+ +
+ + {% if form.errors %} +
+ {% for error in form.errors.values %} +

+ + {{ error }} +

+ {% endfor %} +
+ {% endif %} + {{ form.hcaptcha }} + +
+ + +

+ {% blocktrans %}Please contact us if you have any trouble resetting your password.{% endblocktrans %} +

+
+ +
+
-{% endblock %} +{% endblock content %} \ No newline at end of file diff --git a/templates/account/password_reset_done.html b/templates/account/password_reset_done.html index b46c9e3..4e6606a 100644 --- a/templates/account/password_reset_done.html +++ b/templates/account/password_reset_done.html @@ -1,18 +1,49 @@ {% extends "base.html" %} - {% load i18n %} {% load account %} -{% block head_title %}{% trans "Password Reset" %}{% endblock %} +{% block head_title %}{% trans "Password Reset" %}{% endblock head_title %} {% block content %} -
-

{% trans "Password Reset" %}

-
- {% if user.is_authenticated %} - {% include "account/snippets/already_logged_in.html" %} - {% endif %} - -

{% blocktrans %}We have sent you an e-mail. If you have not received it please check your spam folder. Otherwise contact us if you do not receive it in a few minutes.{% endblocktrans %}

+
+
+
+
+ +
+

{% trans "Check Your Email" %}

+
+ +
+ + {% if user.is_authenticated %} + {% include "account/snippets/already_logged_in.html" %} + {% endif %} + +
+
+ +

{% blocktrans %}We have sent you an e-mail with password reset instructions. If you have not received it, please check your spam folder.{% endblocktrans %}

+
+ +
+ +

{% blocktrans %}Please contact us if you do not receive the email within a few minutes.{% endblocktrans %}

+
+
+ + +
+
+
- {% endblock %} +{% endblock content %} \ No newline at end of file diff --git a/templates/account/password_reset_from_key.html b/templates/account/password_reset_from_key.html index e946ffd..da24887 100644 --- a/templates/account/password_reset_from_key.html +++ b/templates/account/password_reset_from_key.html @@ -1,54 +1,250 @@ -{% extends "account/base.html" %} - +{% extends "base.html" %} {% load i18n %} -{% block head_title %}{% trans "Change Password" %}{% endblock %} +{% block head_title %} + {% trans "Change Password" %} +{% endblock head_title %} {% block head %} - +{% endblock head %} +{% block content %} +
+
+
+ {% if token_fail %} +
+ +
+ {% else %} +
+ +
+ {% endif %} +

+ {% if token_fail %} + {% trans "Invalid Reset Link" %} + {% else %} + {% trans "Set New Password" %} + {% endif %} +

+
+
+ {% if token_fail %} +
+ +
+

{% trans "The password reset link was invalid, possibly because it has already been used." %}

+
+
+ + + {% trans "Request New Reset Link" %} + + {% else %} +
+ {% csrf_token %} +
+
+ + +
+
+ +
+
+ + + + +
+
+

Password Requirements

+
+
+
+
+
+ +
+
+
+
+ + +
+
+ + +
+
+ {% if form.errors %} +
+ {% for error in form.errors.values %} +

+ + {{ error }} +

+ {% endfor %} +
+ {% endif %} + +
+ {% endif %} +
+
+
+ + {% endblock content %} diff --git a/templates/account/password_reset_from_key_done.html b/templates/account/password_reset_from_key_done.html index 0fad540..931a989 100644 --- a/templates/account/password_reset_from_key_done.html +++ b/templates/account/password_reset_from_key_done.html @@ -1,12 +1,36 @@ -{% extends "account/base.html" %} - +{% extends "base.html" %} {% load i18n %} -{% block head_title %}{% trans "Change Password" %}{% endblock %} + +{% block head_title %}{% trans "Change Password" %}{% endblock head_title %} {% block content %} -
-

{% trans "Change Password" %}

-
-

{% trans 'Your password is now changed.' %}

+
+
+
+
+ +
+

{% trans "Password Changed" %}

+
+ +
+ +
+ +

{% trans "Your password has been successfully changed." %}

+
+ + +
+
-{% endblock %} +{% endblock content %} diff --git a/templates/account/password_set.html b/templates/account/password_set.html index a1003c0..00bc823 100644 --- a/templates/account/password_set.html +++ b/templates/account/password_set.html @@ -1,50 +1,253 @@ -{% extends "account/base.html" %} - +{% extends "base.html" %} {% load i18n %} - -{% block head_title %}{% trans "Set Password" %}{% endblock %} +{% block head_title %} + {% trans "Set Password" %} +{% endblock head_title %} {% block head %} - +{% endblock head %} +{% block content %} +
+
+
+
+ +
+

{% trans "Set Password" %}

+

Create a secure password for your account

+
+
+
+ {% csrf_token %} +
+
+ + +
+
+ +
+
+ + + + +
+
+

Password Requirements

+
+
+
+
+
+ +
+
+
+
+ + +
+
+ + +
+
+ +
+
+
+
+ + {% if form.errors %} + + {% for field in form %} + {% for error in field.errors %} + + {% endfor %} + {% endfor %} + {% endif %} +{% endblock content %} diff --git a/templates/account/signup.html b/templates/account/signup.html index 64d6959..6bf1f7e 100644 --- a/templates/account/signup.html +++ b/templates/account/signup.html @@ -1,69 +1,737 @@ {% extends "base.html" %} -{% block head_title %}Signup{% endblock %} -{% block head %} - -{% endblock %} + +@keyframes spin { + from { transform: rotate(0deg); } + to { transform: rotate(360deg); } +} + +@keyframes fadeOut { + 0% { opacity: 1; } + 100% { opacity: 0; } +} + +@keyframes wave { + 0% { transform: rotate(0deg); } + 20% { transform: rotate(-20deg); } + 40% { transform: rotate(10deg); } + 60% { transform: rotate(-10deg); } + 80% { transform: rotate(5deg); } + 100% { transform: rotate(0deg); } +} + +.strength-ring circle { + stroke-linecap: round; + transform-origin: center; + transform: rotate(-90deg); +} + +.strength-ring.strength-weak circle { + stroke: rgb(239 68 68); +} + +.strength-ring.strength-medium circle { + stroke: rgb(245 158 11); +} + +.strength-ring.strength-strong circle { + stroke: rgb(16 185 129); +} + +.animate-fadeIn { + animation: fadeIn 0.4s ease-out forwards; + animation-delay: 0.1s; + opacity: 0; +} + +.animate-slideUp { + animation: slideUp 0.3s ease-out; +} + +.animate-spin { + animation: spin 1s linear infinite; +} + +.animate-fadeOut { + animation: fadeOut 0.3s ease-out forwards; +} + +.animate-wave { + animation: wave 2s ease-in-out infinite; + transform-origin: 70% 70%; +} + + +{% endblock head %} {% block content %} -