chore: release v0.20.7

This commit is contained in:
2026-07-21 18:26:34 +02:00
parent 828f99b8ac
commit 24a5a0480d
15 changed files with 909 additions and 183 deletions
+26 -39
View File
@@ -2,7 +2,6 @@ package main
import (
"database/sql"
"fmt"
"strings"
"time"
@@ -30,8 +29,6 @@ func (a *App) emitLiveStatusChanged() {
// to the DB — it is not a web server. Rows older than a couple of minutes are
// "stale" (operator went offline); the web side ignores them.
const keyLiveStatusEnabled = "livestatus.enabled"
// liveOnlineWindow is how long after the last logged contact an operator still
// counts as "on air". Leaving the log open without working anyone flips them
// offline once this elapses; logging a new QSO flips them back online.
@@ -49,37 +46,6 @@ func (a *App) noteLiveQSO() {
}
}
// GetLiveStatusEnabled reports whether this operator publishes live status.
func (a *App) GetLiveStatusEnabled() bool {
if a.settings == nil {
return false
}
v, _ := a.settings.Get(a.ctx, keyLiveStatusEnabled)
return strings.TrimSpace(v) == "1"
}
// SetLiveStatusEnabled turns live-status publishing on or off (off also removes
// this operator's row immediately).
func (a *App) SetLiveStatusEnabled(on bool) error {
if a.settings == nil {
return fmt.Errorf("db not initialized")
}
val := "0"
if on {
val = "1"
}
if err := a.settings.Set(a.ctx, keyLiveStatusEnabled, val); err != nil {
return err
}
if on {
applog.Printf("livestatus: enabled (logbook backend=%q, mysql conn=%v)", a.dbBackend, a.logDb != nil)
go a.publishLiveStatus() // show up right away
} else {
a.clearLiveStatus()
}
return nil
}
// seedLiveLastQSO primes liveLastQSOAt from the DB at launch, so an operator who
// worked someone shortly before (re)starting OpsLog is shown "on air" right away
// instead of offline until their next QSO.
@@ -143,9 +109,11 @@ func (a *App) liveStatusLoop() {
}
}
// liveStatusActive reports whether publishing should run (MySQL logbook + on).
// liveStatusActive reports whether publishing should run. Always on for a shared
// MySQL logbook (no user toggle: going offline is automatic — no QSO for 5 min
// removes the row — so there is nothing to opt out of).
func (a *App) liveStatusActive() bool {
return a.logDb != nil && a.dbBackend == "mysql" && a.GetLiveStatusEnabled()
return a.logDb != nil && a.dbBackend == "mysql"
}
// liveStatusOperator returns this instance's operator id (the operator callsign,
@@ -197,9 +165,6 @@ func (a *App) publishLiveStatus() {
if a.logDb == nil || a.dbBackend != "mysql" {
return // not a MySQL logbook — nothing to do (silent, runs every 15s)
}
if !a.GetLiveStatusEnabled() {
return // disabled (silent)
}
op, station := a.liveStatusOperator()
if op == "" {
applog.Printf("livestatus: nothing published — no operator/callsign set (Settings → Station)")
@@ -316,7 +281,29 @@ func (a *App) GetLiveStations() []LiveStation {
return out
}
// ensureLiveStatusTable creates/upgrades the live_status table ONCE per logbook
// connection. It used to run the CREATE + 3 ALTERs on every call — and it is
// called from every publish (15s heartbeat) AND every GetLiveStations poll (5s),
// so that was 4 wasted round-trips per call on a remote MySQL, adding latency to
// the "who's on air" widget and holding pool connections for nothing.
func (a *App) ensureLiveStatusTable() error {
db := a.logDb
a.liveTableMu.Lock()
done := a.liveTableFor == db // re-ensure after a profile switch swaps the logbook
a.liveTableMu.Unlock()
if done {
return nil
}
if err := a.ensureLiveStatusTableDDL(); err != nil {
return err
}
a.liveTableMu.Lock()
a.liveTableFor = db
a.liveTableMu.Unlock()
return nil
}
func (a *App) ensureLiveStatusTableDDL() error {
if _, err := a.logDb.ExecContext(a.ctx,
"CREATE TABLE IF NOT EXISTS live_status ("+
"operator VARCHAR(32) PRIMARY KEY, "+