Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add Convex adapter #929

Open
wants to merge 13 commits into
base: main
Choose a base branch
from
Open
15 changes: 15 additions & 0 deletions packages/uploadthing/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,16 @@
"default": "./h3/index.cjs"
}
},
"./convex": {
"import": {
"types": "./convex/index.d.ts",
"default": "./convex/index.js"
},
"require": {
"types": "./convex/index.d.cts",
"default": "./convex/index.cjs"
}
},
juliusmarminge marked this conversation as resolved.
Show resolved Hide resolved
"./remix": {
"import": {
"types": "./remix/index.d.ts",
Expand Down Expand Up @@ -126,6 +136,7 @@
},
"files": [
"client",
"convex",
"effect-platform",
"express",
"fastify",
Expand Down Expand Up @@ -182,6 +193,7 @@
"zod": "^3.23.8"
},
"peerDependencies": {
"convex": "*",
"express": "*",
"fastify": "*",
"h3": "*",
Expand All @@ -198,6 +210,9 @@
"fastify": {
"optional": true
},
"convex": {
"optional": true
},
"h3": {
"optional": true
},
Expand Down
113 changes: 113 additions & 0 deletions packages/uploadthing/src/convex.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
import type {
DataModelFromSchemaDefinition,
GenericActionCtx,
GenericDataModel,
HttpRouter,
SchemaDefinition,
} from "convex/server";
import { httpActionGeneric, httpRouter } from "convex/server";
import * as Effect from "effect/Effect";

import type { Json } from "@uploadthing/shared";

import { makeAdapterHandler } from "./internal/handler";
import type { FileRouter, RouteHandlerOptions } from "./internal/types";
import { createBuilder } from "./internal/upload-builder";
import type { CreateBuilderOptions } from "./internal/upload-builder";

export type { FileRouter };

type MiddlewareArgs<DataModel extends GenericDataModel> = {
req: Request;
res: undefined;
event: GenericActionCtx<DataModel>;
};

type ConvexBuilderOptions<
TErrorShape extends Json,
SchemaDef extends SchemaDefinition<any, boolean>,
> = CreateBuilderOptions<TErrorShape> & {
schema?: SchemaDef;
};

export const createUploadthing = <
TErrorShape extends Json,
SchemaDef extends SchemaDefinition<any, boolean>,
>(
opts?: ConvexBuilderOptions<TErrorShape, SchemaDef>,
) =>
createBuilder<
MiddlewareArgs<DataModelFromSchemaDefinition<SchemaDef>>,
TErrorShape
>(opts);

export const addUploadthingRoutes = <TRouter extends FileRouter>(
router: HttpRouter,
opts: RouteHandlerOptions<TRouter>,
) => {
const handler = makeAdapterHandler<
[GenericActionCtx<GenericDataModel>, Request]
>(
(ctx, req) => Effect.succeed({ req, res: undefined, event: ctx }),
(_, req) => Effect.succeed(req),
opts,
"convex",
);

router.route({
method: "GET",
path: "/api/uploadthing",
handler: httpActionGeneric(handler),
});

router.route({
method: "POST",
path: "/api/uploadthing",
handler: httpActionGeneric(handler),
});

router.route({
method: "OPTIONS",
path: "/api/uploadthing",
handler: httpActionGeneric((_ctx, { headers }) => {
const isCorsRequest =
headers.get("Origin") != null &&
headers.get("Access-Control-Request-Method") != null &&
headers.get("Access-Control-Request-Headers") != null;

if (!isCorsRequest) {
return Promise.resolve(new Response());
}
return Promise.resolve(
new Response(null, {
status: 200,
headers: {
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Methods": "POST, GET, OPTIONS",
"Access-Control-Allow-Headers": "*",
"Access-Control-Max-Age": "86400",
},
}),
);
}),
});
juliusmarminge marked this conversation as resolved.
Show resolved Hide resolved
};

//
// TEST
//

const f = createUploadthing();
const router = {
imageUploader: f({ image: { maxFileSize: "4MB" } })
.middleware(async ({ event }) => {
const identity = await event.auth.getUserIdentity();
return { userId: identity?.subject ?? "nothing" };
})
.onUploadComplete(async (args) => {

Check failure on line 107 in packages/uploadthing/src/convex.ts

View workflow job for this annotation

GitHub Actions / lint

Async arrow function has no 'await' expression
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Remove unnecessary async keyword from arrow function

The arrow function passed to .onUploadComplete does not contain any await expressions, so the async keyword can be removed to simplify the code.

Apply this diff to remove the unnecessary async keyword:

-        .onUploadComplete(async (args) => {
+        .onUploadComplete((args) => {
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
.onUploadComplete(async (args) => {
.onUploadComplete((args) => {
🧰 Tools
🪛 GitHub Check: lint

[failure] 107-107:
Async arrow function has no 'await' expression

return { uploadedBy: args.metadata.userId };
}),
} satisfies FileRouter;

const http = httpRouter();
addUploadthingRoutes(http, { router });
1 change: 1 addition & 0 deletions packages/uploadthing/turbo.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
"build": {
"outputs": [
"client/**",
"convex/**",
"effect-platform/**",
"express/**",
"fastify/**",
Expand Down
Loading
Loading