Add Dutchie AZ data pipeline and public API v1
- Add dutchie-az module with GraphQL product crawler, scheduler, and admin UI - Add public API v1 endpoints (/api/v1/products, /categories, /brands, /specials, /menu) - API key auth maps dispensary to dutchie_az store for per-dispensary data access - Add frontend pages for Dutchie AZ stores, store details, and schedule management - Update Layout with Dutchie AZ navigation section 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -27,18 +27,18 @@ router.get('/', requireRole('superadmin', 'admin'), async (req, res) => {
|
||||
}
|
||||
});
|
||||
|
||||
// Get all stores for dropdown (must be before /:id to avoid route conflict)
|
||||
router.get('/stores', requireRole('superadmin', 'admin'), async (req, res) => {
|
||||
// Get all dispensaries for dropdown (must be before /:id to avoid route conflict)
|
||||
router.get('/dispensaries', requireRole('superadmin', 'admin'), async (req, res) => {
|
||||
try {
|
||||
const result = await pool.query(`
|
||||
SELECT id, name
|
||||
FROM stores
|
||||
FROM dispensaries
|
||||
ORDER BY name
|
||||
`);
|
||||
res.json({ stores: result.rows });
|
||||
res.json({ dispensaries: result.rows });
|
||||
} catch (error) {
|
||||
console.error('Error fetching stores:', error);
|
||||
res.status(500).json({ error: 'Failed to fetch stores' });
|
||||
console.error('Error fetching dispensaries:', error);
|
||||
res.status(500).json({ error: 'Failed to fetch dispensaries' });
|
||||
}
|
||||
});
|
||||
|
||||
@@ -67,22 +67,22 @@ router.get('/:id', requireRole('superadmin', 'admin'), async (req, res) => {
|
||||
// Create new API permission
|
||||
router.post('/', requireRole('superadmin', 'admin'), async (req, res) => {
|
||||
try {
|
||||
const { user_name, allowed_ips, allowed_domains, store_id } = req.body;
|
||||
const { user_name, allowed_ips, allowed_domains, dispensary_id } = req.body;
|
||||
|
||||
if (!user_name) {
|
||||
return res.status(400).json({ error: 'User name is required' });
|
||||
}
|
||||
|
||||
if (!store_id) {
|
||||
return res.status(400).json({ error: 'Store is required' });
|
||||
if (!dispensary_id) {
|
||||
return res.status(400).json({ error: 'Dispensary is required' });
|
||||
}
|
||||
|
||||
// Get store name for display
|
||||
const storeResult = await pool.query('SELECT name FROM stores WHERE id = $1', [store_id]);
|
||||
if (storeResult.rows.length === 0) {
|
||||
return res.status(400).json({ error: 'Invalid store ID' });
|
||||
// Get dispensary name for display
|
||||
const dispensaryResult = await pool.query('SELECT name FROM dispensaries WHERE id = $1', [dispensary_id]);
|
||||
if (dispensaryResult.rows.length === 0) {
|
||||
return res.status(400).json({ error: 'Invalid dispensary ID' });
|
||||
}
|
||||
const storeName = storeResult.rows[0].name;
|
||||
const dispensaryName = dispensaryResult.rows[0].name;
|
||||
|
||||
const apiKey = generateApiKey();
|
||||
|
||||
@@ -93,8 +93,8 @@ router.post('/', requireRole('superadmin', 'admin'), async (req, res) => {
|
||||
allowed_ips,
|
||||
allowed_domains,
|
||||
is_active,
|
||||
store_id,
|
||||
store_name
|
||||
dispensary_id,
|
||||
dispensary_name
|
||||
)
|
||||
VALUES ($1, $2, $3, $4, 1, $5, $6)
|
||||
RETURNING *
|
||||
@@ -103,8 +103,8 @@ router.post('/', requireRole('superadmin', 'admin'), async (req, res) => {
|
||||
apiKey,
|
||||
allowed_ips || null,
|
||||
allowed_domains || null,
|
||||
store_id,
|
||||
storeName
|
||||
dispensary_id,
|
||||
dispensaryName
|
||||
]);
|
||||
|
||||
res.status(201).json({
|
||||
|
||||
Reference in New Issue
Block a user