88{-# LANGUAGE OverloadedLists #-}
99{-# LANGUAGE OverloadedStrings #-}
1010{-# LANGUAGE ScopedTypeVariables #-}
11+ {-# LANGUAGE StandaloneDeriving #-}
1112
1213module Simplex.Messaging.Notifications.Server.Store where
1314
@@ -16,15 +17,16 @@ import Control.Monad
1617import Data.ByteString.Char8 (ByteString )
1718import Data.Functor (($>) )
1819import Data.List.NonEmpty (NonEmpty (.. ), (<|) )
20+ import Data.Map.Strict (Map )
1921import qualified Data.Map.Strict as M
20- import Data.Maybe (catMaybes )
22+ import Data.Maybe (isNothing )
2123import Data.Set (Set )
2224import qualified Data.Set as S
2325import Data.Word (Word16 )
2426import qualified Simplex.Messaging.Crypto as C
2527import Simplex.Messaging.Encoding.String
2628import Simplex.Messaging.Notifications.Protocol
27- import Simplex.Messaging.Protocol (NtfPrivateAuthKey , NtfPublicAuthKey , SMPServer )
29+ import Simplex.Messaging.Protocol (NotifierId , NtfPrivateAuthKey , NtfPublicAuthKey , SMPServer )
2830import Simplex.Messaging.Server.QueueStore (RoundedSystemTime )
2931import Simplex.Messaging.TMap (TMap )
3032import qualified Simplex.Messaging.TMap as TM
@@ -35,8 +37,11 @@ data NtfStore = NtfStore
3537 -- multiple registrations exist to protect from malicious registrations if token is compromised
3638 tokenRegistrations :: TMap DeviceToken (TMap ByteString NtfTokenId ),
3739 subscriptions :: TMap NtfSubscriptionId NtfSubData ,
38- tokenSubscriptions :: TMap NtfTokenId (TVar (Set NtfSubscriptionId )),
39- subscriptionLookup :: TMap SMPQueueNtf NtfSubscriptionId ,
40+ -- the first set is used to delete from `subscriptions` when token is deleted, the second - to cancel SMP subsriptions.
41+ -- TODO [notifications] it can be simplified once NtfSubData is fully removed.
42+ tokenSubscriptions :: TMap NtfTokenId (TMap SMPServer (TVar (Set NtfSubscriptionId ), TVar (Set NotifierId ))),
43+ -- TODO [notifications] for subscriptions that "migrated" to server subscription, we may replace NtfSubData with NtfTokenId here (Either NtfSubData NtfTokenId).
44+ subscriptionLookup :: TMap SMPServer (TMap NotifierId NtfSubData ),
4045 tokenLastNtfs :: TMap NtfTokenId (TVar (NonEmpty PNMessageData ))
4146 }
4247
@@ -134,7 +139,7 @@ removeTokenRegistration st NtfTknData {ntfTknId = tId, token, tknVerifyKey} =
134139 >>= mapM_ (\ tId' -> when (tId == tId') $ TM. delete k regs)
135140 k = C. toPubKey C. pubKeyBytes tknVerifyKey
136141
137- deleteNtfToken :: NtfStore -> NtfTokenId -> STM [ SMPQueueNtf ]
142+ deleteNtfToken :: NtfStore -> NtfTokenId -> STM ( Map SMPServer ( Set NotifierId ))
138143deleteNtfToken st tknId = do
139144 void $
140145 TM. lookupDelete tknId (tokens st) $>>= \ NtfTknData {token, tknVerifyKey} ->
@@ -147,25 +152,25 @@ deleteNtfToken st tknId = do
147152 regs = tokenRegistrations st
148153 regKey = C. toPubKey C. pubKeyBytes
149154
150- deleteTokenSubs :: NtfStore -> NtfTokenId -> STM [SMPQueueNtf ]
151- deleteTokenSubs st tknId = do
152- qs <-
153- TM. lookupDelete tknId (tokenSubscriptions st)
154- >>= mapM (readTVar >=> mapM deleteSub . S. toList)
155- pure $ maybe [] catMaybes qs
155+ deleteTokenSubs :: NtfStore -> NtfTokenId -> STM (Map SMPServer (Set NotifierId ))
156+ deleteTokenSubs st tknId =
157+ TM. lookupDelete tknId (tokenSubscriptions st)
158+ >>= maybe (pure M. empty) (readTVar >=> deleteSrvSubs)
156159 where
157- deleteSub subId = do
158- TM. lookupDelete subId (subscriptions st)
159- $>>= \ NtfSubData {smpQueue} ->
160- TM. delete smpQueue (subscriptionLookup st) $> Just smpQueue
160+ deleteSrvSubs :: Map SMPServer (TVar (Set NtfSubscriptionId ), TVar (Set NotifierId )) -> STM (Map SMPServer (Set NotifierId ))
161+ deleteSrvSubs = M. traverseWithKey $ \ smpServer (sVar, nVar) -> do
162+ sIds <- readTVar sVar
163+ modifyTVar' (subscriptions st) (`M.withoutKeys` sIds)
164+ nIds <- readTVar nVar
165+ TM. lookup smpServer (subscriptionLookup st) >>= mapM_ (`modifyTVar'` (`M.withoutKeys` nIds))
166+ pure nIds
161167
162168getNtfSubscriptionIO :: NtfStore -> NtfSubscriptionId -> IO (Maybe NtfSubData )
163169getNtfSubscriptionIO st subId = TM. lookupIO subId (subscriptions st)
164170
165171findNtfSubscription :: NtfStore -> SMPQueueNtf -> STM (Maybe NtfSubData )
166- findNtfSubscription st smpQueue = do
167- TM. lookup smpQueue (subscriptionLookup st)
168- $>>= \ subId -> TM. lookup subId (subscriptions st)
172+ findNtfSubscription st SMPQueueNtf {smpServer, notifierId} =
173+ TM. lookup smpServer (subscriptionLookup st) $>>= TM. lookup notifierId
169174
170175findNtfSubscriptionToken :: NtfStore -> SMPQueueNtf -> STM (Maybe NtfTknData )
171176findNtfSubscriptionToken st smpQueue = do
@@ -183,30 +188,45 @@ mkNtfSubData ntfSubId (NewNtfSub tokenId smpQueue notifierKey) = do
183188 subStatus <- newTVar NSNew
184189 pure NtfSubData {ntfSubId, smpQueue, tokenId, subStatus, notifierKey}
185190
186- addNtfSubscription :: NtfStore -> NtfSubscriptionId -> NtfSubData -> STM (Maybe () )
187- addNtfSubscription st subId sub@ NtfSubData {smpQueue, tokenId} =
188- TM. lookup tokenId (tokenSubscriptions st) >>= maybe newTokenSub pure >>= insertSub
191+ -- returns False if subscription existed before
192+ addNtfSubscription :: NtfStore -> NtfSubscriptionId -> NtfSubData -> STM Bool
193+ addNtfSubscription st subId sub@ NtfSubData {smpQueue = SMPQueueNtf {smpServer, notifierId}, tokenId} =
194+ TM. lookup tokenId (tokenSubscriptions st)
195+ >>= maybe newTokenSubs pure
196+ >>= \ ts -> TM. lookup smpServer ts
197+ >>= maybe (newTokenSrvSubs ts) pure
198+ >>= insertSub
189199 where
190- newTokenSub = do
191- ts <- newTVar S . empty
200+ newTokenSubs = do
201+ ts <- newTVar M . empty
192202 TM. insert tokenId ts $ tokenSubscriptions st
193203 pure ts
194- insertSub ts = do
195- modifyTVar' ts $ S. insert subId
204+ newTokenSrvSubs ts = do
205+ tss <- (,) <$> newTVar S. empty <*> newTVar S. empty
206+ TM. insert smpServer tss ts
207+ pure tss
208+ insertSub :: (TVar (Set NtfSubscriptionId ), TVar (Set NotifierId )) -> STM Bool
209+ insertSub (sIds, nIds) = do
210+ modifyTVar' sIds $ S. insert subId
211+ modifyTVar' nIds $ S. insert notifierId
196212 TM. insert subId sub $ subscriptions st
197- TM. insert smpQueue subId (subscriptionLookup st)
198- -- return Nothing if subscription existed before
199- pure $ Just ()
213+ TM. lookup smpServer (subscriptionLookup st)
214+ >>= maybe newSubs pure
215+ >>= fmap isNothing . TM. lookupInsert notifierId sub
216+ newSubs = do
217+ ss <- newTVar M. empty
218+ TM. insert smpServer ss $ subscriptionLookup st
219+ pure ss
200220
201221deleteNtfSubscription :: NtfStore -> NtfSubscriptionId -> STM ()
202- deleteNtfSubscription st subId = do
203- TM. lookupDelete subId (subscriptions st)
204- >>= mapM_
205- ( \ NtfSubData {smpQueue, tokenId} -> do
206- TM. delete smpQueue $ subscriptionLookup st
207- ts_ <- TM. lookup tokenId (tokenSubscriptions st)
208- forM_ ts_ $ \ ts -> modifyTVar' ts $ S. delete subId
209- )
222+ deleteNtfSubscription st subId = TM. lookupDelete subId (subscriptions st) >>= mapM_ deleteSubIndices
223+ where
224+ deleteSubIndices NtfSubData {smpQueue = SMPQueueNtf {smpServer, notifierId}, tokenId} = do
225+ TM. lookup smpServer (subscriptionLookup st) >>= mapM_ ( TM. delete notifierId)
226+ tss_ <- TM. lookup tokenId (tokenSubscriptions st) $>>= TM. lookup smpServer
227+ forM_ tss_ $ \ (sIds, nIds) -> do
228+ modifyTVar' sIds $ S. delete subId
229+ modifyTVar' nIds $ S. delete notifierId
210230
211231addTokenLastNtf :: NtfStore -> NtfTokenId -> PNMessageData -> IO (NonEmpty PNMessageData )
212232addTokenLastNtf st tknId newNtf =
0 commit comments