const express = require('express'); const { v4: uuidv4 } = require('uuid'); const fs = require('fs'); const path = require('path'); function createProductRoutes(db) { const router = express.Router(); // List products (optional filter by shop_id) router.get('/', (req, res) => { const { shop_id } = req.query; let products; if (shop_id) { products = db.prepare('SELECT * FROM products WHERE shop_id = ? ORDER BY created_at DESC').all(shop_id); } else { products = db.prepare('SELECT * FROM products ORDER BY created_at DESC').all(); } // Parse shop info const shopStmt = db.prepare('SELECT shop_name FROM shops WHERE id = ?'); products = products.map(p => ({ ...p, shop_name: shopStmt.get(p.shop_id)?.shop_name || 'Unknown' })); res.json(products); }); // Export products (optional filter by shop_id) router.get('/export', (req, res) => { const { shop_id } = req.query; let products; if (shop_id) { products = db.prepare('SELECT p.*, s.shop_name FROM products p LEFT JOIN shops s ON p.shop_id = s.id WHERE p.shop_id = ? ORDER BY p.created_at DESC').all(shop_id); } else { products = db.prepare('SELECT p.*, s.shop_name FROM products p LEFT JOIN shops s ON p.shop_id = s.id ORDER BY p.created_at DESC').all(); } res.setHeader('Content-Disposition', 'attachment; filename=products.json'); res.json(products); }); // Upload reference image for a product router.post('/:id/image', (req, res) => { const product = db.prepare('SELECT * FROM products WHERE id = ?').get(req.params.id); if (!product) return res.status(404).json({ error: 'Product not found' }); const { image } = req.body; if (!image) return res.status(400).json({ error: 'image (base64) required' }); let imagesRoot = path.join(__dirname, '..', 'images'); try { const savePathRow = db.prepare("SELECT value FROM configs WHERE key = 'image_save_path'").get(); if (savePathRow?.value?.trim()) imagesRoot = savePathRow.value.trim(); } catch (_) {} const imagesDir = path.join(imagesRoot, product.id); if (!fs.existsSync(imagesDir)) fs.mkdirSync(imagesDir, { recursive: true }); const filename = 'ref.png'; const filePath = path.join(imagesDir, filename); const base64Data = image.replace(/^data:image\/\w+;base64,/, ''); fs.writeFileSync(filePath, Buffer.from(base64Data, 'base64')); const publicPath = `/api/images/${product.id}/${filename}`; db.prepare("UPDATE products SET reference_image = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?") .run(publicPath, req.params.id); res.json({ reference_image: publicPath }); }); // Delete reference image for a product router.delete('/:id/image', (req, res) => { const product = db.prepare('SELECT * FROM products WHERE id = ?').get(req.params.id); if (!product) return res.status(404).json({ error: 'Product not found' }); if (!product.reference_image) return res.json({ success: true }); let imagesRoot = path.join(__dirname, '..', 'images'); try { const savePathRow = db.prepare("SELECT value FROM configs WHERE key = 'image_save_path'").get(); if (savePathRow?.value?.trim()) imagesRoot = savePathRow.value.trim(); } catch (_) {} const filePath = path.join(imagesRoot, product.id, 'ref.png'); if (fs.existsSync(filePath)) fs.unlinkSync(filePath); db.prepare("UPDATE products SET reference_image = '', updated_at = CURRENT_TIMESTAMP WHERE id = ?") .run(req.params.id); res.json({ success: true }); }); // Get product by id router.get('/:id', (req, res) => { const product = db.prepare('SELECT * FROM products WHERE id = ?').get(req.params.id); if (!product) return res.status(404).json({ error: 'Product not found' }); const shop = db.prepare('SELECT shop_name FROM shops WHERE id = ?').get(product.shop_id); res.json({ ...product, shop_name: shop?.shop_name || 'Unknown' }); }); // Create product router.post('/', (req, res) => { const { shop_id, title } = req.body; if (!shop_id || !title) { return res.status(400).json({ error: 'shop_id and title are required' }); } const shop = db.prepare('SELECT * FROM shops WHERE id = ?').get(shop_id); if (!shop) return res.status(404).json({ error: 'Shop not found' }); const id = uuidv4(); db.prepare('INSERT INTO products (id, shop_id, title) VALUES (?, ?, ?)').run(id, shop_id, title); res.status(201).json(db.prepare('SELECT * FROM products WHERE id = ?').get(id)); }); // Batch create products router.post('/batch', (req, res) => { const { shop_id, titles } = req.body; if (!shop_id || !Array.isArray(titles) || titles.length === 0) { return res.status(400).json({ error: 'shop_id and titles array are required' }); } const shop = db.prepare('SELECT * FROM shops WHERE id = ?').get(shop_id); if (!shop) return res.status(404).json({ error: 'Shop not found' }); const insert = db.prepare('INSERT INTO products (id, shop_id, title) VALUES (?, ?, ?)'); const products = []; const batchInsert = db.transaction((titles) => { for (const title of titles) { const id = uuidv4(); insert.run(id, shop_id, title); products.push(id); } }); batchInsert(titles); const created = db.prepare('SELECT * FROM products WHERE id IN (' + products.map(() => '?').join(',') + ')').all(...products); res.status(201).json(created); }); // Update product router.put('/:id', (req, res) => { const { title, status } = req.body; const product = db.prepare('SELECT * FROM products WHERE id = ?').get(req.params.id); if (!product) return res.status(404).json({ error: 'Product not found' }); db.prepare('UPDATE products SET title = ?, status = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?') .run(title || product.title, status || product.status, req.params.id); res.json(db.prepare('SELECT * FROM products WHERE id = ?').get(req.params.id)); }); // Batch delete products (POST because DELETE with body is not reliable) router.post('/batch-delete', (req, res) => { const { ids } = req.body; if (!Array.isArray(ids) || ids.length === 0) { return res.status(400).json({ error: 'ids array required' }); } const placeholders = ids.map(() => '?').join(','); const result = db.prepare('DELETE FROM products WHERE id IN (' + placeholders + ')').run(...ids); res.json({ deleted: result.changes }); }); // Batch delete products (legacy DELETE route) router.delete('/batch', (req, res) => { const { ids } = req.body; if (!Array.isArray(ids) || ids.length === 0) { return res.status(400).json({ error: 'ids array required' }); } const placeholders = ids.map(() => '?').join(','); const result = db.prepare('DELETE FROM products WHERE id IN (' + placeholders + ')').run(...ids); res.json({ deleted: result.changes }); }); // Delete product router.delete('/:id', (req, res) => { const result = db.prepare('DELETE FROM products WHERE id = ?').run(req.params.id); if (result.changes === 0) return res.status(404).json({ error: 'Product not found' }); res.json({ success: true }); }); // Import products from array router.post('/import', (req, res) => { const { shop_id, products: importProducts } = req.body; if (!shop_id || !Array.isArray(importProducts) || importProducts.length === 0) { return res.status(400).json({ error: 'shop_id and products array required' }); } const shop = db.prepare('SELECT * FROM shops WHERE id = ?').get(shop_id); if (!shop) return res.status(404).json({ error: 'Shop not found' }); const insert = db.prepare('INSERT OR IGNORE INTO products (id, shop_id, title) VALUES (?, ?, ?)'); let imported = 0; const batch = db.transaction(() => { for (const p of importProducts) { const title = typeof p === 'string' ? p : p.title; if (!title) continue; const id = uuidv4(); const info = insert.run(id, shop_id, title); if (info.changes > 0) imported++; } }); batch(); res.json({ total: importProducts.length, imported }); }); return router; } module.exports = createProductRoutes;