diff --git a/README.md b/README.md index 8adda583d..5058fe2ea 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ See [simplex.chat](https://simplex.chat) website for chat demo and the explanati SMP protocol is semi-formally defined [here](https://github.com/simplex-chat/protocol). Currently only these features are available: -- simple 1-2-1 chat with multiple people in the same terminal window. +- simple 1-to-1 chat with multiple people in the same terminal window. - auto-populated recipient name - just type your messages. - default server is available to play with - `smp.simplex.im:5223` - and you can deploy your own (`smp-server` executable in this repo). - no global identity or names visible to the server(s) - for the privacy of contacts and conversations. @@ -51,16 +51,15 @@ $ dog-food > **NOTE:** When running chat client executable built with the latter approach, if you encounter ``version `GLIBC_2.28' not found`` error, rebuild it with `haskell:8.8.4-stretch` base image instead (you'd have to change it in your local [Dockerfile](Dockerfile)). -`dog-food` (as in "eating your own dog food" - it is an early prototype) starts chat client with default parameters. By default, SQLite database file is created in the working directory (`smp-chat.db`), and the default SMP server is `smp.simplex.im:5223`. +`dog-food` (as in "eating your own dog food" - it is an early prototype) starts chat client with default parameters. By default, app data directory is created in the home directory (`~/.simplex`, or `%APPDATA%/simplex` on Windows), and SQLite database file `smp-chat.db` is initialized in it. The default SMP server is `smp.simplex.im:5223`. -To specify a different chat database location run: +To specify a different file path for the chat database use `-d` command line option: ```shell -$ mkdir ~/simplex -$ dog-food -d ~/simplex/my-chat.db +$ dog-food -d my-chat.db ``` -You can deploy your own server and set client to use it via command line option: +If you deployed your own SMP server you can set client to use it via `-s` option: ```shell $ dog-food -s smp.example.com:5223 diff --git a/apps/dog-food/ChatOptions.hs b/apps/dog-food/ChatOptions.hs index 7051796a8..c95ddea85 100644 --- a/apps/dog-food/ChatOptions.hs +++ b/apps/dog-food/ChatOptions.hs @@ -6,6 +6,7 @@ import qualified Data.Attoparsec.ByteString.Char8 as A import qualified Data.ByteString.Char8 as B import Options.Applicative import Simplex.Messaging.Agent.Transmission (SMPServer (..), smpServerP) +import System.FilePath (combine) import Types data ChatOpts = ChatOpts @@ -15,8 +16,9 @@ data ChatOpts = ChatOpts termMode :: TermMode } -chatOpts :: Parser ChatOpts -chatOpts = +chatOpts :: FilePath -> Parser ChatOpts +chatOpts appDir = do + let defaultDbFilePath = combine appDir "smp-chat.db" ChatOpts <$> option (Just <$> str) @@ -30,8 +32,8 @@ chatOpts = ( long "database" <> short 'd' <> metavar "DB_FILE" - <> help "sqlite database filename (smp-chat.db)" - <> value "smp-chat.db" + <> help ("sqlite database file path (" ++ defaultDbFilePath ++ ")") + <> value defaultDbFilePath ) <*> option parseSMPServer @@ -60,12 +62,12 @@ parseTermMode = maybeReader $ \case "editor" -> Just TermModeEditor _ -> Nothing -getChatOpts :: IO ChatOpts -getChatOpts = execParser opts +getChatOpts :: FilePath -> IO ChatOpts +getChatOpts appDir = execParser opts where opts = info - (chatOpts <**> helper) + (chatOpts appDir <**> helper) ( fullDesc <> header "Chat prototype using Simplex Messaging Protocol (SMP)" <> progDesc "Start chat with DB_FILE file and use SERVER as SMP server" diff --git a/apps/dog-food/Main.hs b/apps/dog-food/Main.hs index d93094565..4c1f87abb 100644 --- a/apps/dog-food/Main.hs +++ b/apps/dog-food/Main.hs @@ -27,6 +27,7 @@ import Simplex.Messaging.Agent.Env.SQLite import Simplex.Messaging.Agent.Transmission import Simplex.Messaging.Client (smpDefaultConfig) import Simplex.Messaging.Util (bshow, raceAny_) +import System.Directory (getAppUserDataDirectory) import Types cfg :: AgentConfig @@ -116,8 +117,11 @@ chatHelpInfo = main :: IO () main = do - ChatOpts {dbFileName, smpServer, name, termMode} <- getChatOpts - putStrLn "simpleX chat prototype, \"/help\" for usage information" + appDir <- getAppUserDataDirectory "simplex" + ChatOpts {dbFileName, smpServer, name, termMode} <- getChatOpts appDir + putStrLn "simpleX chat prototype" + putStrLn $ "db: " ++ dbFileName + putStrLn "type \"/help\" for usage information" let user = Contact <$> name t <- getChatClient smpServer user ct <- newChatTerminal (tbqSize cfg) user termMode diff --git a/package.yaml b/package.yaml index fe5704410..83000817e 100644 --- a/package.yaml +++ b/package.yaml @@ -19,6 +19,8 @@ dependencies: - bytestring == 0.10.* - containers - cryptonite == 0.26.* + - directory == 1.3.* + - filepath == 1.4.* - iso8601-time == 0.1.* - memory == 0.15.* - mtl diff --git a/src/Simplex/Messaging/Agent/Env/SQLite.hs b/src/Simplex/Messaging/Agent/Env/SQLite.hs index b0291085b..6ecd85ea2 100644 --- a/src/Simplex/Messaging/Agent/Env/SQLite.hs +++ b/src/Simplex/Messaging/Agent/Env/SQLite.hs @@ -18,7 +18,7 @@ data AgentConfig = AgentConfig rsaKeySize :: Int, connIdBytes :: Int, tbqSize :: Natural, - dbFile :: String, + dbFile :: FilePath, smpCfg :: SMPClientConfig } diff --git a/src/Simplex/Messaging/Agent/Store/SQLite.hs b/src/Simplex/Messaging/Agent/Store/SQLite.hs index b73dea5b6..ad72ca047 100644 --- a/src/Simplex/Messaging/Agent/Store/SQLite.hs +++ b/src/Simplex/Messaging/Agent/Store/SQLite.hs @@ -40,19 +40,23 @@ import Simplex.Messaging.Protocol (MsgBody) import qualified Simplex.Messaging.Protocol as SMP import Simplex.Messaging.Util (liftIOEither) import System.Exit (ExitCode (ExitFailure), exitWith) +import System.FilePath (takeDirectory) import Text.Read (readMaybe) +import UnliftIO.Directory (createDirectoryIfMissing) import qualified UnliftIO.Exception as E -- * SQLite Store implementation data SQLiteStore = SQLiteStore - { dbFilename :: String, + { dbFilePath :: FilePath, dbConn :: DB.Connection } -createSQLiteStore :: MonadUnliftIO m => String -> m SQLiteStore -createSQLiteStore dbFilename = do - store <- connectSQLiteStore dbFilename +createSQLiteStore :: MonadUnliftIO m => FilePath -> m SQLiteStore +createSQLiteStore dbFilePath = do + let dbDir = takeDirectory dbFilePath + createDirectoryIfMissing False dbDir + store <- connectSQLiteStore dbFilePath compileOptions <- liftIO (DB.query_ (dbConn store) "pragma COMPILE_OPTIONS;" :: IO [[T.Text]]) let threadsafeOption = find (isPrefixOf "THREADSAFE=") (concat compileOptions) liftIO $ case threadsafeOption of @@ -65,10 +69,10 @@ createSQLiteStore dbFilename = do liftIO . createSchema $ dbConn store return store -connectSQLiteStore :: MonadUnliftIO m => String -> m SQLiteStore -connectSQLiteStore dbFilename = do - dbConn <- liftIO $ DB.open dbFilename - return SQLiteStore {dbFilename, dbConn} +connectSQLiteStore :: MonadUnliftIO m => FilePath -> m SQLiteStore +connectSQLiteStore dbFilePath = do + dbConn <- liftIO $ DB.open dbFilePath + return SQLiteStore {dbFilePath, dbConn} instance (MonadUnliftIO m, MonadError StoreError m) => MonadAgentStore SQLiteStore m where createRcvConn :: SQLiteStore -> RcvQueue -> m () diff --git a/tests/AgentTests/SQLiteTests.hs b/tests/AgentTests/SQLiteTests.hs index 3fe5c0d6b..fe6593c88 100644 --- a/tests/AgentTests/SQLiteTests.hs +++ b/tests/AgentTests/SQLiteTests.hs @@ -37,7 +37,7 @@ withStore = before createStore . after removeStore removeStore :: SQLiteStore -> IO () removeStore store = do DB.close $ dbConn store - removeFile $ dbFilename store + removeFile $ dbFilePath store returnsResult :: (Eq a, Eq e, Show a, Show e) => ExceptT e IO a -> a -> Expectation action `returnsResult` r = runExceptT action `shouldReturn` Right r