← back to Grant

app/api/news/route.ts

78 lines

import { NextRequest, NextResponse } from 'next/server';
import { query } from '@/lib/db';
import { verifyAuthWithOrg, resolveOrgId } from '@/lib/auth';

/* ─── GET /api/news ───────────────────────────────────────────────────────── */
export async function GET(request: NextRequest) {
  const session = verifyAuthWithOrg(request);
  if (!session) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });

  try {
    const url = new URL(request.url);
    const search = url.searchParams.get('search');
    const sourceType = url.searchParams.get('source_type');

    const orgId = await resolveOrgId(session);
    if (!orgId) return NextResponse.json({ rows: [] });

    let sql = 'SELECT id, org_id, headline, outlet, url, published_at, summary, tags, relevance_score, author_name, source_type, created_at, updated_at FROM news_items WHERE org_id = $1';
    const params: unknown[] = [orgId];
    let paramIdx = 2;

    if (search) {
      sql += ` AND (headline ILIKE $${paramIdx} OR summary ILIKE $${paramIdx} OR outlet ILIKE $${paramIdx})`;
      params.push(`%${search}%`);
      paramIdx++;
    }

    if (sourceType && sourceType !== 'all') {
      sql += ` AND source_type = $${paramIdx}`;
      params.push(sourceType);
      paramIdx++;
    }

    sql += ' ORDER BY published_at DESC NULLS LAST, created_at DESC';

    const result = await query(sql, params);
    return NextResponse.json({ rows: result.rows });
  } catch (err) {
    console.error('[news GET]', err);
    return NextResponse.json({ error: 'Failed to fetch news' }, { status: 500 });
  }
}

/* ─── POST /api/news ──────────────────────────────────────────────────────── */
export async function POST(request: NextRequest) {
  const session = verifyAuthWithOrg(request);
  if (!session) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });

  try {
    const body = await request.json();
    const orgId = await resolveOrgId(session);
    if (!orgId) return NextResponse.json({ error: 'No organization found' }, { status: 400 });

    const result = await query(
      `INSERT INTO news_items (org_id, headline, outlet, url, published_at, summary, tags, relevance_score, author_name, source_type)
       VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)
       RETURNING id, org_id, headline, outlet, url, published_at, summary, tags, relevance_score, author_name, source_type, created_at, updated_at`,
      [
        orgId,
        body.headline,
        body.outlet || null,
        body.url || null,
        body.published_at || null,
        body.summary || null,
        body.tags || [],
        body.relevance_score || null,
        body.author_name || null,
        body.source_type || 'manual',
      ],
    );

    return NextResponse.json(result.rows[0], { status: 201 });
  } catch (err) {
    console.error('[news POST]', err);
    return NextResponse.json({ error: 'Failed to create news item' }, { status: 500 });
  }
}