← back to Patty

app/api/campaigns/route.ts

132 lines

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

export async function GET(request: NextRequest) {
  const user = verifyAuth(request);
  if (!user) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });

  const url = new URL(request.url);
  const status = url.searchParams.get('status');

  let sql = `SELECT c.*, p.title as petition_title
             FROM email_campaigns c
             LEFT JOIN petitions p ON c.petition_id = p.id`;
  const params: unknown[] = [];

  if (status && status !== 'all') {
    params.push(status);
    sql += ` WHERE c.status = $${params.length}`;
  }

  sql += ` ORDER BY c.created_at DESC`;

  try {
    const result = await query(sql, params);

    // Get status counts
    const countsResult = await query(
      `SELECT status, COUNT(*)::int as count FROM email_campaigns GROUP BY status`
    );
    const statusCounts: Record<string, number> = { draft: 0, scheduled: 0, sending: 0, sent: 0, failed: 0 };
    for (const row of countsResult.rows) {
      statusCounts[row.status] = row.count;
    }
    statusCounts.all = Object.values(statusCounts).reduce((a, b) => a + b, 0);

    return NextResponse.json({ rows: result.rows, statusCounts });
  } catch (err) {
    console.error('[api/campaigns] GET error:', (err as Error).message);
    return NextResponse.json({ error: 'Failed to fetch campaigns' }, { status: 500 });
  }
}

export async function POST(request: NextRequest) {
  const user = verifyAuth(request);
  if (!user) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });

  try {
    const body = await request.json();
    const { subject, body_html, body_text, petition_id, send_to, scheduled_at } = body;

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

    // Get user_id
    const userResult = await query(`SELECT id FROM users LIMIT 1`);
    const userId = userResult.rows[0]?.id;
    if (!userId) {
      return NextResponse.json({ error: 'No user found' }, { status: 500 });
    }

    // Count subscribers for recipient_count
    const subCount = await query(`SELECT COUNT(*)::int as count FROM email_subscribers WHERE is_active = true`);
    const recipientCount = subCount.rows[0]?.count || 0;

    const result = await query(
      `INSERT INTO email_campaigns (user_id, petition_id, subject, body_html, body_text, send_to, recipient_count, scheduled_at, status)
       VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)
       RETURNING *`,
      [
        userId,
        petition_id || null,
        subject,
        body_html,
        body_text || null,
        send_to || 'all',
        recipientCount,
        scheduled_at || null,
        scheduled_at ? 'scheduled' : 'draft',
      ]
    );

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

export async function PATCH(request: NextRequest) {
  const user = verifyAuth(request);
  if (!user) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });

  try {
    const body = await request.json();
    const { id, ...updates } = body;

    if (!id) {
      return NextResponse.json({ error: 'Campaign id is required' }, { status: 400 });
    }

    const allowedFields = ['subject', 'body_html', 'body_text', 'status', 'send_to', 'scheduled_at', 'sent_at', 'recipient_count', 'open_count', 'click_count'];
    const setClauses: string[] = [];
    const params: unknown[] = [];

    for (const [key, value] of Object.entries(updates)) {
      if (allowedFields.includes(key)) {
        params.push(value);
        setClauses.push(`${key} = $${params.length}`);
      }
    }

    if (setClauses.length === 0) {
      return NextResponse.json({ error: 'No valid fields to update' }, { status: 400 });
    }

    params.push(id);
    const sql = `UPDATE email_campaigns SET ${setClauses.join(', ')} WHERE id = $${params.length} RETURNING *`;

    const result = await query(sql, params);
    if (result.rows.length === 0) {
      return NextResponse.json({ error: 'Campaign not found' }, { status: 404 });
    }

    return NextResponse.json({ row: result.rows[0] });
  } catch (err) {
    console.error('[api/campaigns] PATCH error:', (err as Error).message);
    return NextResponse.json({ error: 'Failed to update campaign' }, { status: 500 });
  }
}