Scaffolds a Next.js app with email/password auth, encrypted IMAP/SMTP/CalDAV mailbox connections, a background sync job, Claude-generated daily briefings, and an approve-before-send draft reply flow. Includes a clean design system (light/dark) and deployment config (systemd unit, credential rotation script). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012o9j9RezxbZVKQMrB7oRLY
56 lines
1.7 KiB
TypeScript
56 lines
1.7 KiB
TypeScript
export const runtime = "nodejs";
|
|
|
|
import { NextResponse } from "next/server";
|
|
import { z } from "zod";
|
|
import { and, eq } from "drizzle-orm";
|
|
import { getCurrentSession } from "@/lib/auth/session";
|
|
import { db } from "@/lib/db/client";
|
|
import { emails, emailDrafts } from "@/lib/db/schema";
|
|
import { generateDraftReply } from "@/lib/ai/draftReply";
|
|
import { BRIEFING_MODEL } from "@/lib/ai/client";
|
|
|
|
const bodySchema = z.object({ emailId: z.string().min(1) });
|
|
|
|
export async function POST(request: Request) {
|
|
const session = await getCurrentSession();
|
|
if (!session) {
|
|
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
|
|
}
|
|
|
|
const parsed = bodySchema.safeParse(await request.json().catch(() => null));
|
|
if (!parsed.success) {
|
|
return NextResponse.json({ error: "Invalid input" }, { status: 400 });
|
|
}
|
|
|
|
const email = await db.query.emails.findFirst({
|
|
where: and(eq(emails.id, parsed.data.emailId), eq(emails.userId, session.user.id)),
|
|
});
|
|
if (!email) {
|
|
return NextResponse.json({ error: "Email not found" }, { status: 404 });
|
|
}
|
|
|
|
try {
|
|
const draft = await generateDraftReply({
|
|
from: email.fromAddress,
|
|
subject: email.subject,
|
|
bodyText: email.bodyText ?? email.snippet ?? "",
|
|
});
|
|
|
|
const [row] = await db
|
|
.insert(emailDrafts)
|
|
.values({
|
|
userId: session.user.id,
|
|
sourceEmailId: email.id,
|
|
generatedSubject: draft.subject,
|
|
generatedBody: draft.body,
|
|
modelUsed: BRIEFING_MODEL,
|
|
})
|
|
.returning();
|
|
|
|
return NextResponse.json({ draft: row });
|
|
} catch (err) {
|
|
const message = err instanceof Error ? err.message : String(err);
|
|
return NextResponse.json({ error: message }, { status: 500 });
|
|
}
|
|
}
|