Refactor test utilities and enhance ASGI settings

- Cleaned up and standardized assertions in ApiTestMixin for API response validation.
- Updated ASGI settings to use os.environ for setting the DJANGO_SETTINGS_MODULE.
- Removed unused imports and improved formatting in settings.py.
- Refactored URL patterns in urls.py for better readability and organization.
- Enhanced view functions in views.py for consistency and clarity.
- Added .flake8 configuration for linting and style enforcement.
- Introduced type stubs for django-environ to improve type checking with Pylance.
This commit is contained in:
pacnpal
2025-08-20 19:51:59 -04:00
parent 69c07d1381
commit 66ed4347a9
230 changed files with 15094 additions and 11578 deletions

View File

@@ -4,32 +4,43 @@ from django.contrib.sites.models import Site
class Command(BaseCommand):
help = 'Check all social auth related tables'
help = "Check all social auth related tables"
def handle(self, *args, **options):
# Check SocialApp
self.stdout.write('\nChecking SocialApp table:')
self.stdout.write("\nChecking SocialApp table:")
for app in SocialApp.objects.all():
self.stdout.write(
f'ID: {app.pk}, Provider: {app.provider}, Name: {app.name}, Client ID: {app.client_id}')
self.stdout.write('Sites:')
f"ID: {
app.pk}, Provider: {
app.provider}, Name: {
app.name}, Client ID: {
app.client_id}"
)
self.stdout.write("Sites:")
for site in app.sites.all():
self.stdout.write(f' - {site.domain}')
self.stdout.write(f" - {site.domain}")
# Check SocialAccount
self.stdout.write('\nChecking SocialAccount table:')
self.stdout.write("\nChecking SocialAccount table:")
for account in SocialAccount.objects.all():
self.stdout.write(
f'ID: {account.pk}, Provider: {account.provider}, UID: {account.uid}')
f"ID: {
account.pk}, Provider: {
account.provider}, UID: {
account.uid}"
)
# Check SocialToken
self.stdout.write('\nChecking SocialToken table:')
self.stdout.write("\nChecking SocialToken table:")
for token in SocialToken.objects.all():
self.stdout.write(
f'ID: {token.pk}, Account: {token.account}, App: {token.app}')
f"ID: {token.pk}, Account: {token.account}, App: {token.app}"
)
# Check Site
self.stdout.write('\nChecking Site table:')
self.stdout.write("\nChecking Site table:")
for site in Site.objects.all():
self.stdout.write(
f'ID: {site.pk}, Domain: {site.domain}, Name: {site.name}')
f"ID: {site.pk}, Domain: {site.domain}, Name: {site.name}"
)

View File

@@ -1,19 +1,27 @@
from django.core.management.base import BaseCommand
from allauth.socialaccount.models import SocialApp
class Command(BaseCommand):
help = 'Check social app configurations'
help = "Check social app configurations"
def handle(self, *args, **options):
social_apps = SocialApp.objects.all()
if not social_apps:
self.stdout.write(self.style.ERROR('No social apps found'))
self.stdout.write(self.style.ERROR("No social apps found"))
return
for app in social_apps:
self.stdout.write(self.style.SUCCESS(f'\nProvider: {app.provider}'))
self.stdout.write(f'Name: {app.name}')
self.stdout.write(f'Client ID: {app.client_id}')
self.stdout.write(f'Secret: {app.secret}')
self.stdout.write(f'Sites: {", ".join(str(site.domain) for site in app.sites.all())}')
self.stdout.write(
self.style.SUCCESS(
f"\nProvider: {
app.provider}"
)
)
self.stdout.write(f"Name: {app.name}")
self.stdout.write(f"Client ID: {app.client_id}")
self.stdout.write(f"Secret: {app.secret}")
self.stdout.write(
f'Sites: {", ".join(str(site.domain) for site in app.sites.all())}'
)

View File

@@ -1,8 +1,9 @@
from django.core.management.base import BaseCommand
from django.db import connection
class Command(BaseCommand):
help = 'Clean up social auth tables and migrations'
help = "Clean up social auth tables and migrations"
def handle(self, *args, **options):
with connection.cursor() as cursor:
@@ -11,12 +12,17 @@ class Command(BaseCommand):
cursor.execute("DROP TABLE IF EXISTS socialaccount_socialapp_sites")
cursor.execute("DROP TABLE IF EXISTS socialaccount_socialaccount")
cursor.execute("DROP TABLE IF EXISTS socialaccount_socialtoken")
# Remove migration records
cursor.execute("DELETE FROM django_migrations WHERE app='socialaccount'")
cursor.execute("DELETE FROM django_migrations WHERE app='accounts' AND name LIKE '%social%'")
cursor.execute(
"DELETE FROM django_migrations WHERE app='accounts' "
"AND name LIKE '%social%'"
)
# Reset sequences
cursor.execute("DELETE FROM sqlite_sequence WHERE name LIKE '%social%'")
self.stdout.write(self.style.SUCCESS('Successfully cleaned up social auth configuration'))
self.stdout.write(
self.style.SUCCESS("Successfully cleaned up social auth configuration")
)

View File

@@ -1,7 +1,6 @@
from django.core.management.base import BaseCommand
from django.contrib.auth import get_user_model
from django.contrib.auth.models import Group
from parks.models import Park, ParkReview as Review
from parks.models import ParkReview, Park
from rides.models import Ride
from media.models import Photo
@@ -13,22 +12,21 @@ class Command(BaseCommand):
def handle(self, *args, **kwargs):
# Delete test users
test_users = User.objects.filter(
username__in=["testuser", "moderator"])
test_users = User.objects.filter(username__in=["testuser", "moderator"])
count = test_users.count()
test_users.delete()
self.stdout.write(self.style.SUCCESS(f"Deleted {count} test users"))
# Delete test reviews
reviews = Review.objects.filter(
user__username__in=["testuser", "moderator"])
reviews = ParkReview.objects.filter(
user__username__in=["testuser", "moderator"]
)
count = reviews.count()
reviews.delete()
self.stdout.write(self.style.SUCCESS(f"Deleted {count} test reviews"))
# Delete test photos
photos = Photo.objects.filter(uploader__username__in=[
"testuser", "moderator"])
photos = Photo.objects.filter(uploader__username__in=["testuser", "moderator"])
count = photos.count()
photos.delete()
self.stdout.write(self.style.SUCCESS(f"Deleted {count} test photos"))
@@ -64,7 +62,6 @@ class Command(BaseCommand):
os.remove(f)
self.stdout.write(self.style.SUCCESS(f"Deleted {f}"))
except OSError as e:
self.stdout.write(self.style.WARNING(
f"Error deleting {f}: {e}"))
self.stdout.write(self.style.WARNING(f"Error deleting {f}: {e}"))
self.stdout.write(self.style.SUCCESS("Test data cleanup complete"))

View File

@@ -2,47 +2,54 @@ from django.core.management.base import BaseCommand
from django.contrib.sites.models import Site
from allauth.socialaccount.models import SocialApp
class Command(BaseCommand):
help = 'Create social apps for authentication'
help = "Create social apps for authentication"
def handle(self, *args, **options):
# Get the default site
site = Site.objects.get_or_create(
id=1,
defaults={
'domain': 'localhost:8000',
'name': 'ThrillWiki Development'
}
"domain": "localhost:8000",
"name": "ThrillWiki Development",
},
)[0]
# Create Discord app
discord_app, created = SocialApp.objects.get_or_create(
provider='discord',
provider="discord",
defaults={
'name': 'Discord',
'client_id': '1299112802274902047',
'secret': 'ece7Pe_M4mD4mYzAgcINjTEKL_3ftL11',
}
"name": "Discord",
"client_id": "1299112802274902047",
"secret": "ece7Pe_M4mD4mYzAgcINjTEKL_3ftL11",
},
)
if not created:
discord_app.client_id = '1299112802274902047'
discord_app.secret = 'ece7Pe_M4mD4mYzAgcINjTEKL_3ftL11'
discord_app.client_id = "1299112802274902047"
discord_app.secret = "ece7Pe_M4mD4mYzAgcINjTEKL_3ftL11"
discord_app.save()
discord_app.sites.add(site)
self.stdout.write(f'{"Created" if created else "Updated"} Discord app')
# Create Google app
google_app, created = SocialApp.objects.get_or_create(
provider='google',
provider="google",
defaults={
'name': 'Google',
'client_id': '135166769591-nopcgmo0fkqfqfs9qe783a137mtmcrt2.apps.googleusercontent.com',
'secret': 'GOCSPX-Wd_0Ue0Ue0Ue0Ue0Ue0Ue0Ue0Ue',
}
"name": "Google",
"client_id": (
"135166769591-nopcgmo0fkqfqfs9qe783a137mtmcrt2."
"apps.googleusercontent.com"
),
"secret": "GOCSPX-Wd_0Ue0Ue0Ue0Ue0Ue0Ue0Ue0Ue",
},
)
if not created:
google_app.client_id = '135166769591-nopcgmo0fkqfqfs9qe783a137mtmcrt2.apps.googleusercontent.com'
google_app.secret = 'GOCSPX-Wd_0Ue0Ue0Ue0Ue0Ue0Ue0Ue0Ue'
google_app.client_id = (
"135166769591-nopcgmo0fkqfqfs9qe783a137mtmcrt2."
"apps.googleusercontent.com"
)
google_app.secret = "GOCSPX-Wd_0Ue0Ue0Ue0Ue0Ue0Ue0Ue0Ue"
google_app.save()
google_app.sites.add(site)
self.stdout.write(f'{"Created" if created else "Updated"} Google app')

View File

@@ -1,8 +1,5 @@
from django.core.management.base import BaseCommand
from django.contrib.auth import get_user_model
from django.contrib.auth.models import Group, Permission
User = get_user_model()
from django.contrib.auth.models import Group, Permission, User
class Command(BaseCommand):
@@ -11,27 +8,28 @@ class Command(BaseCommand):
def handle(self, *args, **kwargs):
# Create regular test user
if not User.objects.filter(username="testuser").exists():
user = User.objects.create_user(
user = User.objects.create(
username="testuser",
email="testuser@example.com",
password="testpass123",
)
self.stdout.write(self.style.SUCCESS(
f"Created test user: {user.username}"))
user.set_password("testpass123")
user.save()
self.stdout.write(
self.style.SUCCESS(f"Created test user: {user.get_username()}")
)
else:
self.stdout.write(self.style.WARNING("Test user already exists"))
# Create moderator user
if not User.objects.filter(username="moderator").exists():
moderator = User.objects.create_user(
moderator = User.objects.create(
username="moderator",
email="moderator@example.com",
password="modpass123",
)
moderator.set_password("modpass123")
moderator.save()
# Create moderator group if it doesn't exist
moderator_group, created = Group.objects.get_or_create(
name="Moderators")
moderator_group, created = Group.objects.get_or_create(name="Moderators")
# Add relevant permissions
permissions = Permission.objects.filter(
@@ -51,10 +49,10 @@ class Command(BaseCommand):
self.stdout.write(
self.style.SUCCESS(
f"Created moderator user: {moderator.username}")
f"Created moderator user: {moderator.get_username()}"
)
)
else:
self.stdout.write(self.style.WARNING(
"Moderator user already exists"))
self.stdout.write(self.style.WARNING("Moderator user already exists"))
self.stdout.write(self.style.SUCCESS("Test users setup complete"))

View File

@@ -1,10 +1,18 @@
from django.core.management.base import BaseCommand
from django.db import connection
class Command(BaseCommand):
help = 'Fix migration history by removing rides.0001_initial'
help = "Fix migration history by removing rides.0001_initial"
def handle(self, *args, **kwargs):
with connection.cursor() as cursor:
cursor.execute("DELETE FROM django_migrations WHERE app='rides' AND name='0001_initial';")
self.stdout.write(self.style.SUCCESS('Successfully removed rides.0001_initial from migration history'))
cursor.execute(
"DELETE FROM django_migrations WHERE app='rides' "
"AND name='0001_initial';"
)
self.stdout.write(
self.style.SUCCESS(
"Successfully removed rides.0001_initial from migration history"
)
)

View File

@@ -3,33 +3,39 @@ from allauth.socialaccount.models import SocialApp
from django.contrib.sites.models import Site
import os
class Command(BaseCommand):
help = 'Fix social app configurations'
help = "Fix social app configurations"
def handle(self, *args, **options):
# Delete all existing social apps
SocialApp.objects.all().delete()
self.stdout.write('Deleted all existing social apps')
self.stdout.write("Deleted all existing social apps")
# Get the default site
site = Site.objects.get(id=1)
# Create Google provider
google_app = SocialApp.objects.create(
provider='google',
name='Google',
client_id=os.getenv('GOOGLE_CLIENT_ID'),
secret=os.getenv('GOOGLE_CLIENT_SECRET'),
provider="google",
name="Google",
client_id=os.getenv("GOOGLE_CLIENT_ID"),
secret=os.getenv("GOOGLE_CLIENT_SECRET"),
)
google_app.sites.add(site)
self.stdout.write(f'Created Google app with client_id: {google_app.client_id}')
self.stdout.write(
f"Created Google app with client_id: {
google_app.client_id}"
)
# Create Discord provider
discord_app = SocialApp.objects.create(
provider='discord',
name='Discord',
client_id=os.getenv('DISCORD_CLIENT_ID'),
secret=os.getenv('DISCORD_CLIENT_SECRET'),
provider="discord",
name="Discord",
client_id=os.getenv("DISCORD_CLIENT_ID"),
secret=os.getenv("DISCORD_CLIENT_SECRET"),
)
discord_app.sites.add(site)
self.stdout.write(f'Created Discord app with client_id: {discord_app.client_id}')
self.stdout.write(
f"Created Discord app with client_id: {discord_app.client_id}"
)

View File

@@ -2,6 +2,7 @@ from django.core.management.base import BaseCommand
from PIL import Image, ImageDraw, ImageFont
import os
def generate_avatar(letter):
"""Generate an avatar for a given letter or number"""
avatar_size = (100, 100)
@@ -10,7 +11,7 @@ def generate_avatar(letter):
font_size = 100
# Create a blank image with background color
image = Image.new('RGB', avatar_size, background_color)
image = Image.new("RGB", avatar_size, background_color)
draw = ImageDraw.Draw(image)
# Load a font
@@ -19,8 +20,14 @@ def generate_avatar(letter):
# Calculate text size and position using textbbox
text_bbox = draw.textbbox((0, 0), letter, font=font)
text_width, text_height = text_bbox[2] - text_bbox[0], text_bbox[3] - text_bbox[1]
text_position = ((avatar_size[0] - text_width) / 2, (avatar_size[1] - text_height) / 2)
text_width, text_height = (
text_bbox[2] - text_bbox[0],
text_bbox[3] - text_bbox[1],
)
text_position = (
(avatar_size[0] - text_width) / 2,
(avatar_size[1] - text_height) / 2,
)
# Draw the text on the image
draw.text(text_position, letter, font=font, fill=text_color)
@@ -34,11 +41,14 @@ def generate_avatar(letter):
avatar_path = os.path.join(avatar_dir, f"{letter}_avatar.png")
image.save(avatar_path)
class Command(BaseCommand):
help = 'Generate avatars for letters A-Z and numbers 0-9'
help = "Generate avatars for letters A-Z and numbers 0-9"
def handle(self, *args, **kwargs):
characters = [chr(i) for i in range(65, 91)] + [str(i) for i in range(10)] # A-Z and 0-9
characters = [chr(i) for i in range(65, 91)] + [
str(i) for i in range(10)
] # A-Z and 0-9
for char in characters:
generate_avatar(char)
self.stdout.write(self.style.SUCCESS(f"Generated avatar for {char}"))

View File

@@ -1,11 +1,18 @@
from django.core.management.base import BaseCommand
from accounts.models import UserProfile
class Command(BaseCommand):
help = 'Regenerate default avatars for users without an uploaded avatar'
help = "Regenerate default avatars for users without an uploaded avatar"
def handle(self, *args, **kwargs):
profiles = UserProfile.objects.filter(avatar='')
profiles = UserProfile.objects.filter(avatar="")
for profile in profiles:
profile.save() # This will trigger the avatar generation logic in the save method
self.stdout.write(self.style.SUCCESS(f"Regenerated avatar for {profile.user.username}"))
# This will trigger the avatar generation logic in the save method
profile.save()
self.stdout.write(
self.style.SUCCESS(
f"Regenerated avatar for {
profile.user.username}"
)
)

View File

@@ -5,59 +5,75 @@ import uuid
class Command(BaseCommand):
help = 'Reset database and create admin user'
help = "Reset database and create admin user"
def handle(self, *args, **options):
self.stdout.write('Resetting database...')
self.stdout.write("Resetting database...")
# Drop all tables
with connection.cursor() as cursor:
cursor.execute("""
cursor.execute(
"""
DO $$ DECLARE
r RECORD;
BEGIN
FOR r IN (SELECT tablename FROM pg_tables WHERE schemaname = current_schema()) LOOP
EXECUTE 'DROP TABLE IF EXISTS ' || quote_ident(r.tablename) || ' CASCADE';
FOR r IN (
SELECT tablename FROM pg_tables
WHERE schemaname = current_schema()
) LOOP
EXECUTE 'DROP TABLE IF EXISTS ' || \
quote_ident(r.tablename) || ' CASCADE';
END LOOP;
END $$;
""")
"""
)
# Reset sequences
cursor.execute("""
cursor.execute(
"""
DO $$ DECLARE
r RECORD;
BEGIN
FOR r IN (SELECT sequencename FROM pg_sequences WHERE schemaname = current_schema()) LOOP
EXECUTE 'ALTER SEQUENCE ' || quote_ident(r.sequencename) || ' RESTART WITH 1';
FOR r IN (
SELECT sequencename FROM pg_sequences
WHERE schemaname = current_schema()
) LOOP
EXECUTE 'ALTER SEQUENCE ' || \
quote_ident(r.sequencename) || ' RESTART WITH 1';
END LOOP;
END $$;
""")
"""
)
self.stdout.write('All tables dropped and sequences reset.')
self.stdout.write("All tables dropped and sequences reset.")
# Run migrations
from django.core.management import call_command
call_command('migrate')
self.stdout.write('Migrations applied.')
call_command("migrate")
self.stdout.write("Migrations applied.")
# Create superuser using raw SQL
try:
with connection.cursor() as cursor:
# Create user
user_id = str(uuid.uuid4())[:10]
cursor.execute("""
cursor.execute(
"""
INSERT INTO accounts_user (
username, password, email, is_superuser, is_staff,
is_active, date_joined, user_id, first_name,
last_name, role, is_banned, ban_reason,
username, password, email, is_superuser, is_staff,
is_active, date_joined, user_id, first_name,
last_name, role, is_banned, ban_reason,
theme_preference
) VALUES (
'admin', %s, 'admin@thrillwiki.com', true, true,
true, NOW(), %s, '', '', 'SUPERUSER', false, '',
'light'
) RETURNING id;
""", [make_password('admin'), user_id])
""",
[make_password("admin"), user_id],
)
result = cursor.fetchone()
if result is None:
@@ -66,7 +82,8 @@ class Command(BaseCommand):
# Create profile
profile_id = str(uuid.uuid4())[:10]
cursor.execute("""
cursor.execute(
"""
INSERT INTO accounts_userprofile (
profile_id, display_name, pronouns, bio,
twitter, instagram, youtube, discord,
@@ -79,12 +96,18 @@ class Command(BaseCommand):
0, 0, 0, 0,
%s, ''
);
""", [profile_id, user_db_id])
""",
[profile_id, user_db_id],
)
self.stdout.write('Superuser created.')
self.stdout.write("Superuser created.")
except Exception as e:
self.stdout.write(self.style.ERROR(
f'Error creating superuser: {str(e)}'))
self.stdout.write(
self.style.ERROR(
f"Error creating superuser: {
str(e)}"
)
)
raise
self.stdout.write(self.style.SUCCESS('Database reset complete.'))
self.stdout.write(self.style.SUCCESS("Database reset complete."))

View File

@@ -3,34 +3,37 @@ from allauth.socialaccount.models import SocialApp
from django.contrib.sites.models import Site
from django.db import connection
class Command(BaseCommand):
help = 'Reset social apps configuration'
help = "Reset social apps configuration"
def handle(self, *args, **options):
# Delete all social apps using raw SQL to bypass Django's ORM
with connection.cursor() as cursor:
cursor.execute("DELETE FROM socialaccount_socialapp_sites")
cursor.execute("DELETE FROM socialaccount_socialapp")
# Get the default site
site = Site.objects.get(id=1)
# Create Discord app
discord_app = SocialApp.objects.create(
provider='discord',
name='Discord',
client_id='1299112802274902047',
secret='ece7Pe_M4mD4mYzAgcINjTEKL_3ftL11',
provider="discord",
name="Discord",
client_id="1299112802274902047",
secret="ece7Pe_M4mD4mYzAgcINjTEKL_3ftL11",
)
discord_app.sites.add(site)
self.stdout.write(f'Created Discord app with ID: {discord_app.id}')
self.stdout.write(f"Created Discord app with ID: {discord_app.pk}")
# Create Google app
google_app = SocialApp.objects.create(
provider='google',
name='Google',
client_id='135166769591-nopcgmo0fkqfqfs9qe783a137mtmcrt2.apps.googleusercontent.com',
secret='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',
provider="google",
name="Google",
client_id=(
"135166769591-nopcgmo0fkqfqfs9qe783a137mtmcrt2.apps.googleusercontent.com"
),
secret="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",
)
google_app.sites.add(site)
self.stdout.write(f'Created Google app with ID: {google_app.id}')
self.stdout.write(f"Created Google app with ID: {google_app.pk}")

View File

@@ -1,17 +1,24 @@
from django.core.management.base import BaseCommand
from django.db import connection
class Command(BaseCommand):
help = 'Reset social auth configuration'
help = "Reset social auth configuration"
def handle(self, *args, **options):
with connection.cursor() as cursor:
# Delete all social apps
cursor.execute("DELETE FROM socialaccount_socialapp")
cursor.execute("DELETE FROM socialaccount_socialapp_sites")
# Reset sequences
cursor.execute("DELETE FROM sqlite_sequence WHERE name='socialaccount_socialapp'")
cursor.execute("DELETE FROM sqlite_sequence WHERE name='socialaccount_socialapp_sites'")
self.stdout.write(self.style.SUCCESS('Successfully reset social auth configuration'))
cursor.execute(
"DELETE FROM sqlite_sequence WHERE name='socialaccount_socialapp'"
)
cursor.execute(
"DELETE FROM sqlite_sequence WHERE name='socialaccount_socialapp_sites'"
)
self.stdout.write(
self.style.SUCCESS("Successfully reset social auth configuration")
)

View File

@@ -1,26 +1,26 @@
from django.core.management.base import BaseCommand
from django.contrib.auth.models import Group, Permission
from django.contrib.contenttypes.models import ContentType
from django.contrib.auth.models import Group
from accounts.models import User
from accounts.signals import create_default_groups
class Command(BaseCommand):
help = 'Set up default groups and permissions for user roles'
help = "Set up default groups and permissions for user roles"
def handle(self, *args, **options):
self.stdout.write('Creating default groups and permissions...')
self.stdout.write("Creating default groups and permissions...")
try:
# Create default groups with permissions
create_default_groups()
# Sync existing users with groups based on their roles
users = User.objects.exclude(role=User.Roles.USER)
for user in users:
group = Group.objects.filter(name=user.role).first()
if group:
user.groups.add(group)
# Update staff/superuser status based on role
if user.role == User.Roles.SUPERUSER:
user.is_superuser = True
@@ -28,15 +28,22 @@ class Command(BaseCommand):
elif user.role in [User.Roles.ADMIN, User.Roles.MODERATOR]:
user.is_staff = True
user.save()
self.stdout.write(self.style.SUCCESS('Successfully set up groups and permissions'))
self.stdout.write(
self.style.SUCCESS("Successfully set up groups and permissions")
)
# Print summary
for group in Group.objects.all():
self.stdout.write(f'\nGroup: {group.name}')
self.stdout.write('Permissions:')
self.stdout.write(f"\nGroup: {group.name}")
self.stdout.write("Permissions:")
for perm in group.permissions.all():
self.stdout.write(f' - {perm.codename}')
self.stdout.write(f" - {perm.codename}")
except Exception as e:
self.stdout.write(self.style.ERROR(f'Error setting up groups: {str(e)}'))
self.stdout.write(
self.style.ERROR(
f"Error setting up groups: {
str(e)}"
)
)

View File

@@ -1,17 +1,16 @@
from django.core.management.base import BaseCommand
from django.contrib.sites.models import Site
class Command(BaseCommand):
help = 'Set up default site'
help = "Set up default site"
def handle(self, *args, **options):
# Delete any existing sites
Site.objects.all().delete()
# Create default site
site = Site.objects.create(
id=1,
domain='localhost:8000',
name='ThrillWiki Development'
id=1, domain="localhost:8000", name="ThrillWiki Development"
)
self.stdout.write(self.style.SUCCESS(f'Created site: {site.domain}'))
self.stdout.write(self.style.SUCCESS(f"Created site: {site.domain}"))

View File

@@ -4,60 +4,123 @@ from allauth.socialaccount.models import SocialApp
from dotenv import load_dotenv
import os
class Command(BaseCommand):
help = 'Sets up social authentication apps'
help = "Sets up social authentication apps"
def handle(self, *args, **kwargs):
# Load environment variables
load_dotenv()
# Get environment variables
google_client_id = os.getenv('GOOGLE_CLIENT_ID')
google_client_secret = os.getenv('GOOGLE_CLIENT_SECRET')
discord_client_id = os.getenv('DISCORD_CLIENT_ID')
discord_client_secret = os.getenv('DISCORD_CLIENT_SECRET')
google_client_id = os.getenv("GOOGLE_CLIENT_ID")
google_client_secret = os.getenv("GOOGLE_CLIENT_SECRET")
discord_client_id = os.getenv("DISCORD_CLIENT_ID")
discord_client_secret = os.getenv("DISCORD_CLIENT_SECRET")
if not all([google_client_id, google_client_secret, discord_client_id, discord_client_secret]):
self.stdout.write(self.style.ERROR('Missing required environment variables'))
# DEBUG: Log environment variable values
self.stdout.write(
f"DEBUG: google_client_id type: {
type(google_client_id)}, value: {google_client_id}"
)
self.stdout.write(
f"DEBUG: google_client_secret type: {
type(google_client_secret)}, value: {google_client_secret}"
)
self.stdout.write(
f"DEBUG: discord_client_id type: {
type(discord_client_id)}, value: {discord_client_id}"
)
self.stdout.write(
f"DEBUG: discord_client_secret type: {
type(discord_client_secret)}, value: {discord_client_secret}"
)
if not all(
[
google_client_id,
google_client_secret,
discord_client_id,
discord_client_secret,
]
):
self.stdout.write(
self.style.ERROR("Missing required environment variables")
)
self.stdout.write(
f"DEBUG: google_client_id is None: {google_client_id is None}"
)
self.stdout.write(
f"DEBUG: google_client_secret is None: {
google_client_secret is None}"
)
self.stdout.write(
f"DEBUG: discord_client_id is None: {
discord_client_id is None}"
)
self.stdout.write(
f"DEBUG: discord_client_secret is None: {
discord_client_secret is None}"
)
return
# Get or create the default site
site, _ = Site.objects.get_or_create(
id=1,
defaults={
'domain': 'localhost:8000',
'name': 'localhost'
}
id=1, defaults={"domain": "localhost:8000", "name": "localhost"}
)
# Set up Google
google_app, created = SocialApp.objects.get_or_create(
provider='google',
provider="google",
defaults={
'name': 'Google',
'client_id': google_client_id,
'secret': google_client_secret,
}
"name": "Google",
"client_id": google_client_id,
"secret": google_client_secret,
},
)
if not created:
google_app.client_id = google_client_id
google_app.[SECRET-REMOVED]
google_app.save()
self.stdout.write(
f"DEBUG: About to assign google_client_id: {google_client_id} (type: {
type(google_client_id)})"
)
if google_client_id is not None and google_client_secret is not None:
google_app.client_id = google_client_id
google_app.secret = google_client_secret
google_app.save()
self.stdout.write("DEBUG: Successfully updated Google app")
else:
self.stdout.write(
self.style.ERROR(
"Google client_id or secret is None, skipping update."
)
)
google_app.sites.add(site)
# Set up Discord
discord_app, created = SocialApp.objects.get_or_create(
provider='discord',
provider="discord",
defaults={
'name': 'Discord',
'client_id': discord_client_id,
'secret': discord_client_secret,
}
"name": "Discord",
"client_id": discord_client_id,
"secret": discord_client_secret,
},
)
if not created:
discord_app.client_id = discord_client_id
discord_app.[SECRET-REMOVED]
discord_app.save()
self.stdout.write(
f"DEBUG: About to assign discord_client_id: {discord_client_id} (type: {
type(discord_client_id)})"
)
if discord_client_id is not None and discord_client_secret is not None:
discord_app.client_id = discord_client_id
discord_app.secret = discord_client_secret
discord_app.save()
self.stdout.write("DEBUG: Successfully updated Discord app")
else:
self.stdout.write(
self.style.ERROR(
"Discord client_id or secret is None, skipping update."
)
)
discord_app.sites.add(site)
self.stdout.write(self.style.SUCCESS('Successfully set up social auth apps'))
self.stdout.write(self.style.SUCCESS("Successfully set up social auth apps"))

View File

@@ -1,35 +1,43 @@
from django.core.management.base import BaseCommand
from django.contrib.sites.models import Site
from django.contrib.auth import get_user_model
from django.contrib.auth.models import Permission
from allauth.socialaccount.models import SocialApp
User = get_user_model()
class Command(BaseCommand):
help = 'Set up social authentication through admin interface'
help = "Set up social authentication through admin interface"
def handle(self, *args, **options):
# Get or create the default site
site, _ = Site.objects.get_or_create(
id=1,
defaults={
'domain': 'localhost:8000',
'name': 'ThrillWiki Development'
}
"domain": "localhost:8000",
"name": "ThrillWiki Development",
},
)
if not _:
site.domain = 'localhost:8000'
site.name = 'ThrillWiki Development'
site.domain = "localhost:8000"
site.name = "ThrillWiki Development"
site.save()
self.stdout.write(f'{"Created" if _ else "Updated"} site: {site.domain}')
# Create superuser if it doesn't exist
if not User.objects.filter(username='admin').exists():
User.objects.create_superuser('admin', 'admin@example.com', 'admin')
self.stdout.write('Created superuser: admin/admin')
if not User.objects.filter(username="admin").exists():
admin_user = User.objects.create(
username="admin",
email="admin@example.com",
is_staff=True,
is_superuser=True,
)
admin_user.set_password("admin")
admin_user.save()
self.stdout.write("Created superuser: admin/admin")
self.stdout.write(self.style.SUCCESS('''
self.stdout.write(
self.style.SUCCESS(
"""
Social auth setup instructions:
1. Run the development server:
@@ -57,4 +65,6 @@ Social auth setup instructions:
Client id: 135166769591-nopcgmo0fkqfqfs9qe783a137mtmcrt2.apps.googleusercontent.com
Secret key: GOCSPX-Wd_0Ue0Ue0Ue0Ue0Ue0Ue0Ue0Ue
Sites: Add "localhost:8000"
'''))
"""
)
)

View File

@@ -1,60 +1,61 @@
from django.core.management.base import BaseCommand
from django.urls import reverse
from django.test import Client
from allauth.socialaccount.models import SocialApp
from urllib.parse import urljoin
class Command(BaseCommand):
help = 'Test Discord OAuth2 authentication flow'
help = "Test Discord OAuth2 authentication flow"
def handle(self, *args, **options):
client = Client(HTTP_HOST='localhost:8000')
client = Client(HTTP_HOST="localhost:8000")
# Get Discord app
try:
discord_app = SocialApp.objects.get(provider='discord')
self.stdout.write('Found Discord app configuration:')
self.stdout.write(f'Client ID: {discord_app.client_id}')
discord_app = SocialApp.objects.get(provider="discord")
self.stdout.write("Found Discord app configuration:")
self.stdout.write(f"Client ID: {discord_app.client_id}")
# Test login URL
login_url = '/accounts/discord/login/'
response = client.get(login_url, HTTP_HOST='localhost:8000')
self.stdout.write(f'\nTesting login URL: {login_url}')
self.stdout.write(f'Status code: {response.status_code}')
login_url = "/accounts/discord/login/"
response = client.get(login_url, HTTP_HOST="localhost:8000")
self.stdout.write(f"\nTesting login URL: {login_url}")
self.stdout.write(f"Status code: {response.status_code}")
if response.status_code == 302:
redirect_url = response['Location']
self.stdout.write(f'Redirects to: {redirect_url}')
redirect_url = response["Location"]
self.stdout.write(f"Redirects to: {redirect_url}")
# Parse OAuth2 parameters
self.stdout.write('\nOAuth2 Parameters:')
if 'client_id=' in redirect_url:
self.stdout.write('✓ client_id parameter present')
if 'redirect_uri=' in redirect_url:
self.stdout.write('✓ redirect_uri parameter present')
if 'scope=' in redirect_url:
self.stdout.write('✓ scope parameter present')
if 'response_type=' in redirect_url:
self.stdout.write('✓ response_type parameter present')
if 'code_challenge=' in redirect_url:
self.stdout.write('✓ PKCE enabled (code_challenge present)')
self.stdout.write("\nOAuth2 Parameters:")
if "client_id=" in redirect_url:
self.stdout.write("✓ client_id parameter present")
if "redirect_uri=" in redirect_url:
self.stdout.write("✓ redirect_uri parameter present")
if "scope=" in redirect_url:
self.stdout.write("✓ scope parameter present")
if "response_type=" in redirect_url:
self.stdout.write("✓ response_type parameter present")
if "code_challenge=" in redirect_url:
self.stdout.write("✓ PKCE enabled (code_challenge present)")
# Show callback URL
callback_url = 'http://localhost:8000/accounts/discord/login/callback/'
self.stdout.write('\nCallback URL to configure in Discord Developer Portal:')
callback_url = "http://localhost:8000/accounts/discord/login/callback/"
self.stdout.write(
"\nCallback URL to configure in Discord Developer Portal:"
)
self.stdout.write(callback_url)
# Show frontend login URL
frontend_url = 'http://localhost:5173'
self.stdout.write('\nFrontend configuration:')
self.stdout.write(f'Frontend URL: {frontend_url}')
self.stdout.write('Discord login button should use:')
self.stdout.write('/accounts/discord/login/?process=login')
frontend_url = "http://localhost:5173"
self.stdout.write("\nFrontend configuration:")
self.stdout.write(f"Frontend URL: {frontend_url}")
self.stdout.write("Discord login button should use:")
self.stdout.write("/accounts/discord/login/?process=login")
# Show allauth URLs
self.stdout.write('\nAllauth URLs:')
self.stdout.write('Login URL: /accounts/discord/login/?process=login')
self.stdout.write('Callback URL: /accounts/discord/login/callback/')
self.stdout.write("\nAllauth URLs:")
self.stdout.write("Login URL: /accounts/discord/login/?process=login")
self.stdout.write("Callback URL: /accounts/discord/login/callback/")
except SocialApp.DoesNotExist:
self.stdout.write(self.style.ERROR('Discord app not found'))
self.stdout.write(self.style.ERROR("Discord app not found"))

View File

@@ -2,19 +2,22 @@ from django.core.management.base import BaseCommand
from allauth.socialaccount.models import SocialApp
from django.contrib.sites.models import Site
class Command(BaseCommand):
help = 'Update social apps to be associated with all sites'
help = "Update social apps to be associated with all sites"
def handle(self, *args, **options):
# Get all sites
sites = Site.objects.all()
# Update each social app
for app in SocialApp.objects.all():
self.stdout.write(f'Updating {app.provider} app...')
self.stdout.write(f"Updating {app.provider} app...")
# Clear existing sites
app.sites.clear()
# Add all sites
for site in sites:
app.sites.add(site)
self.stdout.write(f'Added sites: {", ".join(site.domain for site in sites)}')
self.stdout.write(
f'Added sites: {", ".join(site.domain for site in sites)}'
)

View File

@@ -1,36 +1,42 @@
from django.core.management.base import BaseCommand
from allauth.socialaccount.models import SocialApp
from django.contrib.sites.models import Site
from django.urls import reverse
from django.conf import settings
class Command(BaseCommand):
help = 'Verify Discord OAuth2 settings'
help = "Verify Discord OAuth2 settings"
def handle(self, *args, **options):
# Get Discord app
try:
discord_app = SocialApp.objects.get(provider='discord')
self.stdout.write('Found Discord app configuration:')
self.stdout.write(f'Client ID: {discord_app.client_id}')
self.stdout.write(f'Secret: {discord_app.secret}')
discord_app = SocialApp.objects.get(provider="discord")
self.stdout.write("Found Discord app configuration:")
self.stdout.write(f"Client ID: {discord_app.client_id}")
self.stdout.write(f"Secret: {discord_app.secret}")
# Get sites
sites = discord_app.sites.all()
self.stdout.write('\nAssociated sites:')
self.stdout.write("\nAssociated sites:")
for site in sites:
self.stdout.write(f'- {site.domain} ({site.name})')
self.stdout.write(f"- {site.domain} ({site.name})")
# Show callback URL
callback_url = 'http://localhost:8000/accounts/discord/login/callback/'
self.stdout.write('\nCallback URL to configure in Discord Developer Portal:')
callback_url = "http://localhost:8000/accounts/discord/login/callback/"
self.stdout.write(
"\nCallback URL to configure in Discord Developer Portal:"
)
self.stdout.write(callback_url)
# Show OAuth2 settings
self.stdout.write('\nOAuth2 settings in settings.py:')
discord_settings = settings.SOCIALACCOUNT_PROVIDERS.get('discord', {})
self.stdout.write(f'PKCE Enabled: {discord_settings.get("OAUTH_PKCE_ENABLED", False)}')
self.stdout.write("\nOAuth2 settings in settings.py:")
discord_settings = settings.SOCIALACCOUNT_PROVIDERS.get("discord", {})
self.stdout.write(
f'PKCE Enabled: {
discord_settings.get(
"OAUTH_PKCE_ENABLED",
False)}'
)
self.stdout.write(f'Scopes: {discord_settings.get("SCOPE", [])}')
except SocialApp.DoesNotExist:
self.stdout.write(self.style.ERROR('Discord app not found'))
self.stdout.write(self.style.ERROR("Discord app not found"))