/** * Queue API Endpoints * * Provides HTTP interface for queue operations: * - POST /api/queue - Enqueue Instagram URL for processing * - GET /api/queue - List all queue items with optional status filtering */ import { json, error } from '@sveltejs/kit'; import { queueManager } from '$lib/server/queue/QueueManager'; import type { RequestHandler } from './$types'; /** * POST /api/queue - Enqueue Instagram URL * * Body: { url: string } * Returns: { id: string, url: string, status: string, enqueuedAt: string } * * Validates Instagram URL format and enqueues for processing. * Returns 400 for invalid URLs, 500 for server errors. */ export const POST: RequestHandler = async ({ request }) => { try { // Parse JSON body with proper error handling let body; try { body = await request.json(); } catch (jsonError) { return error(400, { message: 'Invalid JSON in request body' }); } // Validate request body if (!body || typeof body !== 'object') { return error(400, { message: 'Request body must be JSON object' }); } const { url } = body; // Validate URL presence if (!url || typeof url !== 'string') { return error(400, { message: 'URL is required and must be a string' }); } // Validate Instagram URL format const instagramUrlPattern = /^https:\/\/(www\.)?instagram\.com\/p\/[a-zA-Z0-9_-]+\/?$/; if (!instagramUrlPattern.test(url)) { return error(400, { message: 'Invalid Instagram URL format. Expected: https://instagram.com/p/{post-id}' }); } // Enqueue the URL const queueItem = queueManager.enqueue(url); // Return minimal response (full details available at GET /api/queue/{id}) return json({ id: queueItem.id, url: queueItem.url, status: queueItem.status, enqueuedAt: queueItem.enqueuedAt }); } catch (err) { console.error('Failed to enqueue URL:', err); return error(500, { message: 'Internal server error' }); } }; /** * GET /api/queue - List queue items * * Query params: * - status?: string - Filter by status (pending, in_progress, success, unhealthy, error) * - limit?: number - Maximum items to return (default: 50, max: 200) * - offset?: number - Pagination offset (default: 0) * * Returns: { items: QueueItem[], total: number, hasMore: boolean } */ export const GET: RequestHandler = async ({ url }) => { try { const searchParams = url.searchParams; // Parse query parameters const statusFilter = searchParams.get('status'); const limitParam = searchParams.get('limit'); const offsetParam = searchParams.get('offset'); // Validate and parse limit let limit = 50; // default if (limitParam) { const parsedLimit = parseInt(limitParam, 10); if (isNaN(parsedLimit) || parsedLimit < 1) { return error(400, { message: 'Limit must be a positive integer' }); } if (parsedLimit > 200) { return error(400, { message: 'Limit cannot exceed 200' }); } limit = parsedLimit; } // Validate and parse offset let offset = 0; // default if (offsetParam) { const parsedOffset = parseInt(offsetParam, 10); if (isNaN(parsedOffset) || parsedOffset < 0) { return error(400, { message: 'Offset must be a non-negative integer' }); } offset = parsedOffset; } // Validate status filter const validStatuses = ['pending', 'in_progress', 'success', 'unhealthy', 'error']; if (statusFilter && !validStatuses.includes(statusFilter)) { return error(400, { message: `Invalid status filter. Must be one of: ${validStatuses.join(', ')}` }); } // Get all items let items = queueManager.getAll(); const totalCount = items.length; // Apply status filter if (statusFilter) { items = items.filter(item => item.status === statusFilter); } // Sort by enqueued time (newest first) items.sort((a, b) => new Date(b.enqueuedAt).getTime() - new Date(a.enqueuedAt).getTime()); // Apply pagination const paginatedItems = items.slice(offset, offset + limit); const hasMore = (offset + limit) < items.length; return json({ items: paginatedItems, total: statusFilter ? items.length : totalCount, hasMore, pagination: { offset, limit, count: paginatedItems.length } }); } catch (err) { console.error('Failed to list queue items:', err); return error(500, { message: 'Internal server error' }); } };