Updates split - #26
Merged
Merged
Conversation
Bumps Next.js, React, Radix, Tailwind and related tooling, and updates the two dynamic route handlers whose `params` now arrive as a Promise under Next.js 15's async params API. Also fixes UploadButton's prop typing, which no longer compiles against the updated React 19 types.
Configures Vitest with happy-dom, the @ path alias, and Cloudinary test env vars so subsequent commits can ship unit tests alongside their source changes.
Introduces lib/cloudinary-server.ts as the single place that configures the Cloudinary SDK (previously duplicated across every route), and adds pagination support (maxResults/nextCursor) to getResourcesByTag.
Adds isAllowedCloudinaryUrl/validateRemoteUploadUrl to restrict remote uploads to Cloudinary delivery URLs, and isReadOnlyMode/isDemoUploadAllowed/assertUploadAllowed to support a read-only deployment mode with an optional demo-upload carve-out.
Removes the duplicated cloudinary.config() calls in favor of getCloudinary(), and gates deletes behind isReadOnlyMode().
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Adds validateParamsToSign to allowlist which upload params clients may request a signature for, enforcing the expected folder and required tags before the server signs anything.
Wires validateParamsToSign into the signing endpoint and blocks signing entirely when the app is in read-only mode.
Applies assertUploadAllowed and validateRemoteUploadUrl to the server-side upload route so it respects read-only/demo mode and only accepts Cloudinary-hosted source URLs.
getColorPop used to always send skip-check=true so the demo could preview the effect; it now only does so when NEXT_PUBLIC_PHOTOCRATE_DEMO_MODE is enabled, matching the isDemoUploadAllowed() guard added in the previous commits.
Accepts cursor/limit query params and forwards them to getResourcesByTag, replacing the previous fixed 400-result fetch.
Adds a small IntersectionObserver-based hook that triggers onLoadMore when a sentinel element scrolls into view.
Centralizes the width/height/crop/loading configs used for gallery thumbnails, the media viewer, creation previews, and filter previews, plus a hashTransformations helper for stable React keys.
useResources now drives a useInfiniteQuery against the paginated /api/resources endpoint instead of a single fixed-size fetch, and MediaGallery uses useInfiniteScroll plus getThumbnailPreset/ getCreationPreviewPreset to load further pages and render images.
Replaces inline width/height/crop configs with getViewerPreset/ getFilterPreviewPreset, memoizes the outside-click handler, fixes the edit sheet's default tab and close-on-click exclusion for the management navbar, and adds a render test for the Enhance tab.
CldImage now respects explicit loading/priority props instead of always lazy-loading, and Container forwards arbitrary HTML attributes (e.g. data-* props) to its wrapping div.
…ured Avoids calling getCldOgImageUrl (which requires a configured Cloudinary client) when isCloudinaryConfigured() is false, and marks decorative sidebar icons aria-hidden.
Runs npm test, lint, and build on pushes and PRs against main with Cloudinary env vars stubbed out.
Documents the new read-only/demo-mode env vars and asset tag overrides in .env.example, refreshes README.md, and adds GITHUB_METADATA.md and VERIFICATION.md.
Collaborator
Author
|
Hi @eportis-cloudinary, I have split this into 18 commits. You can click on each commit and see the changed files. A number of these commits are tests, updates to outdated libararies, updates to Cloudinary syntaxes, and refactoring code . |
UploadButton invalidated the ['resources', undefined] query while every gallery reads from ['resources', <tag>], so new uploads never appeared without a full reload. Scope it to libraryTag, which is what uploads are always tagged with. The tags/update route was missing the isReadOnlyMode() guard that delete, upload, and sign-cloudinary-params all received in this PR, allowing tag mutations (favorite/trash/restore) in read-only demo mode. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
validateParamsToSign silently defaulted resource_type to 'image' when the widget's paramsToSign omitted it, so we signed a string containing resource_type=image. The widget's actual upload request never sends resource_type as a signed field (it only selects the API endpoint), so Cloudinary reconstructs a different string and rejects every signature. Verified by reproducing the exact signature computation against a live sign-cloudinary-params call before and after the fix. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Princesso
added a commit
that referenced
this pull request
Sep 9, 2026
* 01: chore: upgrade Next.js 15, React 19, and dependencies Bumps Next.js, React, Radix, Tailwind and related tooling, and updates the two dynamic route handlers whose `params` now arrive as a Promise under Next.js 15's async params API. Also fixes UploadButton's prop typing, which no longer compiles against the updated React 19 types. * 02: test: add Vitest testing infrastructure Configures Vitest with happy-dom, the @ path alias, and Cloudinary test env vars so subsequent commits can ship unit tests alongside their source changes. * 03: refactor: centralize Cloudinary client configuration Introduces lib/cloudinary-server.ts as the single place that configures the Cloudinary SDK (previously duplicated across every route), and adds pagination support (maxResults/nextCursor) to getResourcesByTag. * 04: feat: add upload validation and read-only/demo mode guards Adds isAllowedCloudinaryUrl/validateRemoteUploadUrl to restrict remote uploads to Cloudinary delivery URLs, and isReadOnlyMode/isDemoUploadAllowed/assertUploadAllowed to support a read-only deployment mode with an optional demo-upload carve-out. * 05: refactor: use shared Cloudinary client in delete/tags routes Removes the duplicated cloudinary.config() calls in favor of getCloudinary(), and gates deletes behind isReadOnlyMode(). * 06: feat: add signed-upload parameter validation Adds validateParamsToSign to allowlist which upload params clients may request a signature for, enforcing the expected folder and required tags before the server signs anything. * 07: refactor: validate params in sign-cloudinary-params route Wires validateParamsToSign into the signing endpoint and blocks signing entirely when the app is in read-only mode. * 08: refactor: harden upload route with validation guards Applies assertUploadAllowed and validateRemoteUploadUrl to the server-side upload route so it respects read-only/demo mode and only accepts Cloudinary-hosted source URLs. * 09: fix: gate Color Pop demo skip-check behind demo mode flag getColorPop used to always send skip-check=true so the demo could preview the effect; it now only does so when NEXT_PUBLIC_PHOTOCRATE_DEMO_MODE is enabled, matching the isDemoUploadAllowed() guard added in the previous commits. * 10: feat: paginate the resources list API Accepts cursor/limit query params and forwards them to getResourcesByTag, replacing the previous fixed 400-result fetch. * 11: feat: add useInfiniteScroll hook Adds a small IntersectionObserver-based hook that triggers onLoadMore when a sentinel element scrolls into view. * 12: feat: add shared image delivery presets Centralizes the width/height/crop/loading configs used for gallery thumbnails, the media viewer, creation previews, and filter previews, plus a hashTransformations helper for stable React keys. * 13: refactor: wire infinite scroll and pagination into the gallery useResources now drives a useInfiniteQuery against the paginated /api/resources endpoint instead of a single fixed-size fetch, and MediaGallery uses useInfiniteScroll plus getThumbnailPreset/ getCreationPreviewPreset to load further pages and render images. * 14: refactor: use delivery presets in MediaViewer Replaces inline width/height/crop configs with getViewerPreset/ getFilterPreviewPreset, memoizes the outside-click handler, fixes the edit sheet's default tab and close-on-click exclusion for the management navbar, and adds a render test for the Enhance tab. * 15: refactor: small component polish CldImage now respects explicit loading/priority props instead of always lazy-loading, and Container forwards arbitrary HTML attributes (e.g. data-* props) to its wrapping div. * 16: feat: fall back to a static OG image when Cloudinary isn't configured Avoids calling getCldOgImageUrl (which requires a configured Cloudinary client) when isCloudinaryConfigured() is false, and marks decorative sidebar icons aria-hidden. * 17: chore: add CI workflow Runs npm test, lint, and build on pushes and PRs against main with Cloudinary env vars stubbed out. * 18: docs: update README, add repo metadata/verification docs Documents the new read-only/demo-mode env vars and asset tag overrides in .env.example, refreshes README.md, and adds GITHUB_METADATA.md and VERIFICATION.md. * Fix upload cache invalidation and add missing read-only guard UploadButton invalidated the ['resources', undefined] query while every gallery reads from ['resources', <tag>], so new uploads never appeared without a full reload. Scope it to libraryTag, which is what uploads are always tagged with. The tags/update route was missing the isReadOnlyMode() guard that delete, upload, and sign-cloudinary-params all received in this PR, allowing tag mutations (favorite/trash/restore) in read-only demo mode. * Fix Invalid Signature error on every upload validateParamsToSign silently defaulted resource_type to 'image' when the widget's paramsToSign omitted it, so we signed a string containing resource_type=image. The widget's actual upload request never sends resource_type as a signed field (it only selects the API endpoint), so Cloudinary reconstructs a different string and rejects every signature. Verified by reproducing the exact signature computation against a live sign-cloudinary-params call before and after the fix. --------- Co-authored-by: jlooper-cloudinary <jen.looper@cloudinary.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.