diff --git a/.gitignore b/.gitignore index b9f8d23e..4066736c 100644 --- a/.gitignore +++ b/.gitignore @@ -35,4 +35,5 @@ yarn-error.log* *.tsbuildinfo next-env.d.ts -.env \ No newline at end of file +public/uploads/* +!public/uploads/ diff --git a/app/(customer)/dashboard/profile/page.tsx b/app/(customer)/dashboard/profile/page.tsx index 7b86781f..b52e0054 100644 --- a/app/(customer)/dashboard/profile/page.tsx +++ b/app/(customer)/dashboard/profile/page.tsx @@ -11,6 +11,7 @@ import {Button} from "@/components/ui/button"; import {ButtonWithConfirm} from "@/components/wrappers/Button/ButtonWithConfirm/ButtonWithConfirm"; import {ButtonDeleteAccount} from "@/components/wrappers/Dashboard/Profile/ButtonDeleteAccount/ButtonDeleteAccount"; import {useIsMobile} from "@/hooks/use-mobile"; +import {AvatarWithUpload} from "@/components/wrappers/Dashboard/Profile/Avatar/AvatarWithUpload"; export default async function RoutePage(props: PageParams<{}>) { @@ -18,30 +19,16 @@ export default async function RoutePage(props: PageParams<{}>) { if (!user) { notFound() } - const userInfo = await prisma.user.findUnique({ where: { email: user.email } }) - - console.log(userInfo) - - const test = () => { - console.log("test") - } - return ( - {/**/}
- - {user.name?.[0]} - {user.image ? ( - - ) : null} - + {user.name} {userInfo.authMethod} @@ -49,7 +36,6 @@ export default async function RoutePage(props: PageParams<{}>) {
- {/*
*/} diff --git a/package.json b/package.json index b1b4e371..4f4a2ef6 100644 --- a/package.json +++ b/package.json @@ -52,6 +52,7 @@ "embla-carousel-react": "^8.3.1", "input-otp": "^1.4.0", "lucide-react": "^0.454.0", + "minio": "^8.0.2", "next": "15.0.3", "next-auth": "^5.0.0-beta.25", "next-intl": "^3.24.0", diff --git a/src/components/wrappers/CodeSnippet/CodeSnippet.tsx b/src/components/wrappers/CodeSnippet/CodeSnippet.tsx new file mode 100644 index 00000000..623b451f --- /dev/null +++ b/src/components/wrappers/CodeSnippet/CodeSnippet.tsx @@ -0,0 +1,17 @@ +import {PropsWithChildren} from "react"; + + +export type CodeSnippetProps = PropsWithChildren<{}>; + +export const CodeSnippet = (props: CodeSnippetProps) => { + + return ( +
+
+            {`
+           ${props.children}
+            `}
+          
+
+ ) +} \ No newline at end of file diff --git a/src/components/wrappers/Dashboard/LoggedInButton/LoggedInButton.tsx b/src/components/wrappers/Dashboard/LoggedInButton/LoggedInButton.tsx index 6264312e..d30cd093 100644 --- a/src/components/wrappers/Dashboard/LoggedInButton/LoggedInButton.tsx +++ b/src/components/wrappers/Dashboard/LoggedInButton/LoggedInButton.tsx @@ -11,6 +11,8 @@ export const LoggedInButton = async () => { // return // } + + return ( diff --git a/src/components/wrappers/Dashboard/LoggedInDropdown/LoggedInDropdown.tsx b/src/components/wrappers/Dashboard/LoggedInDropdown/LoggedInDropdown.tsx index f0a644e3..04c59e72 100644 --- a/src/components/wrappers/Dashboard/LoggedInDropdown/LoggedInDropdown.tsx +++ b/src/components/wrappers/Dashboard/LoggedInDropdown/LoggedInDropdown.tsx @@ -3,11 +3,6 @@ import {PropsWithChildren} from "react"; import {DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger} from "@/components/ui/dropdown-menu"; import {signOutAction} from "@/features/auth/auth.action"; -import {Home, LogOut, Square, User, Gauge, User2, ChevronUp} from "lucide-react"; -import Link from "next/link"; -import {useTranslations} from "use-intl"; -import {SidebarMenuButton} from "@/components/ui/sidebar"; -import {UserAvatar} from "@/components/wrappers/Dashboard/UserAvatar/UserAvatar"; import {redirect} from "next/navigation"; export type LoggedInDropdownProps = PropsWithChildren<{}> @@ -28,9 +23,6 @@ export const LoggedInDropdown = (props: LoggedInDropdownProps) => { }}> Account - - Billing - { signOutAction() }}> diff --git a/src/components/wrappers/Dashboard/Profile/Avatar/AvatarWithUpload.tsx b/src/components/wrappers/Dashboard/Profile/Avatar/AvatarWithUpload.tsx new file mode 100644 index 00000000..736c5bdd --- /dev/null +++ b/src/components/wrappers/Dashboard/Profile/Avatar/AvatarWithUpload.tsx @@ -0,0 +1,97 @@ +"use client" +import {Avatar, AvatarFallback, AvatarImage} from "@/components/ui/avatar"; +import {User} from "@prisma/client"; +import {UploadIcon} from "lucide-react"; +import {toast} from "sonner"; +import {uploadImageAction} from "@/features/upload/upload.action"; +import {useMutation} from "@tanstack/react-query"; +import {prisma} from "@/prisma"; +import {updateImageUserAction} from "@/components/wrappers/Dashboard/Profile/Avatar/avatar.action"; +import {useRouter} from "next/navigation"; +import {useSession} from "next-auth/react"; + +export type AvatarWithUploadProps = { + user: User +} + + +export const AvatarWithUpload = (props: AvatarWithUploadProps) => { + const user = props.user + const router = useRouter(); + const { data: session, update } = useSession(); + + + const submitImage = useMutation({ + mutationFn: async (file: File) => { + const formData = new FormData(); + formData.set("file", file); + const uploadImage = await uploadImageAction(formData) + const data = uploadImage?.data?.data + + if (uploadImage?.serverError || !data) { + console.log(uploadImage?.serverError); + toast.error(uploadImage?.serverError); + return; + } + + const updateUser = await updateImageUserAction(data.url) + const dataUser = updateUser?.data?.data + + if (updateUser?.serverError || !dataUser) { + console.log(updateUser?.serverError); + toast.error(updateUser?.serverError); + return; + } + + const newSession = { + ...session, + user: { + ...session?.user, + image: data.url + }, + }; + + await update(newSession); + toast.success("Successfully uploaded user image!"); + router.refresh() + + + + } + }) + + const handleImageUpload = async (event: React.ChangeEvent) => { + const file = event.target.files?.[0]; + if (!file) return; + if (!file.type.includes("image")) { + toast.error("File not an image") + return; + } + submitImage.mutate(file) + }; + + + return ( +
+ + {user.name?.[0]} + {user.image ? ( + + ) : null} + +
{ + const fileInput = document.createElement("input"); + fileInput.type = "file"; + fileInput.accept = "image/*"; + // @ts-ignore + fileInput.onchange = handleImageUpload; + fileInput.click(); + }} + className="cursor-pointer absolute inset-0 flex justify-center items-center opacity-0 transition-opacity hover:opacity-100 hover:bg-gray-500 hover:bg-opacity-50 rounded-full size-14" + > + +
+
+ ) +} \ No newline at end of file diff --git a/src/components/wrappers/Dashboard/Profile/Avatar/avatar.action.ts b/src/components/wrappers/Dashboard/Profile/Avatar/avatar.action.ts new file mode 100644 index 00000000..a4129d23 --- /dev/null +++ b/src/components/wrappers/Dashboard/Profile/Avatar/avatar.action.ts @@ -0,0 +1,24 @@ +"use server" +import {userAction} from "@/safe-actions"; +import {z} from "zod"; +import {prisma} from "@/prisma"; + + +export const updateImageUserAction = userAction + .schema(z.string()) + .action(async ({parsedInput, ctx}) => { + + const user = await prisma.user.update({ + where: { + id: ctx.user.id, + }, + data: { + image: parsedInput, + } + }) + + + return { + data: user, + } + }); \ No newline at end of file diff --git a/src/env.mjs b/src/env.mjs index 907df9e6..2cfa3f8c 100644 --- a/src/env.mjs +++ b/src/env.mjs @@ -16,8 +16,14 @@ export const env = createEnv({ SMTP_USER: z.string(), NEXT_PUBLIC_SECRET: z.string(), NEXTAUTH_URL: z.string(), - AUTH_GOOGLE_ID: z.string(), - AUTH_GOOGLE_SECRET: z.string(), + AUTH_GOOGLE_ID: z.string().optional(), + AUTH_GOOGLE_SECRET: z.string().optional(), + S3_ENDPOINT: z.string().optional(), + S3_ACCESS_KEY: z.string().optional(), + S3_SECRET_KEY: z.string().optional(), + S3_BUCKET_NAME: z.string().optional(), + S3_PORT: z.string().optional(), + S3_USE_SSL: z.string().optional(), }, /* * Environment variables available on the client (and server). @@ -46,5 +52,11 @@ export const env = createEnv({ SMTP_USER: process.env.SMTP_USER, AUTH_GOOGLE_ID: process.env.AUTH_GOOGLE_ID, AUTH_GOOGLE_SECRET: process.env.AUTH_GOOGLE_SECRET, + S3_ENDPOINT: process.env.S3_ENDPOINT, + S3_ACCESS_KEY: process.env.S3_ACCESS_KEY, + S3_SECRET_KEY: process.env.S3_SECRET_KEY, + S3_BUCKET_NAME: process.env.S3_BUCKET_NAME, + S3_PORT: process.env.S3_PORT, + S3_USE_SSL: process.env.S3_USE_SSL, }, }); diff --git a/src/features/upload/upload.action.ts b/src/features/upload/upload.action.ts new file mode 100644 index 00000000..e24e7c5a --- /dev/null +++ b/src/features/upload/upload.action.ts @@ -0,0 +1,46 @@ +"use server" +import {userAction} from "@/safe-actions"; +import {z} from "zod"; +import {v4 as uuidv4} from 'uuid'; +import { writeFile, access, mkdir } from "fs/promises"; +import path from "path"; +import {env} from "@/env.mjs"; + + +export const uploadImageAction = userAction + .schema(z.instanceof(FormData)) + .action(async ({parsedInput: formData, ctx}) => { + + const file = formData.get("file") as File + const uuid = uuidv4() + + const fileFormat = file.name.split(".").slice(-1)[0] + const fileName = uuid + "." + fileFormat + const arrayBuffer = await file.arrayBuffer() + const buffer = Buffer.from(arrayBuffer) + const localDir = "public/uploads/" + try { + await mkdir(path.join(process.cwd(), localDir), { recursive: true }); + const result = await writeFile( + path.join(process.cwd(), localDir + fileName), + buffer + ); + let url: string = ""; + if (env.NODE_ENV === "production") { + // url = `https://${env.S3_ENDPOINT}/${bucketName}/${fileName}` + } else { + url = `http://localhost:8887/uploads/${fileName}` + } + return { + data: {result: result, url: url}, + } + } catch (error) { + console.log("Error occured ", error); + throw new Error('An error occured while importing image'); + + } + + + }); + + diff --git a/src/utils/date-formatting.ts b/src/utils/date-formatting.ts new file mode 100644 index 00000000..724f5259 --- /dev/null +++ b/src/utils/date-formatting.ts @@ -0,0 +1,10 @@ +import {format} from "date-fns"; + +export function humanReadableDate(rawDate: string | number | Date) { + return format(new Date(rawDate), 'dd/MM/yyyy HH:mm') +} + +export function timeAgo(rawDate: string | number | Date) { + const date = new Date(rawDate) + return "Not implemented" +} \ No newline at end of file diff --git a/src/utils/s3-file-management.ts b/src/utils/s3-file-management.ts new file mode 100644 index 00000000..527e1e0d --- /dev/null +++ b/src/utils/s3-file-management.ts @@ -0,0 +1,143 @@ +import * as Minio from 'minio' +import {env} from "@/env.mjs"; +import internal from "node:stream"; + + +// Create a new Minio client with the S3 endpoint, access key, and secret key +export const s3Client = env.NODE_ENV === "production" ? + new Minio.Client({ + endPoint: env.S3_ENDPOINT ?? "", + accessKey: env.S3_ACCESS_KEY ?? "", + secretKey: env.S3_SECRET_KEY ?? "", + }) : new Minio.Client({ + endPoint: env.S3_ENDPOINT ?? "", + port: Number(env.S3_PORT ?? 0), + accessKey: env.S3_ACCESS_KEY ?? "", + secretKey: env.S3_SECRET_KEY ?? "", + useSSL: env.S3_USE_SSL === 'true' + }) + +export async function checkMinioAlive() { + try { + // Try to list buckets to check connectivity + const buckets = await s3Client.listBuckets(); + console.log('MinIO is up and running. Buckets:', buckets); + } catch (error) { + console.error('Error connecting to MinIO:', error); + } +} + +export async function createBucketIfNotExists(bucketName: string) { + const bucketExists = await s3Client.bucketExists(bucketName) + if (!bucketExists) { + console.log(`Creating bucket ${bucketName}`); + await s3Client.makeBucket(bucketName) + } +} + +/** + * Save file in S3 bucket + * @param bucketName name of the bucket + * @param fileName name of the file + * @param file file to save + */ +export async function saveFileInBucket({ + bucketName, + fileName, + file, + }: { + bucketName: string + fileName: string + file: Buffer | internal.Readable +}) { + // Check if Minio is Alive + await checkMinioAlive() + // Create bucket if it doesn't exist + await createBucketIfNotExists(bucketName) + // check if file exists - optional. + // Without this check, the file will be overwritten if it exists + const fileExists = await checkFileExistsInBucket({ + bucketName, + fileName, + }) + + console.log('File exists:', fileExists); + + if (fileExists) { + throw new Error('File already exists') + } + + // Upload image to S3 bucket + const result = await s3Client.putObject(bucketName, fileName, file) + return result +} + +/** + * Check if file exists in bucket + * @param bucketName name of the bucket + * @param fileName name of the file + * @returns true if file exists, false if not + */ +export async function checkFileExistsInBucket({bucketName, fileName}: { bucketName: string; fileName: string }) { + try { + await s3Client.statObject(bucketName, fileName) + } catch (error) { + return false + } + return true +} + +/** + * Generate presigned urls for uploading files to S3 + * @param files files to upload + * @returns promise with array of presigned urls + */ +export async function createPresignedUrlToUpload({ + bucketName, + fileName, + expiry = 60 * 60, // 1 hour + }: { + bucketName: string + fileName: string + expiry?: number +}) { + // Create bucket if it doesn't exist + await createBucketIfNotExists(bucketName) + + return await s3Client.presignedPutObject(bucketName, fileName, expiry) +} + + +// Function to create a bucket and make it public +export async function createPublicBucket({bucketName}: { bucketName: string }) { + try { + // Check if the bucket already exists + const exists = await s3Client.bucketExists(bucketName); + if (!exists) { + // Create the bucket + await s3Client.makeBucket(bucketName); // Change region if needed + console.log(`Bucket ${bucketName} created successfully.`); + } else { + console.log(`Bucket ${bucketName} already exists.`); + } + + // Define a bucket policy for public access + const policy = { + Version: '2012-10-17', + Statement: [ + { + Effect: 'Allow', + Principal: "*", + Action: 's3:GetObject', + Resource: `arn:aws:s3:::${bucketName}/*` + } + ] + }; + + // Set the policy to the bucket + await s3Client.setBucketPolicy(bucketName, JSON.stringify(policy)); + console.log(`Bucket ${bucketName} is now public.`); + } catch (error) { + console.error('Error creating bucket:', error); + } +} \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index c94a8afa..28e54ed0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1316,6 +1316,11 @@ resolved "https://registry.yarnpkg.com/@ungap/structured-clone/-/structured-clone-1.2.0.tgz#756641adb587851b5ccb3e095daf27ae581c8406" integrity sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ== +"@zxing/text-encoding@0.9.0": + version "0.9.0" + resolved "https://registry.yarnpkg.com/@zxing/text-encoding/-/text-encoding-0.9.0.tgz#fb50ffabc6c7c66a0c96b4c03e3d9be74864b70b" + integrity sha512-U/4aVJ2mxI0aDNI8Uq0wEhMgY+u4CNtEb0om3+y3+niDAsoTCOB33UF0sxpzqzdqXLqmvc+vZyAt4O8pPdfkwA== + abbrev@1: version "1.1.1" resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" @@ -1521,6 +1526,11 @@ ast-types-flow@^0.0.8: resolved "https://registry.yarnpkg.com/ast-types-flow/-/ast-types-flow-0.0.8.tgz#0a85e1c92695769ac13a428bb653e7538bea27d6" integrity sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ== +async@^3.2.4: + version "3.2.6" + resolved "https://registry.yarnpkg.com/async/-/async-3.2.6.tgz#1b0728e14929d51b85b449b7f06e27c1145e38ce" + integrity sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA== + available-typed-arrays@^1.0.7: version "1.0.7" resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz#a5cc375d6a03c2efc87a553f3e0b1522def14846" @@ -1556,6 +1566,13 @@ binary-extensions@^2.0.0: resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.3.0.tgz#f6e14a97858d327252200242d4ccfe522c445522" integrity sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw== +block-stream2@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/block-stream2/-/block-stream2-2.1.0.tgz#ac0c5ef4298b3857796e05be8ebed72196fa054b" + integrity sha512-suhjmLI57Ewpmq00qaygS8UgEq2ly2PCItenIyhMqVjo4t4pGzqMvfgJuX8iWTeSDdfSSqS6j38fL4ToNL7Pfg== + dependencies: + readable-stream "^3.4.0" + brace-expansion@^1.1.7: version "1.1.11" resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" @@ -1578,6 +1595,16 @@ braces@^3.0.3, braces@~3.0.2: dependencies: fill-range "^7.1.1" +browser-or-node@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/browser-or-node/-/browser-or-node-2.1.1.tgz#738790b3a86a8fc020193fa581273fbe65eaea0f" + integrity sha512-8CVjaLJGuSKMVTxJ2DpBl5XnlNDiT4cQFeuCJJrvJmts9YrTZDizTX7PjC2s6W4x+MBGZeEY6dGMrF04/6Hgqg== + +buffer-crc32@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-1.0.0.tgz#a10993b9055081d55304bd9feb4a072de179f405" + integrity sha512-Db1SbgBS/fg/392AblrMJk97KggmvYhr4pB5ZIMTWtaivCPMWLkmb7m21cJvpvgK+J3nsU2CmmixNBZx4vFj/w== + busboy@1.6.0: version "1.6.0" resolved "https://registry.yarnpkg.com/busboy/-/busboy-1.6.0.tgz#966ea36a9502e43cdb9146962523b92f531f6893" @@ -1875,6 +1902,11 @@ decimal.js-light@^2.4.1: resolved "https://registry.yarnpkg.com/decimal.js-light/-/decimal.js-light-2.5.1.tgz#134fd32508f19e208f4fb2f8dac0d2626a867934" integrity sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg== +decode-uri-component@^0.2.2: + version "0.2.2" + resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.2.tgz#e69dbe25d37941171dd540e024c444cd5188e1e9" + integrity sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ== + deep-is@^0.1.3: version "0.1.4" resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831" @@ -2324,6 +2356,11 @@ eventemitter3@^4.0.1: resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f" integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw== +eventemitter3@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-5.0.1.tgz#53f5ffd0a492ac800721bb42c66b841de96423c4" + integrity sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA== + fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: version "3.1.3" resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" @@ -2366,6 +2403,13 @@ fast-levenshtein@^2.0.6: resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw== +fast-xml-parser@^4.4.1: + version "4.5.0" + resolved "https://registry.yarnpkg.com/fast-xml-parser/-/fast-xml-parser-4.5.0.tgz#2882b7d01a6825dfdf909638f2de0256351def37" + integrity sha512-/PlTQCI96+fZMAOLMZK4CWG1ItCbfZ/0jx7UIJFChPNrx7tcEgerUgWbeieCM9MfHInUDyK8DWYZ+YrywDJuTg== + dependencies: + strnum "^1.0.5" + fastq@^1.6.0: version "1.17.1" resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.17.1.tgz#2a523f07a4e7b1e81a42b91b8bf2254107753b47" @@ -2387,6 +2431,11 @@ fill-range@^7.1.1: dependencies: to-regex-range "^5.0.1" +filter-obj@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/filter-obj/-/filter-obj-1.1.0.tgz#9b311112bc6c6127a16e016c6c5d7f19e0805c5b" + integrity sha512-8rXg1ZnX7xzy2NGDVkBVaAy+lSlPNwad13BtgSlLuxfIslyt5Vg64U7tFcCt4WS1R0hvtnQybT/IyCkGZ3DpXQ== + find-up@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc" @@ -2699,6 +2748,19 @@ invariant@^2.2.4: dependencies: loose-envify "^1.0.0" +ipaddr.js@^2.0.1: + version "2.2.0" + resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-2.2.0.tgz#d33fa7bac284f4de7af949638c9d68157c6b92e8" + integrity sha512-Ag3wB2o37wslZS19hZqorUnrnzSkpOVy+IiiDEiTqNubEYpYuHWIf6K4psgN2ZWKExS4xhVCrRVfb/wfW8fWJA== + +is-arguments@^1.0.4: + version "1.1.1" + resolved "https://registry.yarnpkg.com/is-arguments/-/is-arguments-1.1.1.tgz#15b3f88fda01f2a97fec84ca761a560f123efa9b" + integrity sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA== + dependencies: + call-bind "^1.0.2" + has-tostringtag "^1.0.0" + is-array-buffer@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/is-array-buffer/-/is-array-buffer-3.0.4.tgz#7a1f92b3d61edd2bc65d24f130530ea93d7fae98" @@ -2791,7 +2853,7 @@ is-fullwidth-code-point@^3.0.0: resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== -is-generator-function@^1.0.10: +is-generator-function@^1.0.10, is-generator-function@^1.0.7: version "1.0.10" resolved "https://registry.yarnpkg.com/is-generator-function/-/is-generator-function-1.0.10.tgz#f1558baf1ac17e0deea7c0415c438351ff2b3c72" integrity sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A== @@ -2866,7 +2928,7 @@ is-symbol@^1.0.2, is-symbol@^1.0.3: dependencies: has-symbols "^1.0.2" -is-typed-array@^1.1.13: +is-typed-array@^1.1.13, is-typed-array@^1.1.3: version "1.1.13" resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.13.tgz#d6c5ca56df62334959322d7d7dd1cca50debe229" integrity sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw== @@ -3073,6 +3135,18 @@ micromatch@^4.0.4, micromatch@^4.0.5: braces "^3.0.3" picomatch "^2.3.1" +mime-db@1.52.0: + version "1.52.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" + integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== + +mime-types@^2.1.35: + version "2.1.35" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" + integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== + dependencies: + mime-db "1.52.0" + minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2: version "3.1.2" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" @@ -3092,6 +3166,26 @@ minimist@^1.2.0, minimist@^1.2.6: resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== +minio@^8.0.2: + version "8.0.2" + resolved "https://registry.yarnpkg.com/minio/-/minio-8.0.2.tgz#ea1703ff52dcf610ba6d4e2ece8c5627f664d581" + integrity sha512-7ipWbtgzzboctf+McK+2cXwCrNOhuboTA/O1g9iWa0gH8R4GkeyFWwk12aVDEHdzjPiG8wxnjwfHS7pgraKuHw== + dependencies: + async "^3.2.4" + block-stream2 "^2.1.0" + browser-or-node "^2.1.1" + buffer-crc32 "^1.0.0" + eventemitter3 "^5.0.1" + fast-xml-parser "^4.4.1" + ipaddr.js "^2.0.1" + lodash "^4.17.21" + mime-types "^2.1.35" + query-string "^7.1.3" + stream-json "^1.8.0" + through2 "^4.0.2" + web-encoding "^1.1.5" + xml2js "^0.5.0 || ^0.6.2" + minipass@^3.0.0: version "3.3.6" resolved "https://registry.yarnpkg.com/minipass/-/minipass-3.3.6.tgz#7bba384db3a1520d18c9c0e5251c3444e95dd94a" @@ -3531,6 +3625,16 @@ punycode@^2.1.0: resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5" integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg== +query-string@^7.1.3: + version "7.1.3" + resolved "https://registry.yarnpkg.com/query-string/-/query-string-7.1.3.tgz#a1cf90e994abb113a325804a972d98276fe02328" + integrity sha512-hh2WYhq4fi8+b+/2Kg9CEge4fDPvHS534aOOvOZeQ3+Vf2mCFsaFBYj0i+iXcAq6I9Vzp5fjMFBlONvayDC1qg== + dependencies: + decode-uri-component "^0.2.2" + filter-obj "^1.1.0" + split-on-first "^1.0.0" + strict-uri-encode "^2.0.0" + queue-microtask@^1.2.2: version "1.2.3" resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" @@ -3635,7 +3739,7 @@ read-cache@^1.0.0: dependencies: pify "^2.3.0" -readable-stream@^3.6.0: +readable-stream@3, readable-stream@^3.4.0, readable-stream@^3.6.0: version "3.6.2" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.2.tgz#56a9b36ea965c00c5a93ef31eb111a0f11056967" integrity sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA== @@ -3771,6 +3875,11 @@ safe-regex-test@^1.0.3: es-errors "^1.3.0" is-regex "^1.1.4" +sax@>=0.6.0: + version "1.4.1" + resolved "https://registry.yarnpkg.com/sax/-/sax-1.4.1.tgz#44cc8988377f126304d3b3fc1010c733b929ef0f" + integrity sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg== + scheduler@0.25.0-rc-66855b96-20241106: version "0.25.0-rc-66855b96-20241106" resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.25.0-rc-66855b96-20241106.tgz#8bbb728eca4de5a5deca1f18370fbce41aee91d1" @@ -3891,11 +4000,33 @@ source-map-js@^1.0.2, source-map-js@^1.2.1: resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.2.1.tgz#1ce5650fddd87abc099eda37dcff024c2667ae46" integrity sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA== +split-on-first@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/split-on-first/-/split-on-first-1.1.0.tgz#f610afeee3b12bce1d0c30425e76398b78249a5f" + integrity sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw== + +stream-chain@^2.2.5: + version "2.2.5" + resolved "https://registry.yarnpkg.com/stream-chain/-/stream-chain-2.2.5.tgz#b30967e8f14ee033c5b9a19bbe8a2cba90ba0d09" + integrity sha512-1TJmBx6aSWqZ4tx7aTpBDXK0/e2hhcNSTV8+CbFJtDjbb+I1mZ8lHit0Grw9GRT+6JbIrrDd8esncgBi8aBXGA== + +stream-json@^1.8.0: + version "1.9.0" + resolved "https://registry.yarnpkg.com/stream-json/-/stream-json-1.9.0.tgz#caae36fd9fff99662f504dce859bc855d5668282" + integrity sha512-TqnfW7hRTKje7UobBzXZJ2qOEDJvdcSVgVIK/fopC03xINFuFqQs8RVjyDT4ry7TmOo2ueAXwpXXXG4tNgtvoQ== + dependencies: + stream-chain "^2.2.5" + streamsearch@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/streamsearch/-/streamsearch-1.1.0.tgz#404dd1e2247ca94af554e841a8ef0eaa238da764" integrity sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg== +strict-uri-encode@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz#b9c7330c7042862f6b142dc274bbcc5866ce3546" + integrity sha512-QwiXZgpRcKkhTj2Scnn++4PKtWsH0kpzZ62L2R6c/LUVYv7hVnZqcg2+sMuT6R7Jusu1vviK/MFsu6kNJfWlEQ== + "string-width-cjs@npm:string-width@^4.2.0": version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" @@ -4024,6 +4155,11 @@ strip-json-comments@^3.1.1: resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== +strnum@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/strnum/-/strnum-1.0.5.tgz#5c4e829fe15ad4ff0d20c3db5ac97b73c9b072db" + integrity sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA== + styled-jsx@5.1.6: version "5.1.6" resolved "https://registry.yarnpkg.com/styled-jsx/-/styled-jsx-5.1.6.tgz#83b90c077e6c6a80f7f5e8781d0f311b2fe41499" @@ -4130,6 +4266,13 @@ thenify-all@^1.0.0: dependencies: any-promise "^1.0.0" +through2@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/through2/-/through2-4.0.2.tgz#a7ce3ac2a7a8b0b966c80e7c49f0484c3b239764" + integrity sha512-iOqSav00cVxEEICeD7TjLB1sueEL+81Wpzp2bY17uZjZN0pWZPuo4suZ/61VujxmqSGFfgOcNuTZ85QJwNZQpw== + dependencies: + readable-stream "3" + tiny-invariant@^1.3.1: version "1.3.3" resolved "https://registry.yarnpkg.com/tiny-invariant/-/tiny-invariant-1.3.3.tgz#46680b7a873a0d5d10005995eb90a70d74d60127" @@ -4288,6 +4431,17 @@ util-deprecate@^1.0.1, util-deprecate@^1.0.2: resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== +util@^0.12.3: + version "0.12.5" + resolved "https://registry.yarnpkg.com/util/-/util-0.12.5.tgz#5f17a6059b73db61a875668781a1c2b136bd6fbc" + integrity sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA== + dependencies: + inherits "^2.0.3" + is-arguments "^1.0.4" + is-generator-function "^1.0.7" + is-typed-array "^1.1.3" + which-typed-array "^1.1.2" + uuid@^11.0.3: version "11.0.3" resolved "https://registry.yarnpkg.com/uuid/-/uuid-11.0.3.tgz#248451cac9d1a4a4128033e765d137e2b2c49a3d" @@ -4320,6 +4474,15 @@ victory-vendor@^36.6.8: d3-time "^3.0.0" d3-timer "^3.0.1" +web-encoding@^1.1.5: + version "1.1.5" + resolved "https://registry.yarnpkg.com/web-encoding/-/web-encoding-1.1.5.tgz#fc810cf7667364a6335c939913f5051d3e0c4864" + integrity sha512-HYLeVCdJ0+lBYV2FvNZmv3HJ2Nt0QYXqZojk3d9FJOLkwnuhzM9tmamh8d7HPM8QqjKH8DeHkFTx+CFlWpZZDA== + dependencies: + util "^0.12.3" + optionalDependencies: + "@zxing/text-encoding" "0.9.0" + webidl-conversions@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" @@ -4372,7 +4535,7 @@ which-collection@^1.0.2: is-weakmap "^2.0.2" is-weakset "^2.0.3" -which-typed-array@^1.1.14, which-typed-array@^1.1.15: +which-typed-array@^1.1.14, which-typed-array@^1.1.15, which-typed-array@^1.1.2: version "1.1.15" resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.15.tgz#264859e9b11a649b388bfaaf4f767df1f779b38d" integrity sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA== @@ -4425,6 +4588,19 @@ wrappy@1: resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== +"xml2js@^0.5.0 || ^0.6.2": + version "0.6.2" + resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.6.2.tgz#dd0b630083aa09c161e25a4d0901e2b2a929b499" + integrity sha512-T4rieHaC1EXcES0Kxxj4JWgaUQHDk+qwHcYOCFHfiwKz7tOVPLq7Hjq9dM1WCMhylqMEfP7hMcOIChvotiZegA== + dependencies: + sax ">=0.6.0" + xmlbuilder "~11.0.0" + +xmlbuilder@~11.0.0: + version "11.0.1" + resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-11.0.1.tgz#be9bae1c8a046e76b31127726347d0ad7002beb3" + integrity sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA== + yallist@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72"