import { NextRequest, NextResponse } from 'next/server'; import { saveTask, listAllTasks, checkRateLimit } from '@/lib/task-store'; import { isAllowedUrl, sanitizeHeaders } from '@/lib/ssrf-guard'; export async function POST(request: NextRequest) { const ip = request.headers.get('x-forwarded-for')?.split(',')[0]?.trim() ?? request.headers.get('x-real-ip') ?? 'unknown'; const allowed = await checkRateLimit(ip, 30, 3600); // 30 tasks/hour per IP if (!allowed) { return NextResponse.json({ error: 'Rate limit exceeded' }, { status: 429 }); } let body: any; try { body = await request.json(); } catch { return NextResponse.json({ error: 'Invalid JSON body' }, { status: 400 }); } const url = typeof body.url === 'string' ? body.url : ''; const executeAt = typeof body.execute_at === 'number' ? body.execute_at : 0; const headers = typeof body.headers === 'object' && body.headers !== null ? body.headers : {}; const payload = body.payload ?? null; const retries = typeof body.retries === 'number' ? Math.max(0, Math.min(body.retries, 10)) : 3; const retryInterval = typeof body.retry_interval === 'number' ? Math.max(1000, body.retry_interval) : 5000; if (!url) { return NextResponse.json({ error: 'Missing url' }, { status: 400 }); } const urlCheck = isAllowedUrl(url); if (!urlCheck.ok) { return NextResponse.json({ error: `Blocked URL: ${urlCheck.reason}` }, { status: 400 }); } const nowSec = Math.floor(Date.now() / 1000); if (executeAt <= nowSec) { return NextResponse.json({ error: 'execute_at must be in the future' }, { status: 400 }); } if (executeAt > nowSec + 86400 * 365 * 2) { return NextResponse.json({ error: 'execute_at too far in the future (max 2 years)' }, { status: 400 }); } const id = `task_${crypto.randomUUID()}`; const task = { id, url, executeAt, headers: sanitizeHeaders(headers), payload, retries, retryInterval, createdAt: nowSec, }; try { await saveTask(task); return NextResponse.json(task, { status: 201 }); } catch (e: any) { return NextResponse.json({ error: e?.message || 'Failed to save task' }, { status: 500 }); } } export async function GET() { try { const tasks = await listAllTasks(); return NextResponse.json(tasks); } catch (e: any) { return NextResponse.json({ error: e?.message || 'Failed to list tasks' }, { status: 500 }); } }