mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2026-03-31 20:36:19 +00:00
* core: preset servers and operators (WIP) * usageConditionsToAdd * simplify * WIP * database entity IDs * preset operators and servers (compiles) * update (most tests pass) * remove imports * fix * update * make preset servers lists potentially empty in some operators, as long as the combined list is not empty * CLI API in progress, validateUserServers * make servers of disabled operators "unknown", consider only enabled servers when switching profile links * exclude disabled operators when receiving files * fix TH in ghc 8.10.7 * add type for ghc 8.10.7 * pattern match for ghc 8.10.7 * ghc 8.10.7 fix attempt * remove additional pattern, update servers * do not strip title from conditions * remove space --------- Co-authored-by: spaced4ndy <8711996+spaced4ndy@users.noreply.github.com>
354 lines
17 KiB
Haskell
354 lines
17 KiB
Haskell
{-# LANGUAGE DuplicateRecordFields #-}
|
|
{-# LANGUAGE NamedFieldPuns #-}
|
|
{-# LANGUAGE TemplateHaskell #-}
|
|
{-# OPTIONS_GHC -fno-warn-ambiguous-fields #-}
|
|
|
|
module Simplex.Chat.Stats where
|
|
|
|
import qualified Data.Aeson.TH as J
|
|
import Data.List (partition)
|
|
import Data.Map.Strict (Map)
|
|
import qualified Data.Map.Strict as M
|
|
import Data.Maybe (fromMaybe, isJust)
|
|
import Data.Time.Clock (UTCTime)
|
|
import Simplex.Chat.Types
|
|
import Simplex.Messaging.Agent.Client
|
|
import Simplex.Messaging.Agent.Protocol (UserId)
|
|
import Simplex.Messaging.Agent.Stats
|
|
import Simplex.Messaging.Parsers (defaultJSON)
|
|
import Simplex.Messaging.Protocol
|
|
|
|
data PresentedServersSummary = PresentedServersSummary
|
|
{ statsStartedAt :: UTCTime,
|
|
allUsersSMP :: SMPServersSummary,
|
|
allUsersXFTP :: XFTPServersSummary,
|
|
allUsersNtf :: NtfServersSummary,
|
|
currentUserSMP :: SMPServersSummary,
|
|
currentUserXFTP :: XFTPServersSummary,
|
|
currentUserNtf :: NtfServersSummary
|
|
}
|
|
deriving (Show)
|
|
|
|
-- Presentation of servers will be split into separate categories,
|
|
-- so users can differentiate currently used (connected) servers,
|
|
-- previously connected servers that were in use in previous sessions,
|
|
-- and servers that are only proxied (not connected directly).
|
|
data SMPServersSummary = SMPServersSummary
|
|
{ -- SMP totals are calculated from all accounted SMP server summaries
|
|
smpTotals :: SMPTotals,
|
|
-- currently used SMP servers are those with Just in sessions and/or subs in SMPServerSummary;
|
|
-- all other servers would fall either into previously used or only proxied servers category
|
|
currentlyUsedSMPServers :: [SMPServerSummary],
|
|
-- previously used SMP servers are those with Nothing in sessions and subs,
|
|
-- and have any of sentDirect, sentProxied, recvMsgs, etc. > 0 in server stats (see toPresentedServersSummary);
|
|
-- remaining servers would fall into only proxied servers category
|
|
previouslyUsedSMPServers :: [SMPServerSummary],
|
|
-- only proxied SMP servers are those that aren't (according to current state - sessions and subs)
|
|
-- and weren't (according to stats) connected directly; they would have Nothing in sessions and subs,
|
|
-- and have all of sentDirect, sentProxied, recvMsgs, etc. = 0 in server stats
|
|
onlyProxiedSMPServers :: [SMPServerSummary]
|
|
}
|
|
deriving (Show)
|
|
|
|
data SMPTotals = SMPTotals
|
|
{ sessions :: ServerSessions,
|
|
subs :: SMPServerSubs,
|
|
stats :: AgentSMPServerStatsData
|
|
}
|
|
deriving (Show)
|
|
|
|
data SMPServerSummary = SMPServerSummary
|
|
{ smpServer :: SMPServer,
|
|
-- known:
|
|
-- for simplicity always Nothing in totalServersSummary - allows us to load configured servers only for current user,
|
|
-- and also unnecessary unless we want to add navigation to other users servers settings;
|
|
-- always Just in currentUserServers - True if server is in list of user servers, otherwise False;
|
|
-- True - allows to navigate to server settings, False - allows to add server to configured as known (SEKnown)
|
|
known :: Maybe Bool,
|
|
sessions :: Maybe ServerSessions,
|
|
subs :: Maybe SMPServerSubs,
|
|
-- stats:
|
|
-- even if sessions and subs are Nothing, stats can be Just - server could be used earlier in session,
|
|
-- or in previous sessions and stats for it were restored; server would fall into a category of
|
|
-- previously used or only proxied servers - see ServersSummary above
|
|
stats :: Maybe AgentSMPServerStatsData
|
|
}
|
|
deriving (Show)
|
|
|
|
data XFTPServersSummary = XFTPServersSummary
|
|
{ -- XFTP totals are calculated from all accounted XFTP server summaries
|
|
xftpTotals :: XFTPTotals,
|
|
-- currently used XFTP servers are those with Just in sessions in XFTPServerSummary,
|
|
-- and/or have upload/download/deletion in progress;
|
|
-- all other servers would fall into previously used servers category
|
|
currentlyUsedXFTPServers :: [XFTPServerSummary],
|
|
-- previously used XFTP servers are those with Nothing in sessions and don't have any process in progress
|
|
previouslyUsedXFTPServers :: [XFTPServerSummary]
|
|
}
|
|
deriving (Show)
|
|
|
|
data XFTPTotals = XFTPTotals
|
|
{ sessions :: ServerSessions,
|
|
stats :: AgentXFTPServerStatsData
|
|
}
|
|
deriving (Show)
|
|
|
|
data XFTPServerSummary = XFTPServerSummary
|
|
{ xftpServer :: XFTPServer,
|
|
known :: Maybe Bool, -- same as for SMPServerSummary
|
|
sessions :: Maybe ServerSessions,
|
|
stats :: Maybe AgentXFTPServerStatsData,
|
|
rcvInProgress :: Bool,
|
|
sndInProgress :: Bool,
|
|
delInProgress :: Bool
|
|
}
|
|
deriving (Show)
|
|
|
|
data NtfServersSummary = NtfServersSummary
|
|
{ ntfTotals :: NtfTotals,
|
|
currentlyUsedNtfServers :: [NtfServerSummary],
|
|
previouslyUsedNtfServers :: [NtfServerSummary]
|
|
}
|
|
deriving (Show)
|
|
|
|
data NtfTotals = NtfTotals
|
|
{ sessions :: ServerSessions,
|
|
stats :: AgentNtfServerStatsData
|
|
}
|
|
deriving (Show)
|
|
|
|
data NtfServerSummary = NtfServerSummary
|
|
{ ntfServer :: NtfServer,
|
|
known :: Maybe Bool,
|
|
sessions :: Maybe ServerSessions,
|
|
stats :: Maybe AgentNtfServerStatsData
|
|
}
|
|
deriving (Show)
|
|
|
|
-- Maps AgentServersSummary to PresentedServersSummary:
|
|
-- - currentUserServers is for currentUser;
|
|
-- - users are passed to exclude hidden users from totalServersSummary;
|
|
-- - if currentUser is hidden, it should be accounted in totalServersSummary;
|
|
-- - known is set only in user level summaries based on passed userSMPSrvs and userXFTPSrvs
|
|
toPresentedServersSummary :: AgentServersSummary -> [User] -> User -> [SMPServer] -> [XFTPServer] -> [NtfServer] -> PresentedServersSummary
|
|
toPresentedServersSummary agentSummary users currentUser userSMPSrvs userXFTPSrvs userNtfSrvs = do
|
|
let (userSMPSrvsSumms, allSMPSrvsSumms) = accSMPSrvsSummaries
|
|
(userSMPCurr, userSMPPrev, userSMPProx) = smpSummsIntoCategories userSMPSrvsSumms
|
|
(allSMPCurr, allSMPPrev, allSMPProx) = smpSummsIntoCategories allSMPSrvsSumms
|
|
let (userXFTPSrvsSumms, allXFTPSrvsSumms) = accXFTPSrvsSummaries
|
|
(userXFTPCurr, userXFTPPrev) = xftpSummsIntoCategories userXFTPSrvsSumms
|
|
(allXFTPCurr, allXFTPPrev) = xftpSummsIntoCategories allXFTPSrvsSumms
|
|
let (userNtfSrvsSumms, allNtfSrvsSumms) = accNtfSrvsSummaries
|
|
(userNtfCurr, userNtfPrev) = ntfSummsIntoCategories userNtfSrvsSumms
|
|
(allNtfCurr, allNtfPrev) = ntfSummsIntoCategories allNtfSrvsSumms
|
|
PresentedServersSummary
|
|
{ statsStartedAt,
|
|
allUsersSMP =
|
|
SMPServersSummary
|
|
{ smpTotals = accSMPTotals allSMPSrvsSumms,
|
|
currentlyUsedSMPServers = allSMPCurr,
|
|
previouslyUsedSMPServers = allSMPPrev,
|
|
onlyProxiedSMPServers = allSMPProx
|
|
},
|
|
allUsersXFTP =
|
|
XFTPServersSummary
|
|
{ xftpTotals = accXFTPTotals allXFTPSrvsSumms,
|
|
currentlyUsedXFTPServers = allXFTPCurr,
|
|
previouslyUsedXFTPServers = allXFTPPrev
|
|
},
|
|
allUsersNtf =
|
|
NtfServersSummary
|
|
{ ntfTotals = accNtfTotals allNtfSrvsSumms,
|
|
currentlyUsedNtfServers = allNtfCurr,
|
|
previouslyUsedNtfServers = allNtfPrev
|
|
},
|
|
currentUserSMP =
|
|
SMPServersSummary
|
|
{ smpTotals = accSMPTotals userSMPSrvsSumms,
|
|
currentlyUsedSMPServers = userSMPCurr,
|
|
previouslyUsedSMPServers = userSMPPrev,
|
|
onlyProxiedSMPServers = userSMPProx
|
|
},
|
|
currentUserXFTP =
|
|
XFTPServersSummary
|
|
{ xftpTotals = accXFTPTotals userXFTPSrvsSumms,
|
|
currentlyUsedXFTPServers = userXFTPCurr,
|
|
previouslyUsedXFTPServers = userXFTPPrev
|
|
},
|
|
currentUserNtf =
|
|
NtfServersSummary
|
|
{ ntfTotals = accNtfTotals userNtfSrvsSumms,
|
|
currentlyUsedNtfServers = userNtfCurr,
|
|
previouslyUsedNtfServers = userNtfPrev
|
|
}
|
|
}
|
|
where
|
|
AgentServersSummary {statsStartedAt, smpServersSessions, smpServersSubs, smpServersStats, xftpServersSessions, xftpServersStats, xftpRcvInProgress, xftpSndInProgress, xftpDelInProgress, ntfServersSessions, ntfServersStats} = agentSummary
|
|
countUserInAll auId = countUserInAllStats (AgentUserId auId) currentUser users
|
|
accSMPTotals :: Map SMPServer SMPServerSummary -> SMPTotals
|
|
accSMPTotals = M.foldr' addTotals initialTotals
|
|
where
|
|
initialTotals = SMPTotals {sessions = ServerSessions 0 0 0, subs = SMPServerSubs 0 0, stats = newAgentSMPServerStatsData}
|
|
addTotals SMPServerSummary {sessions, subs, stats} SMPTotals {sessions = accSess, subs = accSubs, stats = accStats} =
|
|
SMPTotals
|
|
{ sessions = maybe accSess (accSess `addServerSessions`) sessions,
|
|
subs = maybe accSubs (accSubs `addSMPSubs`) subs,
|
|
stats = maybe accStats (accStats `addSMPStatsData`) stats
|
|
}
|
|
accXFTPTotals :: Map XFTPServer XFTPServerSummary -> XFTPTotals
|
|
accXFTPTotals = M.foldr' addTotals initialTotals
|
|
where
|
|
initialTotals = XFTPTotals {sessions = ServerSessions 0 0 0, stats = newAgentXFTPServerStatsData}
|
|
addTotals XFTPServerSummary {sessions, stats} XFTPTotals {sessions = accSess, stats = accStats} =
|
|
XFTPTotals
|
|
{ sessions = maybe accSess (accSess `addServerSessions`) sessions,
|
|
stats = maybe accStats (accStats `addXFTPStatsData`) stats
|
|
}
|
|
accNtfTotals :: Map NtfServer NtfServerSummary -> NtfTotals
|
|
accNtfTotals = M.foldr' addTotals initialTotals
|
|
where
|
|
initialTotals = NtfTotals {sessions = ServerSessions 0 0 0, stats = newAgentNtfServerStatsData}
|
|
addTotals NtfServerSummary {sessions, stats} NtfTotals {sessions = accSess, stats = accStats} =
|
|
NtfTotals
|
|
{ sessions = maybe accSess (accSess `addServerSessions`) sessions,
|
|
stats = maybe accStats (accStats `addNtfStatsData`) stats
|
|
}
|
|
smpSummsIntoCategories :: Map SMPServer SMPServerSummary -> ([SMPServerSummary], [SMPServerSummary], [SMPServerSummary])
|
|
smpSummsIntoCategories = M.foldr' addSummary ([], [], [])
|
|
where
|
|
addSummary srvSumm (curr, prev, prox)
|
|
| isCurrentlyUsed srvSumm = (srvSumm : curr, prev, prox)
|
|
| isPreviouslyUsed srvSumm = (curr, srvSumm : prev, prox)
|
|
| otherwise = (curr, prev, srvSumm : prox)
|
|
isCurrentlyUsed SMPServerSummary {sessions, subs} = isJust sessions || isJust subs
|
|
isPreviouslyUsed SMPServerSummary {stats} = case stats of
|
|
Nothing -> False
|
|
-- add connCompleted, connDeleted?
|
|
-- check: should connCompleted be counted for proxy? is it?
|
|
Just AgentSMPServerStatsData {_sentDirect, _sentProxied, _sentDirectAttempts, _sentProxiedAttempts, _recvMsgs, _connCreated, _connSecured, _connSubscribed, _connSubAttempts} ->
|
|
_sentDirect > 0 || _sentProxied > 0 || _sentDirectAttempts > 0 || _sentProxiedAttempts > 0 || _recvMsgs > 0 || _connCreated > 0 || _connSecured > 0 || _connSubscribed > 0 || _connSubAttempts > 0
|
|
xftpSummsIntoCategories :: Map XFTPServer XFTPServerSummary -> ([XFTPServerSummary], [XFTPServerSummary])
|
|
xftpSummsIntoCategories = partition isCurrentlyUsed . M.elems
|
|
where
|
|
isCurrentlyUsed XFTPServerSummary {sessions, rcvInProgress, sndInProgress, delInProgress} =
|
|
isJust sessions || rcvInProgress || sndInProgress || delInProgress
|
|
ntfSummsIntoCategories :: Map NtfServer NtfServerSummary -> ([NtfServerSummary], [NtfServerSummary])
|
|
ntfSummsIntoCategories = partition isCurrentlyUsed . M.elems
|
|
where
|
|
isCurrentlyUsed NtfServerSummary {sessions} = isJust sessions
|
|
accSMPSrvsSummaries :: (Map SMPServer SMPServerSummary, Map SMPServer SMPServerSummary)
|
|
accSMPSrvsSummaries = M.foldrWithKey' (addServerData addStats) summs2 smpServersStats
|
|
where
|
|
summs1 = M.foldrWithKey' (addServerData addSessions) (M.empty, M.empty) smpServersSessions
|
|
summs2 = M.foldrWithKey' (addServerData addSubs) summs1 smpServersSubs
|
|
addServerData = addServerData_ newSummary newUserSummary
|
|
newUserSummary srv = (newSummary srv :: SMPServerSummary) {known = Just $ srv `elem` userSMPSrvs}
|
|
newSummary srv =
|
|
SMPServerSummary
|
|
{ smpServer = srv,
|
|
known = Nothing,
|
|
sessions = Nothing,
|
|
subs = Nothing,
|
|
stats = Nothing
|
|
}
|
|
addSessions :: ServerSessions -> SMPServerSummary -> SMPServerSummary
|
|
addSessions s summ@SMPServerSummary {sessions} = summ {sessions = Just $ maybe s (s `addServerSessions`) sessions}
|
|
addSubs :: SMPServerSubs -> SMPServerSummary -> SMPServerSummary
|
|
addSubs s summ@SMPServerSummary {subs} = summ {subs = Just $ maybe s (s `addSMPSubs`) subs}
|
|
addStats :: AgentSMPServerStatsData -> SMPServerSummary -> SMPServerSummary
|
|
addStats s summ@SMPServerSummary {stats} = summ {stats = Just $ maybe s (s `addSMPStatsData`) stats}
|
|
accXFTPSrvsSummaries :: (Map XFTPServer XFTPServerSummary, Map XFTPServer XFTPServerSummary)
|
|
accXFTPSrvsSummaries = M.foldrWithKey' (addServerData addStats) summs1 xftpServersStats
|
|
where
|
|
summs1 = M.foldrWithKey' (addServerData addSessions) (M.empty, M.empty) xftpServersSessions
|
|
addServerData = addServerData_ newSummary newUserSummary
|
|
addSessions :: ServerSessions -> XFTPServerSummary -> XFTPServerSummary
|
|
addSessions s summ@XFTPServerSummary {sessions} = summ {sessions = Just $ maybe s (s `addServerSessions`) sessions}
|
|
addStats :: AgentXFTPServerStatsData -> XFTPServerSummary -> XFTPServerSummary
|
|
addStats s summ@XFTPServerSummary {stats} = summ {stats = Just $ maybe s (s `addXFTPStatsData`) stats}
|
|
newUserSummary srv = (newSummary srv :: XFTPServerSummary) {known = Just $ srv `elem` userXFTPSrvs}
|
|
newSummary srv =
|
|
XFTPServerSummary
|
|
{ xftpServer = srv,
|
|
known = Nothing,
|
|
sessions = Nothing,
|
|
stats = Nothing,
|
|
rcvInProgress = srv `elem` xftpRcvInProgress,
|
|
sndInProgress = srv `elem` xftpSndInProgress,
|
|
delInProgress = srv `elem` xftpDelInProgress
|
|
}
|
|
accNtfSrvsSummaries :: (Map NtfServer NtfServerSummary, Map NtfServer NtfServerSummary)
|
|
accNtfSrvsSummaries = M.foldrWithKey' (addServerData addStats) summs1 ntfServersStats
|
|
where
|
|
summs1 = M.foldrWithKey' (addServerData addSessions) (M.empty, M.empty) ntfServersSessions
|
|
addServerData = addServerData_ newSummary newUserSummary
|
|
addSessions :: ServerSessions -> NtfServerSummary -> NtfServerSummary
|
|
addSessions s summ@NtfServerSummary {sessions} = summ {sessions = Just $ maybe s (s `addServerSessions`) sessions}
|
|
addStats :: AgentNtfServerStatsData -> NtfServerSummary -> NtfServerSummary
|
|
addStats s summ@NtfServerSummary {stats} = summ {stats = Just $ maybe s (s `addNtfStatsData`) stats}
|
|
newUserSummary srv = (newSummary srv :: NtfServerSummary) {known = Just $ srv `elem` userNtfSrvs}
|
|
newSummary srv =
|
|
NtfServerSummary
|
|
{ ntfServer = srv,
|
|
known = Nothing,
|
|
sessions = Nothing,
|
|
stats = Nothing
|
|
}
|
|
addServerData_ ::
|
|
(ProtocolServer p -> s) ->
|
|
(ProtocolServer p -> s) ->
|
|
(a -> s -> s) ->
|
|
(UserId, ProtocolServer p) ->
|
|
a ->
|
|
(Map (ProtocolServer p) s, Map (ProtocolServer p) s) ->
|
|
(Map (ProtocolServer p) s, Map (ProtocolServer p) s)
|
|
addServerData_ newSummary newUserSummary addData (userId, srv) d (userSumms, allUsersSumms) = (userSumms', allUsersSumms')
|
|
where
|
|
userSumms'
|
|
| userId == aUserId currentUser = alterSumms (newUserSummary srv) userSumms
|
|
| otherwise = userSumms
|
|
allUsersSumms'
|
|
| countUserInAll userId = alterSumms (newSummary srv) allUsersSumms
|
|
| otherwise = allUsersSumms
|
|
alterSumms n = M.alter (Just . addData d . fromMaybe n) srv
|
|
addServerSessions :: ServerSessions -> ServerSessions -> ServerSessions
|
|
addServerSessions ss1 ss2 =
|
|
ServerSessions
|
|
{ ssConnected = ssConnected ss1 + ssConnected ss2,
|
|
ssErrors = ssErrors ss1 + ssErrors ss2,
|
|
ssConnecting = ssConnecting ss1 + ssConnecting ss2
|
|
}
|
|
|
|
countUserInAllStats :: AgentUserId -> User -> [User] -> Bool
|
|
countUserInAllStats (AgentUserId auId) currentUser users =
|
|
auId == aUserId currentUser || auId `notElem` hiddenUserIds
|
|
where
|
|
hiddenUserIds = map aUserId $ filter (isJust . viewPwdHash) users
|
|
|
|
addSMPSubs :: SMPServerSubs -> SMPServerSubs -> SMPServerSubs
|
|
addSMPSubs ss1 ss2 =
|
|
SMPServerSubs
|
|
{ ssActive = ssActive ss1 + ssActive ss2,
|
|
ssPending = ssPending ss1 + ssPending ss2
|
|
}
|
|
|
|
$(J.deriveJSON defaultJSON ''SMPTotals)
|
|
|
|
$(J.deriveJSON defaultJSON ''SMPServerSummary)
|
|
|
|
$(J.deriveJSON defaultJSON ''SMPServersSummary)
|
|
|
|
$(J.deriveJSON defaultJSON ''XFTPTotals)
|
|
|
|
$(J.deriveJSON defaultJSON ''XFTPServerSummary)
|
|
|
|
$(J.deriveJSON defaultJSON ''XFTPServersSummary)
|
|
|
|
$(J.deriveJSON defaultJSON ''NtfTotals)
|
|
|
|
$(J.deriveJSON defaultJSON ''NtfServerSummary)
|
|
|
|
$(J.deriveJSON defaultJSON ''NtfServersSummary)
|
|
|
|
$(J.deriveJSON defaultJSON ''PresentedServersSummary)
|