13 Commits

Author SHA1 Message Date
b16bca919f Fix send request 500: revert to naive datetime.now() per project contract
The QA fix incorrectly changed datetime.now() to datetime.now(timezone.utc),
but the DB uses TIMESTAMP WITHOUT TIME ZONE (naive). Passing a tz-aware
datetime to a naive column causes asyncpg DataError. Also removes the
temporary diagnostic wrapper and builds response before commit.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-05 21:14:39 +08:00
ea491a4b89 Add temporary diagnostic wrapper to send_connection_request
Exposes actual exception type and message in 500 response detail
to identify the unhandled error. Will be removed after diagnosis.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-05 20:54:38 +08:00
87d232cbcd Fix send request 500: build response before commit to avoid MissingGreenlet
After db.commit(), all ORM objects are expired. Accessing their attributes
in _build_request_response triggered lazy loads which fail in async
SQLAlchemy with MissingGreenlet. Move response construction before commit.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-05 20:48:25 +08:00
9bcf5ace5d Fix search cold-cache gate, 429 handling, and datetime.now() violations
ConnectionSearch.tsx:
- Add loading guard for useSettings() — prevents cold cache from showing
  "enable connections" gate when settings haven't loaded yet
- Add 429 rate limit handling in search catch block — shows user-friendly
  message instead of silently showing "User not found"
- Import axios for isAxiosError type guard

connections.py:
- Fix 3x datetime.now() → datetime.now(timezone.utc) per hard rule
  (lines 187, 378, 565)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-05 20:02:52 +08:00
4e2d48c50b Fix QA review findings: detach cleanup, sf() fallthrough, polling, commit guard
- W-01: Wrap accept flow db.commit() in IntegrityError handler (409)
- W-03: Remove refetchIntervalInBackground from unread count polling
- W-04: detach_umbral_contact now clears all shared fields on Person
- W-05: sf() callers no longer fall through via ?? to stale local data

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-05 19:12:31 +08:00
2fb41e0cf4 Fix toast accept stale closure + harden backend error responses
Toast accept button captured a stale `respond` reference from the
Sonner closure. Use respondRef pattern so clicks always dispatch
through the current mutation. Backend respond endpoint now catches
unhandled exceptions and returns proper JSON with detail field
instead of plain-text 500s.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-05 16:54:28 +08:00
b554ba7151 Fix QA: IntegrityError handling, dict mutation, birthday sync, None guard
- C-01: Wrap accept flow flush/commit in IntegrityError handling (409)
- C-02: Use separate remote_timestamps dict instead of pop() on shared profile
- W-01: Add birthday sync in Link conversion path (existing person → umbral)
- W-02: Add None guard on max(updated_at) comparison in get_person

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 09:05:46 +08:00
73cef1df55 Add umbral name header, preferred name field, and link button for contacts
- Inject umbral_name into shared_fields for umbral contacts (always visible)
- Show @umbralname subtitle in detail panel header
- Add preferred_name to panel fields with synced label for umbral contacts
- Add Link button on standard contacts to tie to umbral user via connection request
- Migration 046: person_id FK on connection_requests with index
- Validate person_id ownership on send, re-validate + convert on accept

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 08:37:01 +08:00
820ff46efa Fix QA W-01/W-05/W-06/W-08: cancel requests, detach umbral, notifications
W-08: Add CHECK constraint on notifications.type (migration 044) with
defensive pre-check and matching __table_args__ on model.

W-05: Auto-detach umbral contact before Person delete — nulls out
connection's person_id so the connection survives deletion.

W-01: Add PUT /requests/{id}/cancel endpoint with atomic UPDATE,
silent notification cleanup, and audit logging. Frontend: direction-aware
ConnectionRequestCard, cancel mutation, pending requests section on
PeoplePage with incoming/outgoing subsections.

W-06: Convert useNotifications to context provider pattern — single
subscription shared via NotificationProvider in AppLayout. Adds
refreshNotifications convenience function.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 07:17:31 +08:00
0e94b6e1f7 Fix QA review findings: race condition, detached session, validation
- C-01: Wrap connection request flush in IntegrityError handler for
  TOCTOU race on partial unique index
- W-02: Extract ntfy config into plain dict before commit to avoid
  DetachedInstanceError in background tasks
- W-04: Add integer range validation (1–2147483647) on notification IDs
- W-07: Add typed response models for respond_to_request endpoint
- W-09: Document resolved_at requirement for future cancel endpoint
- S-02: Use Literal type for ConnectionRequestResponse.status
- S-04: Check ntfy master switch in extract_ntfy_config
- S-05: Move date import to module level in connection service

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 06:36:14 +08:00
e27beb7736 Fix toast notifications, require accept_connections for senders
- Rewrite NotificationToaster with max-ID watermark for reliable
  new-notification detection and faster unread count polling (15s)
- Block connection search and requests when sender has
  accept_connections disabled (backend + frontend gate)
- Remove duplicate sender_settings fetch in send_connection_request
- Show actionable error messages in toast respond failures

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 06:21:43 +08:00
337b50c7ce Fix QA review findings: source_id, N+1 queries, event bubbling, type mismatches
Critical fixes:
- C-01: Add receiver_umbral_name/receiver_preferred_name to frontend ConnectionRequest type
- C-02: Flush connection request before notification to populate source_id
- C-03: Add umbral_name to ProfileResponse/UserProfile, use in Settings Social card
- C-04: Remove dead code in sharing-overrides endpoint, merge instead of replace

Warning fixes:
- W-01/W-02: Batch-fetch settings in incoming/outgoing/list connection endpoints (N+1 fix)
- W-04: Add _purge_resolved_requests job for rejected/cancelled requests (30-day retention)
- W-10: Add e.stopPropagation() to notification mark-read and delete buttons

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 02:29:04 +08:00
3d22568b9c Add user connections, notification centre, and people integration
Implements the full User Connections & Notification Centre feature:

Phase 1 - Database: migrations 039-043 adding umbral_name to users,
profile/social fields to settings, notifications table, connection
request/user_connection tables, and linked_user_id to people.

Phase 2 - Notifications: backend CRUD router + service + 90-day purge,
frontend NotificationsPage with All/Unread filter, bell icon in sidebar
with unread badge polling every 60s.

Phase 3 - Settings: profile fields (phone, mobile, address, company,
job_title), social card with accept_connections toggle and per-field
sharing defaults, umbral name display with CopyableField.

Phase 4 - Connections: timing-safe user search, send/accept/reject flow
with atomic status updates, bidirectional UserConnection + Person records,
in-app + ntfy notifications, per-receiver pending cap, nginx rate limiting.

Phase 5 - People integration: batch-loaded shared profiles (N+1 prevention),
Ghost icon for umbral contacts, Umbral filter pill, split Add Person button,
shared field indicators (synced labels + Lock icons), disabled form inputs
for synced fields on umbral contacts.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 02:10:16 +08:00