server: make the admin test push hit native (APNs/relay) too (#172)
sendTestPush was Web Push only, so the admin "Send test notification" button never exercised APNs — the transport that goes through the push relay. Fan out over apnsFanOut as well (no exclusions, always-show), so one tap tests both browsers and native iOS. The dummy thread/blob can't be decrypted, so the NSE ships the placeholder "New message" banner — the connectivity signal a test wants. No-op on instances without APNs/relay configured.
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 file changed,
+9
−1
push.go+9 −1
diff --git a/push.go b/push.go| index 64c66b8..e18b6be 100644 |
| --- a/push.go |
| +++ b/push.go |
| @@ -180,9 +180,17 @@ func (s *Server) pushNewMessage(blob, threadID, senderToken string) { |
| // sendTestPush delivers a generic "it works" notification to every subscriber, |
| // bypassing the message flow (and ignoring active state, so it always lands). |
| +// It covers BOTH transports: Web Push (browsers) and APNs (native iOS, via the |
| +// relay when configured). The APNs side carries a dummy thread/blob the NSE |
| +// can't decrypt, so the phone shows the placeholder "New message" banner (see |
| +// ios/LChatNSE) — which is exactly the "push reached this device" signal a test |
| +// wants. No exclusions (exceptToken="", recipients=nil) and skipActive=false, so |
| +// it always lands, on every registered device including the admin's own. |
| func (s *Server) sendTestPush() int { |
| payload, _ := json.Marshal(map[string]bool{"test": true}) |
| - return s.sendToRecipients(payload, "test", "", false, nil) |
| + sent := s.sendToRecipients(payload, "test", "", false, nil) |
| + sent += s.apnsFanOut("test", "test", "", false, nil) // no-op unless APNs/relay configured |
| + return sent |
| } |
| // maxPushConcurrency bounds simultaneous sends so a large group doesn't open |