Files
simplex-chat/src/Simplex/Chat/Operators.hs

133 lines
4.4 KiB
Haskell

{-# LANGUAGE DataKinds #-}
{-# LANGUAGE DuplicateRecordFields #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TemplateHaskell #-}
module Simplex.Chat.Operators where
import Data.Aeson (FromJSON (..), ToJSON (..))
import qualified Data.Aeson as J
import qualified Data.Aeson.Encoding as JE
import qualified Data.Aeson.TH as JQ
import Data.FileEmbed
import Data.Int (Int64)
import Data.Map.Strict (Map)
import qualified Data.Map.Strict as M
import Data.Maybe (fromMaybe)
import Data.Text (Text)
import Data.Time.Clock (UTCTime)
import Database.SQLite.Simple.FromField (FromField (..))
import Database.SQLite.Simple.ToField (ToField (..))
import Language.Haskell.TH.Syntax (lift)
import Simplex.Chat.Operators.Conditions
import Simplex.Chat.Types.Util (textParseJSON)
import Simplex.Messaging.Agent.Env.SQLite (OperatorId, ServerCfg (..), ServerRoles)
import Simplex.Messaging.Encoding.String
import Simplex.Messaging.Parsers (defaultJSON, dropPrefix, fromTextField_, sumTypeJSON)
import Simplex.Messaging.Protocol (ProtocolType (..))
import Simplex.Messaging.Util (safeDecodeUtf8)
usageConditionsCommit :: Text
usageConditionsCommit = "165143a1112308c035ac00ed669b96b60599aa1c"
usageConditionsText :: Text
usageConditionsText =
$( let s = $(embedFile =<< makeRelativeToProject "PRIVACY.md")
in [|stripFrontMatter (safeDecodeUtf8 $(lift s))|]
)
data OperatorTag = OTSimplex | OTXyz
deriving (Show)
instance FromField OperatorTag where fromField = fromTextField_ textDecode
instance ToField OperatorTag where toField = toField . textEncode
instance FromJSON OperatorTag where
parseJSON = textParseJSON "OperatorTag"
instance ToJSON OperatorTag where
toJSON = J.String . textEncode
toEncoding = JE.text . textEncode
instance TextEncoding OperatorTag where
textDecode = \case
"simplex" -> Just OTSimplex
"xyz" -> Just OTXyz
_ -> Nothing
textEncode = \case
OTSimplex -> "simplex"
OTXyz -> "xyz"
data UsageConditions = UsageConditions
{ conditionsId :: Int64,
conditionsCommit :: Text,
notifiedAt :: Maybe UTCTime,
createdAt :: UTCTime
}
deriving (Show)
data UsageConditionsAction
= UCAReview {operators :: [ServerOperator], deadline :: Maybe UTCTime, showNotice :: Bool}
| UCAAccepted {operators :: [ServerOperator]}
deriving (Show)
-- TODO UI logic
usageConditionsAction :: [ServerOperator] -> UsageConditionsAction
usageConditionsAction _operators = UCAAccepted []
data ConditionsAcceptance
= CAAccepted {acceptedAt :: UTCTime}
| CARequired {deadline :: Maybe UTCTime}
deriving (Show)
data ServerOperator = ServerOperator
{ operatorId :: OperatorId,
operatorTag :: Maybe OperatorTag,
tradeName :: Text,
legalName :: Maybe Text,
serverDomains :: [Text],
acceptedConditions :: ConditionsAcceptance,
enabled :: Bool,
roles :: ServerRoles
}
deriving (Show)
data UserServers = UserServers
{ operator :: Maybe ServerOperator,
smpServers :: [ServerCfg 'PSMP],
xftpServers :: [ServerCfg 'PXFTP]
}
deriving (Show)
groupByOperator :: [ServerOperator] -> [ServerCfg 'PSMP] -> [ServerCfg 'PXFTP] -> [UserServers]
groupByOperator srvOperators smpSrvs xftpSrvs =
map createOperatorServers (M.toList combinedMap)
where
srvOperatorId :: ServerCfg p -> Maybe Int64
srvOperatorId ServerCfg {operator} = operator
operatorMap :: Map (Maybe Int64) (Maybe ServerOperator)
operatorMap = M.fromList [(Just (operatorId op), Just op) | op <- srvOperators] `M.union` M.singleton Nothing Nothing
initialMap :: Map (Maybe Int64) ([ServerCfg 'PSMP], [ServerCfg 'PXFTP])
initialMap = M.fromList [(key, ([], [])) | key <- M.keys operatorMap]
smpsMap = foldr (\server acc -> M.adjust (\(smps, xftps) -> (server : smps, xftps)) (srvOperatorId server) acc) initialMap smpSrvs
combinedMap = foldr (\server acc -> M.adjust (\(smps, xftps) -> (smps, server : xftps)) (srvOperatorId server) acc) smpsMap xftpSrvs
createOperatorServers (key, (groupedSmps, groupedXftps)) =
UserServers
{ operator = fromMaybe Nothing (M.lookup key operatorMap),
smpServers = groupedSmps,
xftpServers = groupedXftps
}
$(JQ.deriveJSON defaultJSON ''UsageConditions)
$(JQ.deriveJSON (sumTypeJSON $ dropPrefix "CA") ''ConditionsAcceptance)
$(JQ.deriveJSON defaultJSON ''ServerOperator)
$(JQ.deriveJSON (sumTypeJSON $ dropPrefix "UCA") ''UsageConditionsAction)
$(JQ.deriveJSON defaultJSON ''UserServers)