← back to Norma

app/api/pulse/tracked/route.ts

182 lines

import { NextRequest, NextResponse } from 'next/server';
import { query } from '@/lib/db';
import { requireRole } from '@/lib/require-role';
import type { AuthSession } from '@/lib/auth';

/**
 * GET /api/pulse/tracked
 * Returns tracked petitions for the authenticated user.
 * Requires any authenticated role (admin, staff, pulse).
 *
 * Query params:
 *   ?page=1       — page number (default 1)
 *   ?limit=20     — results per page (default 20, max 100)
 */
export async function GET(request: NextRequest) {
  const result = requireRole(request, 'admin', 'staff', 'pulse');
  if (result instanceof NextResponse) return result;
  const session = result as AuthSession;

  try {
    const { searchParams } = new URL(request.url);
    let page = parseInt(searchParams.get('page') || '1', 10);
    if (isNaN(page) || page < 1) page = 1;
    let limit = parseInt(searchParams.get('limit') || '20', 10);
    if (isNaN(limit) || limit < 1) limit = 20;
    if (limit > 100) limit = 100;
    const offset = (page - 1) * limit;

    // Count total tracked
    const countRes = await query(
      `SELECT COUNT(*)::int AS total
       FROM pulse_tracked_petitions
       WHERE username = $1`,
      [session.username],
    );
    const total = countRes.rows[0]?.total ?? 0;

    // Fetch tracked petitions with petition details
    const dataRes = await query(
      `SELECT
         pt.id AS tracking_id,
         pt.created_at AS tracked_at,
         p.id AS petition_id,
         p.title,
         p.description,
         p.body,
         p.category,
         p.status,
         p.target,
         p.signature_count,
         p.signature_goal,
         p.is_featured,
         p.tags,
         p.updated_at AS petition_updated_at
       FROM pulse_tracked_petitions pt
       JOIN petitions p ON p.id = pt.petition_id
       WHERE pt.username = $1
       ORDER BY pt.created_at DESC
       LIMIT $2 OFFSET $3`,
      [session.username, limit, offset],
    );

    return NextResponse.json({
      tracked: dataRes.rows,
      total,
      page,
      limit,
      totalPages: Math.ceil(total / limit),
    });
  } catch (err) {
    console.error('[api/pulse/tracked] GET error:', (err as Error).message);
    return NextResponse.json(
      { error: 'Failed to fetch tracked petitions' },
      { status: 500 },
    );
  }
}

/**
 * POST /api/pulse/tracked
 * Track a petition for the authenticated user.
 * Body: { petition_id: string (UUID) }
 */
export async function POST(request: NextRequest) {
  const result = requireRole(request, 'admin', 'staff', 'pulse');
  if (result instanceof NextResponse) return result;
  const session = result as AuthSession;

  try {
    const body = await request.json();
    const petitionId = body.petition_id;

    if (!petitionId || typeof petitionId !== 'string') {
      return NextResponse.json(
        { error: 'petition_id is required' },
        { status: 400 },
      );
    }

    // Verify petition exists
    const petCheck = await query(
      `SELECT id FROM petitions WHERE id = $1`,
      [petitionId],
    );
    if (petCheck.rows.length === 0) {
      return NextResponse.json(
        { error: 'Petition not found' },
        { status: 404 },
      );
    }

    // Insert tracking (ON CONFLICT ignore duplicate)
    const insertRes = await query(
      `INSERT INTO pulse_tracked_petitions (username, petition_id)
       VALUES ($1, $2)
       ON CONFLICT (username, petition_id) DO NOTHING
       RETURNING id, created_at`,
      [session.username, petitionId],
    );

    if (insertRes.rows.length === 0) {
      return NextResponse.json({ message: 'Already tracking this petition' });
    }

    return NextResponse.json({
      tracked: insertRes.rows[0],
      message: 'Petition tracked successfully',
    }, { status: 201 });
  } catch (err) {
    console.error('[api/pulse/tracked] POST error:', (err as Error).message);
    return NextResponse.json(
      { error: 'Failed to track petition' },
      { status: 500 },
    );
  }
}

/**
 * DELETE /api/pulse/tracked
 * Untrack a petition for the authenticated user.
 * Body: { petition_id: string (UUID) }
 */
export async function DELETE(request: NextRequest) {
  const result = requireRole(request, 'admin', 'staff', 'pulse');
  if (result instanceof NextResponse) return result;
  const session = result as AuthSession;

  try {
    const body = await request.json();
    const petitionId = body.petition_id;

    if (!petitionId || typeof petitionId !== 'string') {
      return NextResponse.json(
        { error: 'petition_id is required' },
        { status: 400 },
      );
    }

    const deleteRes = await query(
      `DELETE FROM pulse_tracked_petitions
       WHERE username = $1 AND petition_id = $2
       RETURNING id`,
      [session.username, petitionId],
    );

    if (deleteRes.rows.length === 0) {
      return NextResponse.json(
        { error: 'Tracking not found' },
        { status: 404 },
      );
    }

    return NextResponse.json({ message: 'Petition untracked successfully' });
  } catch (err) {
    console.error('[api/pulse/tracked] DELETE error:', (err as Error).message);
    return NextResponse.json(
      { error: 'Failed to untrack petition' },
      { status: 500 },
    );
  }
}