The job_run_logs table tracks scheduled job orchestration, not individual worker jobs. Worker info (worker_id, worker_hostname) belongs on dispensary_crawl_jobs, not job_run_logs. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
153 lines
5.0 KiB
JavaScript
153 lines
5.0 KiB
JavaScript
"use strict";
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
const express_1 = require("express");
|
|
const middleware_1 = require("../auth/middleware");
|
|
const migrate_1 = require("../db/migrate");
|
|
const router = (0, express_1.Router)();
|
|
router.use(middleware_1.authMiddleware);
|
|
// Get all changes with optional status filter
|
|
router.get('/', async (req, res) => {
|
|
try {
|
|
const { status } = req.query;
|
|
let query = `
|
|
SELECT
|
|
dc.id,
|
|
dc.dispensary_id,
|
|
dc.field_name,
|
|
dc.old_value,
|
|
dc.new_value,
|
|
dc.source,
|
|
dc.confidence_score,
|
|
dc.change_notes,
|
|
dc.status,
|
|
dc.requires_recrawl,
|
|
dc.created_at,
|
|
dc.reviewed_at,
|
|
dc.reviewed_by,
|
|
dc.rejection_reason,
|
|
d.name as dispensary_name,
|
|
d.slug as dispensary_slug,
|
|
d.city,
|
|
d.state
|
|
FROM dispensary_changes dc
|
|
JOIN dispensaries d ON dc.dispensary_id = d.id
|
|
`;
|
|
const params = [];
|
|
if (status) {
|
|
query += ` WHERE dc.status = $1`;
|
|
params.push(status);
|
|
}
|
|
query += ` ORDER BY dc.created_at DESC`;
|
|
const result = await migrate_1.pool.query(query, params);
|
|
res.json({ changes: result.rows });
|
|
}
|
|
catch (error) {
|
|
console.error('Error fetching changes:', error);
|
|
res.status(500).json({ error: 'Failed to fetch changes' });
|
|
}
|
|
});
|
|
// Get changes statistics (for alert banner)
|
|
router.get('/stats', async (req, res) => {
|
|
try {
|
|
const result = await migrate_1.pool.query(`
|
|
SELECT
|
|
COUNT(*) FILTER (WHERE status = 'pending') as pending_count,
|
|
COUNT(*) FILTER (WHERE status = 'pending' AND requires_recrawl = TRUE) as pending_recrawl_count,
|
|
COUNT(*) FILTER (WHERE status = 'approved') as approved_count,
|
|
COUNT(*) FILTER (WHERE status = 'rejected') as rejected_count
|
|
FROM dispensary_changes
|
|
`);
|
|
res.json(result.rows[0]);
|
|
}
|
|
catch (error) {
|
|
console.error('Error fetching change stats:', error);
|
|
res.status(500).json({ error: 'Failed to fetch change stats' });
|
|
}
|
|
});
|
|
// Approve a change and apply it to the dispensary
|
|
router.post('/:id/approve', async (req, res) => {
|
|
const client = await migrate_1.pool.connect();
|
|
try {
|
|
await client.query('BEGIN');
|
|
const { id } = req.params;
|
|
const userId = req.user?.id; // From auth middleware
|
|
// Get the change record
|
|
const changeResult = await client.query(`
|
|
SELECT * FROM dispensary_changes WHERE id = $1 AND status = 'pending'
|
|
`, [id]);
|
|
if (changeResult.rows.length === 0) {
|
|
await client.query('ROLLBACK');
|
|
return res.status(404).json({ error: 'Pending change not found' });
|
|
}
|
|
const change = changeResult.rows[0];
|
|
// Apply the change to the dispensary table
|
|
const updateQuery = `
|
|
UPDATE dispensaries
|
|
SET ${change.field_name} = $1, updated_at = CURRENT_TIMESTAMP
|
|
WHERE id = $2
|
|
RETURNING *
|
|
`;
|
|
const dispensaryResult = await client.query(updateQuery, [
|
|
change.new_value,
|
|
change.dispensary_id
|
|
]);
|
|
if (dispensaryResult.rows.length === 0) {
|
|
await client.query('ROLLBACK');
|
|
return res.status(404).json({ error: 'Dispensary not found' });
|
|
}
|
|
// Mark the change as approved
|
|
await client.query(`
|
|
UPDATE dispensary_changes
|
|
SET
|
|
status = 'approved',
|
|
reviewed_at = CURRENT_TIMESTAMP,
|
|
reviewed_by = $1
|
|
WHERE id = $2
|
|
`, [userId, id]);
|
|
await client.query('COMMIT');
|
|
res.json({
|
|
message: 'Change approved and applied',
|
|
dispensary: dispensaryResult.rows[0],
|
|
requires_recrawl: change.requires_recrawl
|
|
});
|
|
}
|
|
catch (error) {
|
|
await client.query('ROLLBACK');
|
|
console.error('Error approving change:', error);
|
|
res.status(500).json({ error: 'Failed to approve change' });
|
|
}
|
|
finally {
|
|
client.release();
|
|
}
|
|
});
|
|
// Reject a change with optional reason
|
|
router.post('/:id/reject', async (req, res) => {
|
|
try {
|
|
const { id } = req.params;
|
|
const { reason } = req.body;
|
|
const userId = req.user?.id; // From auth middleware
|
|
const result = await migrate_1.pool.query(`
|
|
UPDATE dispensary_changes
|
|
SET
|
|
status = 'rejected',
|
|
reviewed_at = CURRENT_TIMESTAMP,
|
|
reviewed_by = $1,
|
|
rejection_reason = $2
|
|
WHERE id = $3 AND status = 'pending'
|
|
RETURNING *
|
|
`, [userId, reason, id]);
|
|
if (result.rows.length === 0) {
|
|
return res.status(404).json({ error: 'Pending change not found' });
|
|
}
|
|
res.json({
|
|
message: 'Change rejected',
|
|
change: result.rows[0]
|
|
});
|
|
}
|
|
catch (error) {
|
|
console.error('Error rejecting change:', error);
|
|
res.status(500).json({ error: 'Failed to reject change' });
|
|
}
|
|
});
|
|
exports.default = router;
|