diff --git a/README.md b/README.md index e57672d11..ec40182de 100644 --- a/README.md +++ b/README.md @@ -105,7 +105,7 @@ apt update && apt install openssl #### Using Docker -On Linux, you can deploy smp server using Docker. This will download image from [Docker Hub](https://hub.docker.com/r/simplexchat/simplexmq). +On Linux, you can deploy smp server using Docker. This will download image from [Docker Hub](https://hub.docker.com/r/simplexchat/smp-server). 1. Create `config` and `logs` directories: @@ -121,7 +121,7 @@ On Linux, you can deploy smp server using Docker. This will download image from -p 5223:5223 \ -v $HOME/simplex/config:/etc/opt/simplex:z \ -v $HOME/simplex/logs:/var/opt/simplex:z \ - simplexchat/simplexmq:latest + simplexchat/smp-server:latest ``` #### Ubuntu diff --git a/build.Dockerfile b/build.Dockerfile index a7d670e25..133e95e0b 100644 --- a/build.Dockerfile +++ b/build.Dockerfile @@ -1,10 +1,10 @@ -FROM ubuntu:focal AS final -FROM ubuntu:focal AS build +FROM ubuntu:22.04 AS final +FROM ubuntu:22.04 AS build ### Build stage # Install curl and git and smp-related dependencies -RUN apt-get update && apt-get install -y curl git build-essential libgmp3-dev zlib1g-dev llvm llvm-dev libnuma-dev +RUN apt-get update && apt-get install -y curl git build-essential libgmp3-dev zlib1g-dev llvm-12 llvm-12-dev libnuma-dev # Install ghcup RUN curl --proto '=https' --tlsv1.2 -sSf https://get-ghcup.haskell.org | BOOTSTRAP_HASKELL_NONINTERACTIVE=1 BOOTSTRAP_HASKELL_GHC_VERSION=8.10.7 BOOTSTRAP_HASKELL_CABAL_VERSION=3.6.2.0 sh @@ -21,7 +21,12 @@ WORKDIR /project # Compile smp-server RUN cabal update -RUN cabal install +RUN cabal build exe:smp-server + +# Strip the binary from debug symbols to reduce size +RUN smp=$(find ./dist-newstyle -name "smp-server" -type f -executable) && \ + mv "$smp" ./ && \ + strip ./smp-server ### Final stage @@ -31,7 +36,7 @@ FROM final RUN apt-get update && apt-get install -y openssl libnuma-dev # Copy compiled smp-server from build stage -COPY --from=build /root/.cabal/bin/smp-server /usr/bin/smp-server +COPY --from=build /project/smp-server /usr/bin/smp-server # Copy our helper script COPY ./scripts/docker/entrypoint /usr/bin/entrypoint diff --git a/download.Dockerfile b/download.Dockerfile index 48344c376..55d79c29f 100644 --- a/download.Dockerfile +++ b/download.Dockerfile @@ -3,6 +3,8 @@ FROM ubuntu:focal # Install curl RUN apt-get update && apt-get install -y curl +ARG version=undefined + # Download latest smp-server release and assign executable permission RUN curl -L https://github.com/simplex-chat/simplexmq/releases/latest/download/smp-server-ubuntu-20_04-x86-64 -o /usr/bin/smp-server && \ chmod +x /usr/bin/smp-server diff --git a/scripts/docker/entrypoint b/scripts/docker/entrypoint index cc9f1dc13..ba172c2a1 100755 --- a/scripts/docker/entrypoint +++ b/scripts/docker/entrypoint @@ -20,10 +20,9 @@ if [ ! -f "$confd/smp-server.ini" ]; then fi # backup store log -[ -f "$logd/smp-server-store.log" ] && cp "$logd"/smp-server-store.log "$logd"/smp-server-store.log.bak -# rotate server log -[ -f "$logd/smp-server.log" ] && mv "$logd"/smp-server.log "$logd"/smp-server-"$(date +'%FT%T')".log +[ -f "$logd/smp-server-store.log" ] && cp "$logd"/smp-server-store.log "$logd"/smp-server-store.log."$(date +'%FT%T')" # Finally, run smp-sever. Notice that "exec" here is important: # smp-server replaces our helper script, so that it can catch INT signal -exec smp-server start > "$logd"/smp-server.log 2>&1 +exec smp-server start +RTS -N -RTS + diff --git a/src/Simplex/FileTransfer/Agent.hs b/src/Simplex/FileTransfer/Agent.hs index b2417aa8c..e02a1b007 100644 --- a/src/Simplex/FileTransfer/Agent.hs +++ b/src/Simplex/FileTransfer/Agent.hs @@ -66,7 +66,7 @@ import Simplex.Messaging.Encoding.String import Simplex.Messaging.Protocol (EntityId, XFTPServer, XFTPServerWithAuth) import Simplex.Messaging.TMap (TMap) import qualified Simplex.Messaging.TMap as TM -import Simplex.Messaging.Util (liftError, liftIOEither, tshow, whenM) +import Simplex.Messaging.Util (liftError, liftIOEither, tshow, unlessM, whenM) import System.FilePath (takeFileName, ()) import UnliftIO import UnliftIO.Concurrent @@ -273,9 +273,9 @@ runXFTPRcvLocalWorker c doWork = do getChunkPaths (RcvFileChunk {chunkTmpPath = Nothing} : _cs) = throwError $ INTERNAL "no chunk path" -deleteRcvFile :: AgentMonad m => AgentClient -> UserId -> RcvFileId -> m () -deleteRcvFile c userId rcvFileEntityId = do - RcvFile {rcvFileId, prefixPath, status} <- withStore c $ \db -> getRcvFileByEntityId db userId rcvFileEntityId +deleteRcvFile :: AgentMonad m => AgentClient -> RcvFileId -> m () +deleteRcvFile c rcvFileEntityId = do + RcvFile {rcvFileId, prefixPath, status} <- withStore c $ \db -> getRcvFileByEntityId db rcvFileEntityId if status == RFSComplete || status == RFSError then do removePath prefixPath @@ -480,6 +480,7 @@ runXFTPSndWorker c srv doWork = do uploadFileChunk sndFileChunk@SndFileChunk {sndFileId, userId, chunkSpec = chunkSpec@XFTPChunkSpec {filePath}, digest = chunkDigest} replica = do replica'@SndFileChunkReplica {sndChunkReplicaId} <- addRecipients sndFileChunk replica fsFilePath <- toFSFilePath filePath + unlessM (doesFileExist fsFilePath) $ throwError $ INTERNAL "encrypted file doesn't exist on upload" let chunkSpec' = chunkSpec {filePath = fsFilePath} :: XFTPChunkSpec atomically $ assertAgentForeground c agentXFTPUploadChunk c userId chunkDigest replica' chunkSpec' @@ -572,9 +573,9 @@ runXFTPSndWorker c srv doWork = do chunkUploaded SndFileChunk {replicas} = any (\SndFileChunkReplica {replicaStatus} -> replicaStatus == SFRSUploaded) replicas -deleteSndFileInternal :: AgentMonad m => AgentClient -> UserId -> SndFileId -> m () -deleteSndFileInternal c userId sndFileEntityId = do - SndFile {sndFileId, prefixPath, status} <- withStore c $ \db -> getSndFileByEntityId db userId sndFileEntityId +deleteSndFileInternal :: AgentMonad m => AgentClient -> SndFileId -> m () +deleteSndFileInternal c sndFileEntityId = do + SndFile {sndFileId, prefixPath, status} <- withStore c $ \db -> getSndFileByEntityId db sndFileEntityId if status == SFSComplete || status == SFSError then do forM_ prefixPath $ removePath <=< toFSFilePath @@ -583,7 +584,7 @@ deleteSndFileInternal c userId sndFileEntityId = do deleteSndFileRemote :: forall m. AgentMonad m => AgentClient -> UserId -> SndFileId -> ValidFileDescription 'FSender -> m () deleteSndFileRemote c userId sndFileEntityId (ValidFileDescription FileDescription {chunks}) = do - deleteSndFileInternal c userId sndFileEntityId `catchError` (notify c sndFileEntityId . SFERR) + deleteSndFileInternal c sndFileEntityId `catchError` (notify c sndFileEntityId . SFERR) forM_ chunks $ \ch -> deleteFileChunk ch `catchError` (notify c sndFileEntityId . SFERR) where deleteFileChunk :: FileChunk -> m () diff --git a/src/Simplex/Messaging/Agent.hs b/src/Simplex/Messaging/Agent.hs index 2c6a14913..2b8a6d09f 100644 --- a/src/Simplex/Messaging/Agent.hs +++ b/src/Simplex/Messaging/Agent.hs @@ -345,16 +345,16 @@ xftpReceiveFile :: AgentErrorMonad m => AgentClient -> UserId -> ValidFileDescri xftpReceiveFile c = withAgentEnv c .: receiveFile c -- | Delete XFTP rcv file (deletes work files from file system and db records) -xftpDeleteRcvFile :: AgentErrorMonad m => AgentClient -> UserId -> RcvFileId -> m () -xftpDeleteRcvFile c = withAgentEnv c .: deleteRcvFile c +xftpDeleteRcvFile :: AgentErrorMonad m => AgentClient -> RcvFileId -> m () +xftpDeleteRcvFile c = withAgentEnv c . deleteRcvFile c -- | Send XFTP file xftpSendFile :: AgentErrorMonad m => AgentClient -> UserId -> FilePath -> Int -> m SndFileId xftpSendFile c = withAgentEnv c .:. sendFile c -- | Delete XFTP snd file internally (deletes work files from file system and db records) -xftpDeleteSndFileInternal :: AgentErrorMonad m => AgentClient -> UserId -> SndFileId -> m () -xftpDeleteSndFileInternal c = withAgentEnv c .: deleteSndFileInternal c +xftpDeleteSndFileInternal :: AgentErrorMonad m => AgentClient -> SndFileId -> m () +xftpDeleteSndFileInternal c = withAgentEnv c . deleteSndFileInternal c -- | Delete XFTP snd file chunks on servers xftpDeleteSndFileRemote :: AgentErrorMonad m => AgentClient -> UserId -> SndFileId -> ValidFileDescription 'FSender -> m () diff --git a/src/Simplex/Messaging/Agent/Store/SQLite.hs b/src/Simplex/Messaging/Agent/Store/SQLite.hs index 1a8c00149..e16a8d024 100644 --- a/src/Simplex/Messaging/Agent/Store/SQLite.hs +++ b/src/Simplex/Messaging/Agent/Store/SQLite.hs @@ -1888,15 +1888,15 @@ createRcvFile db gVar userId fd@FileDescription {chunks} prefixPath tmpPath save "INSERT INTO rcv_file_chunk_replicas (replica_number, rcv_file_chunk_id, xftp_server_id, replica_id, replica_key) VALUES (?,?,?,?,?)" (replicaNo, chunkId, srvId, replicaId, replicaKey) -getRcvFileByEntityId :: DB.Connection -> UserId -> RcvFileId -> IO (Either StoreError RcvFile) -getRcvFileByEntityId db userId rcvFileEntityId = runExceptT $ do - rcvFileId <- ExceptT $ getRcvFileIdByEntityId_ db userId rcvFileEntityId +getRcvFileByEntityId :: DB.Connection -> RcvFileId -> IO (Either StoreError RcvFile) +getRcvFileByEntityId db rcvFileEntityId = runExceptT $ do + rcvFileId <- ExceptT $ getRcvFileIdByEntityId_ db rcvFileEntityId ExceptT $ getRcvFile db rcvFileId -getRcvFileIdByEntityId_ :: DB.Connection -> UserId -> RcvFileId -> IO (Either StoreError DBRcvFileId) -getRcvFileIdByEntityId_ db userId rcvFileEntityId = +getRcvFileIdByEntityId_ :: DB.Connection -> RcvFileId -> IO (Either StoreError DBRcvFileId) +getRcvFileIdByEntityId_ db rcvFileEntityId = firstRow fromOnly SEFileNotFound $ - DB.query db "SELECT rcv_file_id FROM rcv_files WHERE user_id = ? AND rcv_file_entity_id = ?" (userId, rcvFileEntityId) + DB.query db "SELECT rcv_file_id FROM rcv_files WHERE rcv_file_entity_id = ?" (Only rcvFileEntityId) getRcvFile :: DB.Connection -> DBRcvFileId -> IO (Either StoreError RcvFile) getRcvFile db rcvFileId = runExceptT $ do @@ -2115,15 +2115,15 @@ createSndFile db gVar userId numRecipients path prefixPath key nonce = "INSERT INTO snd_files (snd_file_entity_id, user_id, num_recipients, key, nonce, path, prefix_path, status) VALUES (?,?,?,?,?,?,?,?)" (sndFileEntityId, userId, numRecipients, key, nonce, path, prefixPath, SFSNew) -getSndFileByEntityId :: DB.Connection -> UserId -> SndFileId -> IO (Either StoreError SndFile) -getSndFileByEntityId db userId sndFileEntityId = runExceptT $ do - sndFileId <- ExceptT $ getSndFileIdByEntityId_ db userId sndFileEntityId +getSndFileByEntityId :: DB.Connection -> SndFileId -> IO (Either StoreError SndFile) +getSndFileByEntityId db sndFileEntityId = runExceptT $ do + sndFileId <- ExceptT $ getSndFileIdByEntityId_ db sndFileEntityId ExceptT $ getSndFile db sndFileId -getSndFileIdByEntityId_ :: DB.Connection -> UserId -> SndFileId -> IO (Either StoreError DBSndFileId) -getSndFileIdByEntityId_ db userId sndFileEntityId = +getSndFileIdByEntityId_ :: DB.Connection -> SndFileId -> IO (Either StoreError DBSndFileId) +getSndFileIdByEntityId_ db sndFileEntityId = firstRow fromOnly SEFileNotFound $ - DB.query db "SELECT snd_file_id FROM snd_files WHERE user_id = ? AND snd_file_entity_id = ?" (userId, sndFileEntityId) + DB.query db "SELECT snd_file_id FROM snd_files WHERE snd_file_entity_id = ?" (Only sndFileEntityId) getSndFile :: DB.Connection -> DBSndFileId -> IO (Either StoreError SndFile) getSndFile db sndFileId = runExceptT $ do diff --git a/tests/XFTPAgent.hs b/tests/XFTPAgent.hs index 44e7b0860..9eaa0f23e 100644 --- a/tests/XFTPAgent.hs +++ b/tests/XFTPAgent.hs @@ -86,7 +86,7 @@ testXFTPAgentSendReceive = withXFTPServer $ do sndr <- getSMPAgentClient' agentCfg initAgentServers testDB (rfd1, rfd2) <- runRight $ do (sfId, _, rfd1, rfd2) <- testSend sndr filePath - xftpDeleteSndFileInternal sndr 1 sfId + xftpDeleteSndFileInternal sndr sfId pure (rfd1, rfd2) -- receive file, delete rcv file @@ -97,7 +97,7 @@ testXFTPAgentSendReceive = withXFTPServer $ do rcp <- getSMPAgentClient' agentCfg initAgentServers testDB runRight_ $ do rfId <- testReceive rcp rfd originalFilePath - xftpDeleteRcvFile rcp 1 rfId + xftpDeleteRcvFile rcp rfId createRandomFile :: IO FilePath createRandomFile = do