Loads only 30 topics at a time with server-side search instead of fetching all 6000+ topics. Debounced search input, pagination, selected-only filter. Much faster for large Victron brokers. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
80 lines
2.7 KiB
TypeScript
80 lines
2.7 KiB
TypeScript
import { Router } from 'express';
|
|
import {
|
|
getTopicsForBroker,
|
|
updateTopic,
|
|
startDiscovery,
|
|
addManualTopic,
|
|
} from '../../modules/mqtt/topic-discovery';
|
|
import { createDatapointFromMqtt, getDatapointByTopicId } from '../../modules/datapoints/datapoint-manager';
|
|
import { getConnection } from './../../modules/mqtt/broker-manager';
|
|
import { getDb } from '../../db/index';
|
|
|
|
const router = Router({ mergeParams: true });
|
|
|
|
// GET /api/mqtt/brokers/:brokerId/topics?search=grid&selected=1&limit=50&offset=0
|
|
router.get('/', (req, res): void => {
|
|
try {
|
|
const brokerId = parseInt(req.params.brokerId);
|
|
const search = req.query.search as string | undefined;
|
|
const selectedOnly = req.query.selected === '1';
|
|
const limit = parseInt(req.query.limit as string) || 50;
|
|
const offset = parseInt(req.query.offset as string) || 0;
|
|
res.json(getTopicsForBroker(brokerId, { search, selectedOnly, limit, offset }));
|
|
} catch (err) {
|
|
res.status(500).json({ error: (err as Error).message });
|
|
}
|
|
});
|
|
|
|
// PUT /api/mqtt/brokers/:brokerId/topics/:id
|
|
router.put('/:id', (req, res): void => {
|
|
try {
|
|
const topicId = parseInt(req.params.id);
|
|
const topicRow = getDb().prepare('SELECT * FROM mqtt_topics WHERE id = ?').get(topicId) as any;
|
|
if (!topicRow) { res.status(404).json({ error: 'Topic not found' }); return; }
|
|
|
|
const topic = updateTopic(topicId, req.body);
|
|
|
|
// If selecting a topic, auto-create a datapoint
|
|
if (req.body.selected === 1 || req.body.selected === true) {
|
|
const existing = getDatapointByTopicId(topicId);
|
|
if (!existing) {
|
|
createDatapointFromMqtt(topicRow.broker_id, topicId, topicRow.topic);
|
|
}
|
|
// Subscribe to the topic on the broker connection
|
|
const client = getConnection(topicRow.broker_id);
|
|
if (client?.connected) client.subscribe(topicRow.topic);
|
|
}
|
|
|
|
res.json(topic);
|
|
} catch (err) {
|
|
res.status(500).json({ error: (err as Error).message });
|
|
}
|
|
});
|
|
|
|
// POST /api/mqtt/brokers/:brokerId/topics/discover
|
|
router.post('/discover', (req, res): void => {
|
|
const brokerId = parseInt(req.params.brokerId);
|
|
const { prefix } = req.body;
|
|
|
|
res.json({ message: 'Discovery started', brokerId, prefix: prefix ?? null });
|
|
|
|
// Run in background
|
|
startDiscovery(brokerId, prefix).catch((err) => {
|
|
console.error('Discovery error:', (err as Error).message);
|
|
});
|
|
});
|
|
|
|
// POST /api/mqtt/brokers/:brokerId/topics/manual
|
|
router.post('/manual', (req, res): void => {
|
|
try {
|
|
const brokerId = parseInt(req.params.brokerId);
|
|
const { topic } = req.body;
|
|
if (!topic) { res.status(400).json({ error: 'topic is required' }); return; }
|
|
res.status(201).json(addManualTopic(brokerId, topic));
|
|
} catch (err) {
|
|
res.status(500).json({ error: (err as Error).message });
|
|
}
|
|
});
|
|
|
|
export default router;
|