diff --git a/components/assets.tsx b/components/assets.tsx new file mode 100644 index 0000000..005f58e --- /dev/null +++ b/components/assets.tsx @@ -0,0 +1,235 @@ +import Uppy from '@uppy/core'; +import type { UppyFile, SuccessResponse } from '@uppy/core' +import { Dashboard } from '@uppy/react'; +import AwsS3 from '@uppy/aws-s3'; +import { useEffect, useState } from 'react'; +import { sha256 } from 'js-sha256'; +import { useTheme } from 'nextra-theme-docs'; +import { bytesToSize } from '@/lib/utils'; +import { Code, Pre } from 'nextra/components'; +import { z } from 'zod'; +import { useForm } from 'react-hook-form'; +import { zodResolver } from '@hookform/resolvers/zod'; +import { Button } from "@/components/ui/button"; +import { + Form, + FormControl, + FormDescription, + FormField, + FormItem, + FormLabel, + FormMessage, +} from "@/components/ui/form"; +import { Input } from "@/components/ui/input"; + +import '@uppy/core/dist/style.min.css'; +import '@uppy/dashboard/dist/style.min.css'; + +const sha256Cache = new Map(); + +const RESOLVER_URL_PREFIXES = [ + "https://rgw.watonomous.ca/asset-perm", + "https://rgw.watonomous.ca/asset-temp", +] + +const extractSha256FromURI = (uri: string) => { + const sha256Match = uri.match(/sha256:([a-f0-9]{64})/); + if (!sha256Match) { + throw new Error("Invalid URI: does not contain a SHA-256 hash."); + } + return sha256Match[1]; +} + +const assetResolverFormSchema = z.object({ + uri: z.string(), +}); + +export function AssetResolver() { + const [isSubmitting, setIsSubmitting] = useState(false); + const [resolvedURL, setResolvedURL] = useState(""); + const [errorMessage, setErrorMessage] = useState(""); + + const form = useForm>({ + resolver: zodResolver(assetResolverFormSchema), + defaultValues: { + uri: "", + }, + }); + + async function onSubmit({ uri }: z.infer) { + setResolvedURL(""); + setErrorMessage(""); + setIsSubmitting(true); + + try { + if (!uri.startsWith('watcloud://v1/')) { + throw new Error(`Invalid URI: must start with "watcloud://v1/". Got: "${uri}"`); + } + + const hash = extractSha256FromURI(uri); + + const urls = await Promise.all(RESOLVER_URL_PREFIXES.map(async (prefix) => { + const r = `${prefix}/${hash}`; + const res = await fetch(r, { method: 'HEAD' }); + if (res.ok) { + return r; + } + })); + + const url = urls.find((url) => url !== undefined); + if (!url) { + throw new Error('Asset not found.'); + } + + setResolvedURL(url); + } catch (error: any) { + console.error('Error while resolving asset:', error); + setErrorMessage(`Error while resolving asset: ${error.message}`); + } + setIsSubmitting(false); + } + + return ( + <> +
+ + ( + + URI + + + + + The URI of the asset you want to resolve. + + + + )} + /> + + + +

Result

+ {resolvedURL && ( +
+ You can access the asset at this URL: +
{resolvedURL}
+
+ )} + {errorMessage && ( +
+ {errorMessage} +
+ )} + {!resolvedURL && !errorMessage && ( +

No result yet. Submit a URI to get started!

+ )} + + ); +} + + +const UPLOADER_MAX_FILE_SIZE = 100 * Math.pow(1024, 2); // Math.pow(1024, 2) = 1MB +const UPLOADER_S3_HOST = 'https://rgw.watonomous.ca'; +const UPLOADER_S3_BUCKET = "asset-temp"; + +export function AssetUploader() { + const { theme } = useTheme(); + const uppyTheme = theme === 'dark' ? 'dark' : theme === 'light' ? 'light' : 'auto'; + + const [successfulUploads, setSuccessfulUploads] = useState<{ + name: string; + uri: string; + }[]>([]); + const [errorMessages, setErrorMessages] = useState([]); + + // IMPORTANT: passing an initializer function to prevent Uppy from being reinstantiated on every render. + const [uppy] = useState(() => new Uppy({ + restrictions: { + maxFileSize: UPLOADER_MAX_FILE_SIZE, + }, + }) + .use(AwsS3, { + shouldUseMultipart: false, + getUploadParameters: async (file) => { + const hash = sha256(await file.data.arrayBuffer()); + sha256Cache.set(file.id, hash); + + return { + method: 'PUT', + url: `${UPLOADER_S3_HOST}/${UPLOADER_S3_BUCKET}/${hash}`, + }; + } + })); + + useEffect(() => { + function handleUpload() { + setErrorMessages([]); + } + async function handleUploadSuccess(file: UppyFile | undefined, response: SuccessResponse) { + if (!file) { + console.warn('Got upload success event without a file:', response) + return; + } + const hash = sha256Cache.get(file.id) || sha256(await file.data.arrayBuffer()); + const watcloudURI = `watcloud://v1/sha256:${hash}?name=${encodeURIComponent(file.name)}`; + console.log('Uploaded file:', file, 'Response:', response, 'watcloud URI:', watcloudURI); + + setSuccessfulUploads((prev) => [{ + name: file.name, + uri: watcloudURI, + }, ...prev]); + } + function handleUppyError(file: UppyFile | undefined, error: any) { + console.error('Failed upload:', file, "Error:", error, "Response status:", error.source?.status); + setErrorMessages((prev) => [`Failed to upload ${file?.name}: "${error.message}", response status: "${error.source?.status}", response body: "${error.source?.responseText}"`, ...prev]); + } + + + uppy.on("upload", handleUpload); + uppy.on('upload-success', handleUploadSuccess); + uppy.on('upload-error', handleUppyError); + return () => { + uppy.off("upload", handleUpload); + uppy.off('upload-success', handleUploadSuccess); + uppy.off('upload-error', handleUppyError); + }; + }, [uppy]) + + return ( + <> + +

Successful Uploads

+ {successfulUploads.map(({name, uri}) => ( +
+ {name} +
{uri}
+
+ ))} + {successfulUploads.length === 0 && ( +

No successful uploads yet. Upload a file to get started!

+ )} + {errorMessages.length > 0 && ( + <> +

Errors

+ {errorMessages.map((message, i) => ( +
+ {message} +
+ ))} + + )} + + ); +} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index d478383..2e95c2b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -27,11 +27,15 @@ "@rjsf/validator-ajv8": "^5.16.1", "@sentry/nextjs": "^7.76.0", "@types/json-schema": "^7.0.15", + "@uppy/aws-s3": "^3.6.2", + "@uppy/dashboard": "^3.7.5", + "@uppy/react": "^3.2.2", "class-variance-authority": "^0.7.0", "clsx": "^2.0.0", "cmdk": "^0.2.0", "core-js": "^3.34.0", "dayjs": "^1.11.10", + "js-sha256": "^0.11.0", "js-base64": "^3.7.7", "lucide-react": "^0.288.0", "next": "^13.4.19", @@ -4025,6 +4029,11 @@ "unist-util-visit": "^5.0.0" } }, + "node_modules/@transloadit/prettier-bytes": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@transloadit/prettier-bytes/-/prettier-bytes-0.3.2.tgz", + "integrity": "sha512-YMRk9+RKgbRcAsgrDgJmcJ7H+R3OfecsyMx56YeRdTeJLXkxapeZmK5NG1Ehvu570SGZo0K63ZeBUS2Z5/8ZzA==" + }, "node_modules/@trysound/sax": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz", @@ -4275,6 +4284,11 @@ "csstype": "^3.0.2" } }, + "node_modules/@types/retry": { + "version": "0.12.2", + "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.2.tgz", + "integrity": "sha512-XISRgDJ2Tc5q4TRqvgJtzsRkFYNJzZrhTdtMoGVBttwzzQJkPnS3WWTFc7kuDRoPtPakl+T+OfdEUjYJj7Jbow==" + }, "node_modules/@types/scheduler": { "version": "0.16.3", "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.3.tgz", @@ -4463,6 +4477,328 @@ "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==" }, + "node_modules/@uppy/aws-s3": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/@uppy/aws-s3/-/aws-s3-3.6.2.tgz", + "integrity": "sha512-pXXSfJbPLR9tmmLFckKU3lyp7Zx4AVvamH/Y5MU2WHKj8TQMrGeM0/M/nXn8SIa7roYEaskY6dVYT/DcHLdO9A==", + "dependencies": { + "@uppy/aws-s3-multipart": "^3.10.2", + "@uppy/companion-client": "^3.7.2", + "@uppy/utils": "^5.7.2", + "@uppy/xhr-upload": "^3.6.2", + "nanoid": "^4.0.0" + }, + "peerDependencies": { + "@uppy/core": "^3.9.1" + } + }, + "node_modules/@uppy/aws-s3-multipart": { + "version": "3.10.2", + "resolved": "https://registry.npmjs.org/@uppy/aws-s3-multipart/-/aws-s3-multipart-3.10.2.tgz", + "integrity": "sha512-bhQ1RAVsLUqEV1e8H6cdGi+MMgs9d5kDTxaSymZGa7CjxkFWE6/ppkU6h+/9eoakB0ssx4Of/RU9rrAaLZJ8jA==", + "dependencies": { + "@uppy/companion-client": "^3.7.2", + "@uppy/utils": "^5.7.2" + }, + "peerDependencies": { + "@uppy/core": "^3.9.1" + } + }, + "node_modules/@uppy/aws-s3/node_modules/nanoid": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-4.0.2.tgz", + "integrity": "sha512-7ZtY5KTCNheRGfEFxnedV5zFiORN1+Y1N6zvPTnHQd8ENUvfaDBeuJDZb2bN/oXwXxu3qkTXDzy57W5vAmDTBw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "bin": { + "nanoid": "bin/nanoid.js" + }, + "engines": { + "node": "^14 || ^16 || >=18" + } + }, + "node_modules/@uppy/companion-client": { + "version": "3.7.4", + "resolved": "https://registry.npmjs.org/@uppy/companion-client/-/companion-client-3.7.4.tgz", + "integrity": "sha512-mBQ9dFZeobPAI3p1VdhGAgjnTGqMKVmW37f/q84Qz9TaxGplQFzYJGYR8/9aoj00ickMtzKtkKtrTAyFxgwHmw==", + "dependencies": { + "@uppy/utils": "^5.7.4", + "namespace-emitter": "^2.0.1", + "p-retry": "^6.1.0" + }, + "peerDependencies": { + "@uppy/core": "^3.9.3" + } + }, + "node_modules/@uppy/core": { + "version": "3.9.3", + "resolved": "https://registry.npmjs.org/@uppy/core/-/core-3.9.3.tgz", + "integrity": "sha512-sUgiJ9Ag3eq8qf3i1unn7BOpjcidBj2lwnpJ8xlMGRcnQqwNznBXP2m39tK8nWEFvrj4kPgIslMZyoR0Lelzxg==", + "peer": true, + "dependencies": { + "@transloadit/prettier-bytes": "^0.3.0", + "@uppy/store-default": "^3.2.2", + "@uppy/utils": "^5.7.4", + "lodash": "^4.17.21", + "mime-match": "^1.0.2", + "namespace-emitter": "^2.0.1", + "nanoid": "^4.0.0", + "preact": "^10.5.13" + } + }, + "node_modules/@uppy/core/node_modules/nanoid": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-4.0.2.tgz", + "integrity": "sha512-7ZtY5KTCNheRGfEFxnedV5zFiORN1+Y1N6zvPTnHQd8ENUvfaDBeuJDZb2bN/oXwXxu3qkTXDzy57W5vAmDTBw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "peer": true, + "bin": { + "nanoid": "bin/nanoid.js" + }, + "engines": { + "node": "^14 || ^16 || >=18" + } + }, + "node_modules/@uppy/dashboard": { + "version": "3.7.5", + "resolved": "https://registry.npmjs.org/@uppy/dashboard/-/dashboard-3.7.5.tgz", + "integrity": "sha512-XIAZTp5sG0dPCBAP6o6hzV07CL7ip/Rt8Hobb11DFgrfrmEXfUG3rRH+QROtBEnrGb1sCdIx1mkvPEY2HzNyNA==", + "dependencies": { + "@transloadit/prettier-bytes": "^0.3.0", + "@uppy/informer": "^3.1.0", + "@uppy/provider-views": "^3.10.0", + "@uppy/status-bar": "^3.3.0", + "@uppy/thumbnail-generator": "^3.0.8", + "@uppy/utils": "^5.7.4", + "classnames": "^2.2.6", + "is-shallow-equal": "^1.0.1", + "lodash": "^4.17.21", + "memoize-one": "^6.0.0", + "nanoid": "^4.0.0", + "preact": "^10.5.13" + }, + "peerDependencies": { + "@uppy/core": "^3.9.3" + } + }, + "node_modules/@uppy/dashboard/node_modules/nanoid": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-4.0.2.tgz", + "integrity": "sha512-7ZtY5KTCNheRGfEFxnedV5zFiORN1+Y1N6zvPTnHQd8ENUvfaDBeuJDZb2bN/oXwXxu3qkTXDzy57W5vAmDTBw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "bin": { + "nanoid": "bin/nanoid.js" + }, + "engines": { + "node": "^14 || ^16 || >=18" + } + }, + "node_modules/@uppy/drag-drop": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@uppy/drag-drop/-/drag-drop-3.0.3.tgz", + "integrity": "sha512-0bCgQKxg+9vkxQipTgrX9yQIuK9a0hZrkipm1+Ynq6jTeig49b7II1bWYnoKdiYhi6nRE4UnDJf4z09yCAU7rA==", + "optional": true, + "peer": true, + "dependencies": { + "@uppy/utils": "^5.4.3", + "preact": "^10.5.13" + }, + "peerDependencies": { + "@uppy/core": "^3.4.0" + } + }, + "node_modules/@uppy/file-input": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@uppy/file-input/-/file-input-3.1.0.tgz", + "integrity": "sha512-trfGGuWLFHb6eMGGflPeDZh2KNK0UWiaTykRk95lYUXm/rSxUDduiLWhR4gMhLRrHXNHOUpJ4V7fnVlc3Qef5A==", + "optional": true, + "peer": true, + "dependencies": { + "@uppy/utils": "^5.7.4", + "preact": "^10.5.13" + }, + "peerDependencies": { + "@uppy/core": "^3.9.3" + } + }, + "node_modules/@uppy/informer": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@uppy/informer/-/informer-3.1.0.tgz", + "integrity": "sha512-vmpTLqzSLmZSuIVDZV0o19yXVqyTh5/uCbKUEiyfBhR726kQiuYQLP/ZHaKcvW3c1ESQGbNg53iNHbFBqF681w==", + "dependencies": { + "@uppy/utils": "^5.7.4", + "preact": "^10.5.13" + }, + "peerDependencies": { + "@uppy/core": "^3.9.3" + } + }, + "node_modules/@uppy/progress-bar": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@uppy/progress-bar/-/progress-bar-3.1.0.tgz", + "integrity": "sha512-jAL1zTt3gc3Q3qBoI6mhtmmMY0kGnx5+wRtI5KUTOvLFiNLxv8G+8+GpWXDMW+bZnS0LVXv2Njz0hXNsJop9EA==", + "optional": true, + "peer": true, + "dependencies": { + "@uppy/utils": "^5.7.4", + "preact": "^10.5.13" + }, + "peerDependencies": { + "@uppy/core": "^3.9.3" + } + }, + "node_modules/@uppy/provider-views": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/@uppy/provider-views/-/provider-views-3.10.0.tgz", + "integrity": "sha512-jelCm60MXlevSXcp0dkXpMbipuZhOFc7fkUCjC9fR2rdXfNpaU8EQmefUD2vxOCzc3C/H/9NZXPwSStay7nt+Q==", + "dependencies": { + "@uppy/utils": "^5.7.4", + "classnames": "^2.2.6", + "nanoid": "^4.0.0", + "p-queue": "^7.3.4", + "preact": "^10.5.13" + }, + "peerDependencies": { + "@uppy/core": "^3.9.3" + } + }, + "node_modules/@uppy/provider-views/node_modules/nanoid": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-4.0.2.tgz", + "integrity": "sha512-7ZtY5KTCNheRGfEFxnedV5zFiORN1+Y1N6zvPTnHQd8ENUvfaDBeuJDZb2bN/oXwXxu3qkTXDzy57W5vAmDTBw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "bin": { + "nanoid": "bin/nanoid.js" + }, + "engines": { + "node": "^14 || ^16 || >=18" + } + }, + "node_modules/@uppy/react": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/@uppy/react/-/react-3.2.2.tgz", + "integrity": "sha512-RNQg4K0o5jgpvEjTgf+e2Gb6GEl9d6b9vScSBINorPQN3UOPVx/k7oZXSmCfikXsTCMST+xJwdCn8muFenynvg==", + "dependencies": { + "@uppy/utils": "^5.7.3", + "prop-types": "^15.6.1" + }, + "peerDependencies": { + "@uppy/core": "^3.9.2", + "@uppy/dashboard": "^3.7.4", + "@uppy/drag-drop": "^3.0.3", + "@uppy/file-input": "^3.0.4", + "@uppy/progress-bar": "^3.0.4", + "@uppy/status-bar": "^3.2.8", + "react": "^16.0.0 || ^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@uppy/dashboard": { + "optional": true + }, + "@uppy/drag-drop": { + "optional": true + }, + "@uppy/file-input": { + "optional": true + }, + "@uppy/progress-bar": { + "optional": true + }, + "@uppy/status-bar": { + "optional": true + } + } + }, + "node_modules/@uppy/status-bar": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/@uppy/status-bar/-/status-bar-3.3.0.tgz", + "integrity": "sha512-UUWvhT+ZixF0auq2ScwVUsyjDyoJlPeTSDOx+iKQHCXVgmbDLbbpyKU6LcU87i5ZkresK38t3YDsWV7VG0+wBA==", + "dependencies": { + "@transloadit/prettier-bytes": "^0.3.0", + "@uppy/utils": "^5.7.4", + "classnames": "^2.2.6", + "preact": "^10.5.13" + }, + "peerDependencies": { + "@uppy/core": "^3.9.3" + } + }, + "node_modules/@uppy/store-default": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/@uppy/store-default/-/store-default-3.2.2.tgz", + "integrity": "sha512-OiSgT++Jj4nLK0N9WTeod3UNjCH81OXE5BcMJCd9oWzl2d0xPNq2T/E9Y6O72XVd+6Y7+tf5vZlPElutfMB3KQ==", + "peer": true + }, + "node_modules/@uppy/thumbnail-generator": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/@uppy/thumbnail-generator/-/thumbnail-generator-3.0.8.tgz", + "integrity": "sha512-bENYEoPqahq/gX8Q6zAn//kQEDSZub8XeuD0i8ovm2xRzg2Ww4PggBemfdZHq6WbBSDhRseUrmXBLPlQN4wo0g==", + "dependencies": { + "@uppy/utils": "^5.7.2", + "exifr": "^7.0.0" + }, + "peerDependencies": { + "@uppy/core": "^3.9.1" + } + }, + "node_modules/@uppy/utils": { + "version": "5.7.4", + "resolved": "https://registry.npmjs.org/@uppy/utils/-/utils-5.7.4.tgz", + "integrity": "sha512-0Xsr7Xqdrb9mgfY3hi0YdhIaAxUw6qJasAflNMwNsyLGt3kH4pLfQHucolBKfWglVGtk1vfb49hZYvJGpcpzYA==", + "dependencies": { + "lodash": "^4.17.21", + "preact": "^10.5.13" + } + }, + "node_modules/@uppy/xhr-upload": { + "version": "3.6.4", + "resolved": "https://registry.npmjs.org/@uppy/xhr-upload/-/xhr-upload-3.6.4.tgz", + "integrity": "sha512-9CI0W5Q2pq7rRLF5Jp9/Va2Dp3LDBjTXIiC8t6QufSodcn6lqvGCk0zRG0nbkDLptuqNkrHMTpyGg9qXI8DDPw==", + "dependencies": { + "@uppy/companion-client": "^3.7.4", + "@uppy/utils": "^5.7.4", + "nanoid": "^4.0.0" + }, + "peerDependencies": { + "@uppy/core": "^3.9.3" + } + }, + "node_modules/@uppy/xhr-upload/node_modules/nanoid": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-4.0.2.tgz", + "integrity": "sha512-7ZtY5KTCNheRGfEFxnedV5zFiORN1+Y1N6zvPTnHQd8ENUvfaDBeuJDZb2bN/oXwXxu3qkTXDzy57W5vAmDTBw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "bin": { + "nanoid": "bin/nanoid.js" + }, + "engines": { + "node": "^14 || ^16 || >=18" + } + }, "node_modules/abbrev": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-2.0.0.tgz", @@ -6180,6 +6516,11 @@ "url": "https://joebell.co.uk" } }, + "node_modules/classnames": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.5.1.tgz", + "integrity": "sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==" + }, "node_modules/cli-progress": { "version": "3.12.0", "resolved": "https://registry.npmjs.org/cli-progress/-/cli-progress-3.12.0.tgz", @@ -9516,6 +9857,11 @@ "node": ">=6" } }, + "node_modules/eventemitter3": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", + "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==" + }, "node_modules/events": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", @@ -9609,6 +9955,11 @@ "node": ">=4" } }, + "node_modules/exifr": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/exifr/-/exifr-7.1.3.tgz", + "integrity": "sha512-g/aje2noHivrRSLbAUtBPWFbxKdKhgj/xr1vATDdUXPOFYJlQ62Ft0oy+72V6XLIpDJfHs6gXLbBLAolqOXYRw==" + }, "node_modules/exit": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", @@ -11797,6 +12148,17 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-network-error": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-network-error/-/is-network-error-1.1.0.tgz", + "integrity": "sha512-tUdRRAnhT+OtCZR/LxZelH/C7QtjtFrTu5tXCA8pl55eTUElUHT+GPYV8MBMBvea/j+NxQqVt3LbWMRir7Gx9g==", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", @@ -11902,6 +12264,11 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-shallow-equal": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-shallow-equal/-/is-shallow-equal-1.0.1.tgz", + "integrity": "sha512-lq5RvK+85Hs5J3p4oA4256M1FEffzmI533ikeDHvJd42nouRRx5wBzt36JuviiGe5dIPyHON/d0/Up+PBo6XkQ==" + }, "node_modules/is-shared-array-buffer": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", @@ -13951,6 +14318,11 @@ "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-3.7.7.tgz", "integrity": "sha512-7rCnleh0z2CkXhH67J8K1Ytz0b2Y+yxTPL+/KOJoa20hfnVQ/3/T6W/KflYI4bRHRagNeXeU2bkNGI3v1oS/lw==" }, + "node_modules/js-sha256": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/js-sha256/-/js-sha256-0.11.0.tgz", + "integrity": "sha512-6xNlKayMZvds9h1Y1VWc0fQHQ82BxTXizWPEtEeGvmOUYpBRy4gbWroHLpzowe6xiQhHpelCQiE7HEdznyBL9Q==" + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -14926,6 +15298,11 @@ "integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==", "dev": true }, + "node_modules/memoize-one": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-6.0.0.tgz", + "integrity": "sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw==" + }, "node_modules/merge-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", @@ -15688,6 +16065,15 @@ "node": ">= 0.6" } }, + "node_modules/mime-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/mime-match/-/mime-match-1.0.2.tgz", + "integrity": "sha512-VXp/ugGDVh3eCLOBCiHZMYWQaTNUHv2IJrut+yXA6+JbLPXHglHwfS/5A5L0ll+jkCY7fIzRJcH6OIunF+c6Cg==", + "peer": true, + "dependencies": { + "wildcard": "^1.1.0" + } + }, "node_modules/mimic-fn": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", @@ -16069,6 +16455,11 @@ "thenify-all": "^1.0.0" } }, + "node_modules/namespace-emitter": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/namespace-emitter/-/namespace-emitter-2.0.1.tgz", + "integrity": "sha512-N/sMKHniSDJBjfrkbS/tpkPj4RAbvW3mr8UAzvlMHyun93XEm83IAvhWtJVHo+RHn/oO8Job5YN4b+wRjSVp5g==" + }, "node_modules/nanoid": { "version": "3.3.7", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", @@ -16769,6 +17160,32 @@ "node": ">=4" } }, + "node_modules/p-queue": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/p-queue/-/p-queue-7.4.1.tgz", + "integrity": "sha512-vRpMXmIkYF2/1hLBKisKeVYJZ8S2tZ0zEAmIJgdVKP2nq0nh4qCdf8bgw+ZgKrkh71AOCaqzwbJJk1WtdcF3VA==", + "dependencies": { + "eventemitter3": "^5.0.1", + "p-timeout": "^5.0.2" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-queue/node_modules/p-timeout": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-5.1.0.tgz", + "integrity": "sha512-auFDyzzzGZZZdHz3BtET9VEz0SE/uMEAx7uWfGPucfzEwwe/xH0iVeZibQmANYE/hp9T2+UUZT5m+BKyrDp3Ew==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/p-reduce": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/p-reduce/-/p-reduce-1.0.0.tgz", @@ -16778,6 +17195,22 @@ "node": ">=4" } }, + "node_modules/p-retry": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-6.2.0.tgz", + "integrity": "sha512-JA6nkq6hKyWLLasXQXUrO4z8BUZGUt/LjlJxx8Gb2+2ntodU/SS63YZ8b0LUTbQ8ZB9iwOfhEPhg4ykKnn2KsA==", + "dependencies": { + "@types/retry": "0.12.2", + "is-network-error": "^1.0.0", + "retry": "^0.13.1" + }, + "engines": { + "node": ">=16.17" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/p-timeout": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-1.2.1.tgz", @@ -17169,6 +17602,15 @@ "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" }, + "node_modules/preact": { + "version": "10.20.0", + "resolved": "https://registry.npmjs.org/preact/-/preact-10.20.0.tgz", + "integrity": "sha512-wU7iZw2BjsaKDal3pDRDy/HpPB6cuFOnVUCcw9aIPKG98+ZrXx3F+szkos8BVME5bquyKDKvRlOJFG8kMkcAbg==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/preact" + } + }, "node_modules/prebuild-install": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.1.tgz", @@ -18304,6 +18746,14 @@ "node": ">=10" } }, + "node_modules/retry": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", + "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==", + "engines": { + "node": ">= 4" + } + }, "node_modules/reusify": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", @@ -21415,6 +21865,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/wildcard": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-1.1.2.tgz", + "integrity": "sha512-DXukZJxpHA8LuotRwL0pP1+rS6CS7FF2qStDDE1C7DDg2rLud2PXRMuEDYIPhgEezwnlHNL4c+N6MfMTjCGTng==", + "peer": true + }, "node_modules/wordwrap": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", diff --git a/package.json b/package.json index 0edd24a..5c3fe3c 100644 --- a/package.json +++ b/package.json @@ -45,11 +45,15 @@ "@rjsf/validator-ajv8": "^5.16.1", "@sentry/nextjs": "^7.76.0", "@types/json-schema": "^7.0.15", + "@uppy/aws-s3": "^3.6.2", + "@uppy/dashboard": "^3.7.5", + "@uppy/react": "^3.2.2", "class-variance-authority": "^0.7.0", "clsx": "^2.0.0", "cmdk": "^0.2.0", "core-js": "^3.34.0", "dayjs": "^1.11.10", + "js-sha256": "^0.11.0", "js-base64": "^3.7.7", "lucide-react": "^0.288.0", "next": "^13.4.19", diff --git a/pages/docs/utilities/assets.mdx b/pages/docs/utilities/assets.mdx new file mode 100644 index 0000000..61ccd24 --- /dev/null +++ b/pages/docs/utilities/assets.mdx @@ -0,0 +1,33 @@ +# Assets + +WATcloud Assets is a system for managing files[^example-uses]. +This page contains tools for interacting with the assets system. + +[^example-uses]: Some use cases are storing files that are too large for git (e.g. images, videos, etc.), or files that need to be shared between multiple projects. + +import { AssetUploader, AssetResolver } from '@/components/assets' + +## Resolver + +The resolver is a tool for resolving `watcloud://` URIs to the actual URL of the asset. + +
+ +
+ +## Uploader + +The uploader is a tool for uploading assets to the assets system. + +
+ +
+ + +{ +// Separate footnotes from the main content +} + +import { Separator } from "@/components/ui/separator" + + diff --git a/styles/global.css b/styles/global.css index 0456fba..7ce75b2 100644 --- a/styles/global.css +++ b/styles/global.css @@ -91,6 +91,14 @@ } } +.nextra-nav-container { + /* + Uppy uses a max z-index of 1005: + https://github.com/transloadit/uppy/blob/68af8a3c0f96a0fc37f6b2aa844df81e6f218fb4/packages/%40uppy/core/src/_variables.scss#L50-L56 + */ + z-index: 1020; +} + .form-description p.nx-leading-7 { /* Change the styling of generated MDX content that are embedded in forms */ line-height: 1.375; @@ -114,4 +122,4 @@ input[type="week"], select:focus, textarea { font-size: 16px; -} \ No newline at end of file +}