← back to Norma

app/api/statements/route.ts

142 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';
import { getBrand } from '@/lib/brand';

/**
 * GET /api/statements
 * List statements with optional filters: status, event_type, urgency
 */
export async function GET(request: NextRequest) {
  const auth = requireRole(request, 'admin', 'staff');
  if (auth instanceof NextResponse) return auth;

  const sp = request.nextUrl.searchParams;
  const status = sp.get('status');
  const eventType = sp.get('event_type');
  const urgency = sp.get('urgency');
  const limit = Math.min(parseInt(sp.get('limit') || '50', 10), 200);
  const offset = parseInt(sp.get('offset') || '0', 10);

  const orgId = auth.role === 'admin' ? getOrgId(request) : auth.orgId;

  const conditions: string[] = [];
  const params: unknown[] = [];
  let idx = 1;

  if (orgId) {
    conditions.push(`org_id = $${idx++}`);
    params.push(orgId);
  }

  if (status) {
    conditions.push(`status = $${idx++}`);
    params.push(status);
  }
  if (eventType) {
    conditions.push(`event_type = $${idx++}`);
    params.push(eventType);
  }
  if (urgency) {
    conditions.push(`urgency = $${idx++}`);
    params.push(urgency);
  }

  const where = conditions.length > 0 ? `WHERE ${conditions.join(' AND ')}` : '';

  const [countRes, rowsRes] = await Promise.all([
    query(`SELECT COUNT(*) FROM statements ${where}`, params),
    query(
      `SELECT id, event_type, event_title, event_url, event_date, title, quote,
              quote_attribution, tone, category, urgency, tags, status,
              published_at, published_channels, ai_confidence, version,
              reviewed_by, reviewed_at, created_at, updated_at
       FROM statements ${where}
       ORDER BY created_at DESC
       LIMIT $${idx++} OFFSET $${idx++}`,
      [...params, limit, offset],
    ),
  ]);

  return NextResponse.json({
    statements: rowsRes.rows,
    total: parseInt(countRes.rows[0].count, 10),
    limit,
    offset,
  });
}

/**
 * POST /api/statements
 * Manually create a statement (not AI-generated).
 */
export async function POST(request: NextRequest) {
  const auth = requireRole(request, 'admin', 'staff');
  if (auth instanceof NextResponse) return auth;

  try {
    const data = await request.json();

    if (!data.title || !data.body_html || !data.event_title) {
      return NextResponse.json(
        { error: 'title, body_html, and event_title are required' },
        { status: 400 },
      );
    }

    // Strip HTML for plain text
    const bodyText = (data.body_html as string)
      .replace(/<br\s*\/?>/gi, '\n')
      .replace(/<\/p>/gi, '\n\n')
      .replace(/<li>/gi, '- ')
      .replace(/<\/li>/gi, '\n')
      .replace(/<[^>]+>/g, '')
      .replace(/\n{3,}/g, '\n\n')
      .trim();

    const orgId = auth.role === 'admin' ? getOrgId(request) : auth.orgId;
    const brand = await getBrand(orgId);
    const defaultAttribution = brand.staff?.[0]
      ? `${brand.staff[0].name}, ${brand.staff[0].role}`
      : brand.name;

    const result = await query(
      `INSERT INTO statements
         (event_type, event_title, event_summary, event_url, event_date,
          title, body_html, body_text, quote, quote_attribution,
          tone, category, urgency, tags, status, news_item_id, org_id)
       VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16,$17)
       RETURNING *`,
      [
        data.event_type || 'news',
        data.event_title,
        data.event_summary || null,
        data.event_url || null,
        data.event_date || null,
        data.title,
        data.body_html,
        bodyText,
        data.quote || null,
        data.quote_attribution || defaultAttribution,
        data.tone || 'press_release',
        data.category || null,
        data.urgency || 'standard',
        data.tags || [],
        data.status || 'draft',
        data.news_item_id || null,
        orgId || null,
      ],
    );

    const ip = request.headers.get('x-forwarded-for') || request.headers.get('x-real-ip') || undefined;
    await auditLog('statement.created', 'statement', result.rows[0].id, { event_title: data.event_title }, ip);

    return NextResponse.json({ statement: result.rows[0] }, { status: 201 });
  } catch (err) {
    console.error('[api/statements] POST error:', (err as Error).message);
    return NextResponse.json({ error: 'Failed to create statement' }, { status: 500 });
  }
}