update data purging, re-enable channel selection
This commit is contained in:
@@ -169,7 +169,7 @@ The dashboard provides several tabs:
|
|||||||
|
|
||||||
1. Navigate to the **Messages** tab
|
1. Navigate to the **Messages** tab
|
||||||
2. Type your message in the text field
|
2. Type your message in the text field
|
||||||
3. Select the channel (0-7)
|
3. Choose a device to send **From**
|
||||||
4. Click **Send**
|
4. Click **Send**
|
||||||
|
|
||||||
Messages will be broadcast to the mesh network via MQTT.
|
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
|
3. Check that there is active mesh traffic on the topic
|
||||||
4. Review logs for any error messages
|
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
|
### Database errors
|
||||||
|
|
||||||
1. Ensure the `data/` directory is writable
|
1. Ensure the `data/` directory is writable
|
||||||
|
|||||||
+17
-7
@@ -118,6 +118,11 @@
|
|||||||
<select id="message-from">
|
<select id="message-from">
|
||||||
<option value="474572292">!1c496604</option>
|
<option value="474572292">!1c496604</option>
|
||||||
</select>
|
</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>
|
<button type="submit" class="btn btn-primary">Send</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
@@ -159,13 +164,18 @@
|
|||||||
<p>Purge old data from the database to free up space.</p>
|
<p>Purge old data from the database to free up space.</p>
|
||||||
<form id="purge-form" class="settings-form">
|
<form id="purge-form" class="settings-form">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="purge-days">Keep data from the last:</label>
|
<label for="purge-hours">Keep data from the last:</label>
|
||||||
<select id="purge-days">
|
<select id="purge-hours">
|
||||||
<option value="7">7 days</option>
|
<option value="1">1 hour</option>
|
||||||
<option value="14">14 days</option>
|
<option value="6">6 hours</option>
|
||||||
<option value="30" selected>30 days</option>
|
<option value="12">12 hours</option>
|
||||||
<option value="60">60 days</option>
|
<option value="24">1 day</option>
|
||||||
<option value="90">90 days</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>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<button type="submit" class="btn btn-danger">Purge Old Data</button>
|
<button type="submit" class="btn btn-danger">Purge Old Data</button>
|
||||||
|
|||||||
+18
-8
@@ -173,17 +173,17 @@ const api = {
|
|||||||
return this.request(`/api/messages?limit=${limit}`);
|
return this.request(`/api/messages?limit=${limit}`);
|
||||||
},
|
},
|
||||||
|
|
||||||
async sendMessage(from, text) {
|
async sendMessage(from, text, channel) {
|
||||||
return this.request('/api/messages/send', {
|
return this.request('/api/messages/send', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
body: JSON.stringify({ text, from })
|
body: JSON.stringify({ from, text, channel })
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
async purgeData(days) {
|
async purgeData(hours) {
|
||||||
return this.request('/api/purge', {
|
return this.request('/api/purge', {
|
||||||
method: 'POST',
|
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 text = document.getElementById('message-text').value;
|
||||||
const from = parseInt(document.getElementById('message-from').value);
|
const from = parseInt(document.getElementById('message-from').value);
|
||||||
|
const channel = parseInt(document.getElementById('message-channel').value);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await api.sendMessage(text, from);
|
await api.sendMessage(from, text, channel);
|
||||||
document.getElementById('message-text').value = '';
|
document.getElementById('message-text').value = '';
|
||||||
|
|
||||||
// Reload messages after a short delay
|
// 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) => {
|
document.getElementById('purge-form')?.addEventListener('submit', async (e) => {
|
||||||
e.preventDefault();
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
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`);
|
alert(`Successfully purged:\n${result.deleted.messages} messages\n${result.deleted.positions} positions\n${result.deleted.telemetry} telemetry records`);
|
||||||
|
|
||||||
// Reload stats
|
// Reload stats
|
||||||
|
|||||||
@@ -95,7 +95,7 @@ const positionQueries = {
|
|||||||
`),
|
`),
|
||||||
|
|
||||||
deleteOldPositions: db.prepare(`
|
deleteOldPositions: db.prepare(`
|
||||||
DELETE FROM positions WHERE timestamp < datetime('now', '-' || ? || ' days')
|
DELETE FROM positions WHERE timestamp < datetime('now', '-' || ? || ' hours')
|
||||||
`),
|
`),
|
||||||
|
|
||||||
getPositionTrails: db.prepare(`
|
getPositionTrails: db.prepare(`
|
||||||
@@ -170,7 +170,7 @@ const messageQueries = {
|
|||||||
`),
|
`),
|
||||||
|
|
||||||
deleteOldMessages: db.prepare(`
|
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(`
|
deleteOldTelemetry: db.prepare(`
|
||||||
DELETE FROM telemetry WHERE timestamp < datetime('now', '-' || ? || ' days')
|
DELETE FROM telemetry WHERE timestamp < datetime('now', '-' || ? || ' hours')
|
||||||
`)
|
`)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
+2
-1
@@ -344,10 +344,11 @@ class MeshtasticMQTTClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Send a text message
|
// Send a text message
|
||||||
async sendTextMessage(from, text) {
|
async sendTextMessage(from, text, channel=0) {
|
||||||
try {
|
try {
|
||||||
const message = JSON.stringify({
|
const message = JSON.stringify({
|
||||||
from: from,
|
from: from,
|
||||||
|
channel: channel,
|
||||||
type: 'sendtext',
|
type: 'sendtext',
|
||||||
payload: text
|
payload: text
|
||||||
});
|
});
|
||||||
|
|||||||
+12
-8
@@ -169,13 +169,13 @@ router.get('/messages/node/:nodeId', requireAuth, (req, res) => {
|
|||||||
// Send a message
|
// Send a message
|
||||||
router.post('/messages/send', requireAuth, async (req, res) => {
|
router.post('/messages/send', requireAuth, async (req, res) => {
|
||||||
try {
|
try {
|
||||||
const { text, from } = req.body;
|
const { from, text, channel } = req.body;
|
||||||
|
|
||||||
if (!text) {
|
if (!text) {
|
||||||
return res.status(400).json({ error: 'Message text required' });
|
return res.status(400).json({ error: 'Message text required' });
|
||||||
}
|
}
|
||||||
|
|
||||||
await mqttClient.sendTextMessage(text, from || 0);
|
await mqttClient.sendTextMessage(from, text, channel);
|
||||||
|
|
||||||
// Log activity
|
// Log activity
|
||||||
logActivity(req.session.userId, 'send_message', text, req.ip);
|
logActivity(req.session.userId, 'send_message', text, req.ip);
|
||||||
@@ -224,20 +224,24 @@ router.get('/stats', requireAuth, (req, res) => {
|
|||||||
// Purge old data
|
// Purge old data
|
||||||
router.post('/purge', requireAuth, (req, res) => {
|
router.post('/purge', requireAuth, (req, res) => {
|
||||||
try {
|
try {
|
||||||
const { days } = req.body;
|
const { hours } = req.body;
|
||||||
const daysToKeep = days || 30;
|
const hoursToKeep = hours || 720; // Default to 30 days (720 hours)
|
||||||
|
|
||||||
const messagesDeleted = messageQueries.deleteOldMessages.run(daysToKeep);
|
const messagesDeleted = messageQueries.deleteOldMessages.run(hoursToKeep);
|
||||||
const positionsDeleted = positionQueries.deleteOldPositions.run(daysToKeep);
|
const positionsDeleted = positionQueries.deleteOldPositions.run(hoursToKeep);
|
||||||
const telemetryDeleted = telemetryQueries.deleteOldTelemetry.run(daysToKeep);
|
const telemetryDeleted = telemetryQueries.deleteOldTelemetry.run(hoursToKeep);
|
||||||
|
|
||||||
logger.info(`Data purged: ${messagesDeleted.changes} messages, ${positionsDeleted.changes} positions, ${telemetryDeleted.changes} telemetry records`);
|
logger.info(`Data purged: ${messagesDeleted.changes} messages, ${positionsDeleted.changes} positions, ${telemetryDeleted.changes} telemetry records`);
|
||||||
|
|
||||||
// Log activity
|
// Log activity
|
||||||
|
const timePeriod = hoursToKeep < 24
|
||||||
|
? `${hoursToKeep} hour${hoursToKeep !== 1 ? 's' : ''}`
|
||||||
|
: `${hoursToKeep / 24} day${hoursToKeep / 24 !== 1 ? 's' : ''}`;
|
||||||
|
|
||||||
logActivity(
|
logActivity(
|
||||||
req.session.userId,
|
req.session.userId,
|
||||||
'purge_data',
|
'purge_data',
|
||||||
`Purged data older than ${daysToKeep} days`,
|
`Purged data older than ${timePeriod}`,
|
||||||
req.ip
|
req.ip
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user