feat: v0.5.7 - Anfragen pro Kanal mit Kanalnamen im Dashboard
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
7bf58a32fb
commit
1d768c6921
|
|
@ -1,5 +1,10 @@
|
|||
# Changelog
|
||||
|
||||
## [0.5.7] - 2026-02-17
|
||||
### Added
|
||||
- Anfragen-Aufschluesselung pro Kanal mit Kanalnamen im Dashboard
|
||||
- Channel-Spalte in der commands-Tabelle (mit DB-Migration fuer bestehende DBs)
|
||||
|
||||
## [0.5.6] - 2026-02-17
|
||||
### Added
|
||||
- Node-Detail-Modal im Dashboard: Klick auf Node-Zeile oeffnet Modal mit allen Node-Daten
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
version: "0.5.6"
|
||||
version: "0.5.7"
|
||||
|
||||
bot:
|
||||
name: "MeshDD-Bot"
|
||||
|
|
|
|||
|
|
@ -324,7 +324,7 @@ class MeshBot:
|
|||
|
||||
if response:
|
||||
await self._send_text(response, channel)
|
||||
await self.db.insert_command(cmd)
|
||||
await self.db.insert_command(cmd, channel)
|
||||
if self.ws_manager:
|
||||
stats = await self.db.get_stats()
|
||||
await self.ws_manager.broadcast("stats_update", stats)
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ class Database:
|
|||
self.db.row_factory = aiosqlite.Row
|
||||
await self.db.execute("PRAGMA journal_mode=WAL")
|
||||
await self._create_tables()
|
||||
await self._migrate()
|
||||
logger.info("Database connected: %s", self.db_path)
|
||||
|
||||
async def close(self):
|
||||
|
|
@ -55,7 +56,8 @@ class Database:
|
|||
CREATE TABLE IF NOT EXISTS commands (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
timestamp REAL,
|
||||
command TEXT
|
||||
command TEXT,
|
||||
channel INTEGER
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS users (
|
||||
|
|
@ -91,6 +93,14 @@ class Database:
|
|||
""")
|
||||
await self.db.commit()
|
||||
|
||||
async def _migrate(self):
|
||||
async with self.db.execute("PRAGMA table_info(commands)") as c:
|
||||
cols = {row[1] async for row in c}
|
||||
if "channel" not in cols:
|
||||
await self.db.execute("ALTER TABLE commands ADD COLUMN channel INTEGER")
|
||||
await self.db.commit()
|
||||
logger.info("Migration: added channel column to commands table")
|
||||
|
||||
# ── Node methods ──────────────────────────────────
|
||||
|
||||
async def upsert_node(self, node_id: str, **kwargs) -> dict:
|
||||
|
|
@ -164,11 +174,11 @@ class Database:
|
|||
|
||||
# ── Command methods ───────────────────────────────
|
||||
|
||||
async def insert_command(self, command: str):
|
||||
async def insert_command(self, command: str, channel: int | None = None):
|
||||
now = time.time()
|
||||
await self.db.execute(
|
||||
"INSERT INTO commands (timestamp, command) VALUES (?, ?)",
|
||||
(now, command),
|
||||
"INSERT INTO commands (timestamp, command, channel) VALUES (?, ?, ?)",
|
||||
(now, command, channel),
|
||||
)
|
||||
await self.db.commit()
|
||||
|
||||
|
|
@ -188,6 +198,10 @@ class Database:
|
|||
"SELECT command, COUNT(*) as cnt FROM commands GROUP BY command ORDER BY cnt DESC"
|
||||
) as cursor:
|
||||
stats["command_breakdown"] = {row[0]: row[1] async for row in cursor}
|
||||
async with self.db.execute(
|
||||
"SELECT channel, COUNT(*) as cnt FROM commands GROUP BY channel ORDER BY cnt DESC"
|
||||
) as cursor:
|
||||
stats["channel_breakdown"] = {row[0]: row[1] async for row in cursor}
|
||||
return stats
|
||||
|
||||
# ── User methods ──────────────────────────────────
|
||||
|
|
|
|||
|
|
@ -104,10 +104,16 @@
|
|||
|
||||
<!-- Command Breakdown -->
|
||||
<div class="card card-outline mb-2">
|
||||
<div class="card-body py-2 px-3 d-flex align-items-center gap-2 flex-wrap">
|
||||
<div class="card-body py-2 px-3 d-flex flex-column gap-1">
|
||||
<div class="d-flex align-items-center gap-2 flex-wrap">
|
||||
<small class="text-body-secondary"><i class="bi bi-bar-chart-fill me-1"></i>Anfragen:</small>
|
||||
<span id="commandBreakdown" class="d-flex gap-1 flex-wrap"></span>
|
||||
</div>
|
||||
<div class="d-flex align-items-center gap-2 flex-wrap">
|
||||
<small class="text-body-secondary"><i class="bi bi-broadcast me-1"></i>Kanaele:</small>
|
||||
<span id="channelBreakdown" class="d-flex gap-1 flex-wrap"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Send Message (auth-gated) -->
|
||||
|
|
|
|||
|
|
@ -175,6 +175,17 @@ function updateStats(stats) {
|
|||
} else {
|
||||
breakdown.innerHTML = '<span class="text-body-secondary small">Noch keine Anfragen</span>';
|
||||
}
|
||||
|
||||
const chBreakdown = document.getElementById('channelBreakdown');
|
||||
const chCounts = stats.channel_breakdown || {};
|
||||
if (Object.keys(chCounts).length > 0) {
|
||||
chBreakdown.innerHTML = Object.entries(chCounts).map(([chIdx, count]) => {
|
||||
const chName = channels[chIdx] || `Ch ${chIdx}`;
|
||||
return `<span class="badge bg-info bg-opacity-75">${escapeHtml(chName)} <span class="badge bg-light text-dark ms-1">${count}</span></span>`;
|
||||
}).join('');
|
||||
} else {
|
||||
chBreakdown.innerHTML = '<span class="text-body-secondary small">Noch keine Anfragen</span>';
|
||||
}
|
||||
}
|
||||
|
||||
function isOnline(lastSeen) {
|
||||
|
|
|
|||
Loading…
Reference in a new issue