Presence & status model + endpoints (Redis heartbeat) #24
Closed
coder-bot
wants to merge 4 commits from
agent/dd2a126b57a1 into main
pull from: agent/dd2a126b57a1
merge into: daddy:main
daddy:main
daddy:agent/5a68d35804c7-pc4-packaging
daddy:agent/2b4212c24690-pc4-checkpoint
daddy:agent/b77bddf31ba5-godot-discover-home-ui
daddy:agent/56c49440d0a6-godot-discovery-client
daddy:agent/33706f401ad9-discovery-trending-sort
daddy:agent/300da1dcddf3-discovery-browse-search
daddy:agent/42726e60fe10-world-visit-tracking
daddy:agent/b377353b8167-world-discovery-metadata
daddy:agent/96e5f11d7896-godot-selling-payout-ui
daddy:agent/9797a0cfd9e6-godot-marketplace-ui
daddy:agent/34c725431645-godot-wallet-ui
daddy:agent/cc09c1428892-godot-economy-api-client
daddy:agent/4ba24a178c1b-marketplace-moderation-agegate
daddy:agent/b52008923e93-payout-review-audit
daddy:agent/f5b4684f2e65-payout-requests-api
daddy:agent/bc90d3f481b4-purchase-escrow-service
daddy:agent/8aaf7c48d376-reports
daddy:agent/5645c26acd5c-public-profile
daddy:agent/d6b3f928e9ad-dm-chat
daddy:agent/86f6ef19b352-profile-model
daddy:agent/8bc166bd983b-friends-router
daddy:agent/cc677e477fe8-groups-api
daddy:agent/4daac3f1cfba-text-filter
daddy:agent/ff065522ba55-group-models
daddy:agent/9eaf37ab424c-rate-limit-dep
daddy:agent/9e1b957d4ba4-chat-models
daddy:agent/f039e52c4b44-social-graph-models
daddy:agent/dd2a126b57a1-presence-recovery
daddy:agent/da6e9f0ae7db-editor-save-persist
daddy:agent/8f1c523a192c-editor-mvp
daddy:agent/0fcbdba8ad02-client-persistence-plumbing
daddy:agent/b34cc0769dd4-world-persistence-backend
daddy:agent/6882fd1336fe-scripting-dsl
daddy:agent/64d9aef16e02-world-persistence-design
daddy:agent/9e7930c1b631
daddy:agent/896fbb97de1d
daddy:agent/48206289ff51
daddy:agent/806cbbff5213
daddy:agent/3bba30f838d1
daddy:agent/9bfb90314053
daddy:agent/0c49a36fc7db
daddy:agent/82015c078037
daddy:agent/b35fdc80cbe7
daddy:agent/a6deeb84eace
daddy:agent/f32370ab515a
daddy:agent/9186405a6373
daddy:agent/470f875e0416
daddy:agent/28357bca192d
daddy:agent/f4c371c981ba
daddy:agent/8ba2e727a079
daddy:agent/0b9e2c5ae6ec
daddy:agent/398bf1a22a2d
daddy:docs/pc1-checkpoint-record
daddy:agent/775379a9bedc
daddy:agent/1de295a0da42
daddy:agent/daf820da4497
daddy:agent/82c3ebd03921
daddy:agent/1bc0bc15886d
daddy:fix/pin-bcrypt-version
daddy:agent/424ce35e1b38
daddy:agent/a7d462fe1322
daddy:docs/test-inventory-and-runner
daddy:agent/d90bce564f55
daddy:agent/a547fd649f4c
daddy:agent/5b3da417b50c
daddy:agent/a755410a1c91
daddy:agent/76aa20cd2e45
daddy:agent/8705247aee3b
daddy:agent/5db523f4ee96
daddy:agent/19af79688cec
daddy:agent/ff065522ba55
daddy:agent/21694e349045
daddy:agent/25d5046bb30c
daddy:agent/101dc4b5b50a
daddy:fix/migration-0001-double-enum-and-port-collision
daddy:agent/3f3f21813946
daddy:agent/eb7dcb48c5d9
daddy:agent/189033f31353
daddy:agent/0969b759803d
daddy:agent/bc240b6723f6
daddy:agent/c88fc6092439
daddy:agent/cf1b515ab412
daddy:agent/55088d5301d4
daddy:agent/b377353b8167
daddy:agent/a048e11b2c2a
daddy:agent/10066a449585
daddy:agent/8343bc0a730f
daddy:agent/f039e52c4b44
daddy:agent/9eaf37ab424c
daddy:agent/7fedeb122f7e
daddy:agent/4daac3f1cfba
daddy:agent/f96c351dfa5e
daddy:agent/9e1b957d4ba4
daddy:agent/bd718b31b595
daddy:agent/5b8c6bf79631
daddy:pr-review
daddy:agent/6be8efe94fab
daddy:agent/8f93a0732163
daddy:agent/1a6e82153969
daddy:agent/684f458e47fc
daddy:agent/b03f366f5538
daddy:agent/521ef1e83a39
daddy:agent/ecd7c0ce8744
daddy:agent/7ab353cfd1ac
daddy:agent/ec0074121ca3
daddy:agent/e8de6a8ddb00
daddy:agent/c92c939b819c
daddy:agent/identitytest111
daddy:agent/997c429bc6b4
daddy:agent/a6930697b17d
daddy:agent/4f174a207618
daddy:agent/fa0834ec1ba7
daddy:agent/1cfe9ef94f55
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Delete Branch "agent/dd2a126b57a1"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Auto-opened for task
dd2a126b57a1.Dashboard: http://cloudbison:7070/task/dd2a126b57a1
Add presence/status support. Add
last_seen_atandstatus_messagecolumns to the User model (Alembic migration). Implement a Redis-backed presence store: aheartbeatendpoint that sets a TTL key per user, and apresenceendpoint that returns online/offline + last_seen + status for a list of user ids. Online = Redis key present (server-side TTL), never trust client claims. Add a reusable Redis dependency/client (the backend has REDIS_URL configured but unused). Status message must be length-limited. Reference docs/planning/social.md.I reviewed the full diff (
git diff main...origin/agent/dd2a126b57a1) plus the PR-branch files. The schema, model, and security posture (JWT-required on all three endpoints, online state derived server-side from a Redis TTL key rather than client claims) are on the right track. But there's a blocking bug that makes the feature non-functional, and a few secondary issues worth fixing at the same time.Blocking: the Redis client is never connected, so every presence endpoint 500s.
backend/app/routers/presence.pycreates_presence_store = PresenceStore()at import time, but nothing ever calls_presence_store.connect().backend/app/services/presence.pydefinesinit_presence_store()/shutdown_presence_store()and apresence_storeglobal, but I grep'd the entire PR branch —init_presence_storeis never called (and the global is never read).backend/app/main.py's lifespan handler only has a comment (# Startup: initialize database, connect to Redis, etc.) with an empty body — no wiring.PresenceStore.clientraisesRuntimeError("PresenceStore is not connected. Call connect() first.")becauseself._client is None, soPOST /api/presence/heartbeatandGET /api/presence/presenceboth blow up with a 500 on every request.Fix: actually wire it up — e.g.
await init_presence_store()in the lifespan startup andawait shutdown_presence_store()on shutdown — and make the router consume the same connected instance rather than its own module-level one. As written there are two separatePresenceStoreinstances and neither is connected.Secondary (please address in the same revision):
get_presenceand status-update logic inline, whilePresenceStore.get_presenceandupdate_status_messageare never called. Pick one path and delete the other, or the store's methods are untested dead weight that will drift._STATUS_MAX_LEN = 140(router/Pydantic) vs_MAX_STATUS_MESSAGE_LENGTH = 200(store). The PATCH endpoint actually enforces 140, but the store advertises 200. Make them a single source of truth.GET /presencecrashes on non-integer input:ids = [int(uid.strip()) ...]has no try/except, so?user_ids=abcraisesValueError→ 500 instead of a 400. Validate and return a clean 400. Also consider capping the number of IDs (unboundedIN/MGET).UPDATE users.last_seen_at+ commit on every beat. For a 60s-TTL design with potentially frequent client heartbeats, this is write amplification; consider throttling (e.g. only persist if stale) or moving last_seen into Redis if you don't need it immediately durable.PresenceStore.clientreturn annotation saysaioredis.Redisbut it can raise;close()vsaclose()is fine on redis-py 5.2.1 (verifiedcloseexists and is awaitable), so no change needed there.I also checked the migration:
2a9b9873244achains correctly off0001_initial, columns are nullable (safe on existing rows), anddowngradedrops in the correct order.user.pymodel matches the migration. No issue there.Net: correct idea, good security model, but it can't work until the Redis client is actually connected and the router uses it. Requesting changes.
Closing: superseded by commit
4eb4476/ branch agent/dd2a126b57a1-presence-recovery which ported presence forward and merged cleanly to main.Pull request closed