From c03dcfdbbc5a99abdb7517a03482bccf875d1953 Mon Sep 17 00:00:00 2001 From: Yuiki Saito Date: Mon, 12 May 2025 00:11:07 +0900 Subject: feat: Add NETSCAPE-Bookmark-file-1 export format support (#1374) * Add function to export bookmarks in NETSCAPE-Bookmark-file-1 format * Update export endpoint to support NETSCAPE format * Add format selection to export UI * include tags in the export --------- Co-authored-by: Mohamed Bassem --- apps/web/app/api/bookmarks/export/route.tsx | 56 +++++++++++++++++++++-------- 1 file changed, 41 insertions(+), 15 deletions(-) (limited to 'apps/web/app/api/bookmarks') diff --git a/apps/web/app/api/bookmarks/export/route.tsx b/apps/web/app/api/bookmarks/export/route.tsx index e550fcb5..f568b9f7 100644 --- a/apps/web/app/api/bookmarks/export/route.tsx +++ b/apps/web/app/api/bookmarks/export/route.tsx @@ -1,15 +1,23 @@ -import { toExportFormat, zExportSchema } from "@/lib/exportBookmarks"; +import { NextRequest } from "next/server"; +import { + toExportFormat, + toNetscapeFormat, + zExportSchema, +} from "@/lib/exportBookmarks"; import { api, createContextFromRequest } from "@/server/api/client"; import { z } from "zod"; import { MAX_NUM_BOOKMARKS_PER_PAGE } from "@karakeep/shared/types/bookmarks"; export const dynamic = "force-dynamic"; -export async function GET(request: Request) { +export async function GET(request: NextRequest) { const ctx = await createContextFromRequest(request); if (!ctx.user) { return Response.json({ error: "Unauthorized" }, { status: 401 }); } + + const format = request.nextUrl.searchParams.get("format") ?? "json"; + const req = { limit: MAX_NUM_BOOKMARKS_PER_PAGE, useCursorV2: true, @@ -17,25 +25,43 @@ export async function GET(request: Request) { }; let resp = await api.bookmarks.getBookmarks(req); - let results = resp.bookmarks.map(toExportFormat); + let bookmarks = resp.bookmarks; while (resp.nextCursor) { resp = await api.bookmarks.getBookmarks({ - ...request, + ...req, cursor: resp.nextCursor, }); - results = [...results, ...resp.bookmarks.map(toExportFormat)]; + bookmarks = [...bookmarks, ...resp.bookmarks]; } - const exportData: z.infer = { - bookmarks: results.filter((b) => b.content !== null), - }; + if (format === "json") { + // Default JSON format + const exportData: z.infer = { + bookmarks: bookmarks + .map(toExportFormat) + .filter((b) => b.content !== null), + }; - return new Response(JSON.stringify(exportData), { - status: 200, - headers: { - "Content-type": "application/json", - "Content-disposition": `attachment; filename="karakeep-export-${new Date().toISOString()}.json"`, - }, - }); + return new Response(JSON.stringify(exportData), { + status: 200, + headers: { + "Content-type": "application/json", + "Content-disposition": `attachment; filename="hoarder-export-${new Date().toISOString()}.json"`, + }, + }); + } else if (format === "netscape") { + // Netscape format + const netscapeContent = toNetscapeFormat(bookmarks); + + return new Response(netscapeContent, { + status: 200, + headers: { + "Content-type": "text/html", + "Content-disposition": `attachment; filename="bookmarks-${new Date().toISOString()}.html"`, + }, + }); + } else { + return Response.json({ error: "Invalid format" }, { status: 400 }); + } } -- cgit v1.2.3-70-g09d2