Skip to content

Commit

Permalink
feat: return file hash from uploaded object (#978)
Browse files Browse the repository at this point in the history
  • Loading branch information
juliusmarminge authored Oct 6, 2024
1 parent 53f4ab6 commit a3fa6af
Show file tree
Hide file tree
Showing 19 changed files with 49 additions and 30 deletions.
5 changes: 5 additions & 0 deletions .changeset/nice-crabs-compete.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"uploadthing": minor
---

feat: return object hash in onUploadComplete
2 changes: 0 additions & 2 deletions examples/minimal-appdir/src/server/uploadthing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,6 @@ export const uploadRouter = {
})
.middleware(({ req, files }) => {
// Check some condition based on the incoming requrest
console.log("Request", req);
//^?
// if (!req.headers.get("x-some-header")) {
// throw new Error("x-some-header is required");
// }
Expand Down
2 changes: 0 additions & 2 deletions examples/minimal-pagedir/src/server/uploadthing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,6 @@ export const uploadRouter = {
})
.middleware(({ req }) => {
// Check some condition based on the incoming requrest
req;
//^?
// if (!req.headers["x-some-header"]) {
// throw new Error("x-some-header is required");
// }
Expand Down
2 changes: 0 additions & 2 deletions examples/minimal-solidstart/src/server/uploadthing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,6 @@ export const uploadRouter = {
})
.middleware(({ req }) => {
// Check some condition based on the incoming request
req;
//^?
// if (!req.headers.get("x-some-header")) {
// throw new Error("x-some-header is required");
// }
Expand Down
2 changes: 0 additions & 2 deletions examples/minimal-sveltekit/src/lib/server/uploadthing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,6 @@ export const uploadRouter = {
})
.middleware(({ req, files }) => {
// Check some condition based on the incoming requrest
req;
//^?
// if (!req.headers.get("x-some-header")) {
// throw new Error("x-some-header is required");
// }
Expand Down
2 changes: 0 additions & 2 deletions examples/with-drizzle-appdir/src/server/uploadthing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,6 @@ export const uploadRouter = {
})
.middleware(({ req }) => {
// Check some condition based on the incoming requrest
req;
//^?
// if (!req.headers.get("x-some-header")) {
// throw new Error("x-some-header is required");
// }
Expand Down
2 changes: 0 additions & 2 deletions examples/with-drizzle-pagesdir/src/server/uploadthing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,6 @@ export const uploadRouter = {
})
.middleware(({ req }) => {
// Check some condition based on the incoming requrest
req;
//^?
// if (!req.headers.get("x-some-header")) {
// throw new Error("x-some-header is required");
// }
Expand Down
2 changes: 0 additions & 2 deletions examples/with-react-image-crop/src/server/uploadthing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,6 @@ export const uploadRouter = {
})
.middleware(({ req }) => {
// Check some condition based on the incoming requrest
req;
//^?
// if (!req.headers.get("x-some-header")) {
// throw new Error("x-some-header is required");
// }
Expand Down
2 changes: 0 additions & 2 deletions examples/with-tailwindcss/src/server/uploadthing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,6 @@ export const uploadRouter = {
})
.middleware(({ req }) => {
// Check some condition based on the incoming requrest
req;
//^?
// if (!req.headers.get("x-some-header")) {
// throw new Error("x-some-header is required");
// }
Expand Down
2 changes: 0 additions & 2 deletions packages/nuxt/playground/server/uploadthing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,6 @@ export const uploadRouter = {
})
.middleware(({ req }) => {
// Check some condition based on the incoming requrest
req;
//^?
// if (!req.headers.get("x-some-header")) {
// throw new Error("x-some-header is required");
// }
Expand Down
2 changes: 2 additions & 0 deletions packages/uploadthing/src/internal/shared-schemas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,13 +71,15 @@ export class FileUploadDataWithCustomId extends FileUploadData.extend<FileUpload
* - a key
* - a direct URL for the file
* - an app-specific URL for the file (useful for scoping eg. for optimization allowed origins)
* - the hash (md5-hex) of the uploaded file's contents
*/
export class UploadedFileData extends FileUploadDataWithCustomId.extend<UploadedFileData>(
"UploadedFileData",
)({
key: S.String,
url: S.String,
appUrl: S.String,
fileHash: S.String,
}) {}

/**
Expand Down
10 changes: 10 additions & 0 deletions packages/uploadthing/src/internal/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -236,3 +236,13 @@ export type UTEvents = {
out: ReadonlyArray<NewPresignedUrl>;
};
};

/**
* Result from the PUT request to the UploadThing Ingest server
*/
export type UploadPutResult<TServerOutput = unknown> = {
url: string;
appUrl: string;
fileHash: string;
serverData: TServerOutput;
};
9 changes: 3 additions & 6 deletions packages/uploadthing/src/internal/upload.browser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import type {
NewPresignedUrl,
UploadFilesOptions,
} from "../types";
import type { UploadPutResult } from "./types";
import { createUTReporter } from "./ut-reporter";

const uploadWithProgress = (
Expand Down Expand Up @@ -92,12 +93,7 @@ export const uploadFile = <
}),
),
),
Micro.map(
unsafeCoerce<
unknown,
{ url: string; appUrl: string; serverData: TServerOutput }
>,
),
Micro.map(unsafeCoerce<unknown, UploadPutResult<TServerOutput>>),
Micro.map((uploadResponse) => ({
name: file.name,
size: file.size,
Expand All @@ -108,6 +104,7 @@ export const uploadFile = <
appUrl: uploadResponse.appUrl,
customId: presigned.customId,
type: file.type,
fileHash: uploadResponse.fileHash,
})),
);

Expand Down
4 changes: 2 additions & 2 deletions packages/uploadthing/src/internal/upload.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { unsafeCoerce } from "effect/Function";
import { UploadThingError } from "@uploadthing/shared";

import type { FileEsque } from "../sdk/types";
import type { UploadPutResult } from "./types";

export const uploadWithoutProgress = (
file: FileEsque,
Expand All @@ -32,8 +33,7 @@ export const uploadWithoutProgress = (
}),
),
HttpClientResponse.json,

Effect.andThen(unsafeCoerce<unknown, { url: string; appUrl: string }>),
Effect.andThen(unsafeCoerce<unknown, UploadPutResult>),
);

yield* Effect.logDebug(`File ${file.name} uploaded successfully`).pipe(
Expand Down
7 changes: 4 additions & 3 deletions packages/uploadthing/src/sdk/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -178,16 +178,17 @@ const uploadFile = (
Effect.gen(function* () {
const { file, presigned } = input;

const { url, appUrl } = yield* uploadWithoutProgress(file, presigned);
const response = yield* uploadWithoutProgress(file, presigned);

return {
key: presigned.key,
url: url,
appUrl: appUrl,
url: response.url,
appUrl: response.appUrl,
lastModified: file.lastModified ?? Date.now(),
name: file.name,
size: file.size,
type: file.type,
customId: file.customId ?? null,
fileHash: response.fileHash,
};
}).pipe(Effect.withLogSpan("uploadFile"));
7 changes: 6 additions & 1 deletion packages/uploadthing/test/__test-helpers.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { createHash } from "crypto";
import * as S from "@effect/schema/Schema";
import type { StrictRequest } from "msw";
import { http, HttpResponse } from "msw";
Expand All @@ -7,6 +8,7 @@ import { afterAll, beforeAll, it as itBase, vi } from "vitest";
import { UPLOADTHING_VERSION } from "../src/internal/config";
import { ParsedToken, UploadThingToken } from "../src/internal/shared-schemas";
import type { ActionType } from "../src/internal/shared-schemas";
import type { UploadPutResult } from "../src/internal/types";

export const requestSpy = vi.fn<(url: string, req: RequestInit) => void>();
export const requestsToDomain = (domain: string) =>
Expand Down Expand Up @@ -115,10 +117,13 @@ export const it = itBase.extend({
async ({ request, params }) => {
await callRequestSpy(request);
const appId = new URLSearchParams(request.url).get("x-ut-identifier");
return HttpResponse.json({
return HttpResponse.json<UploadPutResult>({
url: `${UTFS_IO_URL}/f/${params.key}`,
appUrl: `${UTFS_IO_URL}/a/${appId}/${params.key}`,
serverData: null,
fileHash: createHash("md5")
.update(new Uint8Array(await request.arrayBuffer()))
.digest("hex"),
});
},
),
Expand Down
3 changes: 3 additions & 0 deletions packages/uploadthing/test/client.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@ describe("uploadFiles", () => {
key: expect.stringMatching(/.+/),
url: expect.stringMatching(fileUrlPattern),
appUrl: expect.stringMatching(appUrlPattern()),
fileHash: expect.any(String),
},
]);

Expand Down Expand Up @@ -177,6 +178,7 @@ describe("uploadFiles", () => {
key: expect.stringMatching(/.+/),
url: expect.stringMatching(fileUrlPattern),
appUrl: expect.stringMatching(appUrlPattern()),
fileHash: expect.any(String),
},
]);

Expand Down Expand Up @@ -211,6 +213,7 @@ describe("uploadFiles", () => {
key: expect.stringMatching(/.+/),
url: expect.stringMatching(fileUrlPattern),
appUrl: expect.stringMatching(appUrlPattern()),
fileHash: expect.any(String),
},
]);

Expand Down
4 changes: 4 additions & 0 deletions packages/uploadthing/test/request-handler.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -337,6 +337,7 @@ describe(".onUploadComplete()", () => {
size: 48,
type: "image/png",
customId: null,
fileHash: "some-md5-hash",
}),
});
const signature = await Effect.runPromise(
Expand Down Expand Up @@ -366,6 +367,7 @@ describe(".onUploadComplete()", () => {
type: "image/png",
url: "https://utfs.io/f/some-random-key.png",
appUrl: `https://utfs.io/a/${testToken.decoded.appId}/some-random-key.png`,
fileHash: "some-md5-hash",
},
metadata: {},
});
Expand All @@ -383,6 +385,7 @@ describe(".onUploadComplete()", () => {
size: 48,
type: "image/png",
customId: null,
fileHash: "some-md5-hash",
}),
});

Expand Down Expand Up @@ -415,6 +418,7 @@ describe(".onUploadComplete()", () => {
size: 48,
type: "image/png",
customId: null,
fileHash: "some-md5-hash",
}),
});
const signature = await Effect.runPromise(
Expand Down
10 changes: 10 additions & 0 deletions packages/uploadthing/test/sdk.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ describe("uploadFiles", () => {
appUrl: `${UTFS_IO_URL}/a/${testToken.decoded.appId}/${key}`,
customId: null,
type: "text/plain",
fileHash: expect.any(String),
},
error: null,
});
Expand Down Expand Up @@ -136,6 +137,7 @@ describe("uploadFilesFromUrl", () => {
appUrl: `${UTFS_IO_URL}/a/${testToken.decoded.appId}/${key}`,
customId: null,
type: "text/plain",
fileHash: expect.any(String),
},
error: null,
});
Expand Down Expand Up @@ -235,6 +237,7 @@ describe("uploadFilesFromUrl", () => {
type: "text/plain",
url: `${UTFS_IO_URL}/f/${key1}`,
appUrl: `${UTFS_IO_URL}/a/${testToken.decoded.appId}/${key1}`,
fileHash: expect.any(String),
},
error: null,
},
Expand All @@ -257,6 +260,7 @@ describe("uploadFilesFromUrl", () => {
type: "text/plain",
url: `${UTFS_IO_URL}/f/${key2}`,
appUrl: `${UTFS_IO_URL}/a/${testToken.decoded.appId}/${key2}`,
fileHash: expect.any(String),
},
error: null,
},
Expand Down Expand Up @@ -473,6 +477,7 @@ describe.runIf(shouldRun)(
type: "text/plain",
url: expect.stringMatching(fileUrlPattern),
appUrl: expect.stringMatching(appUrlPattern(appId)),
fileHash: expect.any(String),
},
error: null,
});
Expand Down Expand Up @@ -508,6 +513,7 @@ describe.runIf(shouldRun)(
type: "text/plain",
url: expect.stringMatching(fileUrlPattern),
appUrl: expect.stringMatching(appUrlPattern(appId)),
fileHash: expect.any(String),
},
error: null,
});
Expand Down Expand Up @@ -538,6 +544,7 @@ describe.runIf(shouldRun)(
type: "image/vnd.microsoft.icon",
url: expect.stringMatching(fileUrlPattern),
appUrl: expect.stringMatching(appUrlPattern(appId)),
fileHash: expect.any(String),
},
error: null,
});
Expand All @@ -563,6 +570,7 @@ describe.runIf(shouldRun)(
type: "text/plain",
url: expect.stringMatching(fileUrlPattern),
appUrl: expect.stringMatching(appUrlPattern(appId)),
fileHash: expect.any(String),
},
error: null,
});
Expand Down Expand Up @@ -605,6 +613,7 @@ describe.runIf(shouldRun)(
type: "text/plain",
url: expect.stringMatching(fileUrlPattern),
appUrl: expect.stringMatching(appUrlPattern(appId)),
fileHash: expect.any(String),
},
error: null,
});
Expand Down Expand Up @@ -645,6 +654,7 @@ describe.runIf(shouldRun)(
type: "text/plain",
url: expect.stringMatching(fileUrlPattern),
appUrl: expect.stringMatching(appUrlPattern(appId)),
fileHash: expect.any(String),
},
error: null,
});
Expand Down

0 comments on commit a3fa6af

Please sign in to comment.