aboutsummaryrefslogtreecommitdiffstats
path: root/packages/api/utils
diff options
context:
space:
mode:
authorMohamed Bassem <me@mbassem.com>2025-06-01 20:46:41 +0100
committerGitHub <noreply@github.com>2025-06-01 20:46:41 +0100
commitea1d0023bfee55358ebb1a96f3d06e783a219c0d (patch)
tree5bddd451728cb7dd377574a9ea1ea591bca069c4 /packages/api/utils
parent3afe1e21df6dcc0483e74e0db02d9d82af32ecea (diff)
downloadkarakeep-ea1d0023bfee55358ebb1a96f3d06e783a219c0d.tar.zst
feat: Add support for public lists (#1511)
* WIP: public lists * Drop viewing modes * Add the public endpoint for assets * regen the openapi spec * proper handling for different asset types * Add num bookmarks and a no bookmark banner * Correctly set page title * Add a not-found page * merge the RSS and public list endpoints * Add e2e tests for the public endpoints * Redesign the share list modal * Make NEXTAUTH_SECRET not required * propery render text bookmarks * rebase migration * fix public token tests * Add more tests
Diffstat (limited to 'packages/api/utils')
-rw-r--r--packages/api/utils/assets.ts57
1 files changed, 57 insertions, 0 deletions
diff --git a/packages/api/utils/assets.ts b/packages/api/utils/assets.ts
new file mode 100644
index 00000000..d8a726a6
--- /dev/null
+++ b/packages/api/utils/assets.ts
@@ -0,0 +1,57 @@
+import { Context } from "hono";
+import { stream } from "hono/streaming";
+
+import {
+ createAssetReadStream,
+ getAssetSize,
+ readAssetMetadata,
+} from "@karakeep/shared/assetdb";
+
+import { toWebReadableStream } from "./upload";
+
+export async function serveAsset(c: Context, assetId: string, userId: string) {
+ const [metadata, size] = await Promise.all([
+ readAssetMetadata({
+ userId,
+ assetId,
+ }),
+
+ getAssetSize({
+ userId,
+ assetId,
+ }),
+ ]);
+
+ const range = c.req.header("Range");
+ if (range) {
+ const parts = range.replace(/bytes=/, "").split("-");
+ const start = parseInt(parts[0], 10);
+ const end = parts[1] ? parseInt(parts[1], 10) : size - 1;
+
+ const fStream = createAssetReadStream({
+ userId,
+ assetId,
+ start,
+ end,
+ });
+ c.status(206); // Partial Content
+ c.header("Content-Range", `bytes ${start}-${end}/${size}`);
+ c.header("Accept-Ranges", "bytes");
+ c.header("Content-Length", (end - start + 1).toString());
+ c.header("Content-type", metadata.contentType);
+ return stream(c, async (stream) => {
+ await stream.pipe(toWebReadableStream(fStream));
+ });
+ } else {
+ const fStream = createAssetReadStream({
+ userId,
+ assetId,
+ });
+ c.status(200);
+ c.header("Content-Length", size.toString());
+ c.header("Content-type", metadata.contentType);
+ return stream(c, async (stream) => {
+ await stream.pipe(toWebReadableStream(fStream));
+ });
+ }
+}