import { Router } from 'express'; import Product from '../models/Product.js'; const router = Router(); /** * @swagger * /api/products/search: * get: * summary: Search parapharmacy products * tags: [Products] * parameters: * - in: query * name: q * schema: * type: string * description: Search query * - in: query * name: category * schema: * type: string * description: Filter by category * - in: query * name: brand * schema: * type: string * description: Filter by brand * - in: query * name: page * schema: * type: integer * default: 1 * - in: query * name: limit * schema: * type: integer * default: 20 * responses: * 200: * description: Search results */ router.get('/search', async (req, res) => { try { const { q, category, brand, page = 1, limit = 20 } = req.query; const result = await Product.search(q, { page: parseInt(page), limit: Math.min(parseInt(limit), 50), category, brand, }); res.json(result); } catch (error) { console.error('[Products] Search error:', error); res.status(500).json({ error: 'Error searching products' }); } }); /** * @swagger * /api/products/categories: * get: * summary: Get all categories * tags: [Products] * responses: * 200: * description: List of categories */ router.get('/categories', async (req, res) => { try { const categories = await Product.distinct('category'); res.json(categories.filter(Boolean).sort()); } catch (error) { console.error('[Products] Categories error:', error); res.status(500).json({ error: 'Error fetching categories' }); } }); /** * @swagger * /api/products/brands: * get: * summary: Get all brands * tags: [Products] * responses: * 200: * description: List of brands */ router.get('/brands', async (req, res) => { try { const brands = await Product.distinct('brand'); res.json(brands.filter(Boolean).sort()); } catch (error) { console.error('[Products] Brands error:', error); res.status(500).json({ error: 'Error fetching brands' }); } }); /** * @swagger * /api/products: * get: * summary: List all products * tags: [Products] * parameters: * - in: query * name: page * schema: * type: integer * default: 1 * - in: query * name: limit * schema: * type: integer * default: 20 * - in: query * name: source * schema: * type: string * description: Filter by source * responses: * 200: * description: List of products */ router.get('/', async (req, res) => { try { const { page = 1, limit = 20, source } = req.query; const filter = {}; if (source) filter.source = source; const skip = (parseInt(page) - 1) * parseInt(limit); const [products, total] = await Promise.all([ Product.find(filter) .sort({ scraped_at: -1 }) .skip(skip) .limit(Math.min(parseInt(limit), 50)) .lean(), Product.countDocuments(filter), ]); res.json({ results: products, total, page: parseInt(page), pages: Math.ceil(total / parseInt(limit)), }); } catch (error) { console.error('[Products] List error:', error); res.status(500).json({ error: 'Error fetching products' }); } }); /** * @swagger * /api/products/{id}: * get: * summary: Get product by ID * tags: [Products] * parameters: * - in: path * name: id * required: true * schema: * type: string * responses: * 200: * description: Product details * 404: * description: Product not found */ router.get('/:id', async (req, res) => { try { const product = await Product.findById(req.params.id).lean(); if (!product) { return res.status(404).json({ error: 'Product not found' }); } res.json(product); } catch (error) { console.error('[Products] Get error:', error); res.status(500).json({ error: 'Error fetching product' }); } }); /** * @swagger * /api/products: * post: * summary: Create or update a product (for scraper) * tags: [Products] * requestBody: * required: true * content: * application/json: * schema: * type: object * required: * - name * - price * - source * - source_product_id * - source_url * responses: * 201: * description: Product created/updated */ router.post('/', async (req, res) => { try { const { name, brand, category, subcategory, description, image_url, source_url, price, original_price, source, source_product_id, available, rating, review_count, } = req.body; if (!name || !price || !source || !source_product_id || !source_url) { return res.status(400).json({ error: 'Missing required fields' }); } const product = await Product.upsert({ name, brand, category, subcategory, description, image_url, source_url, price, original_price, source, source_product_id, available: available !== false, rating, review_count, scraped_at: new Date(), }); res.status(201).json(product); } catch (error) { console.error('[Products] Create error:', error); res.status(500).json({ error: 'Error creating product' }); } }); /** * @swagger * /api/products/bulk: * post: * summary: Bulk upsert products (for scraper) * tags: [Products] * requestBody: * required: true * content: * application/json: * schema: * type: object * required: * - products * properties: * products: * type: array * responses: * 200: * description: Upsert results */ router.post('/bulk', async (req, res) => { try { const { products } = req.body; if (!Array.isArray(products)) { return res.status(400).json({ error: 'products must be an array' }); } const results = { created: 0, updated: 0, errors: 0, }; for (const productData of products) { try { const existing = await Product.findBySource( productData.source, productData.source_product_id ); if (existing) { Object.assign(existing, productData, { scraped_at: new Date() }); await existing.save(); results.updated++; } else { await Product.create(productData); results.created++; } } catch (err) { console.error('[Products] Bulk upsert error:', err.message); results.errors++; } } res.json(results); } catch (error) { console.error('[Products] Bulk error:', error); res.status(500).json({ error: 'Error in bulk upsert' }); } }); /** * @swagger * /api/products/{id}: * put: * summary: Update a product * tags: [Products] * parameters: * - in: path * name: id * required: true * schema: * type: string * responses: * 200: * description: Updated product * 404: * description: Product not found */ router.put('/:id', async (req, res) => { try { const product = await Product.findByIdAndUpdate( req.params.id, { ...req.body, updated_at: new Date() }, { new: true } ); if (!product) { return res.status(404).json({ error: 'Product not found' }); } res.json(product); } catch (error) { console.error('[Products] Update error:', error); res.status(500).json({ error: 'Error updating product' }); } }); /** * @swagger * /api/products/{id}: * delete: * summary: Delete a product * tags: [Products] * parameters: * - in: path * name: id * required: true * schema: * type: string * responses: * 204: * description: Product deleted * 404: * description: Product not found */ router.delete('/:id', async (req, res) => { try { const product = await Product.findByIdAndDelete(req.params.id); if (!product) { return res.status(404).json({ error: 'Product not found' }); } res.status(204).end(); } catch (error) { console.error('[Products] Delete error:', error); res.status(500).json({ error: 'Error deleting product' }); } }); export default router;