Files
readlater/src/app/api/add/route.ts
Gemini Agent e1312e8183 Improve POST /api/add for Apple Shortcuts
Cleaner JSON body handling - just send {"url": "..."}
No URL encoding needed.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-17 13:18:52 +00:00

119 lines
3.1 KiB
TypeScript

import { NextRequest, NextResponse } from "next/server";
import { db, schema } from "@/lib/db";
import { extractArticle } from "@/lib/utils/extract";
import { v4 as uuidv4 } from "uuid";
import { eq } from "drizzle-orm";
// GET /api/add?url=... - Simple endpoint for Apple Shortcuts (no auth required)
export async function GET(request: NextRequest) {
const url = request.nextUrl.searchParams.get("url");
if (!url) {
return NextResponse.json({ error: "URL is required" }, { status: 400 });
}
try {
const decodedUrl = decodeURIComponent(url);
// Check if already exists
const existing = await db
.select()
.from(schema.articles)
.where(eq(schema.articles.url, decodedUrl))
.limit(1);
if (existing.length > 0) {
return NextResponse.json({
success: true,
message: "Article already saved",
article: { id: existing[0].id, title: existing[0].title },
});
}
// Extract and save
const extracted = await extractArticle(decodedUrl);
const id = uuidv4();
await db.insert(schema.articles).values({
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,
});
return NextResponse.json({
success: true,
message: "Article saved",
article: { id, title: extracted.title },
});
} catch (error) {
console.error("Error adding article:", error);
return NextResponse.json(
{ error: error instanceof Error ? error.message : "Failed to save" },
{ status: 500 }
);
}
}
// POST /api/add - Simple POST with JSON body for Apple Shortcuts
export async function POST(request: NextRequest) {
try {
const body = await request.json();
const url = body.url;
if (!url) {
return NextResponse.json({ error: "URL is required" }, { status: 400 });
}
// Check if already exists
const existing = await db
.select()
.from(schema.articles)
.where(eq(schema.articles.url, url))
.limit(1);
if (existing.length > 0) {
return NextResponse.json({
success: true,
message: "Already saved",
title: existing[0].title,
});
}
// Extract and save
const extracted = await extractArticle(url);
const id = uuidv4();
await db.insert(schema.articles).values({
id,
url: 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,
});
return NextResponse.json({
success: true,
message: "Saved",
title: extracted.title,
});
} catch (error) {
console.error("Error adding article:", error);
return NextResponse.json(
{ error: error instanceof Error ? error.message : "Failed to save" },
{ status: 500 }
);
}
}