update data purging, re-enable channel selection

This commit is contained in:
Will Bradley
2025-10-11 22:04:03 -07:00
parent d19c071814
commit 18d5114f6f
6 changed files with 59 additions and 28 deletions
+7 -1
View File
@@ -169,7 +169,7 @@ The dashboard provides several tabs:
1. Navigate to the **Messages** tab
2. Type your message in the text field
3. Select the channel (0-7)
3. Choose a device to send **From**
4. Click **Send**
Messages will be broadcast to the mesh network via MQTT.
@@ -269,6 +269,12 @@ Log level can be configured with `LOG_LEVEL` in `.env` (debug, info, warn, error
3. Check that there is active mesh traffic on the topic
4. Review logs for any error messages
### Messages sent over MQTT aren't sent over the radio
1. Ensure the sending radio (node) has a channel called "mqtt"
2. Ensure the node has Uplink and Downlink enabled on both the default channel (LongFast, etc) and the mqtt channel
3. Ensure the node's setting under LoRa is "Ok to MQTT"
### Database errors
1. Ensure the `data/` directory is writable
+17 -7
View File
@@ -118,6 +118,11 @@
<select id="message-from">
<option value="474572292">!1c496604</option>
</select>
<select id="message-channel">
<option value="0">LongFast (public)</option>
<option value="1">Aether (private)</option>
<option value="2">Other (2)</option>
</select>
<button type="submit" class="btn btn-primary">Send</button>
</div>
</form>
@@ -159,13 +164,18 @@
<p>Purge old data from the database to free up space.</p>
<form id="purge-form" class="settings-form">
<div class="form-group">
<label for="purge-days">Keep data from the last:</label>
<select id="purge-days">
<option value="7">7 days</option>
<option value="14">14 days</option>
<option value="30" selected>30 days</option>
<option value="60">60 days</option>
<option value="90">90 days</option>
<label for="purge-hours">Keep data from the last:</label>
<select id="purge-hours">
<option value="1">1 hour</option>
<option value="6">6 hours</option>
<option value="12">12 hours</option>
<option value="24">1 day</option>
<option value="72">3 days</option>
<option value="168">7 days</option>
<option value="336">14 days</option>
<option value="720" selected>30 days</option>
<option value="1440">60 days</option>
<option value="2160">90 days</option>
</select>
</div>
<button type="submit" class="btn btn-danger">Purge Old Data</button>
+18 -8
View File
@@ -173,17 +173,17 @@ const api = {
return this.request(`/api/messages?limit=${limit}`);
},
async sendMessage(from, text) {
async sendMessage(from, text, channel) {
return this.request('/api/messages/send', {
method: 'POST',
body: JSON.stringify({ text, from })
body: JSON.stringify({ from, text, channel })
});
},
async purgeData(days) {
async purgeData(hours) {
return this.request('/api/purge', {
method: 'POST',
body: JSON.stringify({ days })
body: JSON.stringify({ hours })
});
},
@@ -547,9 +547,10 @@ document.getElementById('send-message-form')?.addEventListener('submit', async (
const text = document.getElementById('message-text').value;
const from = parseInt(document.getElementById('message-from').value);
const channel = parseInt(document.getElementById('message-channel').value);
try {
await api.sendMessage(text, from);
await api.sendMessage(from, text, channel);
document.getElementById('message-text').value = '';
// Reload messages after a short delay
@@ -1045,14 +1046,23 @@ document.getElementById('refresh-map-btn')?.addEventListener('click', loadMap);
document.getElementById('purge-form')?.addEventListener('submit', async (e) => {
e.preventDefault();
const days = parseInt(document.getElementById('purge-days').value);
const hours = parseInt(document.getElementById('purge-hours').value);
if (!confirm(`Are you sure you want to delete data older than ${days} days? This cannot be undone.`)) {
// Format time period for display
let timePeriod;
if (hours < 24) {
timePeriod = `${hours} hour${hours !== 1 ? 's' : ''}`;
} else {
const days = hours / 24;
timePeriod = `${days} day${days !== 1 ? 's' : ''}`;
}
if (!confirm(`Are you sure you want to delete data older than ${timePeriod}? This cannot be undone.`)) {
return;
}
try {
const result = await api.purgeData(days);
const result = await api.purgeData(hours);
alert(`Successfully purged:\n${result.deleted.messages} messages\n${result.deleted.positions} positions\n${result.deleted.telemetry} telemetry records`);
// Reload stats
+3 -3
View File
@@ -95,7 +95,7 @@ const positionQueries = {
`),
deleteOldPositions: db.prepare(`
DELETE FROM positions WHERE timestamp < datetime('now', '-' || ? || ' days')
DELETE FROM positions WHERE timestamp < datetime('now', '-' || ? || ' hours')
`),
getPositionTrails: db.prepare(`
@@ -170,7 +170,7 @@ const messageQueries = {
`),
deleteOldMessages: db.prepare(`
DELETE FROM messages WHERE created_at < datetime('now', '-' || ? || ' days')
DELETE FROM messages WHERE created_at < datetime('now', '-' || ? || ' hours')
`)
};
@@ -199,7 +199,7 @@ const telemetryQueries = {
`),
deleteOldTelemetry: db.prepare(`
DELETE FROM telemetry WHERE timestamp < datetime('now', '-' || ? || ' days')
DELETE FROM telemetry WHERE timestamp < datetime('now', '-' || ? || ' hours')
`)
};
+2 -1
View File
@@ -344,10 +344,11 @@ class MeshtasticMQTTClient {
}
// Send a text message
async sendTextMessage(from, text) {
async sendTextMessage(from, text, channel=0) {
try {
const message = JSON.stringify({
from: from,
channel: channel,
type: 'sendtext',
payload: text
});
+12 -8
View File
@@ -169,13 +169,13 @@ router.get('/messages/node/:nodeId', requireAuth, (req, res) => {
// Send a message
router.post('/messages/send', requireAuth, async (req, res) => {
try {
const { text, from } = req.body;
const { from, text, channel } = req.body;
if (!text) {
return res.status(400).json({ error: 'Message text required' });
}
await mqttClient.sendTextMessage(text, from || 0);
await mqttClient.sendTextMessage(from, text, channel);
// Log activity
logActivity(req.session.userId, 'send_message', text, req.ip);
@@ -224,20 +224,24 @@ router.get('/stats', requireAuth, (req, res) => {
// Purge old data
router.post('/purge', requireAuth, (req, res) => {
try {
const { days } = req.body;
const daysToKeep = days || 30;
const { hours } = req.body;
const hoursToKeep = hours || 720; // Default to 30 days (720 hours)
const messagesDeleted = messageQueries.deleteOldMessages.run(daysToKeep);
const positionsDeleted = positionQueries.deleteOldPositions.run(daysToKeep);
const telemetryDeleted = telemetryQueries.deleteOldTelemetry.run(daysToKeep);
const messagesDeleted = messageQueries.deleteOldMessages.run(hoursToKeep);
const positionsDeleted = positionQueries.deleteOldPositions.run(hoursToKeep);
const telemetryDeleted = telemetryQueries.deleteOldTelemetry.run(hoursToKeep);
logger.info(`Data purged: ${messagesDeleted.changes} messages, ${positionsDeleted.changes} positions, ${telemetryDeleted.changes} telemetry records`);
// Log activity
const timePeriod = hoursToKeep < 24
? `${hoursToKeep} hour${hoursToKeep !== 1 ? 's' : ''}`
: `${hoursToKeep / 24} day${hoursToKeep / 24 !== 1 ? 's' : ''}`;
logActivity(
req.session.userId,
'purge_data',
`Purged data older than ${daysToKeep} days`,
`Purged data older than ${timePeriod}`,
req.ip
);