The cleanup printed one number for expired owners, the count of
workspaces it found, so zero meant either that nobody had passed the
grace period or that everyone who had owns nothing. The first is normal
and the second means media is held alive by rows the cleanup cannot
reach, and telling them apart took a hand-written query against
production. Both counts are reported now.
Bunny and R2 results were also discarded. The workspace row is deleted
first, so a refused storage delete leaves media that nothing points at,
and nothing recorded that it happened. logCleanupWarnings already exists
for this and is now called with the per-workspace result.
Two problems with running these unattended, both found while wiring the Bunny
cleanup up to a Coolify scheduled task against production.
A dry run reported a count and nothing else. "Orphaned: 31" is not something
anyone can approve: it says how many objects would go, never which. Both scripts
now list every orphan they would delete, and print the same list when deleting,
so a real run is auditable afterwards too.
Each line carries who the object belongs to, as far as each provider can answer:
- R2 reads the owner out of `videoUploadSession`, which keeps `objectKey`
alongside the initiating and billed user and survives an upload that never
became a video. That is the case producing orphans, so this is an answer
rather than a guess.
- Bunny has no equivalent. `bunny-init` sends the provider a title and nothing
else, and an orphan by definition has no row pointing at it, so there is
nothing authoritative to look up. The title is matched against titles still in
the database instead, which catches the common shape (a version upload that
failed and was retried successfully leaves a live row with the same title).
A hit prints as "possibly", because it is a hint.
The grace periods were also too short to be safe:
- Bunny counted a video abandoned after 24 hours.
- R2 counted an object abandoned after 15 minutes, which is shorter than a slow
multipart upload of a large file. An object still being written, or written
but not yet finalised into a row, looked abandoned and could be deleted out
from under the upload creating it.
Both are seven days now: long enough that no upload, retry or delayed
finalisation can still be in flight.
The audio proxy resolved ownership only through voice comments, so
R2_AUDIO video assets (which store the same proxy path in
videoAsset.sourceUrl) always got 403s. Resolve ownership the way the
image proxy does: query comments and video assets, merge into a
unique-owning-video map, deny on ambiguity.
r2-orphan-cleanup marked videoAsset.sourceUrl as referenced but not
videoAsset.thumbnailUrl, so every R2_VIDEO asset thumbnail older than
the TTL was deleted as an orphan. Widen the query to both columns.
Co-Authored-By: Claude Fable 5 <[email protected]>
- Added support for self-hosted S3 video uploads with new environment variables: OPENFRAME_ENABLE_S3_VIDEO_UPLOADS and OPENFRAME_MAX_VIDEO_UPLOAD_BYTES.
- Updated .env.example and .env.docker.example to reflect new configuration options.
- Enhanced Content Security Policy to include origins for S3-compatible storage.
- Updated dependencies for AWS SDK to support new features.
- Refactored upload logic to accommodate both Bunny and S3 upload providers.
- Updated documentation to clarify the usage of direct uploads and S3 configurations.
- Closes#11
- Introduced a new logger utility (`logError`) to standardize error logging.
- Replaced all instances of `console.error` with `logError` in various API routes and libraries.
- Enhanced error logging to sanitize sensitive information, particularly for Prisma and Stripe errors.
- Ensured consistent error handling and logging practices throughout the codebase.
- Added billing-related fields to the User model in the database.
- Implemented functions for managing billing access, including trial periods and subscription statuses.
- Created new billing utility functions for Stripe integration.
- Updated onboarding page to include billing overview and workspace creation eligibility.
- Enhanced route access checks to require billing access for certain actions.
- Implemented cleanup scripts for expired billing workspaces and associated media.
- Updated header component to conditionally show app navigation based on billing access.
- Added new migrations for billing-related database changes.