tags
This commit is contained in:
67
server.js
67
server.js
@@ -74,11 +74,21 @@ db.exec(`
|
||||
trigger_data TEXT NOT NULL,
|
||||
action_type TEXT NOT NULL,
|
||||
action_data TEXT NOT NULL,
|
||||
sort_order INTEGER DEFAULT 0,
|
||||
color_tag TEXT DEFAULT NULL,
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
||||
)
|
||||
`);
|
||||
|
||||
// Migration: Add sort_order and color_tag if they don't exist
|
||||
try {
|
||||
db.exec('ALTER TABLE rules ADD COLUMN sort_order INTEGER DEFAULT 0');
|
||||
} catch (e) { /* column already exists */ }
|
||||
try {
|
||||
db.exec('ALTER TABLE rules ADD COLUMN color_tag TEXT DEFAULT NULL');
|
||||
} catch (e) { /* column already exists */ }
|
||||
|
||||
const insertStmt = db.prepare(`
|
||||
INSERT INTO readings (dev_id, dev_name, port, port_name, temp_c, humidity, vpd, fan_speed, on_speed, off_speed)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
@@ -87,17 +97,19 @@ const insertStmt = db.prepare(`
|
||||
const getUserByUsername = db.prepare('SELECT * FROM users WHERE username = ?');
|
||||
|
||||
// Rules prepared statements
|
||||
const getAllRules = db.prepare('SELECT * FROM rules ORDER BY id');
|
||||
const getAllRules = db.prepare('SELECT * FROM rules ORDER BY sort_order, id');
|
||||
const getRuleById = db.prepare('SELECT * FROM rules WHERE id = ?');
|
||||
const insertRule = db.prepare(`
|
||||
INSERT INTO rules (name, enabled, trigger_type, trigger_data, action_type, action_data)
|
||||
VALUES (?, ?, ?, ?, ?, ?)
|
||||
INSERT INTO rules (name, enabled, trigger_type, trigger_data, action_type, action_data, sort_order, color_tag)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
||||
`);
|
||||
const updateRule = db.prepare(`
|
||||
UPDATE rules SET name = ?, enabled = ?, trigger_type = ?, trigger_data = ?, action_type = ?, action_data = ?, updated_at = CURRENT_TIMESTAMP
|
||||
UPDATE rules SET name = ?, enabled = ?, trigger_type = ?, trigger_data = ?, action_type = ?, action_data = ?, color_tag = ?, updated_at = CURRENT_TIMESTAMP
|
||||
WHERE id = ?
|
||||
`);
|
||||
const updateRuleOrder = db.prepare('UPDATE rules SET sort_order = ? WHERE id = ?');
|
||||
const deleteRule = db.prepare('DELETE FROM rules WHERE id = ?');
|
||||
const getMaxSortOrder = db.prepare('SELECT COALESCE(MAX(sort_order), 0) as max_order FROM rules');
|
||||
|
||||
// --- AC INFINITY API LOGIC ---
|
||||
let token = null;
|
||||
@@ -342,10 +354,23 @@ function formatRule(row) {
|
||||
action.type = row.action_type;
|
||||
}
|
||||
|
||||
// Parse colorTags (stored as JSON array)
|
||||
let colorTags = [];
|
||||
if (row.color_tag) {
|
||||
try {
|
||||
colorTags = JSON.parse(row.color_tag);
|
||||
} catch (e) {
|
||||
// Backwards compat: single tag as string
|
||||
colorTags = [row.color_tag];
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
id: row.id,
|
||||
name: row.name,
|
||||
enabled: row.enabled === 1,
|
||||
sortOrder: row.sort_order || 0,
|
||||
colorTags,
|
||||
trigger,
|
||||
action
|
||||
};
|
||||
@@ -364,7 +389,7 @@ app.get('/api/rules', (req, res) => {
|
||||
// POST /api/rules - admin only
|
||||
app.post('/api/rules', requireAuth, requireAdmin, (req, res) => {
|
||||
try {
|
||||
const { name, enabled, trigger, action } = req.body;
|
||||
const { name, enabled, trigger, action, colorTags } = req.body;
|
||||
if (!name || !trigger || !action) {
|
||||
return res.status(400).json({ error: 'name, trigger, and action required' });
|
||||
}
|
||||
@@ -374,9 +399,14 @@ app.post('/api/rules', requireAuth, requireAdmin, (req, res) => {
|
||||
if (trigger.timeRange && !trigger.sensors?.length) triggerType = 'time';
|
||||
if (!trigger.timeRange && trigger.sensors?.length) triggerType = 'sensor';
|
||||
|
||||
// Get next sort order
|
||||
const maxOrder = getMaxSortOrder.get().max_order;
|
||||
const sortOrder = maxOrder + 1;
|
||||
|
||||
const triggerData = JSON.stringify(trigger);
|
||||
const actionData = JSON.stringify(action);
|
||||
const result = insertRule.run(name, enabled ? 1 : 0, triggerType, triggerData, action.type, actionData);
|
||||
const colorTagsData = colorTags?.length ? JSON.stringify(colorTags) : null;
|
||||
const result = insertRule.run(name, enabled ? 1 : 0, triggerType, triggerData, action.type, actionData, sortOrder, colorTagsData);
|
||||
|
||||
const newRule = getRuleById.get(result.lastInsertRowid);
|
||||
res.status(201).json(formatRule(newRule));
|
||||
@@ -394,7 +424,7 @@ app.put('/api/rules/:id', requireAuth, requireAdmin, (req, res) => {
|
||||
return res.status(404).json({ error: 'Rule not found' });
|
||||
}
|
||||
|
||||
const { name, enabled, trigger, action } = req.body;
|
||||
const { name, enabled, trigger, action, colorTags } = req.body;
|
||||
|
||||
// Determine trigger type for storage
|
||||
let triggerType = 'combined';
|
||||
@@ -403,7 +433,8 @@ app.put('/api/rules/:id', requireAuth, requireAdmin, (req, res) => {
|
||||
|
||||
const triggerData = JSON.stringify(trigger);
|
||||
const actionData = JSON.stringify(action);
|
||||
updateRule.run(name, enabled ? 1 : 0, triggerType, triggerData, action.type, actionData, id);
|
||||
const colorTagsData = colorTags?.length ? JSON.stringify(colorTags) : null;
|
||||
updateRule.run(name, enabled ? 1 : 0, triggerType, triggerData, action.type, actionData, colorTagsData, id);
|
||||
|
||||
const updated = getRuleById.get(id);
|
||||
res.json(formatRule(updated));
|
||||
@@ -412,6 +443,26 @@ app.put('/api/rules/:id', requireAuth, requireAdmin, (req, res) => {
|
||||
}
|
||||
});
|
||||
|
||||
// PUT /api/rules/reorder - admin only (reorder rules)
|
||||
app.put('/api/rules/reorder', requireAuth, requireAdmin, (req, res) => {
|
||||
try {
|
||||
const { ruleIds } = req.body; // Array of rule IDs in new order
|
||||
if (!Array.isArray(ruleIds)) {
|
||||
return res.status(400).json({ error: 'ruleIds array required' });
|
||||
}
|
||||
|
||||
// Update sort_order for each rule
|
||||
ruleIds.forEach((ruleId, index) => {
|
||||
updateRuleOrder.run(index, ruleId);
|
||||
});
|
||||
|
||||
const rows = getAllRules.all();
|
||||
res.json(rows.map(formatRule));
|
||||
} catch (error) {
|
||||
res.status(500).json({ error: error.message });
|
||||
}
|
||||
});
|
||||
|
||||
// DELETE /api/rules/:id - admin only
|
||||
app.delete('/api/rules/:id', requireAuth, requireAdmin, (req, res) => {
|
||||
try {
|
||||
|
||||
Reference in New Issue
Block a user