diff options
| author | Mohamed Bassem <me@mbassem.com> | 2025-05-18 16:58:08 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-05-18 16:58:08 +0100 |
| commit | 3505cb7d6416d101a4fcb1be27fc22e0171bacd2 (patch) | |
| tree | ef9f55504b8a5b20add8c0ebe916972ab4ab0178 /packages/api/utils/upload.ts | |
| parent | 74e74fa6425f072107de3a9bc9dd8f91c5ac9a7d (diff) | |
| download | karakeep-3505cb7d6416d101a4fcb1be27fc22e0171bacd2.tar.zst | |
refactor: Migrate from NextJs's API routes to Hono based routes for the API (#1432)
* Setup Hono and migrate the highlights API there
* Implement the tags and lists endpoint
* Implement the bookmarks and users endpoints
* Add the trpc error code adapter
* Remove the old nextjs handlers
* fix api key not found handling
* Fix trpc error handling
* Fix 204 handling
* Fix search ordering
* Implement the singlefile endpoint
* Implement the asset serving endpoints
* Implement webauth
* Add hono as a catch all route under api
* fix tests
Diffstat (limited to '')
| -rw-r--r-- | packages/api/utils/upload.ts (renamed from apps/web/app/api/assets/route.ts) | 64 |
1 files changed, 25 insertions, 39 deletions
diff --git a/apps/web/app/api/assets/route.ts b/packages/api/utils/upload.ts index e2e1e63e..d96a0f60 100644 --- a/apps/web/app/api/assets/route.ts +++ b/packages/api/utils/upload.ts @@ -3,10 +3,7 @@ import * as os from "os"; import * as path from "path"; import { Readable } from "stream"; import { pipeline } from "stream/promises"; -import { createContextFromRequest } from "@/server/api/client"; -import { TRPCError } from "@trpc/server"; -import type { ZUploadResponse } from "@karakeep/shared/types/uploads"; import { assets, AssetTypes } from "@karakeep/db/schema"; import { newAssetId, @@ -18,20 +15,34 @@ import { AuthedContext } from "@karakeep/trpc"; const MAX_UPLOAD_SIZE_BYTES = serverConfig.maxAssetSizeMb * 1024 * 1024; -export const dynamic = "force-dynamic"; - // Helper to convert Web Stream to Node Stream (requires Node >= 16.5 / 14.18) -function webStreamToNode(webStream: ReadableStream<Uint8Array>): Readable { +export function webStreamToNode( + webStream: ReadableStream<Uint8Array>, +): Readable { // eslint-disable-next-line @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-explicit-any return Readable.fromWeb(webStream as any); // Type assertion might be needed } -export async function uploadFromPostData( +export function toWebReadableStream( + nodeStream: fs.ReadStream, +): ReadableStream<Uint8Array> { + const reader = nodeStream as unknown as Readable; + + return new ReadableStream({ + start(controller) { + reader.on("data", (chunk) => controller.enqueue(new Uint8Array(chunk))); + reader.on("end", () => controller.close()); + reader.on("error", (err) => controller.error(err)); + }, + }); +} + +export async function uploadAsset( user: AuthedContext["user"], db: AuthedContext["db"], - formData: FormData, + formData: { file: File } | { image: File }, ): Promise< - | { error: string; status: number } + | { error: string; status: 400 | 413 } | { assetId: string; contentType: string; @@ -39,10 +50,11 @@ export async function uploadFromPostData( size: number; } > { - const data = formData.get("file") ?? formData.get("image"); - - if (!(data instanceof File)) { - return { error: "Bad request", status: 400 }; + let data: File; + if ("file" in formData) { + data = formData.file; + } else { + data = formData.image; } const contentType = data.type; @@ -96,29 +108,3 @@ export async function uploadFromPostData( } } } - -export async function POST(request: Request) { - const ctx = await createContextFromRequest(request); - if (ctx.user === null) { - return Response.json({ error: "Unauthorized" }, { status: 401 }); - } - if (serverConfig.demoMode) { - throw new TRPCError({ - message: "Mutations are not allowed in demo mode", - code: "FORBIDDEN", - }); - } - const formData = await request.formData(); - - const resp = await uploadFromPostData(ctx.user, ctx.db, formData); - if ("error" in resp) { - return Response.json({ error: resp.error }, { status: resp.status }); - } - - return Response.json({ - assetId: resp.assetId, - contentType: resp.contentType, - size: resp.size, - fileName: resp.fileName, - } satisfies ZUploadResponse); -} |
