on master help sign in
Simplify client code and improve type safety: remove unused AuthResult, eliminate dead getJson, replace LoadedState with discriminated union
f11ecd2iko
client/ApiClient.fs+3−13
@@ -11,7 +11,6 @@ module ApiClient =
11 type EventPayload = { EncryptedBlob: string; Timestamp: int64 }
11 type EventPayload = { EncryptedBlob: string; Timestamp: int64 }
12 type PollEvent = { Id: string; Payload: EventPayload }
12 type PollEvent = { Id: string; Payload: EventPayload }
13
13
14− type AuthResult = { Token: string; ExpiresAt: int64 }
15 type SendResult = { Status: string; MessageId: string }
14 type SendResult = { Status: string; MessageId: string }
16 type PollResponse = { Events: PollEvent list; Cursor: uint64 }
15 type PollResponse = { Events: PollEvent list; Cursor: uint64 }
17
16
@@ -45,15 +44,6 @@ module ApiClient =
45
44
46 let private postJson url body token = postJsonWith client url body token
45 let private postJson url body token = postJsonWith client url body token
47
46
48− let private getJson (url: string) (token: string) =
49− async {
50− let request = new HttpRequestMessage(HttpMethod.Get, url)
51− request.Headers.Add("Authorization", $"Bearer {token}")
52− let! response = client.SendAsync(request) |> awaitTask
53− let! text = response.Content.ReadAsStringAsync() |> awaitTask
54− return JsonDocument.Parse(text)
55− }
56−
57 let private expectObject (doc: JsonDocument) (endpoint: string) =
47 let private expectObject (doc: JsonDocument) (endpoint: string) =
58 let root = doc.RootElement
48 let root = doc.RootElement
59
49
@@ -78,9 +68,9 @@ module ApiClient =
78
68
79 let! doc = postJson $"{serverUrl}/auth/verify" body None
69 let! doc = postJson $"{serverUrl}/auth/verify" body None
80 let root = expectObject doc "/auth/verify"
70 let root = expectObject doc "/auth/verify"
81− return
82− { Token = root.GetProperty("token").GetString()
71+ let token = root.GetProperty("token").GetString()
83− ExpiresAt = root.GetProperty("expiresAt").GetInt64() }
72+ if String.IsNullOrEmpty(token) then failwith "Authentication rejected by server"
73+ return token
84 }
74 }
85
75
86 let sendMessage (serverUrl: string) (token: string) (toHex: string) (blobHex: string) (timestamp: int64) =
76 let sendMessage (serverUrl: string) (token: string) (toHex: string) (blobHex: string) (timestamp: int64) =
client/App.fs+11−12
@@ -50,8 +50,8 @@ module App =
50 Cursor: uint64 }
50 Cursor: uint64 }
51
51
52 type LoadedState =
52 type LoadedState =
53− { Identity: Identity option
53+ | NoIdentity
54− Data: Store.DataDto option }
54+ | Loaded of identity: Identity * data: Store.DataDto option
55
55
56 // ── Model ──
56 // ── Model ──
57
57
@@ -262,8 +262,8 @@ module App =
262
262
263 | DismissError -> { model with Error = None }, []
263 | DismissError -> { model with Error = None }, []
264
264
265− | StateLoaded { Identity = None } -> model, []
265+ | StateLoaded NoIdentity -> model, []
266− | StateLoaded { Identity = Some identity; Data = dataOpt } ->
266+ | StateLoaded(Loaded(identity, dataOpt)) ->
267 let model' = { model with Identity = Some identity; Conn = Connecting identity }
267 let model' = { model with Identity = Some identity; Conn = Connecting identity }
268
268
269 let model' =
269 let model' =
@@ -333,12 +333,8 @@ module App =
333 try
333 try
334 let! challenge = ApiClient.requestChallenge url identity.PubKeyHex
334 let! challenge = ApiClient.requestChallenge url identity.PubKeyHex
335 let sigHex = Crypto.signChallenge identity.PrivKey challenge
335 let sigHex = Crypto.signChallenge identity.PrivKey challenge
336− let! result = ApiClient.verify url identity.PubKeyHex challenge sigHex
336+ let! token = ApiClient.verify url identity.PubKeyHex challenge sigHex
337−
338− if String.IsNullOrEmpty(result.Token) then
339− return AuthErr "Authentication rejected by server"
340− else
341− return AuthOk result.Token
337+ return AuthOk token
342 with ex ->
338 with ex ->
343 return AuthErr ex.Message
339 return AuthErr ex.Message
344 })
340 })
@@ -399,8 +395,11 @@ module App =
399 | CmdLoadState ->
395 | CmdLoadState ->
400 asyncCmd (async {
396 asyncCmd (async {
401 let! identity = Store.loadIdentity ()
397 let! identity = Store.loadIdentity ()
398+ match identity with
399+ | None -> return StateLoaded NoIdentity
400+ | Some id ->
402− let data = Store.loadData ()
401+ let data = Store.loadData ()
403− return StateLoaded { Identity = identity; Data = data }
402+ return StateLoaded(Loaded(id, data))
404 })
403 })
405
404
406 | CmdSaveIdentity identity ->
405 | CmdSaveIdentity identity ->
server.knot+88−110
@@ -49,7 +49,7 @@ type GossipEventType = Text where \s -> s == "online" || s == "offline"
49
49
50type GossipEvent = {eventType: GossipEventType, pubkey: Text, server: Text, timestamp: Timestamp, ttl: Int<S>}
50type GossipEvent = {eventType: GossipEventType, pubkey: Text, server: Text, timestamp: Timestamp, ttl: Int<S>}
51
51
52−type ValidGossipEntry = {eventType: GossipEventType, pubkey: PubkeyHex, server: Text, expiresAt: Timestamp, timestamp: Timestamp, ttl: Int<S>}
52+type ValidGossipEntry = {eventType: GossipEventType, pubkey: PubkeyHex, server: ServerName, expiresAt: Timestamp, timestamp: Timestamp, ttl: Int<S>}
53
53
54type MessageStatus = Text where \s -> s == "unauthorized" || s == "rejected" || s == "delivered" || s == "federated" || s == "queued"
54type MessageStatus = Text where \s -> s == "unauthorized" || s == "rejected" || s == "delivered" || s == "federated" || s == "queued"
55
55
@@ -145,6 +145,7 @@ route Api = AuthApi | ClientApi | FederationApi | MaintenanceApi
145
145
146any = \xs pred -> fold (\acc x -> if acc then acc else pred x) (False {}) xs
146any = \xs pred -> fold (\acc x -> if acc then acc else pred x) (False {}) xs
147none = \xs pred -> not (any xs pred)
147none = \xs pred -> not (any xs pred)
148+when = \cond action -> if cond then action else yield {}
148headOf = \items -> fold (\acc x -> case acc of
149headOf = \items -> fold (\acc x -> case acc of
149 Nothing {} -> Just {value: x}
150 Nothing {} -> Just {value: x}
150 _ -> acc) Nothing {} items
151 _ -> acc) Nothing {} items
@@ -583,13 +584,11 @@ broadcastOfflineGossip = \t peerServers expiredLocal -> do
583 let expiredEntries = mergeByKey (\a b -> a.pubkey == b.pubkey) [] (do
584 let expiredEntries = mergeByKey (\a b -> a.pubkey == b.pubkey) [] (do
584 p <- expiredLocal
585 p <- expiredLocal
585 yield {pubkey: p.pubkey, ttl: (let rawTtl = (p.expiresAt - p.timestamp) / 1000 in if rawTtl < offlineGossipMinTtl then offlineGossipMinTtl else if rawTtl > maxPresenceTtl then maxPresenceTtl else rawTtl), expiresAt: p.expiresAt})
586 yield {pubkey: p.pubkey, ttl: (let rawTtl = (p.expiresAt - p.timestamp) / 1000 in if rawTtl < offlineGossipMinTtl then offlineGossipMinTtl else if rawTtl > maxPresenceTtl then maxPresenceTtl else rawTtl), expiresAt: p.expiresAt})
586− if federationEnabled && count expiredEntries > 0 && count peerServers > 0
587+ when (federationEnabled && count expiredEntries > 0 && count peerServers > 0) (do
587− then do
588− let gossipEvents = do
588+ let gossipEvents = do
589− e <- expiredEntries
589+ e <- expiredEntries
590− yield {eventType: "offline", pubkey: e.pubkey, server: serverHostN, timestamp: e.expiresAt, ttl: e.ttl}
590+ yield {eventType: "offline", pubkey: e.pubkey, server: serverHostN, timestamp: e.expiresAt, ttl: e.ttl}
591− sendBatchedGossip peerServers gossipEvents
591+ sendBatchedGossip peerServers gossipEvents)
592− else yield {}
593
592
594forkBroadcastOffline = \t result ->
593forkBroadcastOffline = \t result ->
595 fork (broadcastOfflineGossip t result.peerServers result.expiredLocal)
594 fork (broadcastOfflineGossip t result.peerServers result.expiredLocal)
@@ -658,14 +657,11 @@ pruneAndRetry = \t -> do
658 let pendingAcks = do a <- ackedIds; where isFederatedOrigin a.originServer; yield a
657 let pendingAcks = do a <- ackedIds; where isFederatedOrigin a.originServer; yield a
659 yield {expiredLocal: pr.expiredLocal, peerServers: pr.peerServers, pendingForwards: forwards, pendingAcks: pendingAcks}
658 yield {expiredLocal: pr.expiredLocal, peerServers: pr.peerServers, pendingForwards: forwards, pendingAcks: pendingAcks}
660 broadcastOfflineGossip t result.peerServers result.expiredLocal
659 broadcastOfflineGossip t result.peerServers result.expiredLocal
661− if federationEnabled && count result.pendingForwards > 0
660+ when (federationEnabled && count result.pendingForwards > 0)
662− then retryPendingForwards result.pendingForwards
661+ (retryPendingForwards result.pendingForwards)
663− else yield {}
664− if federationEnabled && count result.pendingAcks > 0
662+ when (federationEnabled && count result.pendingAcks > 0) (do
665− then do
666− let ackOriginServers = collectPeerServers result.pendingAcks (\a -> a.originServer)
663+ let ackOriginServers = collectPeerServers result.pendingAcks (\a -> a.originServer)
667− sendAcksAndClean result.pendingAcks ackOriginServers
664+ sendAcksAndClean result.pendingAcks ackOriginServers)
668− else yield {}
669
665
670pollOnce : Text -> Int -> IO {clock} {authorized: Bool, cursor: Int, events: [PollEvent]}
666pollOnce : Text -> Int -> IO {clock} {authorized: Bool, cursor: Int, events: [PollEvent]}
671pollOnce = \token cursor -> do
667pollOnce = \token cursor -> do
@@ -751,40 +747,35 @@ serve = \req -> case req of
751 let sigValid = case matched of
747 let sigValid = case matched of
752 Nothing {} -> False {}
748 Nothing {} -> False {}
753 _ -> verify (bytesFromHex pubkey) (textToBytes challenge) (bytesFromHex signature)
749 _ -> verify (bytesFromHex pubkey) (textToBytes challenge) (bytesFromHex signature)
754− set *challenges = if sigValid
750+ if not sigValid
755− then do
751+ then yield {sigValid: False {}, peerServers: [], expiredLocal: pr.expiredLocal, offlinePeers: pr.peerServers}
752+ else do
753+ set *challenges = do
756− c <- challenges
754+ c <- challenges
757− where not (c.pubkey == pubkey && c.challenge == challenge)
755+ where not (c.pubkey == pubkey && c.challenge == challenge)
758− yield c
756+ yield c
759− else challenges
760− let sessionsCapped = capPerKey sessions (\s -> s.pubkey == pubkey) maxSessionsPerKey (\s -> s.expiresAt) t
757+ let sessionsCapped = capPerKey sessions (\s -> s.pubkey == pubkey) maxSessionsPerKey (\s -> s.expiresAt) t
761− let activeSessionsCapped = do s <- sessionsCapped; where s.expiresAt > t; yield s
758+ let activeSessionsCapped = do s <- sessionsCapped; where s.expiresAt > t; yield s
762− let globalSessionsCapped = capGlobal activeSessionsCapped maxSessions (\s -> s.expiresAt)
759+ let globalSessionsCapped = capGlobal activeSessionsCapped maxSessions (\s -> s.expiresAt)
763− set *sessions = if sigValid
764− then union globalSessionsCapped [{token: token, pubkey: pubkey, expiresAt: expiry}]
760+ set *sessions = union globalSessionsCapped [{token: token, pubkey: pubkey, expiresAt: expiry}]
765− else sessions
766− let presenceWithout = do
761+ let presenceWithout = do
767− p <- presence
762+ p <- presence
768− where not (p.pubkey == pubkey && p.server == serverHostN)
763+ where not (p.pubkey == pubkey && p.server == serverHostN)
769− yield p
764+ yield p
770− let presenceEvicted = capGlobal presenceWithout maxPresenceEntries (\p -> p.expiresAt)
765+ let presenceEvicted = capGlobal presenceWithout maxPresenceEntries (\p -> p.expiresAt)
771− let updatedPresence = if sigValid
772− then union presenceEvicted [{pubkey: pubkey, server: serverHostN, expiresAt: expiry, timestamp: t}]
766+ let updatedPresence = union presenceEvicted [{pubkey: pubkey, server: serverHostN, expiresAt: expiry, timestamp: t}]
773− else presence
774− set *presence = updatedPresence
767+ set *presence = updatedPresence
775− let peerServers = if sigValid && federationEnabled
768+ let peerServers = if federationEnabled
776− then collectPeerServers updatedPresence (\p -> p.server)
769+ then collectPeerServers updatedPresence (\p -> p.server)
777− else []
770+ else []
778− yield {sigValid: sigValid, peerServers: peerServers, expiredLocal: pr.expiredLocal, offlinePeers: pr.peerServers}
771+ yield {sigValid: True {}, peerServers: peerServers, expiredLocal: pr.expiredLocal, offlinePeers: pr.peerServers}
779 fork (do
772 fork (do
780− if result.sigValid && federationEnabled
773+ when (result.sigValid && federationEnabled)
781− then sendToEach result.peerServers (\server -> do
774+ (sendToEach result.peerServers (\server -> do
782 _ <- fetch (federationScheme ++ server) (RecvGossip {
775 _ <- fetch (federationScheme ++ server) (RecvGossip {
783 authorization: "Bearer " ++ federationSecret,
776 authorization: "Bearer " ++ federationSecret,
784 events: [{eventType: "online", pubkey: pubkey, server: serverHostN, timestamp: t, ttl: onlineGossipTtl}], hopsRemaining: 2, fromServer: serverHostN})
777 events: [{eventType: "online", pubkey: pubkey, server: serverHostN, timestamp: t, ttl: onlineGossipTtl}], hopsRemaining: 2, fromServer: serverHostN})
785− yield {}
778+ yield {}))
786− )
787− else yield {}
788 broadcastOfflineGossip t result.offlinePeers (do
779 broadcastOfflineGossip t result.offlinePeers (do
789 p <- result.expiredLocal
780 p <- result.expiredLocal
790 where not (result.sigValid && p.pubkey == pubkey)
781 where not (result.sigValid && p.pubkey == pubkey)
@@ -846,20 +837,17 @@ serve = \req -> case req of
846 else forwards
837 else forwards
847 yield {status: status, shouldInsert: shouldInsert, remoteServers: remoteServers, fromKey: fromKey, expiredLocal: pr.expiredLocal, peerServers: pr.peerServers, forwardsAtCapacity: forwardsAtCapacity}
838 yield {status: status, shouldInsert: shouldInsert, remoteServers: remoteServers, fromKey: fromKey, expiredLocal: pr.expiredLocal, peerServers: pr.peerServers, forwardsAtCapacity: forwardsAtCapacity}
848 fork (do
839 fork (do
849− if count result.remoteServers > 0 && result.shouldInsert && federationEnabled
840+ when (count result.remoteServers > 0 && result.shouldInsert && federationEnabled)
850− then sendToEach result.remoteServers (\server -> do
841+ (sendToEach result.remoteServers (\server -> do
851 fwdResult <- fetch (federationScheme ++ server) (ForwardMessage {
842 fwdResult <- fetch (federationScheme ++ server) (ForwardMessage {
852 toKey: to, fromKey: result.fromKey, encryptedBlob: encryptedBlob,
843 toKey: to, fromKey: result.fromKey, encryptedBlob: encryptedBlob,
853 timestamp: clampedTs, originServer: serverHostN, messageId: msgId,
844 timestamp: clampedTs, originServer: serverHostN, messageId: msgId,
854 authorization: "Bearer " ++ federationSecret})
845 authorization: "Bearer " ++ federationSecret})
855 case fwdResult of
846 case fwdResult of
856 Ok _ -> removeForward msgId server
847 Ok _ -> removeForward msgId server
857− Err _ -> yield {}
848+ Err _ -> yield {}))
858− )
859− else yield {}
860− if result.forwardsAtCapacity && count result.remoteServers > 0 && result.shouldInsert
849+ when (result.forwardsAtCapacity && count result.remoteServers > 0 && result.shouldInsert)
861− then println ("sendMessage: forwards at capacity (" ++ show maxForwardsEntries ++ "), skipping forward tracking")
850+ (println ("sendMessage: forwards at capacity (" ++ show maxForwardsEntries ++ "), skipping forward tracking"))
862− else yield {}
863 broadcastOfflineGossip t result.peerServers result.expiredLocal)
851 broadcastOfflineGossip t result.peerServers result.expiredLocal)
864 yield (respond {status: result.status, messageId: if result.shouldInsert then msgId else ""})
852 yield (respond {status: result.status, messageId: if result.shouldInsert then msgId else ""})
865
853
@@ -897,41 +885,34 @@ serve = \req -> case req of
897 yield {ok: True {}, pendingAcks: ackedFederated, expiredLocal: pr.expiredLocal, peerServers: pr.peerServers, evictedEntries: evictedEntries}
885 yield {ok: True {}, pendingAcks: ackedFederated, expiredLocal: pr.expiredLocal, peerServers: pr.peerServers, evictedEntries: evictedEntries}
898 let originServers = collectPeerServers result.pendingAcks (\a -> a.originServer)
886 let originServers = collectPeerServers result.pendingAcks (\a -> a.originServer)
899 fork (do
887 fork (do
900− if result.ok
888+ when result.ok (do
901− then do
902− if count originServers > 0 && federationEnabled
889+ when (count originServers > 0 && federationEnabled)
903− then sendAcksAndClean result.pendingAcks originServers
890+ (sendAcksAndClean result.pendingAcks originServers)
904− else yield {}
905− let evictedToRetry = do
891+ let evictedToRetry = do
906− a <- result.evictedEntries
892+ a <- result.evictedEntries
907− where none result.pendingAcks (\a2 -> a2.messageId == a.messageId && a2.toKey == a.toKey)
893+ where none result.pendingAcks (\a2 -> a2.messageId == a.messageId && a2.toKey == a.toKey)
908− yield a
894+ yield a
909− if count evictedToRetry > 0 && federationEnabled
895+ when (count evictedToRetry > 0 && federationEnabled) (do
910− then do
911− let evictedOriginServers = collectPeerServers evictedToRetry (\a -> a.originServer)
896+ let evictedOriginServers = collectPeerServers evictedToRetry (\a -> a.originServer)
912− sendToEach evictedOriginServers (\server -> do
897+ sendToEach evictedOriginServers (\server -> do
913− fetchResult <- fetch (federationScheme ++ server) (FederationAck {
898+ fetchResult <- fetch (federationScheme ++ server) (FederationAck {
914− authorization: "Bearer " ++ federationSecret,
899+ authorization: "Bearer " ++ federationSecret,
915− messageIds: (do a <- evictedToRetry; where a.originServer == server; yield a.messageId),
900+ messageIds: (do a <- evictedToRetry; where a.originServer == server; yield a.messageId),
916− originServer: server, fromServer: serverHostN})
901+ originServer: server, fromServer: serverHostN})
917− case fetchResult of
902+ case fetchResult of
918− Err _ -> do
903+ Err _ -> do
919− dropped <- atomic do
904+ dropped <- atomic do
920− ackedIds <- *ackedIds
905+ ackedIds <- *ackedIds
921− let reinsert = do a <- evictedToRetry; where a.originServer == server; yield a
906+ let reinsert = do a <- evictedToRetry; where a.originServer == server; yield a
922− let combined = union ackedIds reinsert
907+ let combined = union ackedIds reinsert
923− let excess = count combined - maxAckedIds
908+ let excess = count combined - maxAckedIds
924− let afterEviction = if excess <= 0 then combined
909+ let afterEviction = if excess <= 0 then combined
925− else evictOldestAckedIds combined excess
910+ else evictOldestAckedIds combined excess
926− set *ackedIds = afterEviction
911+ set *ackedIds = afterEviction
927− yield excess
912+ yield excess
928− if dropped > 0
913+ when (dropped > 0)
929− then println ("ackMessages: ackedIds at capacity (" ++ show maxAckedIds ++ "), " ++ show dropped ++ " ack entries permanently lost for " ++ server)
914+ (println ("ackMessages: ackedIds at capacity (" ++ show maxAckedIds ++ "), " ++ show dropped ++ " ack entries permanently lost for " ++ server))
930− else yield {}
931− _ -> yield {}
915+ _ -> yield {})))
932− )
933− else yield {}
934− else yield {}
935 broadcastOfflineGossip t result.peerServers result.expiredLocal)
916 broadcastOfflineGossip t result.peerServers result.expiredLocal)
936 yield (respond {ok: result.ok})
917 yield (respond {ok: result.ok})
937
918
@@ -973,7 +954,7 @@ serve = \req -> case req of
973 where none offlineEntries (\o -> o.server == fromServer && samePubkeyServer o e && o.timestamp > e.timestamp)
954 where none offlineEntries (\o -> o.server == fromServer && samePubkeyServer o e && o.timestamp > e.timestamp)
974 yield e
955 yield e
975 let affectedKeys = do
956 let affectedKeys = do
976− e <- union onlineEntries offlineEntries
957+ e <- allValidEntries
977 yield {pubkey: e.pubkey, server: e.server}
958 yield {pubkey: e.pubkey, server: e.server}
978 let deduped = mergeByPubkeyServer [] filteredOnline
959 let deduped = mergeByPubkeyServer [] filteredOnline
979 gossipResult <- atomic do
960 gossipResult <- atomic do
@@ -1027,23 +1008,19 @@ serve = \req -> case req of
1027 e <- union filteredOnline filteredOffline
1008 e <- union filteredOnline filteredOffline
1028 where any gossipResult.appliedKeys (\a -> samePubkeyServer a e)
1009 where any gossipResult.appliedKeys (\a -> samePubkeyServer a e)
1029 yield {eventType: e.eventType, pubkey: e.pubkey, server: e.server, timestamp: e.timestamp, ttl: e.ttl}
1010 yield {eventType: e.eventType, pubkey: e.pubkey, server: e.server, timestamp: e.timestamp, ttl: e.ttl}
1030− if not gossipResult.withinCapacity
1011+ when (not gossipResult.withinCapacity)
1031− then println ("gossip: presence at capacity (" ++ show maxPresenceEntries ++ "), limiting new entries from " ++ fromServer)
1012+ (println ("gossip: presence at capacity (" ++ show maxPresenceEntries ++ "), limiting new entries from " ++ fromServer))
1032− else yield {}
1033 fork (do
1013 fork (do
1034− if validRequest && cappedHops > 1 && federationEnabled && count effectiveEvents > 0
1014+ when (validRequest && cappedHops > 1 && federationEnabled && count effectiveEvents > 0) (do
1035− then do
1036− let targets = do
1015+ let targets = do
1037− s <- gossipResult.gossipPeerServers
1016+ s <- gossipResult.gossipPeerServers
1038− where not (s == fromServer)
1017+ where not (s == fromServer)
1039− yield s
1018+ yield s
1040− sendToEach targets (\server -> do
1019+ sendToEach targets (\server -> do
1041− _ <- fetch (federationScheme ++ server) (RecvGossip {
1020+ _ <- fetch (federationScheme ++ server) (RecvGossip {
1042− authorization: "Bearer " ++ federationSecret,
1021+ authorization: "Bearer " ++ federationSecret,
1043− events: effectiveEvents, hopsRemaining: cappedHops - 1, fromServer: serverHostN})
1022+ events: effectiveEvents, hopsRemaining: cappedHops - 1, fromServer: serverHostN})
1044− yield {}
1023+ yield {}))
1045− )
1046− else yield {}
1047 broadcastOfflineGossip t gossipResult.peerServers gossipResult.expiredLocal)
1024 broadcastOfflineGossip t gossipResult.peerServers gossipResult.expiredLocal)
1048 yield (respond {ok: validRequest})
1025 yield (respond {ok: validRequest})
1049
1026
@@ -1063,10 +1040,11 @@ serve = \req -> case req of
1063 seqCounter <- *seqCounter
1040 seqCounter <- *seqCounter
1064 let seqTs = nextSeqTs t seqCounter
1041 let seqTs = nextSeqTs t seqCounter
1065 ackedIds <- *ackedIds
1042 ackedIds <- *ackedIds
1066− let exists = authorized && validOrigin
1043+ let valid = authorized && validOrigin
1044+ let alreadyExists = valid
1067 && (any messages (\m -> m.messageId == messageId && m.toKey == toKey)
1045 && (any messages (\m -> m.messageId == messageId && m.toKey == toKey)
1068 || any ackedIds (\a -> a.messageId == messageId && a.toKey == toKey))
1046 || any ackedIds (\a -> a.messageId == messageId && a.toKey == toKey))
1069− let shouldInsert = authorized && validOrigin && not exists
1047+ let shouldInsert = valid && not alreadyExists
1070 && countMessagesFor messages toKey t < maxMessagesPerRecipient
1048 && countMessagesFor messages toKey t < maxMessagesPerRecipient
1071 set *seqCounter = if shouldInsert then seqTs else seqCounter
1049 set *seqCounter = if shouldInsert then seqTs else seqCounter
1072 set *messages = if shouldInsert
1050 set *messages = if shouldInsert
@@ -1074,7 +1052,7 @@ serve = \req -> case req of
1074 [{messageId: messageId, toKey: toKey, fromKey: fromKey,
1052 [{messageId: messageId, toKey: toKey, fromKey: fromKey,
1075 encryptedBlob: encryptedBlob, timestamp: clampedTs, originServer: originServer, receivedAt: seqTs}]
1053 encryptedBlob: encryptedBlob, timestamp: clampedTs, originServer: originServer, receivedAt: seqTs}]
1076 else messages
1054 else messages
1077− yield {peerServers: pr.peerServers, expiredLocal: pr.expiredLocal, ok: shouldInsert || exists}
1055+ yield {peerServers: pr.peerServers, expiredLocal: pr.expiredLocal, ok: shouldInsert || alreadyExists}
1078 forkBroadcastOffline t result
1056 forkBroadcastOffline t result
1079 yield (respond {ok: result.ok})
1057 yield (respond {ok: result.ok})
1080
1058
@@ -1104,7 +1082,7 @@ serve = \req -> case req of
1104 Prune {authorization, respond} -> do
1082 Prune {authorization, respond} -> do
1105 t <- now
1083 t <- now
1106 let authorized = verifyMaintenanceAuth authorization
1084 let authorized = verifyMaintenanceAuth authorization
1107− if authorized then fork (pruneAndRetry t) else yield {}
1085+ when authorized (fork (pruneAndRetry t))
1108 yield (respond {ok: authorized})
1086 yield (respond {ok: authorized})
1109
1087
1110-- ============================================
1088-- ============================================