ProjectR Docs
Backend (Convex)

API — storage

Generated catalog of Convex functions in convex/storage/. Regenerated by `bun run docs:api`.

Auto-generated from source. Do not edit by hand — run bun run docs:api.

query (3)

FunctionDescriptionArgs
getUploadConfigGet upload config Returns upload configuration for a category. Clients use this to validate files before uploading.{ category: v.union( v.literal("avatar"), v.literal("avatarCatalog"), v.literal("review"), v.literal("carousel"), v.literal("campaign"), v.literal("productGr…
listAllMediaList all media files (Dashboard — Media Library Picker) Returns all image files from R2 regardless of key format. Handles both category-prefixed keys (carousel/...) and UUID keys. Excludes non-image files (exports, APKs, etc.).{ limit: v.optional(v.number()), }
listFilesByCategoryList files by category (Dashboard) Lists all files in a category. Requires dashboard access.{ category: v.union( v.literal("avatar"), v.literal("avatarCatalog"), v.literal("review"), v.literal("carousel"), v.literal("campaign"), v.literal("productGr…

mutation (3)

FunctionDescriptionArgs
deleteFileDelete a file from R2 Removes a file by its R2 key. Requires authentication.{ key: v.string(), }
generateUploadUrlGenerate a signed PUT URL, and record who it was minted for. category is required. The component's own generateUploadUrl (and useUploadFile, which calls it with no arguments) mints a bare-UUID key with no role, MIME or size gate, so this mutation never falls back to it. Every mint is a {category}/{userId}/{uuid}_{file} key behind the role gate, the MIME gate, the size gate, and the truth gate later on. filename, contentType and size stay optional because the gates only judge what the client declares.{ category: categoryValidator, filename: v.optional(v.string()), contentType: v.optional(v.string()), size: v.optional(v.number()), }
updateAvatarUpdate user avatar Updates the user's avatar URL after uploading a new image to R2. The r2Key is stored in the profile - use getFileUrl to get a signed URL.{ r2Key: v.string(), }

internalMutation (1)

FunctionDescriptionArgs
purgeExpiredUploadSessionsPurge aged-out R2 upload mint records. A row in r2UploadSessions only has to outlive one upload: mint the signed PUT, PUT the bytes, sync the metadata, attach the key. A week covers a browser tab left open over a weekend. After that the profile row itself is the proof of ownership (see updateAvatar's second branch), so the mint record is dead weight and the table would grow forever without this. Batched the way the checkout-session sweep is: bounded .take, self-limiting, safe to run again.{ limit: v.optional(v.number()), }

On this page