basic working
This commit is contained in:
@@ -11,6 +11,7 @@ MQTT_PORT=1883
|
|||||||
MQTT_USERNAME=meshdev
|
MQTT_USERNAME=meshdev
|
||||||
MQTT_PASSWORD=large4cats
|
MQTT_PASSWORD=large4cats
|
||||||
MQTT_TOPIC=msh/US/#
|
MQTT_TOPIC=msh/US/#
|
||||||
|
MQTT_PUB_TOPIC="msh/US/2/json/mqtt/"
|
||||||
|
|
||||||
# Optional: If you have your own MQTT broker
|
# Optional: If you have your own MQTT broker
|
||||||
# MQTT_BROKER=mqtt://your-broker.com
|
# MQTT_BROKER=mqtt://your-broker.com
|
||||||
|
|||||||
@@ -0,0 +1,27 @@
|
|||||||
|
# Server Configuration
|
||||||
|
PORT=3000
|
||||||
|
NODE_ENV=development
|
||||||
|
|
||||||
|
# Session Secret
|
||||||
|
SESSION_SECRET=test-secret-for-development-only
|
||||||
|
|
||||||
|
# MQTT Configuration (Meshtastic Public Broker)
|
||||||
|
MQTT_BROKER=mqtt://mqtt.meshtastic.org
|
||||||
|
MQTT_PORT=1883
|
||||||
|
MQTT_USERNAME=meshdev
|
||||||
|
MQTT_PASSWORD=large4cats
|
||||||
|
MQTT_TOPIC=#
|
||||||
|
MQTT_PUB_TOPIC="msh/US/2/json/mqtt/"
|
||||||
|
|
||||||
|
# Data Retention (in days)
|
||||||
|
DATA_RETENTION_DAYS=30
|
||||||
|
|
||||||
|
# Cron schedule for automatic data purging (daily at 2 AM)
|
||||||
|
PURGE_CRON_SCHEDULE=0 2 * * *
|
||||||
|
|
||||||
|
# Logging
|
||||||
|
LOG_LEVEL=info
|
||||||
|
|
||||||
|
# Rate Limiting
|
||||||
|
RATE_LIMIT_WINDOW_MS=900000
|
||||||
|
RATE_LIMIT_MAX_REQUESTS=1000
|
||||||
@@ -114,6 +114,18 @@ body {
|
|||||||
font-size: 0.875rem;
|
font-size: 0.875rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.data-last-received {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.25rem;
|
||||||
|
font-size: 0.875rem;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.data-label {
|
||||||
|
opacity: 0.7;
|
||||||
|
}
|
||||||
|
|
||||||
/* Navigation Tabs */
|
/* Navigation Tabs */
|
||||||
.nav-tabs {
|
.nav-tabs {
|
||||||
background: var(--surface-color);
|
background: var(--surface-color);
|
||||||
|
|||||||
+3
-10
@@ -43,7 +43,7 @@
|
|||||||
MQTT
|
MQTT
|
||||||
</span>
|
</span>
|
||||||
<span class="data-last-received" id="data-last-received" title="Time since last data update">
|
<span class="data-last-received" id="data-last-received" title="Time since last data update">
|
||||||
<span style="opacity: 0.7;">Last data:</span> <span id="data-timer">0s ago</span>
|
<span class="data-label">Last data:</span> <span id="data-timer">0s ago</span>
|
||||||
</span>
|
</span>
|
||||||
<span class="user-info" id="user-info"></span>
|
<span class="user-info" id="user-info"></span>
|
||||||
<button id="logout-btn" class="btn btn-secondary">Logout</button>
|
<button id="logout-btn" class="btn btn-secondary">Logout</button>
|
||||||
@@ -115,15 +115,8 @@
|
|||||||
<form id="send-message-form" class="message-form">
|
<form id="send-message-form" class="message-form">
|
||||||
<div class="form-row">
|
<div class="form-row">
|
||||||
<input type="text" id="message-text" placeholder="Type your message..." required>
|
<input type="text" id="message-text" placeholder="Type your message..." required>
|
||||||
<select id="message-channel">
|
<select id="message-from">
|
||||||
<option value="0">Channel 0</option>
|
<option value="474572292">!1c496604</option>
|
||||||
<option value="1">Channel 1</option>
|
|
||||||
<option value="2">Channel 2</option>
|
|
||||||
<option value="3">Channel 3</option>
|
|
||||||
<option value="4">Channel 4</option>
|
|
||||||
<option value="5">Channel 5</option>
|
|
||||||
<option value="6">Channel 6</option>
|
|
||||||
<option value="7">Channel 7</option>
|
|
||||||
</select>
|
</select>
|
||||||
<button type="submit" class="btn btn-primary">Send</button>
|
<button type="submit" class="btn btn-primary">Send</button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
+4
-4
@@ -173,10 +173,10 @@ const api = {
|
|||||||
return this.request(`/api/messages?limit=${limit}`);
|
return this.request(`/api/messages?limit=${limit}`);
|
||||||
},
|
},
|
||||||
|
|
||||||
async sendMessage(text, channel = 0) {
|
async sendMessage(from, text) {
|
||||||
return this.request('/api/messages/send', {
|
return this.request('/api/messages/send', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
body: JSON.stringify({ text, channel })
|
body: JSON.stringify({ text, from })
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -546,10 +546,10 @@ document.getElementById('send-message-form')?.addEventListener('submit', async (
|
|||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
const text = document.getElementById('message-text').value;
|
const text = document.getElementById('message-text').value;
|
||||||
const channel = parseInt(document.getElementById('message-channel').value);
|
const from = parseInt(document.getElementById('message-from').value);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await api.sendMessage(text, channel);
|
await api.sendMessage(text, from);
|
||||||
document.getElementById('message-text').value = '';
|
document.getElementById('message-text').value = '';
|
||||||
|
|
||||||
// Reload messages after a short delay
|
// Reload messages after a short delay
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ module.exports = {
|
|||||||
username: process.env.MQTT_USERNAME || 'meshdev',
|
username: process.env.MQTT_USERNAME || 'meshdev',
|
||||||
password: process.env.MQTT_PASSWORD || 'large4cats',
|
password: process.env.MQTT_PASSWORD || 'large4cats',
|
||||||
topic: process.env.MQTT_TOPIC || 'msh/US/#',
|
topic: process.env.MQTT_TOPIC || 'msh/US/#',
|
||||||
|
pubTopic: process.env.MQTT_PUB_TOPIC || 'msh/US/2/json/mqtt/', // The ending / is apparently critical
|
||||||
options: {
|
options: {
|
||||||
clientId: `meshtastic-dashboard-${Math.random().toString(16).substr(2, 8)}`,
|
clientId: `meshtastic-dashboard-${Math.random().toString(16).substr(2, 8)}`,
|
||||||
clean: true,
|
clean: true,
|
||||||
|
|||||||
+7
-9
@@ -88,8 +88,8 @@ class MeshtasticMQTTClient {
|
|||||||
try {
|
try {
|
||||||
const { from, to, channel, id, sender } = payload;
|
const { from, to, channel, id, sender } = payload;
|
||||||
|
|
||||||
// Extract node ID (convert to hex string if it's a number)
|
// Extract node ID (convert to hex string if it's a number, fallback to sender if none
|
||||||
const fromNode = sender || (from ? `!${from.toString(16).padStart(8, '0')}` : null);
|
const fromNode = from ? `!${from.toString(16).padStart(8, '0')}` : sender;
|
||||||
const toNode = to ? `!${to.toString(16).padStart(8, '0')}` : null;
|
const toNode = to ? `!${to.toString(16).padStart(8, '0')}` : null;
|
||||||
|
|
||||||
// Handle different message types based on actual JSON structure
|
// Handle different message types based on actual JSON structure
|
||||||
@@ -344,18 +344,16 @@ class MeshtasticMQTTClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Send a text message
|
// Send a text message
|
||||||
async sendTextMessage(text, channel = 0) {
|
async sendTextMessage(from, text) {
|
||||||
try {
|
try {
|
||||||
const message = JSON.stringify({
|
const message = JSON.stringify({
|
||||||
type: 'text',
|
from: from,
|
||||||
text,
|
type: 'sendtext',
|
||||||
channel,
|
payload: text
|
||||||
timestamp: Date.now()
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Publish to the appropriate topic
|
// Publish to the appropriate topic
|
||||||
const baseTopic = config.mqtt.topic.replace('/#', '');
|
await this.publish(config.mqtt.pubTopic, message);
|
||||||
await this.publish(`${baseTopic}/2/json/mqtt`, message);
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
+2
-2
@@ -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, channel } = req.body;
|
const { text, from } = 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, channel || 0);
|
await mqttClient.sendTextMessage(text, from || 0);
|
||||||
|
|
||||||
// Log activity
|
// Log activity
|
||||||
logActivity(req.session.userId, 'send_message', text, req.ip);
|
logActivity(req.session.userId, 'send_message', text, req.ip);
|
||||||
|
|||||||
Reference in New Issue
Block a user