mirror of
https://github.com/pacnpal/thrillwiki_django_no_react.git
synced 2026-01-02 01:47:04 -05:00
feat: Implement initial schema and add various API, service, and management command enhancements across the application.
This commit is contained in:
@@ -22,20 +22,14 @@ class Command(BaseCommand):
|
||||
# Check SocialAccount
|
||||
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}"
|
||||
)
|
||||
self.stdout.write(f"ID: {account.pk}, Provider: {account.provider}, UID: {account.uid}")
|
||||
|
||||
# Check SocialToken
|
||||
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}"
|
||||
)
|
||||
self.stdout.write(f"ID: {token.pk}, Account: {token.account}, App: {token.app}")
|
||||
|
||||
# Check Site
|
||||
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}"
|
||||
)
|
||||
self.stdout.write(f"ID: {site.pk}, Domain: {site.domain}, Name: {site.name}")
|
||||
|
||||
@@ -17,6 +17,4 @@ class Command(BaseCommand):
|
||||
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(f"Sites: {', '.join(str(site.domain) for site in app.sites.all())}")
|
||||
|
||||
@@ -15,14 +15,9 @@ class Command(BaseCommand):
|
||||
|
||||
# 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"))
|
||||
|
||||
@@ -18,24 +18,18 @@ class Command(BaseCommand):
|
||||
self.stdout.write(self.style.SUCCESS(f"Deleted {count} test users"))
|
||||
|
||||
# Delete test reviews
|
||||
reviews = ParkReview.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 - both park and ride photos
|
||||
park_photos = ParkPhoto.objects.filter(
|
||||
uploader__username__in=["testuser", "moderator"]
|
||||
)
|
||||
park_photos = ParkPhoto.objects.filter(uploader__username__in=["testuser", "moderator"])
|
||||
park_count = park_photos.count()
|
||||
park_photos.delete()
|
||||
self.stdout.write(self.style.SUCCESS(f"Deleted {park_count} test park photos"))
|
||||
|
||||
ride_photos = RidePhoto.objects.filter(
|
||||
uploader__username__in=["testuser", "moderator"]
|
||||
)
|
||||
ride_photos = RidePhoto.objects.filter(uploader__username__in=["testuser", "moderator"])
|
||||
ride_count = ride_photos.count()
|
||||
ride_photos.delete()
|
||||
self.stdout.write(self.style.SUCCESS(f"Deleted {ride_count} test ride photos"))
|
||||
|
||||
@@ -37,18 +37,12 @@ class Command(BaseCommand):
|
||||
provider="google",
|
||||
defaults={
|
||||
"name": "Google",
|
||||
"client_id": (
|
||||
"135166769591-nopcgmo0fkqfqfs9qe783a137mtmcrt2."
|
||||
"apps.googleusercontent.com"
|
||||
),
|
||||
"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.client_id = "135166769591-nopcgmo0fkqfqfs9qe783a137mtmcrt2." "apps.googleusercontent.com"
|
||||
google_app.secret = "GOCSPX-Wd_0Ue0Ue0Ue0Ue0Ue0Ue0Ue0Ue"
|
||||
google_app.save()
|
||||
google_app.sites.add(site)
|
||||
|
||||
@@ -14,9 +14,7 @@ class Command(BaseCommand):
|
||||
)
|
||||
user.set_password("testpass123")
|
||||
user.save()
|
||||
self.stdout.write(
|
||||
self.style.SUCCESS(f"Created test user: {user.get_username()}")
|
||||
)
|
||||
self.stdout.write(self.style.SUCCESS(f"Created test user: {user.get_username()}"))
|
||||
else:
|
||||
self.stdout.write(self.style.WARNING("Test user already exists"))
|
||||
|
||||
@@ -47,11 +45,7 @@ class Command(BaseCommand):
|
||||
# Add user to moderator group
|
||||
moderator.groups.add(moderator_group)
|
||||
|
||||
self.stdout.write(
|
||||
self.style.SUCCESS(
|
||||
f"Created moderator user: {moderator.get_username()}"
|
||||
)
|
||||
)
|
||||
self.stdout.write(self.style.SUCCESS(f"Created moderator user: {moderator.get_username()}"))
|
||||
else:
|
||||
self.stdout.write(self.style.WARNING("Moderator user already exists"))
|
||||
|
||||
|
||||
@@ -17,9 +17,7 @@ class Command(BaseCommand):
|
||||
help = "Delete a user while preserving all their submissions"
|
||||
|
||||
def add_arguments(self, parser):
|
||||
parser.add_argument(
|
||||
"username", nargs="?", type=str, help="Username of the user to delete"
|
||||
)
|
||||
parser.add_argument("username", nargs="?", type=str, help="Username of the user to delete")
|
||||
parser.add_argument(
|
||||
"--user-id",
|
||||
type=str,
|
||||
@@ -30,9 +28,7 @@ class Command(BaseCommand):
|
||||
action="store_true",
|
||||
help="Show what would be deleted without actually deleting",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--force", action="store_true", help="Skip confirmation prompt"
|
||||
)
|
||||
parser.add_argument("--force", action="store_true", help="Skip confirmation prompt")
|
||||
|
||||
def handle(self, *args, **options):
|
||||
username = options.get("username")
|
||||
@@ -52,7 +48,7 @@ class Command(BaseCommand):
|
||||
user = User.objects.get(username=username) if username else User.objects.get(user_id=user_id)
|
||||
except User.DoesNotExist:
|
||||
identifier = username or user_id
|
||||
raise CommandError(f'User "{identifier}" does not exist')
|
||||
raise CommandError(f'User "{identifier}" does not exist') from None
|
||||
|
||||
# Check if user can be deleted
|
||||
can_delete, reason = UserDeletionService.can_delete_user(user)
|
||||
@@ -61,27 +57,13 @@ class Command(BaseCommand):
|
||||
|
||||
# Count submissions
|
||||
submission_counts = {
|
||||
"park_reviews": getattr(
|
||||
user, "park_reviews", user.__class__.objects.none()
|
||||
).count(),
|
||||
"ride_reviews": getattr(
|
||||
user, "ride_reviews", user.__class__.objects.none()
|
||||
).count(),
|
||||
"uploaded_park_photos": getattr(
|
||||
user, "uploaded_park_photos", user.__class__.objects.none()
|
||||
).count(),
|
||||
"uploaded_ride_photos": getattr(
|
||||
user, "uploaded_ride_photos", user.__class__.objects.none()
|
||||
).count(),
|
||||
"top_lists": getattr(
|
||||
user, "top_lists", user.__class__.objects.none()
|
||||
).count(),
|
||||
"edit_submissions": getattr(
|
||||
user, "edit_submissions", user.__class__.objects.none()
|
||||
).count(),
|
||||
"photo_submissions": getattr(
|
||||
user, "photo_submissions", user.__class__.objects.none()
|
||||
).count(),
|
||||
"park_reviews": getattr(user, "park_reviews", user.__class__.objects.none()).count(),
|
||||
"ride_reviews": getattr(user, "ride_reviews", user.__class__.objects.none()).count(),
|
||||
"uploaded_park_photos": getattr(user, "uploaded_park_photos", user.__class__.objects.none()).count(),
|
||||
"uploaded_ride_photos": getattr(user, "uploaded_ride_photos", user.__class__.objects.none()).count(),
|
||||
"top_lists": getattr(user, "top_lists", user.__class__.objects.none()).count(),
|
||||
"edit_submissions": getattr(user, "edit_submissions", user.__class__.objects.none()).count(),
|
||||
"photo_submissions": getattr(user, "photo_submissions", user.__class__.objects.none()).count(),
|
||||
}
|
||||
|
||||
total_submissions = sum(submission_counts.values())
|
||||
@@ -98,9 +80,7 @@ class Command(BaseCommand):
|
||||
self.stdout.write(self.style.WARNING("\nSubmissions to preserve:"))
|
||||
for submission_type, count in submission_counts.items():
|
||||
if count > 0:
|
||||
self.stdout.write(
|
||||
f' {submission_type.replace("_", " ").title()}: {count}'
|
||||
)
|
||||
self.stdout.write(f' {submission_type.replace("_", " ").title()}: {count}')
|
||||
|
||||
self.stdout.write(f"\nTotal submissions: {total_submissions}")
|
||||
|
||||
@@ -111,9 +91,7 @@ class Command(BaseCommand):
|
||||
)
|
||||
)
|
||||
else:
|
||||
self.stdout.write(
|
||||
self.style.WARNING("\nNo submissions found for this user.")
|
||||
)
|
||||
self.stdout.write(self.style.WARNING("\nNo submissions found for this user."))
|
||||
|
||||
if dry_run:
|
||||
self.stdout.write(self.style.SUCCESS("\n[DRY RUN] No changes were made."))
|
||||
@@ -136,11 +114,7 @@ class Command(BaseCommand):
|
||||
try:
|
||||
result = UserDeletionService.delete_user_preserve_submissions(user)
|
||||
|
||||
self.stdout.write(
|
||||
self.style.SUCCESS(
|
||||
f'\nSuccessfully deleted user "{result["deleted_user"]["username"]}"'
|
||||
)
|
||||
)
|
||||
self.stdout.write(self.style.SUCCESS(f'\nSuccessfully deleted user "{result["deleted_user"]["username"]}"'))
|
||||
|
||||
preserved_count = sum(result["preserved_submissions"].values())
|
||||
if preserved_count > 0:
|
||||
@@ -154,9 +128,7 @@ class Command(BaseCommand):
|
||||
self.stdout.write(self.style.WARNING("\nPreservation Summary:"))
|
||||
for submission_type, count in result["preserved_submissions"].items():
|
||||
if count > 0:
|
||||
self.stdout.write(
|
||||
f' {submission_type.replace("_", " ").title()}: {count}'
|
||||
)
|
||||
self.stdout.write(f' {submission_type.replace("_", " ").title()}: {count}')
|
||||
|
||||
except Exception as e:
|
||||
raise CommandError(f"Error deleting user: {str(e)}")
|
||||
raise CommandError(f"Error deleting user: {str(e)}") from None
|
||||
|
||||
@@ -7,12 +7,5 @@ class Command(BaseCommand):
|
||||
|
||||
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"))
|
||||
|
||||
@@ -34,6 +34,4 @@ class Command(BaseCommand):
|
||||
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}")
|
||||
|
||||
@@ -47,9 +47,7 @@ class Command(BaseCommand):
|
||||
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}"))
|
||||
|
||||
@@ -11,6 +11,4 @@ class Command(BaseCommand):
|
||||
for profile in profiles:
|
||||
# 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}")
|
||||
)
|
||||
self.stdout.write(self.style.SUCCESS(f"Regenerated avatar for {profile.user.username}"))
|
||||
|
||||
@@ -69,18 +69,18 @@ class Command(BaseCommand):
|
||||
|
||||
# Security: Using Django ORM instead of raw SQL for user creation
|
||||
user = User.objects.create_superuser(
|
||||
username='admin',
|
||||
email='admin@thrillwiki.com',
|
||||
password='admin',
|
||||
role='SUPERUSER',
|
||||
username="admin",
|
||||
email="admin@thrillwiki.com",
|
||||
password="admin",
|
||||
role="SUPERUSER",
|
||||
)
|
||||
|
||||
# Create profile using ORM
|
||||
UserProfile.objects.create(
|
||||
user=user,
|
||||
display_name='Admin',
|
||||
pronouns='they/them',
|
||||
bio='ThrillWiki Administrator',
|
||||
display_name="Admin",
|
||||
pronouns="they/them",
|
||||
bio="ThrillWiki Administrator",
|
||||
)
|
||||
|
||||
self.stdout.write("Superuser created.")
|
||||
|
||||
@@ -30,9 +30,7 @@ class Command(BaseCommand):
|
||||
google_app = SocialApp.objects.create(
|
||||
provider="google",
|
||||
name="Google",
|
||||
client_id=(
|
||||
"135166769591-nopcgmo0fkqfqfs9qe783a137mtmcrt2.apps.googleusercontent.com"
|
||||
),
|
||||
client_id=("135166769591-nopcgmo0fkqfqfs9qe783a137mtmcrt2.apps.googleusercontent.com"),
|
||||
secret="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",
|
||||
)
|
||||
google_app.sites.add(site)
|
||||
|
||||
@@ -12,13 +12,7 @@ class Command(BaseCommand):
|
||||
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'"
|
||||
)
|
||||
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")
|
||||
)
|
||||
self.stdout.write(self.style.SUCCESS("Successfully reset social auth configuration"))
|
||||
|
||||
@@ -30,9 +30,7 @@ class Command(BaseCommand):
|
||||
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():
|
||||
|
||||
@@ -10,7 +10,5 @@ class Command(BaseCommand):
|
||||
Site.objects.all().delete()
|
||||
|
||||
# Create default site
|
||||
site = Site.objects.create(
|
||||
id=1, domain="localhost:8000", name="ThrillWiki Development"
|
||||
)
|
||||
site = Site.objects.create(id=1, domain="localhost:8000", name="ThrillWiki Development")
|
||||
self.stdout.write(self.style.SUCCESS(f"Created site: {site.domain}"))
|
||||
|
||||
@@ -49,27 +49,15 @@ class Command(BaseCommand):
|
||||
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}"
|
||||
)
|
||||
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"}
|
||||
)
|
||||
site, _ = Site.objects.get_or_create(id=1, defaults={"domain": "localhost:8000", "name": "localhost"})
|
||||
|
||||
# Set up Google
|
||||
google_app, created = SocialApp.objects.get_or_create(
|
||||
@@ -92,11 +80,7 @@ class Command(BaseCommand):
|
||||
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."
|
||||
)
|
||||
)
|
||||
self.stdout.write(self.style.ERROR("Google client_id or secret is None, skipping update."))
|
||||
google_app.sites.add(site)
|
||||
|
||||
# Set up Discord
|
||||
@@ -120,11 +104,7 @@ class Command(BaseCommand):
|
||||
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."
|
||||
)
|
||||
)
|
||||
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"))
|
||||
|
||||
@@ -42,6 +42,4 @@ class Command(BaseCommand):
|
||||
for app in SocialApp.objects.all():
|
||||
self.stdout.write(f"- {app.name} ({app.provider}): {app.client_id}")
|
||||
|
||||
self.stdout.write(
|
||||
self.style.SUCCESS(f"\nTotal social apps: {SocialApp.objects.count()}")
|
||||
)
|
||||
self.stdout.write(self.style.SUCCESS(f"\nTotal social apps: {SocialApp.objects.count()}"))
|
||||
|
||||
@@ -40,9 +40,7 @@ class Command(BaseCommand):
|
||||
|
||||
# Show callback URL
|
||||
callback_url = "http://localhost:8000/accounts/discord/login/callback/"
|
||||
self.stdout.write(
|
||||
"\nCallback URL to configure in Discord Developer Portal:"
|
||||
)
|
||||
self.stdout.write("\nCallback URL to configure in Discord Developer Portal:")
|
||||
self.stdout.write(callback_url)
|
||||
|
||||
# Show frontend login URL
|
||||
|
||||
@@ -18,6 +18,4 @@ class Command(BaseCommand):
|
||||
# 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)}")
|
||||
|
||||
@@ -22,17 +22,13 @@ class Command(BaseCommand):
|
||||
|
||||
# Show callback URL
|
||||
callback_url = "http://localhost:8000/accounts/discord/login/callback/"
|
||||
self.stdout.write(
|
||||
"\nCallback URL to configure in Discord Developer Portal:"
|
||||
)
|
||||
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(f"PKCE Enabled: {discord_settings.get('OAUTH_PKCE_ENABLED', False)}")
|
||||
self.stdout.write(f"Scopes: {discord_settings.get('SCOPE', [])}")
|
||||
|
||||
except SocialApp.DoesNotExist:
|
||||
|
||||
Reference in New Issue
Block a user