From 049b58c1831f8de007fa7fcfaedd2e09a4c7e804 Mon Sep 17 00:00:00 2001 From: jevb Date: Sun, 15 Mar 2026 12:21:10 +0100 Subject: [PATCH] fix: prevent stale "online" status for users not connected via WebSocket - Remove premature UpdateUserStatus("online") from REST login handler; the WebSocket serve.go already sets "online" on actual WS connect - Add ResetAllUserStatuses() called at server startup to clear stale statuses from previous runs or crashes (alongside ClearAllVoiceStates) --- Server/api/auth_handler.go | 5 ++++- Server/db/auth_queries.go | 10 ++++++++++ Server/main.go | 7 ++++++- 3 files changed, 20 insertions(+), 2 deletions(-) diff --git a/Server/api/auth_handler.go b/Server/api/auth_handler.go index 56a2a5cd..74250ff7 100644 --- a/Server/api/auth_handler.go +++ b/Server/api/auth_handler.go @@ -251,7 +251,10 @@ func handleLogin(database *db.DB, limiter *auth.RateLimiter) http.HandlerFunc { return } - _ = database.UpdateUserStatus(user.ID, "online") + // Don't set status to "online" here — the WebSocket connection in + // serve.go does that when the user actually connects. Setting it here + // would leave the user permanently "online" if they never open a WS + // connection or if the client crashes before connecting. slog.Info("user logged in", "username", user.Username, "user_id", user.ID, "ip", ip) _ = database.LogAudit(user.ID, "user_login", "user", user.ID, "logged in from "+ip) diff --git a/Server/db/auth_queries.go b/Server/db/auth_queries.go index 152e370c..f96b27a8 100644 --- a/Server/db/auth_queries.go +++ b/Server/db/auth_queries.go @@ -78,6 +78,16 @@ func (d *DB) UpdateUserStatus(id int64, status string) error { return nil } +// ResetAllUserStatuses sets all users to "offline". Called on server startup +// to clear stale statuses from a previous run or crash. +func (d *DB) ResetAllUserStatuses() error { + _, err := d.sqlDB.Exec(`UPDATE users SET status = 'offline' WHERE status != 'offline'`) + if err != nil { + return fmt.Errorf("ResetAllUserStatuses: %w", err) + } + return nil +} + // BanUser marks a user as banned with an optional expiry. Pass nil for a // permanent ban. func (d *DB) BanUser(id int64, reason string, expires *time.Time) error { diff --git a/Server/main.go b/Server/main.go index cb3850c8..29075cfd 100644 --- a/Server/main.go +++ b/Server/main.go @@ -75,7 +75,12 @@ func run(log *slog.Logger) error { return fmt.Errorf("running migrations: %w", err) } - // Clear stale voice states from a previous run. + // Clear stale state from a previous run or crash. + if err := database.ResetAllUserStatuses(); err != nil { + log.Warn("failed to reset stale user statuses", "error", err) + } else { + log.Info("reset all user statuses to offline") + } if err := database.ClearAllVoiceStates(); err != nil { log.Warn("failed to clear stale voice states", "error", err) } else {