Skip to content

Fix profile picture disappearing after upload (possible R2 presigned URL expiration) #34

@amoscicki

Description

@amoscicki

Summary

Uploaded profile pictures disappear after approximately 1 hour due to R2 presigned URL expiration. The presigned URL is stored directly in the database instead of generating it on-demand.

Root Cause

In src/utils/storage/r2.ts (lines 101, 113), presigned URLs are configured to expire in 1 hour:

expiresIn: 3600 // 1 hour

The upload flow in src/fn/profiles.ts (lines 73-99) returns both:

  • imageKey - the permanent R2 path (profiles/{userId}/{timestamp}-{fileName})
  • imageUrl - the presigned URL (expires in 1 hour)

In src/routes/profile/edit.tsx (lines 234-239), both are saved to the database:

updateProfileMutation({ imageId: imageKey, image: imageUrl })

The image field (presigned URL) is then used for display, which breaks after 1 hour.

Steps to Reproduce

  1. Go to profile edit page (/profile/edit)
  2. Upload a profile picture
  3. Confirm the picture displays correctly
  4. Wait ~1 hour
  5. Refresh the page - image is broken

Current Behavior

Profile picture displays initially but returns 403/broken after presigned URL expires (~1 hour).

Expected Behavior

Profile picture should persist indefinitely after upload.

Proposed Solution

The imageId (R2 key) is already stored correctly. The fix should:

  1. Generate presigned URLs on-demand when rendering profile images
  2. Or use public bucket URLs if images don't require access control
  3. Update all places that read profile.image to generate fresh URLs from profile.imageId

Affected Areas

  • Routes: /src/routes/profile/edit.tsx (upload handling)
  • Routes: /src/routes/profile/$userId.tsx (profile display)
  • Routes: /src/routes/members.tsx (user list avatars)
  • Server Functions: /src/fn/profiles.ts (presigned URL generation)
  • Storage: /src/utils/storage/r2.ts (1-hour expiry config)
  • Database: /src/db/schema.ts - profiles table stores both image and imageId

Acceptance Criteria

  • Profile pictures persist after upload indefinitely
  • Images load correctly after hours/days
  • Member list avatars don't break over time
  • Use imageId to generate fresh presigned URLs on-demand (or switch to public URLs)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions