Files
readlater/src/app/api/folders/route.ts
Gemini Agent 513576b90e v2.0: Major feature update
New Features:
- API key authentication for external access
- Apple Shortcuts integration endpoint (/api/v1/add)
- Full-text search across all articles
- Folders for organizing articles
- Highlights and notes on articles
- Reading stats with streaks
- Reading goals (daily/weekly/monthly)
- Import from Pocket/Instapaper
- RSS feed output
- PWA support for mobile
- Auto theme scheduling (day/night)
- Settings page with all configuration

API Endpoints:
- /api/v1/add - Add articles via API key
- /api/keys - Manage API keys
- /api/search - Full-text search
- /api/folders - Folder management
- /api/highlights - Highlights/notes
- /api/stats - Reading statistics
- /api/goals - Reading goals
- /api/import - Pocket/Instapaper import
- /api/rss - RSS feed

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-17 12:19:57 +00:00

95 lines
3.0 KiB
TypeScript

import { NextRequest, NextResponse } from "next/server";
import { db, schema } from "@/lib/db";
import { eq, asc } from "drizzle-orm";
import { v4 as uuidv4 } from "uuid";
// GET /api/folders - List all folders
export async function GET() {
try {
const folders = await db
.select()
.from(schema.folders)
.orderBy(asc(schema.folders.sortOrder));
return NextResponse.json(folders);
} catch (error) {
console.error("Error listing folders:", error);
return NextResponse.json({ error: "Failed to list folders" }, { status: 500 });
}
}
// POST /api/folders - Create folder
export async function POST(request: NextRequest) {
try {
const body = await request.json();
const { name, color, icon, parentId } = body;
if (!name) {
return NextResponse.json({ error: "Name is required" }, { status: 400 });
}
const id = uuidv4();
await db.insert(schema.folders).values({
id,
name,
color: color || "#3b82f6",
icon: icon || "folder",
parentId: parentId || null,
});
const folder = await db.select().from(schema.folders).where(eq(schema.folders.id, id)).limit(1);
return NextResponse.json(folder[0], { status: 201 });
} catch (error) {
console.error("Error creating folder:", error);
return NextResponse.json({ error: "Failed to create folder" }, { status: 500 });
}
}
// PATCH /api/folders - Update folder
export async function PATCH(request: NextRequest) {
try {
const body = await request.json();
const { id, name, color, icon, sortOrder } = body;
if (!id) {
return NextResponse.json({ error: "ID is required" }, { status: 400 });
}
const updates: Partial<schema.Folder> = {};
if (name) updates.name = name;
if (color) updates.color = color;
if (icon) updates.icon = icon;
if (sortOrder !== undefined) updates.sortOrder = sortOrder;
await db.update(schema.folders).set(updates).where(eq(schema.folders.id, id));
const folder = await db.select().from(schema.folders).where(eq(schema.folders.id, id)).limit(1);
return NextResponse.json(folder[0]);
} catch (error) {
console.error("Error updating folder:", error);
return NextResponse.json({ error: "Failed to update folder" }, { status: 500 });
}
}
// DELETE /api/folders?id=...
export async function DELETE(request: NextRequest) {
try {
const { searchParams } = new URL(request.url);
const id = searchParams.get("id");
if (!id) {
return NextResponse.json({ error: "ID is required" }, { status: 400 });
}
// Remove folder reference from articles
await db.update(schema.articles).set({ folderId: null }).where(eq(schema.articles.folderId, id));
// Delete folder
await db.delete(schema.folders).where(eq(schema.folders.id, id));
return NextResponse.json({ success: true });
} catch (error) {
console.error("Error deleting folder:", error);
return NextResponse.json({ error: "Failed to delete folder" }, { status: 500 });
}
}