← back to Norma
app/api/drafts/[id]/versions/route.ts
109 lines
import { NextRequest, NextResponse } from 'next/server';
import { query } from '@/lib/db';
import { requireRole } from '@/lib/require-role';
import { auditLog } from '@/lib/audit';
import { getOrgId } from '@/lib/orgId';
type RouteContext = { params: Promise<{ id: string }> };
/**
* GET /api/drafts/[id]/versions
* List all versions for a draft, ordered by version_number DESC.
*/
export async function GET(request: NextRequest, context: RouteContext) {
const auth = requireRole(request, 'admin', 'staff');
if (auth instanceof NextResponse) return auth;
const orgId = auth.role === 'admin' ? getOrgId(request) : auth.orgId;
if (!orgId) {
return NextResponse.json({ error: 'Missing org context' }, { status: 400 });
}
const { id } = await context.params;
try {
// Verify draft exists and belongs to org
const draftCheck = await query(`SELECT id FROM drafts WHERE id = $1 AND org_id = $2`, [id, orgId]);
if (draftCheck.rowCount === 0) {
return NextResponse.json({ error: 'Draft not found' }, { status: 404 });
}
const result = await query(
`SELECT * FROM draft_versions
WHERE draft_id = $1
ORDER BY version_number DESC`,
[id]
);
return NextResponse.json({ versions: result.rows });
} catch (err) {
console.error('[api/drafts/[id]/versions] GET error:', (err as Error).message);
return NextResponse.json({ error: 'Failed to fetch versions' }, { status: 500 });
}
}
/**
* POST /api/drafts/[id]/versions
* Manually save a version snapshot. Copies current draft content into a new version entry.
* Body: { change_summary?: string }
*/
export async function POST(request: NextRequest, context: RouteContext) {
const auth = requireRole(request, 'admin', 'staff');
if (auth instanceof NextResponse) return auth;
const orgId = auth.role === 'admin' ? getOrgId(request) : auth.orgId;
if (!orgId) {
return NextResponse.json({ error: 'Missing org context' }, { status: 400 });
}
const { id } = await context.params;
try {
const body = await request.json().catch(() => ({}));
const changeSummary = body.change_summary || 'Manual snapshot';
// Fetch the current draft
const draftResult = await query(`SELECT * FROM drafts WHERE id = $1 AND org_id = $2`, [id, orgId]);
if (draftResult.rowCount === 0) {
return NextResponse.json({ error: 'Draft not found' }, { status: 404 });
}
const draft = draftResult.rows[0];
// Create the version snapshot using current_version as the version_number
const versionResult = await query(
`INSERT INTO draft_versions (draft_id, version_number, subject, body_html, body_text, change_summary, created_by)
VALUES ($1, $2, $3, $4, $5, $6, $7)
RETURNING *`,
[
id,
draft.current_version,
draft.subject,
draft.body_html,
draft.body_text || '',
changeSummary, auth.username,
]
);
// Increment the draft's current_version
await query(
`UPDATE drafts SET current_version = current_version + 1 WHERE id = $1 AND org_id = $2`,
[id, orgId]
);
const ip = request.headers.get('x-forwarded-for') || request.headers.get('x-real-ip') || undefined;
await auditLog(
'draft.version_saved',
'draft',
id,
{ version_number: draft.current_version, change_summary: changeSummary },
ip
);
return NextResponse.json({ version: versionResult.rows[0] }, { status: 201 });
} catch (err) {
console.error('[api/drafts/[id]/versions] POST error:', (err as Error).message);
return NextResponse.json({ error: 'Failed to save version' }, { status: 500 });
}
}