labi1240 / project101

0 stars 0 forks source link

Sweep: Please setup the cloudinary for uploading the images in nextjs14 #17

Closed labi1240 closed 6 months ago

labi1240 commented 6 months ago

for example i have added these components which was shared their boildercode for nextjs app router components. I have setup the route.ts files for login, signup, me, logout, verifyemail in api folder you can check by route.ts file extension. I need your help to setup a update/route.ts file for me similar to my setup route.ts files which are provide me crud operations funcnality using mongoose. I want to add the image upload feature when user signup and directed to profile page there or i have 1 more account page (Account-page) you can setup the cloudinary over there too.

import React, { useState, useCallback, forwardRef, SyntheticEvent } from 'react'; import Image, { ImageProps } from 'next/image'; import { getTransformations } from '@cloudinary-util/util'; import { transformationPlugins } from '@cloudinary-util/url-loader'; import type { ImageOptions, ConfigOptions } from '@cloudinary-util/url-loader';

import { pollForProcessingImage } from '../../lib/cloudinary'; import { getCldImageUrl } from '../../helpers/getCldImageUrl';

import { cloudinaryLoader } from '../../loaders/cloudinary-loader';

export type CldImageProps = Omit<ImageProps, 'src' | 'quality'> & ImageOptions & { config?: ConfigOptions; preserveTransformations?: boolean; src: string; unoptimized?: boolean; };

const CldImage = forwardRef<HTMLImageElement, CldImageProps>(function CldImage(props, ref) { let hasThrownError = false;

const CLD_OPTIONS = [ 'deliveryType', 'preserveTransformations', 'strictTransformations', 'assetType', ];

transformationPlugins.forEach(({ props }: { props: Record<string, unknown> }) => { const pluginProps = Object.keys(props); pluginProps.forEach(prop => { if ( CLD_OPTIONS.includes(prop) ) { throw new Error(Option ${prop} already exists!); } CLD_OPTIONS.push(prop); }); });

// Construct the base Image component props by filtering out Cloudinary-specific props

const imageProps: ImageProps = { alt: props.alt, src: props.src, };

(Object.keys(props) as Array) .filter(key => typeof key === 'string' && !CLD_OPTIONS.includes(key)) .forEach(key => imageProps[key as keyof ImageProps] = props[key]);

const defaultImgKey = (Object.keys(imageProps) as Array).map(key => ${key}:${imageProps[key]}).join(';'); const [imgKey, setImgKey] = useState(defaultImgKey);

// Construct Cloudinary-specific props by looking for values for any of the supported prop keys

type CldOptions = Omit<ImageOptions, 'src'>;

const cldOptions: CldOptions = {};

CLD_OPTIONS.forEach((key) => { const prop = props[key as keyof ImageOptions]; if ( prop ) { cldOptions[key as keyof CldOptions] = prop || undefined; } });

// Try to preserve the original transformations from the Cloudinary URL passed in // to the component. This only works if the URL has a version number on it and otherwise // will fail to load

if (props.preserveTransformations) { try { const transformations = getTransformations(props.src).map(t => t.join(',')); cldOptions.rawTransformations = [...transformations.flat(), ...(props.rawTransformations || [])]; } catch(e) { console.warn(Failed to preserve transformations: ${(e as Error).message}) } }

// The unoptimized flag is intended to remove all optimizations including quality, format, and sizing // via responsive sizing. When passing this in, it also prevents the loader from running, thus // breaking this component. This rewrites the src to construct a fully formed Cloudinary URL // that also disables format and quality transformations, to deliver it as unoptimized // See about unoptimized not working with loader: https://github.com/vercel/next.js/issues/50764

const IMAGE_OPTIONS: { unoptimized?: boolean } = (process.env.__NEXT_IMAGE_OPTS || {}) as unknown as object;

if ( props.unoptimized === true || IMAGE_OPTIONS?.unoptimized === true ) { imageProps.src = getCldImageUrl({ ...cldOptions, width: imageProps.width, height: imageProps.height, src: imageProps.src as string, format: 'default', quality: 'default', }, props.config); }

/**

export default CldImage; export { default } from './CldImage'; export type { CldImageProps } from './CldImage'; import React from 'react'; import Head from 'next/head';

import { CldImageProps } from '../CldImage/CldImage'; import { getCldOgImageUrl } from '../../helpers/getCldOgImageUrl'; import { OG_IMAGE_WIDTH, OG_IMAGE_HEIGHT } from '../../constants/sizes';

const TWITTER_CARD = 'summary_large_image';

export type CldOgImageProps = CldImageProps & { excludeTags?: Array; keys?: object; twitterTitle?: string; }

const CldOgImage = ({ excludeTags = [], twitterTitle, keys = {}, ...props }: CldOgImageProps) => { const { alt } = props;

// We need to separately handle the width and the height to allow our user to pass in // a custom value, but also we need to know this at the component level so that we can // use it when rendering the meta tags

let { width = OG_IMAGE_WIDTH, height = OG_IMAGE_HEIGHT } = props;

// Normalize the width and height

width = typeof width === 'string' ? parseInt(width) : width; height = typeof height === 'string' ? parseInt(height) : height;

// Render the final URLs. We use two different format versions to deliver // webp for Twitter as it supports it (and we can control with tags) where // other platforms may not support webp, so we deliver jpg

const ogImageUrl = getCldOgImageUrl({ ...props, width, height });

const twitterImageUrl = getCldOgImageUrl({ ...props, width, height, format: props.format || 'webp', });

const metaKeys = { 'og:image': 'og-image', 'og:image:secure_url': 'og-image-secureurl', 'og:image:width': 'og-image-width', 'og:image:height': 'og-image-height', 'og:image:alt': 'og-image-alt', 'twitter:title': 'twitter-title', 'twitter:card': 'twitter-card', 'twitter:image': 'twitter-image', ...keys }

// We need to include the tags within the Next.js Head component rather than // direcly adding them inside of the Head otherwise we get unexpected results

return (
<Head>
  <meta key={metaKeys['og:image']} property="og:image" content={ogImageUrl} />
  <meta key={metaKeys['og:image:secure_url']} property="og:image:secure_url" content={ogImageUrl} />
  <meta key={metaKeys['og:image:width']} property="og:image:width" content={`${width}`} />
  <meta key={metaKeys['og:image:height']} property="og:image:height" content={`${height}`} />

  {alt && (
    <meta key={metaKeys['og:image:alt']} property="og:image:alt" content={alt} />
  )}

  {/* Required for summary_large_image, exclude vai excludeTags */}
  {/* https://developer.twitter.com/en/docs/twitter-for-websites/cards/overview/summary-card-with-large-image */}

  {!excludeTags.includes('twitter:title') && (
    <meta key={metaKeys['twitter:title']} property="twitter:title" content={twitterTitle || ' '} />
  )}

  <meta key={metaKeys['twitter:card']} property="twitter:card" content={TWITTER_CARD} />
  <meta key={metaKeys['twitter:image']} property="twitter:image" content={twitterImageUrl} />
</Head>

); }

export default CldOgImage; export { default } from './CldOgImage'; export type { CldOgImageProps } from './CldOgImage'; import React from 'react'; import CldUploadWidget, { CldUploadWidgetProps } from '../CldUploadWidget';

export interface CldUploadButtonProps extends Omit<CldUploadWidgetProps, 'children'> { className?: string; children?: JSX.Element | string | Array<JSX.Element|string>; onClick?: Function; }

const CldUploadButton = ({ className, children, onClick, onError, onOpen, onUpload, onAbort, onBatchCancelled, onClose, onDisplayChanged, onPublicId, onQueuesEnd, onQueuesStart, onRetry, onShowCompleted, onSourceChanged, onSuccess, onTags, onUploadAdded, options, signatureEndpoint, uploadPreset, ...props }: CldUploadButtonProps) => {

return ( <> <CldUploadWidget onError={onError} onOpen={onOpen} onUpload={onUpload} onAbort={onAbort} onBatchCancelled={onBatchCancelled} onClose={onClose} onDisplayChanged={onDisplayChanged} onPublicId={onPublicId} onQueuesEnd={onQueuesEnd} onQueuesStart={onQueuesStart} onRetry={onRetry} onShowCompleted={onShowCompleted} onSourceChanged={onSourceChanged} onSuccess={onSuccess} onTags={onTags} onUploadAdded={onUploadAdded} options={options} signatureEndpoint={signatureEndpoint} uploadPreset={uploadPreset}

{({ open, isLoading }) => { function handleOnClick(e: React.MouseEvent<HTMLButtonElement, MouseEvent>) { e.preventDefault();

        open();

        if ( typeof onClick === 'function' ) {
          onClick(e);
        }
      }
      return (
        <button {...props} className={className || ''} onClick={handleOnClick} disabled={isLoading} >
          {children || 'Upload'}
        </button>
      );
    }}
  </CldUploadWidget>
</>

); };

export default CldUploadButton;export { default } from './CldUploadButton'; export type { CldUploadButtonProps } from './CldUploadButton';import React, { useState, useEffect, useRef } from 'react'; import Script from 'next/script'; import { CloudinaryUploadWidgetResults, CloudinaryUploadWidgetInstanceMethods, CloudinaryUploadWidgetInstanceMethodCloseOptions, CloudinaryUploadWidgetInstanceMethodDestroyOptions, CloudinaryUploadWidgetInstanceMethodOpenOptions, CloudinaryUploadWidgetSources, CloudinaryUploadWidgetError } from '@cloudinary-util/types';

import { triggerOnIdle } from '../../lib/util';

import { CldUploadEventCallback, CldUploadWidgetCloudinaryInstance, CldUploadWidgetProps, CldUploadWidgetWidgetInstance, } from './CldUploadWidget.types';

import {checkForCloudName} from "../../lib/cloudinary";

const WIDGET_WATCHED_EVENTS = [ 'success', ];

const WIDGET_EVENTS: { [key: string]: string } = { 'abort': 'onAbort', 'batch-cancelled': 'onBatchCancelled', 'close': 'onClose', 'display-changed': 'onDisplayChanged', 'publicid': 'onPublicId', 'queues-end': 'onQueuesEnd', 'queues-start': 'onQueuesStart', 'retry': 'onRetry', 'show-completed': 'onShowCompleted', 'source-changed': 'onSourceChanged', 'success': 'onSuccess', 'tags': 'onTags', 'upload-added': 'onUploadAdded', }

const CldUploadWidget = ({ children, onError, onOpen, onUpload, options, signatureEndpoint, uploadPreset, ...props }: CldUploadWidgetProps) => { const cloudinary: CldUploadWidgetCloudinaryInstance = useRef(); const widget: CldUploadWidgetWidgetInstance = useRef();

const signed = !!signatureEndpoint;

const [error, setError] = useState<CloudinaryUploadWidgetError | undefined>(undefined); const [results, setResults] = useState<CloudinaryUploadWidgetResults | undefined>(undefined); const [isScriptLoading, setIsScriptLoading] = useState(true);

// When creating a signed upload, you need to provide both your Cloudinary API Key // as well as a signature generator function that will sign any paramters // either on page load or during the upload process. Read more about signed uploads at: // https://cloudinary.com/documentation/upload_widget#signed_uploads

const uploadOptions = { cloudName: process.env.NEXT_PUBLIC_CLOUDINARY_CLOUD_NAME, uploadPreset: uploadPreset || process.env.NEXT_PUBLIC_CLOUDINARY_UPLOAD_PRESET, apiKey: process.env.NEXT_PUBLIC_CLOUDINARY_API_KEY, ...options, };

//Check if Cloud Name exists checkForCloudName(process.env.NEXT_PUBLIC_CLOUDINARY_CLOUD_NAME);

if ( signed ) { uploadOptions.uploadSignature = generateSignature;

if (!uploadOptions.apiKey) {
  console.warn(`Missing dependency: Signed Upload requires an API key`);
}

}

// Handle result states and callbacks

useEffect(() => { if ( typeof results === 'undefined' ) return;

const isSuccess = results.event === 'success';

if ( isSuccess && typeof onUpload === 'function' ) {
  if ( process.env.NODE_ENVIRONMENT === 'development' ) {
    console.warn('The onUpload callback is deprecated. Please use onSuccess instead.');
  }
  onUpload(results, widget.current);
}

}, [results])

/**

export default CldUploadWidget;import { CloudinaryUploadWidgetOptions, CloudinaryUploadWidgetResults, CloudinaryUploadWidgetInstanceMethods, CloudinaryUploadWidgetError } from '@cloudinary-util/types';

export type CldUploadWidgetCloudinaryInstance = any; export type CldUploadWidgetWidgetInstance = any;

export interface CldUploadWidgetProps { children?: ({ cloudinary, widget, open, results, error }: CldUploadWidgetPropsChildren) => JSX.Element; onError?: CldUploadEventCallbackError; onOpen?: CldUploadEventCallbackWidgetOnly; /**

export type CldUploadWidgetPropsChildren = { cloudinary: CldUploadWidgetCloudinaryInstance; widget: CldUploadWidgetWidgetInstance; error?: CloudinaryUploadWidgetError; isLoading?: boolean; results?: CloudinaryUploadWidgetResults; } & CloudinaryUploadWidgetInstanceMethods;

export type CldUploadEventCallback = (results: CloudinaryUploadWidgetResults, widget: CldUploadEventCallbackWidget) => void; export type CldUploadEventCallbackNoOptions = (results: CloudinaryUploadWidgetResults, widget: CldUploadWidgetWidgetInstance) => void; export type CldUploadEventCallbackWidgetOnly = (widget: CldUploadWidgetWidgetInstance) => void; export type CldUploadEventCallbackError = (error: CloudinaryUploadWidgetError, widget: CldUploadEventCallbackWidget) => void;

export type CldUploadEventCallbackWidget = { widget: CldUploadWidgetWidgetInstance; } & CloudinaryUploadWidgetInstanceMethods;export { default } from './CldUploadWidget'; export type { CldUploadWidgetProps, CldUploadWidgetPropsChildren } from './CldUploadWidget.types';import nextPkg from 'next/package.json'; import pkg from '../../package.json';

export const NEXT_CLOUDINARY_ANALYTICS_PRODUCT_ID = 'A'; export const NEXT_CLOUDINARY_ANALYTICS_ID = 'V'; export const NEXT_VERSION = normalizeVersion(nextPkg.version); export const NEXT_CLOUDINARY_VERSION = normalizeVersion(pkg.version);

function normalizeVersion(version: string) { let normalized = version; if ( normalized.includes('-') ) { normalized = normalized.split('-')[0]; } return normalized; }export const OG_IMAGE_WIDTH = 1200; export const OG_IMAGE_HEIGHT = 627; helpers import { constructCloudinaryUrl } from '@cloudinary-util/url-loader'; import type { ImageOptions, ConfigOptions, AnalyticsOptions } from '@cloudinary-util/url-loader';

import { NEXT_CLOUDINARY_ANALYTICS_PRODUCT_ID, NEXT_CLOUDINARY_ANALYTICS_ID, NEXT_CLOUDINARY_VERSION, NEXT_VERSION } from '../constants/analytics'; import {checkForCloudName} from "../lib/cloudinary";

/**

export type GetCldImageUrlOptions = ImageOptions; export type GetCldImageUrlConfig = ConfigOptions; export type GetCldImageUrlAnalytics = AnalyticsOptions;

export function getCldImageUrl(options: GetCldImageUrlOptions, config?: GetCldImageUrlConfig, analytics?: GetCldImageUrlAnalytics) { // @ts-expect-error Property 'cloud' does not exist on type 'CloudinaryConfigurationOptions'. const cloudName = config?.cloud?.cloudName ?? process.env.NEXT_PUBLIC_CLOUDINARY_CLOUD_NAME; checkForCloudName(cloudName); return constructCloudinaryUrl({ options, config: Object.assign({ cloud: { cloudName: cloudName }, }, config), analytics: Object.assign({ product: NEXT_CLOUDINARY_ANALYTICS_PRODUCT_ID, sdkCode: NEXT_CLOUDINARY_ANALYTICS_ID, sdkSemver: NEXT_CLOUDINARY_VERSION, techVersion: NEXT_VERSION, feature: '' }, analytics) }); }import { OG_IMAGE_WIDTH, OG_IMAGE_HEIGHT } from '../constants/sizes';

import { getCldImageUrl } from './getCldImageUrl'; import type { GetCldImageUrlOptions } from './getCldImageUrl';

/**

export type GetCldOgImageUrlOptions = GetCldImageUrlOptions;

export function getCldOgImageUrl(options: GetCldOgImageUrlOptions) { return getCldImageUrl({ ...options, format: options.format || 'jpg', width: options.width || OG_IMAGE_WIDTH, height: options.height || OG_IMAGE_HEIGHT, crop: options.crop || { type: 'fill', gravity: 'center', source: true } }); }/**

export interface PollForProcessingImageOptions { src: string; }

export async function pollForProcessingImage(options: PollForProcessingImageOptions): Promise { const { src } = options; try { await new Promise((resolve, reject) => { fetch(src).then(res => { if ( !res.ok ) { reject(res); return; } resolve(res); }); }); } catch(e: any) { // Timeout for 200ms before trying to fetch again to avoid overwhelming requests

if ( e.status === 423 ) {
  await new Promise((resolve) => setTimeout(() => resolve(undefined), 200));
  return await pollForProcessingImage(options);
}
return false;

} return true; }

export function checkForCloudName(cloudName: string | undefined) { if (!cloudName) { throw new Error('A Cloudinary Cloud name is required, please make sure NEXT_PUBLIC_CLOUDINARY_CLOUD_NAME is set and configured in your environment.'); } }/**

export function triggerOnIdle(callback: any) { if ( window && 'requestIdleCallback' in window ) { return requestIdleCallback(callback); } return setTimeout(() => callback(), 1); }import { ImageProps } from 'next/image';

import { getCldImageUrl } from '../helpers/getCldImageUrl';

export interface CloudinaryLoaderCldOptions { }

export interface CloudinaryLoaderLoaderOptions { height?: string | number; width?: string | number; }

export interface CloudinaryLoader { loaderOptions: CloudinaryLoaderLoaderOptions; imageProps: ImageProps; cldOptions: CloudinaryLoaderCldOptions; cldConfig?: object; }

export function cloudinaryLoader({ loaderOptions, imageProps, cldOptions, cldConfig = {} }: CloudinaryLoader) { const options = { ...imageProps, ...cldOptions }

options.width = typeof options.width === 'string' ? parseInt(options.width) : options.width; options.height = typeof options.height === 'string' ? parseInt(options.height) : options.height;

// // The loader options are used to create dynamic sizing when working with responsive images // // so these should override the default options collected from the props alone if // // the results are different. While we don't always use the height in the loader logic, // // we always pass it here, as the usage is determined later based on cropping.js

if ( typeof loaderOptions?.width === 'number' && typeof options.width === 'number' && loaderOptions.width !== options.width ) { const multiplier = loaderOptions.width / options.width;

options.width = loaderOptions.width;

// The height may change based off of the value passed through via the loader options
// In an example where the user sizes is 800x600, but the loader is passing in 400
// due to responsive sizing, we want to ensure we're using a height that will
// resolve to the same aspect ratio

if ( typeof options.height === 'number' ) {
  options.height = Math.floor(options.height * multiplier);
}

} else if ( typeof loaderOptions?.width === 'number' && typeof options?.width !== 'number' ) { // If we don't have a width on the options object, this may mean that the component is using // the fill option: https://nextjs.org/docs/pages/api-reference/components/image#fill // The Fill option does not allow someone to pass in a width or a height // If this is the case, we still need to define a width for sizing optimization but also // for responsive sizing to take effect, so we can utilize the loader width for the base width options.width = loaderOptions?.width; }

// @ts-ignore return getCldImageUrl(options, cldConfig); }import { ImageProps } from 'next/image';

import { getCldImageUrl } from '../helpers/getCldImageUrl';

export interface CloudinaryLoaderCldOptions { }

export interface CloudinaryLoaderLoaderOptions { height?: string | number; width?: string | number; }

export interface CloudinaryLoader { loaderOptions: CloudinaryLoaderLoaderOptions; imageProps: ImageProps; cldOptions: CloudinaryLoaderCldOptions; cldConfig?: object; }

export function cloudinaryLoader({ loaderOptions, imageProps, cldOptions, cldConfig = {} }: CloudinaryLoader) { const options = { ...imageProps, ...cldOptions }

options.width = typeof options.width === 'string' ? parseInt(options.width) : options.width; options.height = typeof options.height === 'string' ? parseInt(options.height) : options.height;

// // The loader options are used to create dynamic sizing when working with responsive images // // so these should override the default options collected from the props alone if // // the results are different. While we don't always use the height in the loader logic, // // we always pass it here, as the usage is determined later based on cropping.js

if ( typeof loaderOptions?.width === 'number' && typeof options.width === 'number' && loaderOptions.width !== options.width ) { const multiplier = loaderOptions.width / options.width;

options.width = loaderOptions.width;

// The height may change based off of the value passed through via the loader options
// In an example where the user sizes is 800x600, but the loader is passing in 400
// due to responsive sizing, we want to ensure we're using a height that will
// resolve to the same aspect ratio

if ( typeof options.height === 'number' ) {
  options.height = Math.floor(options.height * multiplier);
}

} else if ( typeof loaderOptions?.width === 'number' && typeof options?.width !== 'number' ) { // If we don't have a width on the options object, this may mean that the component is using // the fill option: https://nextjs.org/docs/pages/api-reference/components/image#fill // The Fill option does not allow someone to pass in a width or a height // If this is the case, we still need to define a width for sizing optimization but also // for responsive sizing to take effect, so we can utilize the loader width for the base width options.width = loaderOptions?.width; }

// @ts-ignore return getCldImageUrl(options, cldConfig); }export { default as CldImage } from './components/CldImage'; export type { CldImageProps } from './components/CldImage';

export { default as CldOgImage } from './components/CldOgImage'; export type { CldOgImageProps } from './components/CldOgImage';

export { default as CldUploadButton } from './components/CldUploadButton'; export type { CldUploadButtonProps } from './components/CldUploadButton';

export { default as CldUploadWidget } from './components/CldUploadWidget'; export type { CldUploadWidgetProps, CldUploadWidgetPropsChildren } from './components/CldUploadWidget'; export type { CloudinaryUploadWidgetOptions, CloudinaryUploadWidgetResults, CloudinaryUploadWidgetInfo, CloudinaryUploadWidgetInstanceMethods, CloudinaryUploadWidgetInstanceMethodCloseOptions, CloudinaryUploadWidgetInstanceMethodDestroyOptions, CloudinaryUploadWidgetInstanceMethodOpenOptions, CloudinaryUploadWidgetInstanceMethodUpdateOptions, CloudinaryUploadWidgetSources, CloudinaryUploadWidgetError } from '@cloudinary-util/types';

export { default as CldVideoPlayer } from './components/CldVideoPlayer'; export type { CldVideoPlayerProps, CldVideoPlayerPropsLogo } from './components/CldVideoPlayer'; export type { CloudinaryVideoPlayer, CloudinaryVideoPlayerOptions, CloudinaryVideoPlayerOptionsColors, CloudinaryVideoPlayerOptionsLogo } from '@cloudinary-util/types';

export { cloudinaryLoader } from './loaders/cloudinary-loader'; export type { CloudinaryLoader, CloudinaryLoaderLoaderOptions, CloudinaryLoaderCldOptions } from './loaders/cloudinary-loader';

export { getCldImageUrl } from './helpers/getCldImageUrl'; export type { GetCldImageUrlOptions, GetCldImageUrlConfig, GetCldImageUrlAnalytics } from './helpers/getCldImageUrl';

export { getCldOgImageUrl } from './helpers/getCldOgImageUrl'; export type { GetCldOgImageUrlOptions } from './helpers/getCldOgImageUrl';

export { getCldVideoUrl } from './helpers/getCldVideoUrl'; export type { GetCldVideoUrlOptions, GetCldVideoUrlConfig, GetCldVideoUrlAnalytics } from './helpers/getCldVideoUrl';import { describe, it, expect } from 'vitest';

import { cloudinaryLoader } from '../../src/loaders/cloudinary-loader';

const cldConfig = { cloud: { cloudName: 'test-cloud' }, }

describe('Cloudinary Loader', () => { describe('cloudinaryLoader', () => { it('should return a Cloudinary URL with basic features', async () => { const imageProps = { height: '600', sizes: '100vw', src: 'images/turtle', width: '960', }

  const loaderOptions = {
    quality: 75,
    src: 'images/turtle',
    width: 960,
  }

  const cldOptions = {};

  const result = cloudinaryLoader({ loaderOptions, imageProps, cldOptions, cldConfig });

  expect(result).toContain('https://res.cloudinary.com/test-cloud/image/upload/c_limit,w_960/f_auto/q_auto/v1/images/turtle')
});

it('should return a Cloudinary URL with advanced options', () => {
  const imageProps = {
    width: 987,
    height: 1481,
    src: 'images/woman-headphones',
    sizes: '100vw',
  }

  const loaderOptions = {
    src: 'images/woman-headphones',
    width: 987,
  }

  const cldOptions = {
    removeBackground: true,
    crop: [
      {
        type: 'thumb',
        source: true
      },
      {
        type: 'scale'
      }
    ],
    gravity: 'faces',
    tint: '40:253f8c',
    underlays: [
      {
        publicId: 'images/city-skyline',
        width: 987,
        height: 987,
        crop: 'fill'
      }
    ]
  };

  const result = cloudinaryLoader({ loaderOptions, imageProps, cldOptions, cldConfig });

  // Note: g_auto is expected in the initial transformation as we can't infer usage of
  // gravity for which transformation to apply it to via URL Loader
  expect(result).toContain(`https://res.cloudinary.com/test-cloud/image/upload/e_background_removal/c_thumb,w_987,h_1481,g_auto/e_tint:40:253f8c/u_images:city-skyline,w_987,h_987,c_fill/fl_layer_apply,fl_no_overflow/c_scale,w_${imageProps.width},h_${imageProps.height}/f_auto/q_auto/v1/images/woman-headphones`)
});

it('should return a Cloudinary URL with fetch features', async () => {
  const loaderOptions = {};

  const imageProps = {
    height: '600',
    sizes: '100vw',
    src: 'https://upload.wikimedia.org/wikipedia/commons/4/44/Jelly_cc11.jpg',
    width: '960',
    deliveryType: 'fetch'
  }

  const cldOptions = {};

  const result = cloudinaryLoader({ loaderOptions, imageProps, cldOptions, cldConfig });

  expect(result).toContain('https://res.cloudinary.com/test-cloud/image/fetch/c_limit,w_960/f_auto/q_auto/https://upload.wikimedia.org/wikipedia/commons/4/44/Jelly_cc11.jpg')
});

it('should return responsive images', async () => {
  const imageProps = {
    height: '2724',
    sizes: '100vw',
    src: 'https://upload.wikimedia.org/wikipedia/commons/4/44/Jelly_cc11.jpg',
    width: '4096',
    deliveryType: 'fetch'
  }

  const cldOptions = {};

  // The resulting widths should only be resized from imageProps if the value is smaller, to avoid upscaling an image
  const loaderOptions1 = { width: 2048 };
  const result1 = cloudinaryLoader({ loaderOptions: loaderOptions1, imageProps, cldOptions, cldConfig });
  expect(result1).toContain(`https://res.cloudinary.com/test-cloud/image/fetch/c_limit,w_${loaderOptions1.width}/f_auto/q_auto/https://upload.wikimedia.org/wikipedia/commons/4/44/Jelly_cc11.jpg`)

  const loaderOptions2 = { width: 3840 };
  const result2 = cloudinaryLoader({ loaderOptions: loaderOptions2, imageProps, cldOptions, cldConfig });
  expect(result2).toContain(`https://res.cloudinary.com/test-cloud/image/fetch/c_limit,w_${loaderOptions2.width}/f_auto/q_auto/https://upload.wikimedia.org/wikipedia/commons/4/44/Jelly_cc11.jpg`)

  const loaderOptions3 = { width: 640 };
  const result3 = cloudinaryLoader({ loaderOptions: loaderOptions3, imageProps, cldOptions, cldConfig });
  expect(result3).toContain(`https://res.cloudinary.com/test-cloud/image/fetch/c_limit,w_${loaderOptions3.width}/f_auto/q_auto/https://upload.wikimedia.org/wikipedia/commons/4/44/Jelly_cc11.jpg`)
});

it('should add any resizing after any effects are added', async () => {
  const imageProps = {
    height: 600,
    sizes: '100vw',
    src: 'https://upload.wikimedia.org/wikipedia/commons/4/44/Jelly_cc11.jpg',
    width: 960,
    deliveryType: 'fetch',
  }

  const cldOptions = {
    crop: 'fill',
    overlays: [{
      url: 'https://user-images.githubusercontent.com/1045274/199872380-ced2b84d-fce4-4fc9-9e76-48cb4a7fb35f.png'
    }]
  };

  const urlBufer = Buffer.from(cldOptions.overlays[0].url);
  const urlBase64 = urlBufer.toString('base64');
  const urlParam = encodeURIComponent(urlBase64);

  // The resulting widths should only be resized from imageProps if the value is smaller, to avoid upscaling an image

  const loaderOptions1 = { width: 2048 };
  const result1 = cloudinaryLoader({ loaderOptions: loaderOptions1, imageProps, cldOptions, cldConfig });
  expect(result1).toContain(`https://res.cloudinary.com/test-cloud/image/fetch/l_fetch:${urlParam}/fl_layer_apply,fl_no_overflow/c_fill,w_${loaderOptions1.width},h_${(loaderOptions1.width / imageProps.width) * imageProps.height},g_auto/f_auto/q_auto/https://upload.wikimedia.org/wikipedia/commons/4/44/Jelly_cc11.jpg`)

  const loaderOptions2 = { width: 3840 };
  const result2 = cloudinaryLoader({ loaderOptions: loaderOptions2, imageProps, cldOptions, cldConfig });
  expect(result2).toContain(`https://res.cloudinary.com/test-cloud/image/fetch/l_fetch:${urlParam}/fl_layer_apply,fl_no_overflow/c_fill,w_${loaderOptions2.width},h_${(loaderOptions2.width / imageProps.width) * imageProps.height},g_auto/f_auto/q_auto/https://upload.wikimedia.org/wikipedia/commons/4/44/Jelly_cc11.jpg`)

  const loaderOptions3 = { width: 640 };
  const result3 = cloudinaryLoader({ loaderOptions: loaderOptions3, imageProps, cldOptions, cldConfig });
  expect(result3).toContain(`https://res.cloudinary.com/test-cloud/image/fetch/l_fetch:${urlParam}/fl_layer_apply,fl_no_overflow/c_fill,w_${loaderOptions3.width},h_${(loaderOptions3.width / imageProps.width) * imageProps.height},g_auto/f_auto/q_auto/https://upload.wikimedia.org/wikipedia/commons/4/44/Jelly_cc11.jpg`)
});

it('should add two-stage resizing and cropping', async () => {
  const imageProps = {
    height: 600,
    sizes: '100vw',
    src: 'https://upload.wikimedia.org/wikipedia/commons/4/44/Jelly_cc11.jpg',
    width: 960,
    deliveryType: 'fetch',
  }

  const cldOptions = {
    crop: {
      width: 1234,
      height: 4321,
      type: 'fill',
      source: true
    },
    overlays: [{
      url: 'https://user-images.githubusercontent.com/1045274/199872380-ced2b84d-fce4-4fc9-9e76-48cb4a7fb35f.png'
    }]
  };

  const urlBufer = Buffer.from(cldOptions.overlays[0].url);
  const urlBase64 = urlBufer.toString('base64');
  const urlParam = encodeURIComponent(urlBase64);

  // The resulting widths should only be resized from imageProps if the value is smaller, to avoid upscaling an image

  const loaderOptions1 = { width: 2048 };
  const result1 = cloudinaryLoader({ loaderOptions: loaderOptions1, imageProps, cldOptions, cldConfig });
  expect(result1).toContain(`https://res.cloudinary.com/test-cloud/image/fetch/c_${cldOptions.crop.type},w_${cldOptions.crop.width},h_${cldOptions.crop.height},g_auto/l_fetch:${urlParam}/fl_layer_apply,fl_no_overflow/c_limit,w_${loaderOptions1.width}/f_auto/q_auto/https://upload.wikimedia.org/wikipedia/commons/4/44/Jelly_cc11.jpg`)

  const loaderOptions2 = { width: 3840 };
  const result2 = cloudinaryLoader({ loaderOptions: loaderOptions2, imageProps, cldOptions, cldConfig });
  expect(result2).toContain(`https://res.cloudinary.com/test-cloud/image/fetch/c_${cldOptions.crop.type},w_${cldOptions.crop.width},h_${cldOptions.crop.height},g_auto/l_fetch:${urlParam}/fl_layer_apply,fl_no_overflow/c_limit,w_${loaderOptions2.width}/f_auto/q_auto/https://upload.wikimedia.org/wikipedia/commons/4/44/Jelly_cc11.jpg`)

  const loaderOptions3 = { width: 640 };
  const result3 = cloudinaryLoader({ loaderOptions: loaderOptions3, imageProps, cldOptions, cldConfig });
  expect(result3).toContain(`https://res.cloudinary.com/test-cloud/image/fetch/c_${cldOptions.crop.type},w_${cldOptions.crop.width},h_${cldOptions.crop.height},g_auto/l_fetch:${urlParam}/fl_layer_apply,fl_no_overflow/c_limit,w_${loaderOptions3.width}/f_auto/q_auto/https://upload.wikimedia.org/wikipedia/commons/4/44/Jelly_cc11.jpg`)
});

it('should add any resizing when fill is set to true without a width or height', async () => {
  const imageProps = {
    sizes: '100vw',
    src: 'images/woman-headphones',
    fill: true
  }

  const cldOptions = {};

  // The resulting widths should only be resized from imageProps if the value is smaller, to avoid upscaling an image

  const loaderOptions1 = { width: 2048 };
  const result1 = cloudinaryLoader({ loaderOptions: loaderOptions1, imageProps, cldOptions, cldConfig });
  expect(result1).toContain(`https://res.cloudinary.com/test-cloud/image/upload/c_limit,w_${loaderOptions1.width}/f_auto/q_auto/v1/${imageProps.src}`)

  const loaderOptions2 = { width: 3840 };
  const result2 = cloudinaryLoader({ loaderOptions: loaderOptions2, imageProps, cldOptions, cldConfig });
  expect(result2).toContain(`https://res.cloudinary.com/test-cloud/image/upload/c_limit,w_${loaderOptions2.width}/f_auto/q_auto/v1/${imageProps.src}`)

  const loaderOptions3 = { width: 640 };
  const result3 = cloudinaryLoader({ loaderOptions: loaderOptions3, imageProps, cldOptions, cldConfig });
  expect(result3).toContain(`https://res.cloudinary.com/test-cloud/image/upload/c_limit,w_${loaderOptions3.width}/f_auto/q_auto/v1/${imageProps.src}`)
});

it('should add any resizing after raw transformations', async () => {

  const imageProps = {
    height: '600',
    sizes: '100vw',
    src: 'images/turtle',
    width: '960',
  }

  const loaderOptions = {
    quality: 75,
    src: 'images/turtle',
    width: 960,
  }

  const cldOptions = {
    rawTransformations: ['c_crop,h_359,w_517,x_1483,y_0/c_scale,h_359,w_517/f_auto,q_auto']
  };

  const result = cloudinaryLoader({ loaderOptions, imageProps, cldOptions, cldConfig });

  expect(result).toContain(`image/upload/${cldOptions.rawTransformations.join('/')}/c_limit,w_${imageProps.width}/v1/${imageProps.src}`)
});

it('should return a Cloudinary URL from a Cloudinary URL source with spaces', async () => {

  const publicId = 'images/woman%20headphones';
  const src = `https://res.cloudinary.com/next-cloudinary/image/upload/c_limit,w_987/f_auto/q_auto/v1/${publicId}`;

  const imageProps = {
    height: '600',
    sizes: '100vw',
    src,
    width: '960',
  }

  const loaderOptions = {
    quality: 75,
    src: 'images/turtle',
    width: 960,
  }

  const cldOptions = {};

  const result = cloudinaryLoader({ loaderOptions, imageProps, cldOptions, cldConfig });

  expect(result).toContain(`image/upload/c_limit,w_${imageProps.width}/f_auto/q_auto/v1/${publicId}`)
});

describe('Config', () => {
  it('should add a custom cname via secureDistribution', async () => {
    const imageProps = {
      height: '600',
      sizes: '100vw',
      src: 'images/turtle',
      width: '960',
    }

    const loaderOptions = {
      quality: 75,
      src: 'images/turtle',
      width: 960,
    }

    const config = {
      url: {
        secureDistribution: 'spacejelly.dev'
      }
    }

    const result = cloudinaryLoader({
      loaderOptions,
      imageProps,
      cldConfig: Object.assign({}, cldConfig, config)
    });

    expect(result).toContain(`https://${config.url.secureDistribution}/${cldConfig.cloud.cloudName}/image/upload`)
  });

});

}) });import { vi, describe, it, beforeEach, afterAll, expect } from 'vitest';

import { getCldImageUrl } from '../../src/helpers/getCldImageUrl';

describe('Cloudinary', () => { const OLD_ENV = process.env;

beforeEach(() => { vi.resetModules() process.env = { ...OLD_ENV }; });

afterAll(() => { process.env = OLD_ENV; });

describe('getCldImageUrl', () => { it('should pass', () => { const cloudName = 'customtestcloud';

  process.env.NEXT_PUBLIC_CLOUDINARY_CLOUD_NAME = cloudName;

  const url = getCldImageUrl({
    src: 'turtle',
    width: 100,
    height: 100
  });

  expect(url).toContain(`https://res.cloudinary.com/${cloudName}/image/upload/c_limit,w_100/f_auto/q_auto/turtle`);
});

}); })"use client";

import { CldImage, CldUploadWidget, CldUploadButton, CldVideoPlayer } from '../../../'; import { getCldImageUrl, getCldOgImageUrl, getCldVideoUrl } from '../../../';

import '../../../dist/cld-video-player.css';

export default function Home() { console.log(getCldImageUrl({ src: 'images/turtle' })) console.log(getCldOgImageUrl({ src: 'images/turtle' })) console.log(getCldVideoUrl({ src: 'videos/mountain-stars' })) return ( <> <div style={{ marginBottom: '2em' }}>

CldImage

    <CldImage src="images/woman-headphones" width="100" height="100" alt="test" />
  </div>
  <div style={{
    marginBottom: '2em'
  }}>
    <h2>CldUploadWidget</h2>
    <CldUploadWidget
      uploadPreset="next-cloudinary-unsigned"
      options={{
        sources: ['local', 'camera']
      }}
    >
      {({ open }) => {
        return (
          <button onClick={() => open()}>Upload</button>
        )
      }}
    </CldUploadWidget>
  </div>
  <div style={{
    marginBottom: '2em'
  }}>
    <h2>CldUploadButton</h2>
    <CldUploadButton uploadPreset="next-cloudinary-unsigned" />
  </div>
  <div style={{
    marginBottom: '2em'
  }}>
    <h2>CldVideoPlayer</h2>
    <CldVideoPlayer
      id="test"
      width="1620"
      height="1080"
      src={`videos/mountain-stars`}
    />
  </div>
</>

) }

Checklist - [X] Create `src/app/api/users/upload/route.ts` ✓ https://github.com/labi1240/project101/commit/6f35c4e6b31a13bda5420e8d17958de605821057 [Edit](https://github.com/labi1240/project101/edit/sweep/please_setup_the_cloudinary_for_uploadin_100a7/src/app/api/users/upload/route.ts) - [X] Running GitHub Actions for `src/app/api/users/upload/route.ts` ✓ [Edit](https://github.com/labi1240/project101/edit/sweep/please_setup_the_cloudinary_for_uploadin_100a7/src/app/api/users/upload/route.ts) - [X] Modify `src/app/api/users/signup/route.ts` ✓ https://github.com/labi1240/project101/commit/0a51212681cf110ae64a2249c0f07bb3ae3eb0c2 [Edit](https://github.com/labi1240/project101/edit/sweep/please_setup_the_cloudinary_for_uploadin_100a7/src/app/api/users/signup/route.ts) - [X] Running GitHub Actions for `src/app/api/users/signup/route.ts` ✓ [Edit](https://github.com/labi1240/project101/edit/sweep/please_setup_the_cloudinary_for_uploadin_100a7/src/app/api/users/signup/route.ts) - [X] Modify `src/app/api/users/me/route.ts` ✓ https://github.com/labi1240/project101/commit/377e8878ecc0ca4e917bab1d19eab4671dd10fbc [Edit](https://github.com/labi1240/project101/edit/sweep/please_setup_the_cloudinary_for_uploadin_100a7/src/app/api/users/me/route.ts) - [X] Running GitHub Actions for `src/app/api/users/me/route.ts` ✓ [Edit](https://github.com/labi1240/project101/edit/sweep/please_setup_the_cloudinary_for_uploadin_100a7/src/app/api/users/me/route.ts)
sweep-ai[bot] commented 6 months ago

🚀 Here's the PR! #19

See Sweep's progress at the progress dashboard!
💎 Sweep Pro: I'm using GPT-4. You have unlimited GPT-4 tickets. (tracking ID: 030285bb5e)
Install Sweep Configs: Pull Request

[!TIP] I can email you next time I complete a pull request if you set up your email here!


Actions (click)


Step 1: 🔎 Searching

I found the following snippets in your repository. I will now analyze these snippets and come up with a plan.

Some code snippets I think are relevant in decreasing order of relevance (click to expand). If some file is missing from here, you can mention the path in the ticket description. https://github.com/labi1240/project101/blob/94ad2833fb3117f29131cd25a767491efa9cdc7d/package.json#L1-L85 https://github.com/labi1240/project101/blob/94ad2833fb3117f29131cd25a767491efa9cdc7d/src/app/api/users/me/route.ts#L1-L17 https://github.com/labi1240/project101/blob/94ad2833fb3117f29131cd25a767491efa9cdc7d/src/app/api/users/signup/route.ts#L1-L36

Step 2: ⌨️ Coding

Ran GitHub Actions for 6f35c4e6b31a13bda5420e8d17958de605821057:

--- 
+++ 
@@ -12,7 +12,7 @@
 export async function POST(request: NextRequest) {
               try {
                             const reqBody = await request.json()
-                            const { username, email, password } = reqBody
+                            const { username, email, password, imageUrl } = reqBody

                             console.log(reqBody);

@@ -30,7 +30,8 @@
                             const newUser = new User({
                                           username,
                                           email,
-                                          password: hashedPassword
+                                          password: hashedPassword,
+                                          imageUrl: imageUrl || '' // imageUrl is optional
                             })

                             const savedUser = await newUser.save()

Ran GitHub Actions for 0a51212681cf110ae64a2249c0f07bb3ae3eb0c2:

--- 
+++ 
@@ -9,7 +9,7 @@

               try {
                             const userId = await getDataFromToken(request);
-                            const user = await User.findOne({ _id: userId }).select("-password");
+                            const user = await User.findOne({ _id: userId }).select("-password imageUrl");
                             return NextResponse.json({
                                           mesaaage: "User found",
                                           data: user

Ran GitHub Actions for 377e8878ecc0ca4e917bab1d19eab4671dd10fbc:


Step 3: 🔁 Code Review

I have finished reviewing the code for completeness. I did not find errors for sweep/please_setup_the_cloudinary_for_uploadin_100a7.


🎉 Latest improvements to Sweep:
  • New dashboard launched for real-time tracking of Sweep issues, covering all stages from search to coding.
  • Integration of OpenAI's latest Assistant API for more efficient and reliable code planning and editing, improving speed by 3x.
  • Use the GitHub issues extension for creating Sweep issues directly from your editor.

💡 To recreate the pull request edit the issue title or description. Something wrong? Let us know.

This is an automated message generated by Sweep AI.

labi1240 commented 6 months ago

🚀 Here's the PR! #18

See Sweep's progress at the progress dashboard!
💎 Sweep Pro: I'm using GPT-4. You have unlimited GPT-4 tickets. (tracking ID: None)
Install Sweep Configs: Pull Request

[!TIP] I can email you next time I complete a pull request if you set up your email here!


Actions (click)


Step 1: 🔎 Searching

I found the following snippets in your repository. I will now analyze these snippets and come up with a plan.

Some code snippets I think are relevant in decreasing order of relevance (click to expand). If some file is missing from here, you can mention the path in the ticket description. https://github.com/labi1240/project101/blob/94ad2833fb3117f29131cd25a767491efa9cdc7d/package.json#L1-L85 https://github.com/labi1240/project101/blob/94ad2833fb3117f29131cd25a767491efa9cdc7d/src/app/api/users/login/route.ts#L1-L48 https://github.com/labi1240/project101/blob/94ad2833fb3117f29131cd25a767491efa9cdc7d/src/app/api/users/signup/route.ts#L1-L54 https://github.com/labi1240/project101/blob/94ad2833fb3117f29131cd25a767491efa9cdc7d/src/app/api/users/me/route.ts#L1-L19

Step 2: ⌨️ Coding

--- 
+++ 
@@ -1,6 +1,9 @@
 import { connect } from "@/database/dbConfig/dbConfig"; // Adjusted if dbConfig is indeed in the database folder
 import User from "@/database/models/userModel"; // Adjusted to use the absolute path alias
 import { NextRequest, NextResponse } from "next/server";
+import cloudinary from 'cloudinary';
+
+cloudinary.v2.config({ cloud_name: process.env.CLOUDINARY_CLOUD_NAME, api_key: process.env.CLOUDINARY_API_KEY, api_secret: process.env.CLOUDINARY_API_SECRET });
 import bcryptjs from "bcryptjs";
 import { sendEmail } from "@/database/helpers/mailer";

@@ -33,7 +36,17 @@
                                           password: hashedPassword
                             })

-                            const savedUser = await newUser.save()
+                            // Assuming 'request' contains the image file in a field named 'image'
+const imageFile = request.files?.image;
+let uploadedImageUrl = '';
+if (imageFile) {
+    const result = await cloudinary.v2.uploader.upload(imageFile.path);
+    uploadedImageUrl = result.url;
+}
+
+newUser.profileImageUrl = uploadedImageUrl;
+
+const savedUser = await newUser.save()
                             console.log(savedUser);

                             //send verification email

Ran GitHub Actions for 3847b68b7e741fb25f968bf99bf6944b4af8afe2:

Ran GitHub Actions for 00969edfa028cd6044e1961b193019a174bf7874:


Step 3: 🔁 Code Review

I have finished reviewing the code for completeness. I did not find errors for sweep/please_setup_the_cloudinary_for_uploadin.


🎉 Latest improvements to Sweep:
  • New dashboard launched for real-time tracking of Sweep issues, covering all stages from search to coding.
  • Integration of OpenAI's latest Assistant API for more efficient and reliable code planning and editing, improving speed by 3x.
  • Use the GitHub issues extension for creating Sweep issues directly from your editor.

💡 To recreate the pull request edit the issue title or description. Something wrong? Let us know.

This is an automated message generated by Sweep AI.