aboutsummaryrefslogtreecommitdiffstats
path: root/apps/web/lib
diff options
context:
space:
mode:
authorMohamedBassem <me@mbassem.com>2024-10-13 01:49:13 +0000
committerMohamedBassem <me@mbassem.com>2024-10-13 01:49:13 +0000
commitde9cf0a45227da9d33feabe9c51a71845dad6763 (patch)
tree1b34bacc1cf767d63bf55d5b898afd013cbbeca5 /apps/web/lib
parent2ccc15ea8865966618cf804968a5ca14ae364345 (diff)
downloadkarakeep-de9cf0a45227da9d33feabe9c51a71845dad6763.tar.zst
feature: Allow importing hoarder's own bookmark file. Fixes #527
Diffstat (limited to 'apps/web/lib')
-rw-r--r--apps/web/lib/exportBookmarks.ts60
-rw-r--r--apps/web/lib/importBookmarkParser.ts29
2 files changed, 89 insertions, 0 deletions
diff --git a/apps/web/lib/exportBookmarks.ts b/apps/web/lib/exportBookmarks.ts
new file mode 100644
index 00000000..dd1913fb
--- /dev/null
+++ b/apps/web/lib/exportBookmarks.ts
@@ -0,0 +1,60 @@
+import { z } from "zod";
+
+import { BookmarkTypes, ZBookmark } from "@hoarder/shared/types/bookmarks";
+
+export const zExportBookmarkSchema = z.object({
+ createdAt: z.number(),
+ title: z.string().nullable(),
+ tags: z.array(z.string()),
+ content: z
+ .discriminatedUnion("type", [
+ z.object({
+ type: z.literal(BookmarkTypes.LINK),
+ url: z.string(),
+ }),
+ z.object({
+ type: z.literal(BookmarkTypes.TEXT),
+ text: z.string(),
+ }),
+ ])
+ .nullable(),
+ note: z.string().nullable(),
+});
+
+export const zExportSchema = z.object({
+ bookmarks: z.array(zExportBookmarkSchema),
+});
+
+export function toExportFormat(
+ bookmark: ZBookmark,
+): z.infer<typeof zExportBookmarkSchema> {
+ let content = null;
+ switch (bookmark.content.type) {
+ case BookmarkTypes.LINK: {
+ content = {
+ type: bookmark.content.type,
+ url: bookmark.content.url,
+ };
+ break;
+ }
+ case BookmarkTypes.TEXT: {
+ content = {
+ type: bookmark.content.type,
+ text: bookmark.content.text,
+ };
+ break;
+ }
+ // Exclude asset types for now
+ }
+ return {
+ createdAt: Math.floor(bookmark.createdAt.getTime() / 1000),
+ title:
+ bookmark.title ??
+ (bookmark.content.type === BookmarkTypes.LINK
+ ? bookmark.content.title ?? null
+ : null),
+ tags: bookmark.tags.map((t) => t.name),
+ content,
+ note: bookmark.note ?? null,
+ };
+}
diff --git a/apps/web/lib/importBookmarkParser.ts b/apps/web/lib/importBookmarkParser.ts
index 1f80e5f4..3134af55 100644
--- a/apps/web/lib/importBookmarkParser.ts
+++ b/apps/web/lib/importBookmarkParser.ts
@@ -1,11 +1,16 @@
// Copied from https://gist.github.com/devster31/4e8c6548fd16ffb75c02e6f24e27f9b9
import * as cheerio from "cheerio";
+import { BookmarkTypes } from "@hoarder/shared/types/bookmarks";
+
+import { zExportSchema } from "./exportBookmarks";
+
export interface ParsedBookmark {
title: string;
url?: string;
tags: string[];
addDate?: number;
+ notes?: string;
}
export async function parseNetscapeBookmarkFile(
@@ -68,3 +73,27 @@ export async function parsePocketBookmarkFile(
})
.get();
}
+
+export async function parseHoarderBookmarkFile(
+ file: File,
+): Promise<ParsedBookmark[]> {
+ const textContent = await file.text();
+
+ const parsed = zExportSchema.safeParse(JSON.parse(textContent));
+ if (!parsed.success) {
+ throw new Error(
+ `The uploaded JSON file contains an invalid bookmark file: ${parsed.error.toString()}`,
+ );
+ }
+
+ return parsed.data.bookmarks.map((bookmark) => ({
+ title: bookmark.title ?? "",
+ url:
+ bookmark.content?.type == BookmarkTypes.LINK
+ ? bookmark.content.url
+ : undefined,
+ tags: bookmark.tags,
+ addDate: bookmark.createdAt,
+ notes: bookmark.note ?? undefined,
+ }));
+}