← back to Norma
app/api/scheduled/route.ts
77 lines
import { NextRequest, NextResponse } from 'next/server';
import { query } from '@/lib/db';
import { requireRole } from '@/lib/require-role';
/**
* GET /api/scheduled
* List scheduled dispatches. Supports filters:
* ?status=scheduled (default: all)
* ?platform=twitter
* ?from=2026-03-01T00:00:00Z
* ?to=2026-03-31T23:59:59Z
* ?limit=50
*/
export async function GET(request: NextRequest) {
const auth = requireRole(request, 'admin', 'staff');
if (auth instanceof NextResponse) return auth;
try {
const url = new URL(request.url);
const status = url.searchParams.get('status');
const platform = url.searchParams.get('platform');
const from = url.searchParams.get('from');
const to = url.searchParams.get('to');
const limit = Math.min(parseInt(url.searchParams.get('limit') || '100', 10), 500);
const conditions: string[] = [];
const values: unknown[] = [];
let idx = 1;
if (status) {
conditions.push(`sd.status = $${idx++}`);
values.push(status);
}
if (platform) {
conditions.push(`sd.platform = $${idx++}`);
values.push(platform);
}
if (from) {
conditions.push(`sd.scheduled_at >= $${idx++}`);
values.push(from);
}
if (to) {
conditions.push(`sd.scheduled_at <= $${idx++}`);
values.push(to);
}
const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(' AND ')}` : '';
const { rows } = await query(
`SELECT sd.*,
pp.status AS pipeline_status,
pp.ai_confidence,
pp.auto_generated
FROM scheduled_dispatches sd
LEFT JOIN petition_pipeline pp ON sd.pipeline_id = pp.id
${whereClause}
ORDER BY sd.scheduled_at ASC
LIMIT $${idx}`,
[...values, limit],
);
// Summary counts
const { rows: summary } = await query(
`SELECT status, COUNT(*)::int AS count FROM scheduled_dispatches GROUP BY status`,
);
return NextResponse.json({
items: rows,
total: rows.length,
summary: Object.fromEntries(summary.map(r => [r.status, r.count])),
});
} catch (err) {
console.error('[api/scheduled] error:', (err as Error).message);
return NextResponse.json({ error: 'Failed to list scheduled dispatches' }, { status: 500 });
}
}