Initial commit - Dutchie dispensary scraper

This commit is contained in:
Kelly
2025-11-28 19:45:44 -07:00
commit 5757a8e9bd
23375 changed files with 3788799 additions and 0 deletions

183
backend/src/routes/campaigns.ts Executable file
View File

@@ -0,0 +1,183 @@
import { Router } from 'express';
import { authMiddleware, requireRole } from '../auth/middleware';
import { pool } from '../db/migrate';
const router = Router();
router.use(authMiddleware);
// Get all campaigns
router.get('/', async (req, res) => {
try {
const result = await pool.query(`
SELECT c.*, COUNT(cp.product_id) as product_count
FROM campaigns c
LEFT JOIN campaign_products cp ON c.id = cp.campaign_id
GROUP BY c.id
ORDER BY c.created_at DESC
`);
res.json({ campaigns: result.rows });
} catch (error) {
console.error('Error fetching campaigns:', error);
res.status(500).json({ error: 'Failed to fetch campaigns' });
}
});
// Get single campaign with products
router.get('/:id', async (req, res) => {
try {
const { id } = req.params;
const campaignResult = await pool.query(`
SELECT * FROM campaigns WHERE id = $1
`, [id]);
if (campaignResult.rows.length === 0) {
return res.status(404).json({ error: 'Campaign not found' });
}
const productsResult = await pool.query(`
SELECT p.*, cp.display_order
FROM products p
JOIN campaign_products cp ON p.id = cp.product_id
WHERE cp.campaign_id = $1
ORDER BY cp.display_order
`, [id]);
res.json({
campaign: campaignResult.rows[0],
products: productsResult.rows
});
} catch (error) {
console.error('Error fetching campaign:', error);
res.status(500).json({ error: 'Failed to fetch campaign' });
}
});
// Create campaign
router.post('/', requireRole('superadmin', 'admin'), async (req, res) => {
try {
const { name, slug, description, display_style, active, start_date, end_date } = req.body;
if (!name || !slug) {
return res.status(400).json({ error: 'Name and slug required' });
}
const result = await pool.query(`
INSERT INTO campaigns (name, slug, description, display_style, active, start_date, end_date)
VALUES ($1, $2, $3, $4, $5, $6, $7)
RETURNING *
`, [name, slug, description, display_style || 'grid', active !== false, start_date, end_date]);
res.status(201).json({ campaign: result.rows[0] });
} catch (error: any) {
console.error('Error creating campaign:', error);
if (error.code === '23505') {
return res.status(409).json({ error: 'Campaign slug already exists' });
}
res.status(500).json({ error: 'Failed to create campaign' });
}
});
// Update campaign
router.put('/:id', requireRole('superadmin', 'admin'), async (req, res) => {
try {
const { id } = req.params;
const { name, slug, description, display_style, active, start_date, end_date } = req.body;
const result = await pool.query(`
UPDATE campaigns
SET name = COALESCE($1, name),
slug = COALESCE($2, slug),
description = COALESCE($3, description),
display_style = COALESCE($4, display_style),
active = COALESCE($5, active),
start_date = COALESCE($6, start_date),
end_date = COALESCE($7, end_date),
updated_at = CURRENT_TIMESTAMP
WHERE id = $8
RETURNING *
`, [name, slug, description, display_style, active, start_date, end_date, id]);
if (result.rows.length === 0) {
return res.status(404).json({ error: 'Campaign not found' });
}
res.json({ campaign: result.rows[0] });
} catch (error: any) {
console.error('Error updating campaign:', error);
if (error.code === '23505') {
return res.status(409).json({ error: 'Campaign slug already exists' });
}
res.status(500).json({ error: 'Failed to update campaign' });
}
});
// Delete campaign
router.delete('/:id', requireRole('superadmin'), async (req, res) => {
try {
const { id } = req.params;
const result = await pool.query(`
DELETE FROM campaigns WHERE id = $1 RETURNING id
`, [id]);
if (result.rows.length === 0) {
return res.status(404).json({ error: 'Campaign not found' });
}
res.json({ message: 'Campaign deleted successfully' });
} catch (error) {
console.error('Error deleting campaign:', error);
res.status(500).json({ error: 'Failed to delete campaign' });
}
});
// Add product to campaign
router.post('/:id/products', requireRole('superadmin', 'admin'), async (req, res) => {
try {
const { id } = req.params;
const { product_id, display_order } = req.body;
if (!product_id) {
return res.status(400).json({ error: 'Product ID required' });
}
const result = await pool.query(`
INSERT INTO campaign_products (campaign_id, product_id, display_order)
VALUES ($1, $2, $3)
ON CONFLICT (campaign_id, product_id)
DO UPDATE SET display_order = $3
RETURNING *
`, [id, product_id, display_order || 0]);
res.status(201).json({ campaign_product: result.rows[0] });
} catch (error) {
console.error('Error adding product to campaign:', error);
res.status(500).json({ error: 'Failed to add product to campaign' });
}
});
// Remove product from campaign
router.delete('/:id/products/:product_id', requireRole('superadmin', 'admin'), async (req, res) => {
try {
const { id, product_id } = req.params;
const result = await pool.query(`
DELETE FROM campaign_products
WHERE campaign_id = $1 AND product_id = $2
RETURNING *
`, [id, product_id]);
if (result.rows.length === 0) {
return res.status(404).json({ error: 'Product not in campaign' });
}
res.json({ message: 'Product removed from campaign' });
} catch (error) {
console.error('Error removing product from campaign:', error);
res.status(500).json({ error: 'Failed to remove product from campaign' });
}
});
export default router;