From 4cb6acf79889612817081cef8fed16aa7b70a139 Mon Sep 17 00:00:00 2001 From: juliusmarminge Date: Thu, 25 Jan 2024 16:58:43 +0100 Subject: [PATCH 1/9] init --- package.json | 7 ++- pnpm-lock.yaml | 29 ++++++++++++ src/drivers/uploadthing.ts | 90 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 125 insertions(+), 1 deletion(-) create mode 100644 src/drivers/uploadthing.ts diff --git a/package.json b/package.json index a47dd1b5..69b97cdd 100644 --- a/package.json +++ b/package.json @@ -92,6 +92,7 @@ "types-cloudflare-worker": "^1.2.0", "typescript": "^5.3.3", "unbuild": "^2.0.0", + "uploadthing": "^6.3.0", "vite": "^5.0.11", "vitest": "^1.2.1", "vue": "^3.4.14" @@ -108,7 +109,8 @@ "@planetscale/database": "^1.13.0", "@upstash/redis": "^1.28.1", "@vercel/kv": "^0.2.4", - "idb-keyval": "^6.2.1" + "idb-keyval": "^6.2.1", + "uploadthing": "^6.0.0" }, "peerDependenciesMeta": { "@azure/app-configuration": { @@ -146,6 +148,9 @@ }, "idb-keyval": { "optional": true + }, + "uploadthing": { + "optional": true } }, "packageManager": "pnpm@8.14.1" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 17b7920a..0cc00e5f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -148,6 +148,9 @@ devDependencies: unbuild: specifier: ^2.0.0 version: 2.0.0(typescript@5.3.3) + uploadthing: + specifier: ^6.3.0 + version: 6.3.0 vite: specifier: ^5.0.11 version: 5.0.11(@types/node@20.11.5) @@ -1743,6 +1746,22 @@ packages: resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==} dev: true + /@uploadthing/mime-types@0.2.2: + resolution: {integrity: sha512-ZUo1JHOPPMZDsUw1mOhhVDIvJGlsjj6T0xJ/YJtulyJwL43S9B5pxg1cHcRuTEgjaxj7B55jiqQ6r9mDrrjH9A==} + dev: true + + /@uploadthing/shared@6.2.0(@uploadthing/mime-types@0.2.2): + resolution: {integrity: sha512-GMC1gwZa9X48RZwuY7gGse7E8TXYMAMUNaYWEQL2ctVhMZv7viCPyMyZ0yMqBsQmOeGfbmycPnUeNE9IuesJGg==} + peerDependencies: + '@uploadthing/mime-types': ^0.2.2 + peerDependenciesMeta: + '@uploadthing/mime-types': + optional: true + dependencies: + '@uploadthing/mime-types': 0.2.2 + std-env: 3.7.0 + dev: true + /@upstash/redis@1.24.3: resolution: {integrity: sha512-gw6d4IA1biB4eye5ESaXc0zOlVQI94aptsBvVcTghYWu1kRmOrJFoMFEDCa8p5uzluyYAOFCuY2GWLR6O4ZoIw==} dependencies: @@ -7420,6 +7439,16 @@ packages: picocolors: 1.0.0 dev: true + /uploadthing@6.3.0: + resolution: {integrity: sha512-Herfy6a59jFv+5pAKuyUHZrOGcDrtfo7jqKOOhfd80/5vae8LCESVauJj37ZIoUVaPQzNs6sqvF3g86O4+JNFg==} + engines: {node: '>=18.13.0'} + dependencies: + '@uploadthing/mime-types': 0.2.2 + '@uploadthing/shared': 6.2.0(@uploadthing/mime-types@0.2.2) + consola: 3.2.3 + std-env: 3.7.0 + dev: true + /uqr@0.1.2: resolution: {integrity: sha512-MJu7ypHq6QasgF5YRTjqscSzQp/W11zoUk6kvmlH+fmWEs63Y0Eib13hYFwAzagRJcVY8WVnlV+eBDUGMJ5IbA==} dev: false diff --git a/src/drivers/uploadthing.ts b/src/drivers/uploadthing.ts new file mode 100644 index 00000000..762ebbd2 --- /dev/null +++ b/src/drivers/uploadthing.ts @@ -0,0 +1,90 @@ +import { defineDriver } from "./utils"; +import { $fetch, ofetch, $Fetch } from "ofetch"; +import { UTApi } from "uploadthing/server"; + +export interface UploadThingOptions { + apiKey: string; +} + +export default defineDriver((opts) => { + let client: UTApi; + let utFetch: $Fetch; + + const getClient = () => { + return (client ??= new UTApi({ + apiKey: opts.apiKey, + fetch: $fetch.native, + })); + }; + + const getUTFetch = () => { + return (utFetch ??= ofetch.create({ + baseURL: "https://uploadthing.com/api", + headers: { + "x-uploadthing-api-key": opts.apiKey, + }, + })); + }; + + return { + hasItem(key) { + // This is the best endpoint we got currently... + return getUTFetch()("/getFileUrl", { + body: { fileKeys: [key] }, + }).then((res) => res.ok); + }, + getItem(key) { + return ofetch(`https://utfs.io/f/${key}`); + }, + getItemRaw(key) { + return ofetch + .native(`https://utfs.io/f/${key}`) + .then((res) => res.arrayBuffer()); + }, + getItems(items) { + return Promise.all( + items.map((item) => + ofetch(`https://utfs.io/f/${item.key}`).then((res) => ({ + key: item.key, + value: res, + })) + ) + ); + }, + getKeys() { + return getClient() + .listFiles({}) + .then((res) => res.map((file) => file.key)); + }, + setItem(key, value, opts) { + return getClient() + .uploadFiles(new Blob([value]), { + metadata: opts.metadata, + }) + .then(() => {}); + }, + setItems(items, opts) { + return getClient() + .uploadFiles( + items.map((item) => new Blob([item.value])), + { + metadata: opts?.metadata, + } + ) + .then(() => {}); + }, + removeItem(key, opts) { + return getClient() + .deleteFiles([key]) + .then(() => {}); + }, + async clear() { + const client = getClient(); + const keys = await client.listFiles({}).then((r) => r.map((f) => f.key)); + return client.deleteFiles(keys).then(() => {}); + }, + // getMeta(key, opts) { + // // TODO: We don't currently have an endpoint to fetch metadata, but it does exist + // }, + }; +}); From cac6a066bc2afea1c3336321155339b1285612a9 Mon Sep 17 00:00:00 2001 From: juliusmarminge Date: Thu, 25 Jan 2024 17:04:33 +0100 Subject: [PATCH 2/9] nit --- src/drivers/uploadthing.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/drivers/uploadthing.ts b/src/drivers/uploadthing.ts index 762ebbd2..d3fd2740 100644 --- a/src/drivers/uploadthing.ts +++ b/src/drivers/uploadthing.ts @@ -1,5 +1,5 @@ import { defineDriver } from "./utils"; -import { $fetch, ofetch, $Fetch } from "ofetch"; +import { ofetch, $Fetch } from "ofetch"; import { UTApi } from "uploadthing/server"; export interface UploadThingOptions { @@ -13,7 +13,7 @@ export default defineDriver((opts) => { const getClient = () => { return (client ??= new UTApi({ apiKey: opts.apiKey, - fetch: $fetch.native, + fetch: ofetch.native, })); }; From f9a5a15216f55ae732e21957793540350457f39e Mon Sep 17 00:00:00 2001 From: juliusmarminge Date: Thu, 25 Jan 2024 17:54:41 +0100 Subject: [PATCH 3/9] rm getItems --- src/drivers/uploadthing.ts | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/src/drivers/uploadthing.ts b/src/drivers/uploadthing.ts index d3fd2740..64631d9e 100644 --- a/src/drivers/uploadthing.ts +++ b/src/drivers/uploadthing.ts @@ -4,6 +4,16 @@ import { UTApi } from "uploadthing/server"; export interface UploadThingOptions { apiKey: string; + /** + * Primarily used for testing + * @default "https://uploadthing.com/api" + */ + uploadthignApiUrl?: string; + /** + * Primarily used for testing + * @default "https://utfs.io/f" + */ + uploadthingFileUrl?: string; } export default defineDriver((opts) => { @@ -19,7 +29,7 @@ export default defineDriver((opts) => { const getUTFetch = () => { return (utFetch ??= ofetch.create({ - baseURL: "https://uploadthing.com/api", + baseURL: opts.uploadthignApiUrl ?? "https://uploadthing.com/api", headers: { "x-uploadthing-api-key": opts.apiKey, }, @@ -34,23 +44,15 @@ export default defineDriver((opts) => { }).then((res) => res.ok); }, getItem(key) { - return ofetch(`https://utfs.io/f/${key}`); + return ofetch(`/${key}`, { + baseURL: opts.uploadthingFileUrl ?? "https://utfs.io/f", + }); }, getItemRaw(key) { return ofetch .native(`https://utfs.io/f/${key}`) .then((res) => res.arrayBuffer()); }, - getItems(items) { - return Promise.all( - items.map((item) => - ofetch(`https://utfs.io/f/${item.key}`).then((res) => ({ - key: item.key, - value: res, - })) - ) - ); - }, getKeys() { return getClient() .listFiles({}) From 8d46dab0809afc719f2dc51fd845b93ad5bebfbb Mon Sep 17 00:00:00 2001 From: juliusmarminge Date: Thu, 25 Jan 2024 22:35:36 +0100 Subject: [PATCH 4/9] track internal keys --- src/drivers/uploadthing.ts | 102 +++++++++++++++++++------------ test/drivers/uploadthing.test.ts | 81 ++++++++++++++++++++++++ 2 files changed, 144 insertions(+), 39 deletions(-) create mode 100644 test/drivers/uploadthing.test.ts diff --git a/src/drivers/uploadthing.ts b/src/drivers/uploadthing.ts index 64631d9e..2f22ae7f 100644 --- a/src/drivers/uploadthing.ts +++ b/src/drivers/uploadthing.ts @@ -4,87 +4,111 @@ import { UTApi } from "uploadthing/server"; export interface UploadThingOptions { apiKey: string; - /** - * Primarily used for testing - * @default "https://uploadthing.com/api" - */ - uploadthignApiUrl?: string; - /** - * Primarily used for testing - * @default "https://utfs.io/f" - */ - uploadthingFileUrl?: string; } export default defineDriver((opts) => { let client: UTApi; - let utFetch: $Fetch; + let utApiFetch: $Fetch; + let utFsFetch: $Fetch; + + const internalToUTKeyMap = new Map(); + const fromUTKey = (utKey: string) => { + for (const [key, value] of internalToUTKeyMap.entries()) { + if (value === utKey) { + return key; + } + } + }; const getClient = () => { return (client ??= new UTApi({ apiKey: opts.apiKey, fetch: ofetch.native, + logLevel: "debug", })); }; - const getUTFetch = () => { - return (utFetch ??= ofetch.create({ - baseURL: opts.uploadthignApiUrl ?? "https://uploadthing.com/api", + // The UTApi doesn't have all methods we need right now, so use raw fetch + const getUTApiFetch = () => { + return (utApiFetch ??= ofetch.create({ + method: "POST", + baseURL: "https://uploadthing.com/api", headers: { "x-uploadthing-api-key": opts.apiKey, }, })); }; + const getUTFsFetch = () => { + return (utFsFetch ??= ofetch.create({ + baseURL: "https://utfs.io/f", + })); + }; + return { hasItem(key) { + const utkey = internalToUTKeyMap.get(key); + if (!utkey) return false; // This is the best endpoint we got currently... - return getUTFetch()("/getFileUrl", { - body: { fileKeys: [key] }, - }).then((res) => res.ok); - }, - getItem(key) { - return ofetch(`/${key}`, { - baseURL: opts.uploadthingFileUrl ?? "https://utfs.io/f", + return getUTApiFetch()("/getFileUrl", { + body: { fileKeys: [utkey] }, + }).then((res) => { + return !!res?.data?.length; }); }, - getItemRaw(key) { - return ofetch - .native(`https://utfs.io/f/${key}`) - .then((res) => res.arrayBuffer()); + getItem(key) { + const utkey = internalToUTKeyMap.get(key); + if (!utkey) return null; + return getUTFsFetch()(`/${utkey}`).then((r) => r.text()); }, getKeys() { return getClient() .listFiles({}) - .then((res) => res.map((file) => file.key)); + .then((res) => res.map((file) => fromUTKey(file.key) ?? file.key)); }, setItem(key, value, opts) { return getClient() - .uploadFiles(new Blob([value]), { - metadata: opts.metadata, + .uploadFiles(Object.assign(new Blob([value]), { name: key }), { + metadata: opts?.metadata, }) - .then(() => {}); + .then((response) => { + if (response.error) throw response.error; + internalToUTKeyMap.set(key, response.data.key); + }); }, setItems(items, opts) { return getClient() .uploadFiles( - items.map((item) => new Blob([item.value])), - { - metadata: opts?.metadata, - } + items.map((item) => + Object.assign(new Blob([item.value]), { name: item.key }) + ), + { metadata: opts?.metadata } ) - .then(() => {}); + .then((responses) => { + responses.map((response) => { + if (response.error) throw response.error; + internalToUTKeyMap.set(response.data.name, response.data.key); + }); + }); }, removeItem(key, opts) { + const utkey = internalToUTKeyMap.get(key); + if (!utkey) throw new Error(`Unknown key: ${key}`); return getClient() - .deleteFiles([key]) - .then(() => {}); + .deleteFiles([utkey]) + .then(() => { + internalToUTKeyMap.delete(key); + }); }, async clear() { - const client = getClient(); - const keys = await client.listFiles({}).then((r) => r.map((f) => f.key)); - return client.deleteFiles(keys).then(() => {}); + const utkeys = Array.from(internalToUTKeyMap.values()); + return getClient() + .deleteFiles(utkeys) + .then(() => { + internalToUTKeyMap.clear(); + }); }, + // getMeta(key, opts) { // // TODO: We don't currently have an endpoint to fetch metadata, but it does exist // }, diff --git a/test/drivers/uploadthing.test.ts b/test/drivers/uploadthing.test.ts new file mode 100644 index 00000000..f1ca59dc --- /dev/null +++ b/test/drivers/uploadthing.test.ts @@ -0,0 +1,81 @@ +import { afterAll, beforeAll, describe, it } from "vitest"; +import driver from "../../src/drivers/uploadthing"; +import { testDriver } from "./utils"; +import { setupServer } from "msw/node"; +import { rest } from "msw"; + +const store: Record = {}; + +const utapiUrl = "https://uploadthing.com/api"; +const utfsUrl = "https://utfs.io/f"; + +const server = setupServer( + rest.post(`${utapiUrl}/getFileUrl`, async (req, res, ctx) => { + const { fileKeys } = await req.json(); + const key = fileKeys[0]; + if (!(key in store)) { + return res(ctx.status(401), ctx.json({ error: "Unauthorized" })); + } + return res( + ctx.status(200), + ctx.json({ + result: { + [key]: `https://utfs.io/f/${key}`, + }, + }) + ); + }), + rest.get(`${utfsUrl}/:key`, (req, res, ctx) => { + const key = req.params.key as string; + if (!(key in store)) { + return res(ctx.status(404), ctx.json(null)); + } + return res( + ctx.status(200), + ctx.set("content-type", "application/octet-stream"), + ctx.body(store[key]) + ); + }), + rest.post(`${utapiUrl}/uploadFiles`, async (req, res, ctx) => { + console.log("intercepted request"); + return res( + ctx.status(200), + ctx.json({ + data: [ + { + presignedUrls: [`https://my-s3-server.com/:key`], + }, + ], + }) + ); + }), + rest.post(`${utapiUrl}/deleteFile`, async (req, res, ctx) => { + console.log("hello????"); + const { fileKeys } = await req.json(); + for (const key of fileKeys) { + delete store[key]; + } + return res(ctx.status(200), ctx.json({ success: true })); + }) +); + +describe( + "drivers: uploadthing", + () => { + // beforeAll(() => { + // server.listen(); + // }); + // afterAll(() => { + // server.close(); + // }); + + testDriver({ + driver: driver({ + apiKey: + "sk_live_4603822c7c4574cc90495ff3b31204adf20311bc953903d8081be7a5176f31aa", + }), + async additionalTests(ctx) {}, + }); + }, + { timeout: 30e3 } +); From 6444a42326ee0ab873de159c7aaa75bee2f31299 Mon Sep 17 00:00:00 2001 From: juliusmarminge Date: Thu, 25 Jan 2024 23:04:49 +0100 Subject: [PATCH 5/9] fix --- src/drivers/uploadthing.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/drivers/uploadthing.ts b/src/drivers/uploadthing.ts index 2f22ae7f..0837438c 100644 --- a/src/drivers/uploadthing.ts +++ b/src/drivers/uploadthing.ts @@ -24,7 +24,6 @@ export default defineDriver((opts) => { return (client ??= new UTApi({ apiKey: opts.apiKey, fetch: ofetch.native, - logLevel: "debug", })); }; @@ -64,7 +63,9 @@ export default defineDriver((opts) => { getKeys() { return getClient() .listFiles({}) - .then((res) => res.map((file) => fromUTKey(file.key) ?? file.key)); + .then((res) => + res.map((file) => fromUTKey(file.key)).filter((k): k is string => !!k) + ); }, setItem(key, value, opts) { return getClient() From e98edabc52dc2f40046899e16b1dcb3b9fc09de7 Mon Sep 17 00:00:00 2001 From: juliusmarminge Date: Fri, 26 Jan 2024 00:00:43 +0100 Subject: [PATCH 6/9] remove my apikey --- test/drivers/uploadthing.test.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/test/drivers/uploadthing.test.ts b/test/drivers/uploadthing.test.ts index f1ca59dc..a54ab863 100644 --- a/test/drivers/uploadthing.test.ts +++ b/test/drivers/uploadthing.test.ts @@ -71,8 +71,7 @@ describe( testDriver({ driver: driver({ - apiKey: - "sk_live_4603822c7c4574cc90495ff3b31204adf20311bc953903d8081be7a5176f31aa", + apiKey: "sk_live_xxx", }), async additionalTests(ctx) {}, }); From 4d55f54aefc989042ae04d6a08305a608ec4004a Mon Sep 17 00:00:00 2001 From: juliusmarminge Date: Fri, 26 Jan 2024 00:06:12 +0100 Subject: [PATCH 7/9] stub out docs --- docs/content/2.drivers/uploadthing.md | 32 +++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 docs/content/2.drivers/uploadthing.md diff --git a/docs/content/2.drivers/uploadthing.md b/docs/content/2.drivers/uploadthing.md new file mode 100644 index 00000000..061355b7 --- /dev/null +++ b/docs/content/2.drivers/uploadthing.md @@ -0,0 +1,32 @@ +# UploadThing + +Store data using UploadThing. + +::note{to="https://uploadthing.com/"} +Learn more about UploadThing. +:: + +```js +import { createStorage } from "unstorage"; +import uploadthingDriver from "unstorage/drivers/uploadthing"; + +const storage = createStorage({ + driver: uploadthingDriver({ + // apiKey: "", + }), +}); +``` + +To use, you will need to install `uploadthing` dependency in your project: + +```json +{ + "dependencies": { + "uploadthing": "latest" + } +} +``` + +**Options:** + +- `apiKey`: Your UploadThing API key. Will be automatically inferred from the `UPLOADTHING_SECRET` environment variable if not provided. From 4c9a1885eb350c2ff5d4f204b2007f338e12c660 Mon Sep 17 00:00:00 2001 From: juliusmarminge Date: Tue, 30 Jan 2024 12:26:31 +0100 Subject: [PATCH 8/9] use custom ids --- package.json | 2 +- pnpm-lock.yaml | 8 ++-- src/drivers/uploadthing.ts | 98 ++++++++++++++++---------------------- src/storage.ts | 2 +- 4 files changed, 46 insertions(+), 64 deletions(-) diff --git a/package.json b/package.json index 69b97cdd..3005d7ec 100644 --- a/package.json +++ b/package.json @@ -92,7 +92,7 @@ "types-cloudflare-worker": "^1.2.0", "typescript": "^5.3.3", "unbuild": "^2.0.0", - "uploadthing": "^6.3.0", + "uploadthing": "6.3.2-canary.a35b49f", "vite": "^5.0.11", "vitest": "^1.2.1", "vue": "^3.4.14" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 0cc00e5f..f98c61d9 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -149,8 +149,8 @@ devDependencies: specifier: ^2.0.0 version: 2.0.0(typescript@5.3.3) uploadthing: - specifier: ^6.3.0 - version: 6.3.0 + specifier: 6.3.2-canary.a35b49f + version: 6.3.2-canary.a35b49f vite: specifier: ^5.0.11 version: 5.0.11(@types/node@20.11.5) @@ -7439,8 +7439,8 @@ packages: picocolors: 1.0.0 dev: true - /uploadthing@6.3.0: - resolution: {integrity: sha512-Herfy6a59jFv+5pAKuyUHZrOGcDrtfo7jqKOOhfd80/5vae8LCESVauJj37ZIoUVaPQzNs6sqvF3g86O4+JNFg==} + /uploadthing@6.3.2-canary.a35b49f: + resolution: {integrity: sha512-62FAyXOsz9PGX4pzpCwXKmW0hgLrd/NgYyx5nfmKZXZMNT4ceA83PA/8MAZ0M2l3ySCbyJikbt4ZFnA+xbEZHw==} engines: {node: '>=18.13.0'} dependencies: '@uploadthing/mime-types': 0.2.2 diff --git a/src/drivers/uploadthing.ts b/src/drivers/uploadthing.ts index 0837438c..4b42412c 100644 --- a/src/drivers/uploadthing.ts +++ b/src/drivers/uploadthing.ts @@ -9,16 +9,6 @@ export interface UploadThingOptions { export default defineDriver((opts) => { let client: UTApi; let utApiFetch: $Fetch; - let utFsFetch: $Fetch; - - const internalToUTKeyMap = new Map(); - const fromUTKey = (utKey: string) => { - for (const [key, value] of internalToUTKeyMap.entries()) { - if (value === utKey) { - return key; - } - } - }; const getClient = () => { return (client ??= new UTApi({ @@ -38,76 +28,68 @@ export default defineDriver((opts) => { })); }; - const getUTFsFetch = () => { - return (utFsFetch ??= ofetch.create({ - baseURL: "https://utfs.io/f", - })); - }; + function getKeys() { + return getClient() + .listFiles({}) + .then((res) => + res.map((file) => file.customId).filter((k): k is string => !!k) + ); + } return { - hasItem(key) { - const utkey = internalToUTKeyMap.get(key); - if (!utkey) return false; - // This is the best endpoint we got currently... - return getUTApiFetch()("/getFileUrl", { - body: { fileKeys: [utkey] }, - }).then((res) => { - return !!res?.data?.length; - }); + hasItem(id) { + return getClient() + .getFileUrls(id, { keyType: "customId" }) + .then((res) => { + return !!res.length; + }); }, - getItem(key) { - const utkey = internalToUTKeyMap.get(key); - if (!utkey) return null; - return getUTFsFetch()(`/${utkey}`).then((r) => r.text()); + async getItem(id) { + const url = await getClient() + .getFileUrls(id, { keyType: "customId" }) + .then((res) => { + return res[0]?.url; + }); + if (!url) return null; + return ofetch(url).then((res) => res.text()); }, getKeys() { - return getClient() - .listFiles({}) - .then((res) => - res.map((file) => fromUTKey(file.key)).filter((k): k is string => !!k) - ); + return getKeys(); }, setItem(key, value, opts) { return getClient() - .uploadFiles(Object.assign(new Blob([value]), { name: key }), { - metadata: opts?.metadata, - }) - .then((response) => { - if (response.error) throw response.error; - internalToUTKeyMap.set(key, response.data.key); - }); + .uploadFiles( + Object.assign(new Blob([value]), { + name: key, + customId: key, + }), + { metadata: opts?.metadata } + ) + .then(() => {}); }, setItems(items, opts) { return getClient() .uploadFiles( items.map((item) => - Object.assign(new Blob([item.value]), { name: item.key }) + Object.assign(new Blob([item.value]), { + name: item.key, + customId: item.key, + }) ), { metadata: opts?.metadata } ) - .then((responses) => { - responses.map((response) => { - if (response.error) throw response.error; - internalToUTKeyMap.set(response.data.name, response.data.key); - }); - }); + .then(() => {}); }, removeItem(key, opts) { - const utkey = internalToUTKeyMap.get(key); - if (!utkey) throw new Error(`Unknown key: ${key}`); return getClient() - .deleteFiles([utkey]) - .then(() => { - internalToUTKeyMap.delete(key); - }); + .deleteFiles([key], { keyType: "customId" }) + .then(() => {}); }, async clear() { - const utkeys = Array.from(internalToUTKeyMap.values()); + const keys = await getKeys(); return getClient() - .deleteFiles(utkeys) - .then(() => { - internalToUTKeyMap.clear(); - }); + .deleteFiles(keys, { keyType: "customId" }) + .then(() => {}); }, // getMeta(key, opts) { diff --git a/src/storage.ts b/src/storage.ts index d53c755d..fe435525 100644 --- a/src/storage.ts +++ b/src/storage.ts @@ -231,7 +231,7 @@ export function createStorage( async setItems(items, commonOptions) { await runBatch(items, commonOptions, async (batch) => { if (batch.driver.setItems) { - await asyncCall( + return asyncCall( batch.driver.setItems, batch.items.map((item) => ({ key: item.relativeKey, From a54c6485aaef43aca523301e56653410fd697fe9 Mon Sep 17 00:00:00 2001 From: juliusmarminge Date: Tue, 30 Jan 2024 12:32:29 +0100 Subject: [PATCH 9/9] ref --- src/drivers/uploadthing.ts | 87 +++++++++++++------------------------- 1 file changed, 30 insertions(+), 57 deletions(-) diff --git a/src/drivers/uploadthing.ts b/src/drivers/uploadthing.ts index 4b42412c..83aee6d7 100644 --- a/src/drivers/uploadthing.ts +++ b/src/drivers/uploadthing.ts @@ -8,8 +8,6 @@ export interface UploadThingOptions { export default defineDriver((opts) => { let client: UTApi; - let utApiFetch: $Fetch; - const getClient = () => { return (client ??= new UTApi({ apiKey: opts.apiKey, @@ -17,34 +15,17 @@ export default defineDriver((opts) => { })); }; - // The UTApi doesn't have all methods we need right now, so use raw fetch - const getUTApiFetch = () => { - return (utApiFetch ??= ofetch.create({ - method: "POST", - baseURL: "https://uploadthing.com/api", - headers: { - "x-uploadthing-api-key": opts.apiKey, - }, - })); - }; - - function getKeys() { - return getClient() - .listFiles({}) - .then((res) => - res.map((file) => file.customId).filter((k): k is string => !!k) - ); + async function getKeys() { + const res = await getClient().listFiles({}); + return res.map((file) => file.customId).filter((k): k is string => !!k); } return { - hasItem(id) { - return getClient() - .getFileUrls(id, { keyType: "customId" }) - .then((res) => { - return !!res.length; - }); + hasItem: async (id) => { + const res = await getClient().getFileUrls(id, { keyType: "customId" }); + return res.length > 0; }, - async getItem(id) { + getItem: async (id) => { const url = await getClient() .getFileUrls(id, { keyType: "customId" }) .then((res) => { @@ -53,43 +34,35 @@ export default defineDriver((opts) => { if (!url) return null; return ofetch(url).then((res) => res.text()); }, - getKeys() { + getKeys: () => { return getKeys(); }, - setItem(key, value, opts) { - return getClient() - .uploadFiles( - Object.assign(new Blob([value]), { - name: key, - customId: key, - }), - { metadata: opts?.metadata } - ) - .then(() => {}); + setItem: async (key, value, opts) => { + await getClient().uploadFiles( + Object.assign(new Blob([value]), { + name: key, + customId: key, + }), + { metadata: opts?.metadata } + ); }, - setItems(items, opts) { - return getClient() - .uploadFiles( - items.map((item) => - Object.assign(new Blob([item.value]), { - name: item.key, - customId: item.key, - }) - ), - { metadata: opts?.metadata } - ) - .then(() => {}); + setItems: async (items, opts) => { + await getClient().uploadFiles( + items.map((item) => + Object.assign(new Blob([item.value]), { + name: item.key, + customId: item.key, + }) + ), + { metadata: opts?.metadata } + ); }, - removeItem(key, opts) { - return getClient() - .deleteFiles([key], { keyType: "customId" }) - .then(() => {}); + removeItem: async (key, opts) => { + await getClient().deleteFiles([key], { keyType: "customId" }); }, - async clear() { + clear: async () => { const keys = await getKeys(); - return getClient() - .deleteFiles(keys, { keyType: "customId" }) - .then(() => {}); + await getClient().deleteFiles(keys, { keyType: "customId" }); }, // getMeta(key, opts) {