File serving in a nextjs app via http action authenticated with clerk
After looking at the docs, this is the implementation I came with for serving my files securely via http action. Any feedback appreciated.
/convex/http.ts
/convex/http.ts
import { HonoWithConvex, HttpRouterWithHono } from "convex-helpers/server/hono";
import { Hono } from "hono";
import { ActionCtx } from "./_generated/server";
const app: HonoWithConvex<ActionCtx> = new Hono();
app.get("/storage", async (ctx) => {
const identity = await ctx.env.auth.getUserIdentity();
if (!identity?.subject) {
return new Response("User is not signed in.", { status: 401 });
}
const storageId = ctx.req.query("storageId");
const fileType = ctx.req.query("fileType");
if (!storageId) {
return new Response("Storage ID is required.", { status: 400 });
}
if (!fileType) {
return new Response("File type is required.", { status: 400 });
}
switch (fileType) {
case "document":
case "documentThumbnail":
await ctx.env.runQuery(internal.documents.query.verify, {
fileType,
storageId: storageId as Id<"_storage">,
userExternalId: identity.subject.toString(),
});
break;
default:
return new Response("Invalid file type.", { status: 400 });
}
const blob = await ctx.env.storage.get(storageId as Id<"_storage">);
if (blob === null) {
return new Response("File not found", {
status: 404,
});
}
return new Response(blob);
});
export default new HttpRouterWithHono(app);import { HonoWithConvex, HttpRouterWithHono } from "convex-helpers/server/hono";
import { Hono } from "hono";
import { ActionCtx } from "./_generated/server";
const app: HonoWithConvex<ActionCtx> = new Hono();
app.get("/storage", async (ctx) => {
const identity = await ctx.env.auth.getUserIdentity();
if (!identity?.subject) {
return new Response("User is not signed in.", { status: 401 });
}
const storageId = ctx.req.query("storageId");
const fileType = ctx.req.query("fileType");
if (!storageId) {
return new Response("Storage ID is required.", { status: 400 });
}
if (!fileType) {
return new Response("File type is required.", { status: 400 });
}
switch (fileType) {
case "document":
case "documentThumbnail":
await ctx.env.runQuery(internal.documents.query.verify, {
fileType,
storageId: storageId as Id<"_storage">,
userExternalId: identity.subject.toString(),
});
break;
default:
return new Response("Invalid file type.", { status: 400 });
}
const blob = await ctx.env.storage.get(storageId as Id<"_storage">);
if (blob === null) {
return new Response("File not found", {
status: 404,
});
}
return new Response(blob);
});
export default new HttpRouterWithHono(app);