feat: Implement avatar upload system with Cloudflare integration

- Added migration to transition avatar data from CloudflareImageField to ForeignKey structure in UserProfile.
- Fixed UserProfileEvent avatar field to align with new avatar structure.
- Created serializers for social authentication, including connected and available providers.
- Developed request logging middleware for comprehensive request/response logging.
- Updated moderation and parks migrations to remove outdated triggers and adjust foreign key relationships.
- Enhanced rides migrations to ensure proper handling of image uploads and triggers.
- Introduced a test script for the 3-step avatar upload process, ensuring functionality with Cloudflare.
- Documented the fix for avatar upload issues, detailing root cause, implementation, and verification steps.
- Implemented automatic deletion of Cloudflare images upon avatar, park, and ride photo changes or removals.
This commit is contained in:
pacnpal
2025-08-30 21:20:25 -04:00
parent fb6726f89a
commit 9bed782784
75 changed files with 4571 additions and 1962 deletions

View File

@@ -1,32 +0,0 @@
# Generated by Django 5.2.5 on 2025-08-28 18:17
import cloudflare_images.field
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
("parks", "0008_parkphoto_parkphotoevent_and_more"),
]
operations = [
migrations.AlterField(
model_name="parkphoto",
name="image",
field=cloudflare_images.field.CloudflareImagesField(
help_text="Park photo stored on Cloudflare Images",
upload_to="",
variant="public",
),
),
migrations.AlterField(
model_name="parkphotoevent",
name="image",
field=cloudflare_images.field.CloudflareImagesField(
help_text="Park photo stored on Cloudflare Images",
upload_to="",
variant="public",
),
),
]

View File

@@ -9,7 +9,7 @@ from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("parks", "0009_cloudflare_images_integration"),
("parks", "0008_parkphoto_parkphotoevent_and_more"),
]
operations = [

View File

@@ -0,0 +1,75 @@
# Generated by Django 5.2.5 on 2025-08-30 21:42
import django.db.models.deletion
import pgtrigger.compiler
import pgtrigger.migrations
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("django_cloudflareimages_toolkit", "0001_initial"),
("parks", "0011_remove_park_insert_insert_remove_park_update_update_and_more"),
]
operations = [
pgtrigger.migrations.RemoveTrigger(
model_name="parkphoto",
name="insert_insert",
),
pgtrigger.migrations.RemoveTrigger(
model_name="parkphoto",
name="update_update",
),
migrations.AlterField(
model_name="parkphoto",
name="image",
field=models.ForeignKey(
help_text="Park photo stored on Cloudflare Images",
on_delete=django.db.models.deletion.CASCADE,
to="django_cloudflareimages_toolkit.cloudflareimage",
),
),
migrations.AlterField(
model_name="parkphotoevent",
name="image",
field=models.ForeignKey(
db_constraint=False,
help_text="Park photo stored on Cloudflare Images",
on_delete=django.db.models.deletion.DO_NOTHING,
related_name="+",
related_query_name="+",
to="django_cloudflareimages_toolkit.cloudflareimage",
),
),
pgtrigger.migrations.AddTrigger(
model_name="parkphoto",
trigger=pgtrigger.compiler.Trigger(
name="insert_insert",
sql=pgtrigger.compiler.UpsertTriggerSql(
func='INSERT INTO "parks_parkphotoevent" ("alt_text", "caption", "created_at", "date_taken", "id", "image_id", "is_approved", "is_primary", "park_id", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "updated_at", "uploaded_by_id") VALUES (NEW."alt_text", NEW."caption", NEW."created_at", NEW."date_taken", NEW."id", NEW."image_id", NEW."is_approved", NEW."is_primary", NEW."park_id", _pgh_attach_context(), NOW(), \'insert\', NEW."id", NEW."updated_at", NEW."uploaded_by_id"); RETURN NULL;',
hash="403652164d3e615dae5a14052a56db2851c5cf05",
operation="INSERT",
pgid="pgtrigger_insert_insert_e2033",
table="parks_parkphoto",
when="AFTER",
),
),
),
pgtrigger.migrations.AddTrigger(
model_name="parkphoto",
trigger=pgtrigger.compiler.Trigger(
name="update_update",
sql=pgtrigger.compiler.UpsertTriggerSql(
condition="WHEN (OLD.* IS DISTINCT FROM NEW.*)",
func='INSERT INTO "parks_parkphotoevent" ("alt_text", "caption", "created_at", "date_taken", "id", "image_id", "is_approved", "is_primary", "park_id", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "updated_at", "uploaded_by_id") VALUES (NEW."alt_text", NEW."caption", NEW."created_at", NEW."date_taken", NEW."id", NEW."image_id", NEW."is_approved", NEW."is_primary", NEW."park_id", _pgh_attach_context(), NOW(), \'update\', NEW."id", NEW."updated_at", NEW."uploaded_by_id"); RETURN NULL;',
hash="29c60ad09c570b8c03ad6c17052a8f9874314895",
operation="UPDATE",
pgid="pgtrigger_update_update_42711",
table="parks_parkphoto",
when="AFTER",
),
),
),
]