import { NextRequest, NextResponse } from "next/server"; import { db, schema } from "@/lib/db"; import { extractArticle, extractFromHtml } from "@/lib/utils/extract"; import { v4 as uuidv4 } from "uuid"; import { eq } from "drizzle-orm"; // GET /api/save?url=... - Save article and return HTML response for bookmarklet export async function GET(request: NextRequest) { const searchParams = request.nextUrl.searchParams; const url = searchParams.get("url"); const htmlResponse = (status: "success" | "error" | "exists", message: string) => { const bgColor = status === "success" ? "#22c55e" : status === "exists" ? "#eab308" : "#ef4444"; const html = ` ReadLater
${status === "success" ? '' : status === "exists" ? '' : ''}

${status === "success" ? "Saved!" : status === "exists" ? "Already Saved" : "Error"}

${message}

`; return new NextResponse(html, { headers: { "Content-Type": "text/html" }, }); }; if (!url) { return htmlResponse("error", "No URL provided"); } try { const decodedUrl = decodeURIComponent(url); // Check if article already exists const existing = await db .select() .from(schema.articles) .where(eq(schema.articles.url, decodedUrl)) .limit(1); if (existing.length > 0) { return htmlResponse("exists", `"${existing[0].title}" is already in your reading list`); } // Extract article content const extracted = await extractArticle(decodedUrl); const id = uuidv4(); const newArticle: schema.NewArticle = { id, url: decodedUrl, title: extracted.title, author: extracted.author, siteName: extracted.siteName, excerpt: extracted.excerpt, content: extracted.content, textContent: extracted.textContent, leadImage: extracted.leadImage, wordCount: extracted.wordCount, }; await db.insert(schema.articles).values(newArticle); return htmlResponse("success", `"${extracted.title}" has been added to your reading list`); } catch (error) { console.error("Error saving article:", error); return htmlResponse( "error", error instanceof Error ? error.message : "Failed to save article" ); } } // POST /api/save - Save article with HTML content from bookmarklet export async function POST(request: NextRequest) { const htmlResponse = (status: "success" | "error" | "exists", message: string) => { const bgColor = status === "success" ? "#22c55e" : status === "exists" ? "#eab308" : "#ef4444"; const html = ` ReadLater
${status === "success" ? '' : status === "exists" ? '' : ''}

${status === "success" ? "Saved!" : status === "exists" ? "Already Saved" : "Error"}

${message}

`; return new NextResponse(html, { headers: { "Content-Type": "text/html" }, }); }; try { // Parse form data from bookmarklet const formData = await request.formData(); const url = formData.get("url") as string; const html = formData.get("html") as string; const title = formData.get("title") as string; if (!url) { return htmlResponse("error", "No URL provided"); } // Check if article already exists const existing = await db .select() .from(schema.articles) .where(eq(schema.articles.url, url)) .limit(1); if (existing.length > 0) { return htmlResponse("exists", `"${existing[0].title}" is already in your reading list`); } // Extract article from provided HTML content const extracted = await extractFromHtml(html, url, title); const id = uuidv4(); const newArticle: schema.NewArticle = { id, url, title: extracted.title, author: extracted.author, siteName: extracted.siteName, excerpt: extracted.excerpt, content: extracted.content, textContent: extracted.textContent, leadImage: extracted.leadImage, wordCount: extracted.wordCount, }; await db.insert(schema.articles).values(newArticle); return htmlResponse("success", `"${extracted.title}" has been added to your reading list`); } catch (error) { console.error("Error saving article from HTML:", error); return htmlResponse( "error", error instanceof Error ? error.message : "Failed to save article" ); } }