← back to Norma
app/api/email-analyzer/subject-lines/route.ts
116 lines
/**
* Subject Line Maker + history.
*
* POST /api/email-analyzer/subject-lines/generate — AI: generate N subject options
* GET /api/email-analyzer/subject-lines?session_id=xxx — list saved subjects
* POST /api/email-analyzer/subject-lines — save a subject the user picked
* PATCH /api/email-analyzer/subject-lines — update favorite / notes
* DELETE /api/email-analyzer/subject-lines?id=xxx — remove
*/
import { NextRequest, NextResponse } from 'next/server';
import { query } from '@/lib/db';
import { requireRole } from '@/lib/require-role';
export async function GET(request: NextRequest) {
const auth = requireRole(request, 'admin', 'staff');
if (auth instanceof NextResponse) return auth;
try {
const sessionId = new URL(request.url).searchParams.get('session_id');
const where = sessionId ? 'WHERE session_id = $1' : '';
const params = sessionId ? [sessionId] : [];
const { rows } = await query(
`SELECT id, session_id, version_id, subject, style, reasoning,
is_favorite, notes, created_at
FROM email_analyzer_subject_lines
${where}
ORDER BY is_favorite DESC, created_at DESC
LIMIT 200`,
params,
);
return NextResponse.json({ subjects: rows });
} catch (err) {
console.error('[subject-lines] GET error:', (err as Error).message);
return NextResponse.json({ error: 'Failed to load subjects' }, { status: 500 });
}
}
export async function POST(request: NextRequest) {
const auth = requireRole(request, 'admin', 'staff');
if (auth instanceof NextResponse) return auth;
try {
const body = await request.json();
const { session_id, version_id, subject, style, reasoning, notes } = body;
if (!session_id || !subject) {
return NextResponse.json({ error: 'session_id and subject are required' }, { status: 400 });
}
const { rows } = await query(
`INSERT INTO email_analyzer_subject_lines
(session_id, version_id, subject, style, reasoning, notes)
VALUES ($1, $2, $3, $4, $5, $6)
RETURNING *`,
[session_id, version_id || null, subject, style || null, reasoning || null, notes || null],
);
return NextResponse.json({ subject: rows[0] }, { status: 201 });
} catch (err) {
console.error('[subject-lines] POST error:', (err as Error).message);
return NextResponse.json({ error: 'Failed to save subject' }, { status: 500 });
}
}
export async function PATCH(request: NextRequest) {
const auth = requireRole(request, 'admin', 'staff');
if (auth instanceof NextResponse) return auth;
try {
const { id, is_favorite, notes } = await request.json();
if (!id) return NextResponse.json({ error: 'id required' }, { status: 400 });
const sets: string[] = [];
const params: unknown[] = [];
if (is_favorite !== undefined) {
params.push(is_favorite);
sets.push(`is_favorite = $${params.length}`);
}
if (notes !== undefined) {
params.push(notes);
sets.push(`notes = $${params.length}`);
}
if (sets.length === 0) {
return NextResponse.json({ error: 'nothing to update' }, { status: 400 });
}
params.push(id);
const { rows, rowCount } = await query(
`UPDATE email_analyzer_subject_lines SET ${sets.join(', ')}
WHERE id = $${params.length} RETURNING *`,
params,
);
if (!rowCount) return NextResponse.json({ error: 'not found' }, { status: 404 });
return NextResponse.json({ subject: rows[0] });
} catch (err) {
console.error('[subject-lines] PATCH error:', (err as Error).message);
return NextResponse.json({ error: 'Failed to update' }, { status: 500 });
}
}
export async function DELETE(request: NextRequest) {
const auth = requireRole(request, 'admin', 'staff');
if (auth instanceof NextResponse) return auth;
try {
const id = new URL(request.url).searchParams.get('id');
if (!id) return NextResponse.json({ error: 'id required' }, { status: 400 });
const { rowCount } = await query(
`DELETE FROM email_analyzer_subject_lines WHERE id = $1`,
[id],
);
if (!rowCount) return NextResponse.json({ error: 'not found' }, { status: 404 });
return NextResponse.json({ deleted: true, id });
} catch (err) {
console.error('[subject-lines] DELETE error:', (err as Error).message);
return NextResponse.json({ error: 'Failed to delete' }, { status: 500 });
}
}