mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2026-10-22 13:11:55 +00:00
core: Mobile.hs postgres interface (#5545)
* core: Mobile.hs postgres interface * sqlite * fix * errors * postgres * rename * rename, refactor * merge files * rename * update simplexmq --------- Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
This commit is contained in:
1 parent
0e940719c1
commit
20fa30eacc
23 files changed
+177
-182
No files matched your search
+37
-29
@@ -1,3 +1,4 @@
|
||||
{-# LANGUAGE CPP #-}
|
||||
{-# LANGUAGE DuplicateRecordFields #-}
|
||||
{-# LANGUAGE LambdaCase #-}
|
||||
{-# LANGUAGE NamedFieldPuns #-}
|
||||
@@ -16,7 +17,6 @@ import qualified Data.Aeson as J
|
||||
import qualified Data.Aeson.TH as JQ
|
||||
import Data.Bifunctor (first)
|
||||
import Data.ByteArray (ScrubbedBytes)
|
||||
import qualified Data.ByteArray as BA
|
||||
import qualified Data.ByteString.Base64.URL as U
|
||||
import Data.ByteString.Char8 (ByteString)
|
||||
import qualified Data.ByteString.Char8 as B
|
||||
@@ -26,8 +26,6 @@ import Data.List (find)
|
||||
import qualified Data.List.NonEmpty as L
|
||||
import Data.Maybe (fromMaybe)
|
||||
import Data.Word (Word8)
|
||||
import Database.SQLite.Simple (SQLError (..))
|
||||
import qualified Database.SQLite.Simple as DB
|
||||
import Foreign.C.String
|
||||
import Foreign.C.Types (CInt (..))
|
||||
import Foreign.Ptr
|
||||
@@ -49,7 +47,7 @@ import Simplex.Chat.Store.Profiles
|
||||
import Simplex.Chat.Types
|
||||
import Simplex.Messaging.Agent.Client (agentClientStore)
|
||||
import Simplex.Messaging.Agent.Env.SQLite (createAgentStore)
|
||||
import Simplex.Messaging.Agent.Store (closeStore, reopenStore)
|
||||
import Simplex.Messaging.Agent.Store.Interface (closeDBStore, reopenDBStore)
|
||||
import Simplex.Messaging.Agent.Store.Shared (MigrationConfirmation (..), MigrationError)
|
||||
import qualified Simplex.Messaging.Crypto as C
|
||||
import Simplex.Messaging.Encoding.String
|
||||
@@ -58,6 +56,10 @@ import Simplex.Messaging.Protocol (AProtoServerWithAuth (..), AProtocolType (..)
|
||||
import Simplex.Messaging.Util (catchAll, liftEitherWith, safeDecodeUtf8)
|
||||
import System.IO (utf8)
|
||||
import System.Timeout (timeout)
|
||||
#if !defined(dbPostgres)
|
||||
import Database.SQLite.Simple (SQLError (..))
|
||||
import qualified Database.SQLite.Simple as DB
|
||||
#endif
|
||||
|
||||
data DBMigrationResult
|
||||
= DBMOk
|
||||
@@ -112,9 +114,11 @@ foreign export ccall "chat_encrypt_file" cChatEncryptFile :: StablePtr ChatContr
|
||||
foreign export ccall "chat_decrypt_file" cChatDecryptFile :: CString -> CString -> CString -> CString -> IO CString
|
||||
|
||||
-- | check / migrate database and initialize chat controller on success
|
||||
-- For postgres first param is schema prefix, second param is database connection string.
|
||||
cChatMigrateInit :: CString -> CString -> CString -> Ptr (StablePtr ChatController) -> IO CJSONString
|
||||
cChatMigrateInit fp key conf = cChatMigrateInitKey fp key 0 conf 0
|
||||
|
||||
-- For postgres first param is schema prefix, second param is database connection string.
|
||||
cChatMigrateInitKey :: CString -> CString -> CInt -> CString -> CInt -> Ptr (StablePtr ChatController) -> IO CJSONString
|
||||
cChatMigrateInitKey fp key keepKey conf background ctrl = do
|
||||
-- ensure we are set to UTF-8; iOS does not have locale, and will default to
|
||||
@@ -123,11 +127,10 @@ cChatMigrateInitKey fp key keepKey conf background ctrl = do
|
||||
setFileSystemEncoding utf8
|
||||
setForeignEncoding utf8
|
||||
|
||||
dbPath <- peekCString fp
|
||||
dbKey <- BA.convert <$> B.packCString key
|
||||
chatDbOpts <- mobileDbOpts fp key
|
||||
confirm <- peekCAString conf
|
||||
r <-
|
||||
chatMigrateInitKey dbPath dbKey (keepKey /= 0) confirm (background /= 0) >>= \case
|
||||
chatMigrateInitKey chatDbOpts (keepKey /= 0) confirm (background /= 0) >>= \case
|
||||
Right cc -> (newStablePtr cc >>= poke ctrl) $> DBMOk
|
||||
Left e -> pure e
|
||||
newCStringFromLazyBS $ J.encode r
|
||||
@@ -185,17 +188,12 @@ cChatValidName cName = newCString . mkValidName =<< peekCString cName
|
||||
cChatJsonLength :: CString -> IO CInt
|
||||
cChatJsonLength s = fromIntegral . subtract 2 . LB.length . J.encode . safeDecodeUtf8 <$> B.packCString s
|
||||
|
||||
mobileChatOpts :: String -> ChatOpts
|
||||
mobileChatOpts dbFilePrefix =
|
||||
mobileChatOpts :: ChatDbOpts -> ChatOpts
|
||||
mobileChatOpts dbOptions =
|
||||
ChatOpts
|
||||
{ coreOptions =
|
||||
CoreChatOpts
|
||||
{ dbOptions =
|
||||
ChatDbOpts
|
||||
{ dbFilePrefix,
|
||||
dbKey = "", -- for API database is already opened, and the key in options is not used
|
||||
vacuumOnMigration = True
|
||||
},
|
||||
{ dbOptions,
|
||||
smpServers = [],
|
||||
xftpServers = [],
|
||||
simpleNetCfg = defaultSimpleNetCfg,
|
||||
@@ -235,40 +233,50 @@ defaultMobileConfig =
|
||||
getActiveUser_ :: DBStore -> IO (Maybe User)
|
||||
getActiveUser_ st = find activeUser <$> withTransaction st getUsers
|
||||
|
||||
#if !defined(dbPostgres)
|
||||
-- only used in tests
|
||||
chatMigrateInit :: String -> ScrubbedBytes -> String -> IO (Either DBMigrationResult ChatController)
|
||||
chatMigrateInit dbFilePrefix dbKey confirm = chatMigrateInitKey dbFilePrefix dbKey False confirm False
|
||||
chatMigrateInit dbFilePrefix dbKey confirm = do
|
||||
let chatDBOpts = ChatDbOpts {dbFilePrefix, dbKey, vacuumOnMigration = True}
|
||||
chatMigrateInitKey chatDBOpts False confirm False
|
||||
#endif
|
||||
|
||||
chatMigrateInitKey :: String -> ScrubbedBytes -> Bool -> String -> Bool -> IO (Either DBMigrationResult ChatController)
|
||||
chatMigrateInitKey dbFilePrefix dbKey keepKey confirm backgroundMode = runExceptT $ do
|
||||
chatMigrateInitKey :: ChatDbOpts -> Bool -> String -> Bool -> IO (Either DBMigrationResult ChatController)
|
||||
chatMigrateInitKey chatDbOpts keepKey confirm backgroundMode = runExceptT $ do
|
||||
confirmMigrations <- liftEitherWith (const DBMInvalidConfirmation) $ strDecode $ B.pack confirm
|
||||
chatStore <- migrate createChatStore (chatStoreFile dbFilePrefix) confirmMigrations
|
||||
agentStore <- migrate createAgentStore (agentStoreFile dbFilePrefix) confirmMigrations
|
||||
chatStore <- migrate createChatStore (toDBOpts chatDbOpts chatSuffix keepKey) confirmMigrations
|
||||
agentStore <- migrate createAgentStore (toDBOpts chatDbOpts agentSuffix keepKey) confirmMigrations
|
||||
liftIO $ initialize chatStore ChatDatabase {chatStore, agentStore}
|
||||
where
|
||||
opts = mobileChatOpts dbFilePrefix
|
||||
opts = mobileChatOpts $ removeDbKey chatDbOpts
|
||||
initialize st db = do
|
||||
user_ <- getActiveUser_ st
|
||||
newChatController db user_ defaultMobileConfig opts backgroundMode
|
||||
migrate createStore dbFile confirmMigrations =
|
||||
migrate createStore dbOpts confirmMigrations =
|
||||
ExceptT $
|
||||
(first (DBMErrorMigration dbFile) <$> createStore dbFile dbKey keepKey confirmMigrations (vacuumOnMigration $ dbOptions $ coreOptions opts))
|
||||
(first (DBMErrorMigration errDbStr) <$> createStore dbOpts confirmMigrations)
|
||||
#if !defined(dbPostgres)
|
||||
`catch` (pure . checkDBError)
|
||||
#endif
|
||||
`catchAll` (pure . dbError)
|
||||
where
|
||||
errDbStr = errorDbStr dbOpts
|
||||
#if !defined(dbPostgres)
|
||||
checkDBError e = case sqlError e of
|
||||
DB.ErrorNotADatabase -> Left $ DBMErrorNotADatabase dbFile
|
||||
DB.ErrorNotADatabase -> Left $ DBMErrorNotADatabase errDbStr
|
||||
_ -> dbError e
|
||||
dbError e = Left . DBMErrorSQL dbFile $ show e
|
||||
#endif
|
||||
dbError e = Left . DBMErrorSQL errDbStr $ show e
|
||||
|
||||
chatCloseStore :: ChatController -> IO String
|
||||
chatCloseStore ChatController {chatStore, smpAgent} = handleErr $ do
|
||||
closeStore chatStore
|
||||
closeStore $ agentClientStore smpAgent
|
||||
closeDBStore chatStore
|
||||
closeDBStore $ agentClientStore smpAgent
|
||||
|
||||
chatReopenStore :: ChatController -> IO String
|
||||
chatReopenStore ChatController {chatStore, smpAgent} = handleErr $ do
|
||||
reopenStore chatStore
|
||||
reopenStore (agentClientStore smpAgent)
|
||||
reopenDBStore chatStore
|
||||
reopenDBStore (agentClientStore smpAgent)
|
||||
|
||||
handleErr :: IO () -> IO String
|
||||
handleErr a = (a $> "") `catch` (pure . show @SomeException)
|
||||
|
||||
Reference in new issue
Block a user