Files
project-claude/src/app/api/drafts/generate/route.ts
T
ogrechkoandClaude Sonnet 5 bcf0e86618 Build AI Chief of Staff MVP: auth, mailbox sync, briefings, draft replies
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
2026-07-26 10:54:34 +00:00

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 });
}
}