Mobile notifications for iOS (#210)

* mobile: wire notification registration and listener

* implement backend components for push notifications

* refactor: agentic comment cleanup

* docs: use proper module name for particle processor

* set required env variables for push notifications

* bump version

* fix: always upsert push token on mobile start

* Revert "fix: always upsert push token on mobile start"

This reverts commit 90ff18a788.

* send push notifications regardless of online status
This commit was merged in pull request #210.
This commit is contained in:
Arjun Patel
2026-05-18 12:44:31 -07:00
committed by GitHub
parent a564ea819b
commit d262f734f0
61 changed files with 1682 additions and 531 deletions
+7 -19
View File
@@ -25,29 +25,22 @@ func main() {
port := utils.MustGetEnv("PORT")
grpcPort := utils.MustGetEnv("GRPC_PORT")
// Initialize database (for network membership checks)
db.Init()
defer db.Cleanup()
// Redis for auth session validation (same DB as orion)
authRedis := internal.ConnectAndTestRedis(db.RedisDBAuth)
authRedis := internal.ConnectAndTestRedis(db.RedisDBAuth) // shared with orion
pusherRedis := internal.ConnectAndTestRedis(db.RedisDBPusher) // presence + pub/sub
// Redis for pusher state (presence hashes, pub/sub)
pusherRedis := internal.ConnectAndTestRedis(db.RedisDBPusher)
sessionReader := auth.NewSessionReader(authRedis)
networkReader := network.NewReader(db.Pool())
// Services
sessionReader := auth.NewSessionReader(authRedis) // pusher only validates sessions
networkReader := network.NewReader(db.Pool()) // pusher only checks membership
// Pod identity (use hostname in k8s, which is the pod name)
// Hostname is the k8s pod name.
podID, err := os.Hostname()
if err != nil {
podID = fmt.Sprintf("pod-%d", os.Getpid())
}
// Pusher core
bridge := pusher.NewRedisBridge(pusherRedis, podID)
// Context for graceful shutdown
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
@@ -56,16 +49,11 @@ func main() {
bridge.SetHub(hub)
server := pusher.NewServer(ctx, hub, bridge, sessionReader)
// Start hub event loop
go hub.Run(ctx)
// Start Redis Pub/Sub listener
go bridge.Listen(ctx)
// Start pod heartbeat + stale pod cleanup
go bridge.Heartbeat(ctx)
// --- gRPC server (internal, for presence queries) ---
// --- gRPC server (internal presence queries) ---
grpcListener, err := net.Listen("tcp", fmt.Sprintf("0.0.0.0:%s", grpcPort))
if err != nil {
slog.Error("failed to listen for gRPC", "port", grpcPort, "error", err)
@@ -104,7 +92,7 @@ func main() {
<-sigCh
slog.Info("shutting down...")
cancel() // stops hub, bridge listener, heartbeat
cancel()
grpcServer.GracefulStop()
httpServer.Shutdown(context.Background())