fix(presence): reset stale users.status on boot; drop REST-login online write
users.status was only flipped back to offline by the WebSocket disconnect path (5s grace timer in ConnectionManager). Process exits (deploy/crash/OOM) lose those in-memory timers, freezing any non-offline row at its last value and making the user appear permanently online to friends and space co-members. Confirmed in production on the Pi instance: a user appeared online for ~3 days with no live socket. Add resetStalePresenceOnBoot() in utils/presenceBoot.ts and call it from index.ts after getDb()/seedDatabase() and before WebSocket route registration. The reset is federation-safe: it only updates rows where home_instance IS NULL (replicated stubs are projections of remote presence and must not be stomped) and is_deleted = 0 (tombstoned users are excluded from broadcasts). Also remove the redundant status='online' write from POST /api/auth/login. A successful REST login does not imply a live socket; the WS auth handshake is the single source of truth. Login alone could otherwise produce the same stuck-online row when a client logs in and never establishes a WS. Tests cover: locally-homed online/idle/dnd reset, replicated rows untouched, tombstoned rows untouched, idempotence, mixed populations. Updates docs/systems/activity-presence.md (Connect/Disconnect Flow, new Boot Reset section) and docs/systems/auth.md (login no longer mutates status).
This commit is contained in:
@@ -28,6 +28,7 @@ import { federationRoutes } from './routes/federation.js';
|
||||
import { startFederationWorkers, stopFederationWorkers } from './utils/federationWorker.js';
|
||||
import './utils/federationRollback.js'; // Side-effect: registers rollback callbacks for outbox terminal failures.
|
||||
import { registerCallRelayHooks } from './ws/events.js';
|
||||
import { resetStalePresenceOnBoot } from './utils/presenceBoot.js';
|
||||
|
||||
import { registerWebSocket } from './ws/handler.js';
|
||||
import path from 'path';
|
||||
@@ -82,6 +83,14 @@ async function main(): Promise<void> {
|
||||
getDb();
|
||||
await seedDatabase();
|
||||
|
||||
// Reset orphaned `users.status` rows for locally-homed users. The previous
|
||||
// process's in-memory disconnect timers are gone, so any non-offline row
|
||||
// is stale by construction. Replicated (federated) rows are skipped — their
|
||||
// status is a projection of remote presence, not local WS state. Must run
|
||||
// before WS auth is accepted so the first connection broadcasts the correct
|
||||
// online transition. See utils/presenceBoot.ts.
|
||||
resetStalePresenceOnBoot();
|
||||
|
||||
await app.register(authRoutes);
|
||||
await app.register(userRoutes);
|
||||
await app.register(spaceRoutes);
|
||||
|
||||
Reference in New Issue
Block a user