mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2026-04-27 23:46:18 +00:00
core: support postgres backend (#5403)
* postgres: modules structure (#5401) * postgres: schema, field conversions (#5430) * postgres: rework chat list pagination query (#5441) * prepare cabal for merge * restore cabal changes * simplexmq * postgres: implementation wip (tests don't pass) (#5481) * restore ios file * postgres: implementation - tests pass (#5487) * refactor DB options * refactor * line * style * style * refactor * $ * update simplexmq * constraintError * handleDBErrors * fix * remove param * Ok * case * case * case * comment --------- Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
{-# LANGUAGE CPP #-}
|
||||
{-# LANGUAGE OverloadedStrings #-}
|
||||
|
||||
module Simplex.Chat.Store.AppSettings where
|
||||
@@ -6,10 +7,14 @@ import Control.Monad (join)
|
||||
import Control.Monad.IO.Class (liftIO)
|
||||
import qualified Data.Aeson as J
|
||||
import Data.Maybe (fromMaybe)
|
||||
import Database.SQLite.Simple (Only (..))
|
||||
import Simplex.Chat.AppSettings (AppSettings (..), combineAppSettings, defaultAppSettings, defaultParseAppSettings)
|
||||
import Simplex.Messaging.Agent.Store.AgentStore (maybeFirstRow)
|
||||
import qualified Simplex.Messaging.Agent.Store.SQLite.DB as DB
|
||||
import qualified Simplex.Messaging.Agent.Store.DB as DB
|
||||
#if defined(dbPostgres)
|
||||
import Database.PostgreSQL.Simple (Only (..))
|
||||
#else
|
||||
import Database.SQLite.Simple (Only (..))
|
||||
#endif
|
||||
|
||||
saveAppSettings :: DB.Connection -> AppSettings -> IO ()
|
||||
saveAppSettings db appSettings = do
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
{-# LANGUAGE CPP #-}
|
||||
{-# LANGUAGE DuplicateRecordFields #-}
|
||||
{-# LANGUAGE LambdaCase #-}
|
||||
{-# LANGUAGE NamedFieldPuns #-}
|
||||
@@ -25,8 +26,6 @@ import Control.Monad.IO.Class
|
||||
import Data.Bitraversable (bitraverse)
|
||||
import Data.Int (Int64)
|
||||
import Data.Maybe (catMaybes, fromMaybe)
|
||||
import Database.SQLite.Simple (Only (..), (:.) (..))
|
||||
import Database.SQLite.Simple.QQ (sql)
|
||||
import Simplex.Chat.Protocol
|
||||
import Simplex.Chat.Store.Direct
|
||||
import Simplex.Chat.Store.Files
|
||||
@@ -36,8 +35,16 @@ import Simplex.Chat.Store.Shared
|
||||
import Simplex.Chat.Types
|
||||
import Simplex.Messaging.Agent.Protocol (ConnId)
|
||||
import Simplex.Messaging.Agent.Store.AgentStore (firstRow, firstRow', maybeFirstRow)
|
||||
import qualified Simplex.Messaging.Agent.Store.SQLite.DB as DB
|
||||
import Simplex.Messaging.Agent.Store.DB (BoolInt (..))
|
||||
import qualified Simplex.Messaging.Agent.Store.DB as DB
|
||||
import Simplex.Messaging.Util (eitherToMaybe)
|
||||
#if defined(dbPostgres)
|
||||
import Database.PostgreSQL.Simple (Only (..), (:.) (..))
|
||||
import Database.PostgreSQL.Simple.SqlQQ (sql)
|
||||
#else
|
||||
import Database.SQLite.Simple (Only (..), (:.) (..))
|
||||
import Database.SQLite.Simple.QQ (sql)
|
||||
#endif
|
||||
|
||||
getChatLockEntity :: DB.Connection -> AgentConnId -> ExceptT StoreError IO ChatLockEntity
|
||||
getChatLockEntity db agentConnId = do
|
||||
@@ -110,40 +117,42 @@ getConnectionEntity db vr user@User {userId, userContactId} agentConnId = do
|
||||
|]
|
||||
(userId, contactId)
|
||||
toContact' :: Int64 -> Connection -> [ChatTagId] -> ContactRow' -> Contact
|
||||
toContact' contactId conn chatTags ((profileId, localDisplayName, viaGroup, displayName, fullName, image, contactLink, localAlias, contactUsed, contactStatus) :. (enableNtfs_, sendRcpts, favorite, preferences, userPreferences, createdAt, updatedAt, chatTs) :. (contactGroupMemberId, contactGrpInvSent, uiThemes, chatDeleted, customData)) =
|
||||
toContact' contactId conn chatTags ((profileId, localDisplayName, viaGroup, displayName, fullName, image, contactLink, localAlias, BI contactUsed, contactStatus) :. (enableNtfs_, sendRcpts, BI favorite, preferences, userPreferences, createdAt, updatedAt, chatTs) :. (contactGroupMemberId, BI contactGrpInvSent, uiThemes, BI chatDeleted, customData)) =
|
||||
let profile = LocalProfile {profileId, displayName, fullName, image, contactLink, preferences, localAlias}
|
||||
chatSettings = ChatSettings {enableNtfs = fromMaybe MFAll enableNtfs_, sendRcpts, favorite}
|
||||
chatSettings = ChatSettings {enableNtfs = fromMaybe MFAll enableNtfs_, sendRcpts = unBI <$> sendRcpts, favorite}
|
||||
mergedPreferences = contactUserPreferences user userPreferences preferences $ connIncognito conn
|
||||
activeConn = Just conn
|
||||
in Contact {contactId, localDisplayName, profile, activeConn, viaGroup, contactUsed, contactStatus, chatSettings, userPreferences, mergedPreferences, createdAt, updatedAt, chatTs, contactGroupMemberId, contactGrpInvSent, chatTags, uiThemes, chatDeleted, customData}
|
||||
getGroupAndMember_ :: Int64 -> Connection -> ExceptT StoreError IO (GroupInfo, GroupMember)
|
||||
getGroupAndMember_ groupMemberId c = do
|
||||
gm <- ExceptT $ firstRow (toGroupAndMember c) (SEInternalError "referenced group member not found") $
|
||||
DB.query
|
||||
db
|
||||
[sql|
|
||||
SELECT
|
||||
-- GroupInfo
|
||||
g.group_id, g.local_display_name, gp.display_name, gp.full_name, gp.description, gp.image,
|
||||
g.host_conn_custom_user_profile_id, g.enable_ntfs, g.send_rcpts, g.favorite, gp.preferences,
|
||||
g.created_at, g.updated_at, g.chat_ts, g.user_member_profile_sent_at, g.business_chat, g.business_member_id, g.customer_member_id, g.ui_themes, g.custom_data,
|
||||
-- GroupInfo {membership}
|
||||
mu.group_member_id, mu.group_id, mu.member_id, mu.peer_chat_min_version, mu.peer_chat_max_version, mu.member_role, mu.member_category,
|
||||
mu.member_status, mu.show_messages, mu.member_restriction, mu.invited_by, mu.invited_by_group_member_id, mu.local_display_name, mu.contact_id, mu.contact_profile_id, pu.contact_profile_id,
|
||||
-- GroupInfo {membership = GroupMember {memberProfile}}
|
||||
pu.display_name, pu.full_name, pu.image, pu.contact_link, pu.local_alias, pu.preferences,
|
||||
-- from GroupMember
|
||||
m.group_member_id, m.group_id, m.member_id, m.peer_chat_min_version, m.peer_chat_max_version, m.member_role, m.member_category, m.member_status, m.show_messages, m.member_restriction,
|
||||
m.invited_by, m.invited_by_group_member_id, m.local_display_name, m.contact_id, m.contact_profile_id, p.contact_profile_id, p.display_name, p.full_name, p.image, p.contact_link, p.local_alias, p.preferences
|
||||
FROM group_members m
|
||||
JOIN contact_profiles p ON p.contact_profile_id = COALESCE(m.member_profile_id, m.contact_profile_id)
|
||||
JOIN groups g ON g.group_id = m.group_id
|
||||
JOIN group_profiles gp USING (group_profile_id)
|
||||
JOIN group_members mu ON g.group_id = mu.group_id
|
||||
JOIN contact_profiles pu ON pu.contact_profile_id = COALESCE(mu.member_profile_id, mu.contact_profile_id)
|
||||
WHERE m.group_member_id = ? AND g.user_id = ? AND mu.contact_id = ?
|
||||
|]
|
||||
(groupMemberId, userId, userContactId)
|
||||
gm <-
|
||||
ExceptT $
|
||||
firstRow (toGroupAndMember c) (SEInternalError "referenced group member not found") $
|
||||
DB.query
|
||||
db
|
||||
[sql|
|
||||
SELECT
|
||||
-- GroupInfo
|
||||
g.group_id, g.local_display_name, gp.display_name, gp.full_name, gp.description, gp.image,
|
||||
g.host_conn_custom_user_profile_id, g.enable_ntfs, g.send_rcpts, g.favorite, gp.preferences,
|
||||
g.created_at, g.updated_at, g.chat_ts, g.user_member_profile_sent_at, g.business_chat, g.business_member_id, g.customer_member_id, g.ui_themes, g.custom_data,
|
||||
-- GroupInfo {membership}
|
||||
mu.group_member_id, mu.group_id, mu.member_id, mu.peer_chat_min_version, mu.peer_chat_max_version, mu.member_role, mu.member_category,
|
||||
mu.member_status, mu.show_messages, mu.member_restriction, mu.invited_by, mu.invited_by_group_member_id, mu.local_display_name, mu.contact_id, mu.contact_profile_id, pu.contact_profile_id,
|
||||
-- GroupInfo {membership = GroupMember {memberProfile}}
|
||||
pu.display_name, pu.full_name, pu.image, pu.contact_link, pu.local_alias, pu.preferences,
|
||||
-- from GroupMember
|
||||
m.group_member_id, m.group_id, m.member_id, m.peer_chat_min_version, m.peer_chat_max_version, m.member_role, m.member_category, m.member_status, m.show_messages, m.member_restriction,
|
||||
m.invited_by, m.invited_by_group_member_id, m.local_display_name, m.contact_id, m.contact_profile_id, p.contact_profile_id, p.display_name, p.full_name, p.image, p.contact_link, p.local_alias, p.preferences
|
||||
FROM group_members m
|
||||
JOIN contact_profiles p ON p.contact_profile_id = COALESCE(m.member_profile_id, m.contact_profile_id)
|
||||
JOIN groups g ON g.group_id = m.group_id
|
||||
JOIN group_profiles gp USING (group_profile_id)
|
||||
JOIN group_members mu ON g.group_id = mu.group_id
|
||||
JOIN contact_profiles pu ON pu.contact_profile_id = COALESCE(mu.member_profile_id, mu.contact_profile_id)
|
||||
WHERE m.group_member_id = ? AND g.user_id = ? AND mu.contact_id = ?
|
||||
|]
|
||||
(groupMemberId, userId, userContactId)
|
||||
liftIO $ bitraverse (addGroupChatTags db) pure gm
|
||||
toGroupAndMember :: Connection -> GroupInfoRow :. GroupMemberRow -> (GroupInfo, GroupMember)
|
||||
toGroupAndMember c (groupInfoRow :. memberRow) =
|
||||
@@ -212,7 +221,7 @@ getContactConnEntityByConnReqHash db vr user@User {userId} (cReqHash1, cReqHash2
|
||||
WHERE user_id = ? AND via_contact_uri_hash IN (?,?) AND conn_status != ?
|
||||
ORDER BY conn_ord DESC, created_at DESC
|
||||
LIMIT 1
|
||||
)
|
||||
) c
|
||||
|]
|
||||
(userId, cReqHash1, cReqHash2, ConnDeleted)
|
||||
maybe (pure Nothing) (fmap eitherToMaybe . runExceptT . getConnectionEntity db vr user) connId_
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
{-# LANGUAGE CPP #-}
|
||||
{-# LANGUAGE DuplicateRecordFields #-}
|
||||
{-# LANGUAGE FlexibleContexts #-}
|
||||
{-# LANGUAGE LambdaCase #-}
|
||||
@@ -93,8 +94,6 @@ import Data.Int (Int64)
|
||||
import Data.Maybe (fromMaybe, isJust, isNothing)
|
||||
import Data.Text (Text)
|
||||
import Data.Time.Clock (UTCTime (..), getCurrentTime)
|
||||
import Database.SQLite.Simple (NamedParam (..), Only (..), (:.) (..))
|
||||
import Database.SQLite.Simple.QQ (sql)
|
||||
import Simplex.Chat.Messages
|
||||
import Simplex.Chat.Store.Shared
|
||||
import Simplex.Chat.Types
|
||||
@@ -102,11 +101,19 @@ import Simplex.Chat.Types.Preferences
|
||||
import Simplex.Chat.Types.UITheme
|
||||
import Simplex.Messaging.Agent.Protocol (ConnId, InvitationId, UserId)
|
||||
import Simplex.Messaging.Agent.Store.AgentStore (firstRow, maybeFirstRow)
|
||||
import qualified Simplex.Messaging.Agent.Store.SQLite.DB as DB
|
||||
import Simplex.Messaging.Agent.Store.DB (Binary (..), BoolInt (..))
|
||||
import qualified Simplex.Messaging.Agent.Store.DB as DB
|
||||
import Simplex.Messaging.Crypto.Ratchet (PQSupport)
|
||||
import Simplex.Messaging.Protocol (SubscriptionMode (..))
|
||||
import Simplex.Messaging.Util ((<$$>))
|
||||
import Simplex.Messaging.Version
|
||||
#if defined(dbPostgres)
|
||||
import Database.PostgreSQL.Simple (Only (..), (:.) (..))
|
||||
import Database.PostgreSQL.Simple.SqlQQ (sql)
|
||||
#else
|
||||
import Database.SQLite.Simple (Only (..), (:.) (..))
|
||||
import Database.SQLite.Simple.QQ (sql)
|
||||
#endif
|
||||
|
||||
getPendingContactConnection :: DB.Connection -> UserId -> Int64 -> ExceptT StoreError IO PendingContactConnection
|
||||
getPendingContactConnection db userId connId = do
|
||||
@@ -160,9 +167,9 @@ createConnReqConnection db userId acId cReqHash xContactId incognitoProfile grou
|
||||
created_at, updated_at, to_subscribe, conn_chat_version, pq_support, pq_encryption
|
||||
) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)
|
||||
|]
|
||||
( (userId, acId, pccConnStatus, ConnContact, True, cReqHash, xContactId)
|
||||
:. (customUserProfileId, isJust groupLinkId, groupLinkId)
|
||||
:. (createdAt, createdAt, subMode == SMOnlyCreate, chatV, pqSup, pqSup)
|
||||
( (userId, acId, pccConnStatus, ConnContact, BI True, cReqHash, xContactId)
|
||||
:. (customUserProfileId, BI (isJust groupLinkId), groupLinkId)
|
||||
:. (createdAt, createdAt, BI (subMode == SMOnlyCreate), chatV, pqSup, pqSup)
|
||||
)
|
||||
pccConnId <- insertedRowId db
|
||||
pure PendingContactConnection {pccConnId, pccAgentConnId = AgentConnId acId, pccConnStatus, viaContactUri = True, viaUserContactLink = Nothing, groupLinkId, customUserProfileId, connReqInv = Nothing, localAlias = "", createdAt, updatedAt = createdAt}
|
||||
@@ -183,26 +190,27 @@ getConnReqContactXContactId db vr user@User {userId} cReqHash = do
|
||||
|
||||
getContactByConnReqHash :: DB.Connection -> VersionRangeChat -> User -> ConnReqUriHash -> IO (Maybe Contact)
|
||||
getContactByConnReqHash db vr user@User {userId} cReqHash = do
|
||||
ct_ <- maybeFirstRow (toContact vr user []) $
|
||||
DB.query
|
||||
db
|
||||
[sql|
|
||||
SELECT
|
||||
-- Contact
|
||||
ct.contact_id, ct.contact_profile_id, ct.local_display_name, ct.via_group, cp.display_name, cp.full_name, cp.image, cp.contact_link, cp.local_alias, ct.contact_used, ct.contact_status, ct.enable_ntfs, ct.send_rcpts, ct.favorite,
|
||||
cp.preferences, ct.user_preferences, ct.created_at, ct.updated_at, ct.chat_ts, ct.contact_group_member_id, ct.contact_grp_inv_sent, ct.ui_themes, ct.chat_deleted, ct.custom_data,
|
||||
-- Connection
|
||||
c.connection_id, c.agent_conn_id, c.conn_level, c.via_contact, c.via_user_contact_link, c.via_group_link, c.group_link_id, c.custom_user_profile_id, c.conn_status, c.conn_type, c.contact_conn_initiated, c.local_alias,
|
||||
c.contact_id, c.group_member_id, c.snd_file_id, c.rcv_file_id, c.user_contact_link_id, c.created_at, c.security_code, c.security_code_verified_at, c.pq_support, c.pq_encryption, c.pq_snd_enabled, c.pq_rcv_enabled, c.auth_err_counter, c.quota_err_counter,
|
||||
c.conn_chat_version, c.peer_chat_min_version, c.peer_chat_max_version
|
||||
FROM contacts ct
|
||||
JOIN contact_profiles cp ON ct.contact_profile_id = cp.contact_profile_id
|
||||
JOIN connections c ON c.contact_id = ct.contact_id
|
||||
WHERE c.user_id = ? AND c.via_contact_uri_hash = ? AND ct.contact_status = ? AND ct.deleted = 0
|
||||
ORDER BY c.created_at DESC
|
||||
LIMIT 1
|
||||
|]
|
||||
(userId, cReqHash, CSActive)
|
||||
ct_ <-
|
||||
maybeFirstRow (toContact vr user []) $
|
||||
DB.query
|
||||
db
|
||||
[sql|
|
||||
SELECT
|
||||
-- Contact
|
||||
ct.contact_id, ct.contact_profile_id, ct.local_display_name, ct.via_group, cp.display_name, cp.full_name, cp.image, cp.contact_link, cp.local_alias, ct.contact_used, ct.contact_status, ct.enable_ntfs, ct.send_rcpts, ct.favorite,
|
||||
cp.preferences, ct.user_preferences, ct.created_at, ct.updated_at, ct.chat_ts, ct.contact_group_member_id, ct.contact_grp_inv_sent, ct.ui_themes, ct.chat_deleted, ct.custom_data,
|
||||
-- Connection
|
||||
c.connection_id, c.agent_conn_id, c.conn_level, c.via_contact, c.via_user_contact_link, c.via_group_link, c.group_link_id, c.custom_user_profile_id, c.conn_status, c.conn_type, c.contact_conn_initiated, c.local_alias,
|
||||
c.contact_id, c.group_member_id, c.snd_file_id, c.rcv_file_id, c.user_contact_link_id, c.created_at, c.security_code, c.security_code_verified_at, c.pq_support, c.pq_encryption, c.pq_snd_enabled, c.pq_rcv_enabled, c.auth_err_counter, c.quota_err_counter,
|
||||
c.conn_chat_version, c.peer_chat_min_version, c.peer_chat_max_version
|
||||
FROM contacts ct
|
||||
JOIN contact_profiles cp ON ct.contact_profile_id = cp.contact_profile_id
|
||||
JOIN connections c ON c.contact_id = ct.contact_id
|
||||
WHERE c.user_id = ? AND c.via_contact_uri_hash = ? AND ct.contact_status = ? AND ct.deleted = 0
|
||||
ORDER BY c.created_at DESC
|
||||
LIMIT 1
|
||||
|]
|
||||
(userId, cReqHash, CSActive)
|
||||
mapM (addDirectChatTags db) ct_
|
||||
|
||||
createDirectConnection :: DB.Connection -> User -> ConnId -> ConnReqInvitation -> ConnStatus -> Maybe Profile -> SubscriptionMode -> VersionChat -> PQSupport -> IO PendingContactConnection
|
||||
@@ -218,8 +226,8 @@ createDirectConnection db User {userId} acId cReq pccConnStatus incognitoProfile
|
||||
created_at, updated_at, to_subscribe, conn_chat_version, pq_support, pq_encryption)
|
||||
VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?)
|
||||
|]
|
||||
( (userId, acId, cReq, pccConnStatus, ConnContact, contactConnInitiated, customUserProfileId)
|
||||
:. (createdAt, createdAt, subMode == SMOnlyCreate, chatV, pqSup, pqSup)
|
||||
( (userId, acId, cReq, pccConnStatus, ConnContact, BI contactConnInitiated, customUserProfileId)
|
||||
:. (createdAt, createdAt, BI (subMode == SMOnlyCreate), chatV, pqSup, pqSup)
|
||||
)
|
||||
pccConnId <- insertedRowId db
|
||||
pure PendingContactConnection {pccConnId, pccAgentConnId = AgentConnId acId, pccConnStatus, viaContactUri = False, viaUserContactLink = Nothing, groupLinkId = Nothing, customUserProfileId, connReqInv = Just cReq, localAlias = "", createdAt, updatedAt = createdAt}
|
||||
@@ -342,31 +350,33 @@ deleteContactProfile_ db userId contactId =
|
||||
|
||||
deleteUnusedProfile_ :: DB.Connection -> UserId -> ProfileId -> IO ()
|
||||
deleteUnusedProfile_ db userId profileId =
|
||||
DB.executeNamed
|
||||
DB.execute
|
||||
db
|
||||
[sql|
|
||||
DELETE FROM contact_profiles
|
||||
WHERE user_id = :user_id AND contact_profile_id = :profile_id
|
||||
WHERE user_id = ? AND contact_profile_id = ?
|
||||
AND 1 NOT IN (
|
||||
SELECT 1 FROM connections
|
||||
WHERE user_id = :user_id AND custom_user_profile_id = :profile_id LIMIT 1
|
||||
WHERE user_id = ? AND custom_user_profile_id = ? LIMIT 1
|
||||
)
|
||||
AND 1 NOT IN (
|
||||
SELECT 1 FROM contacts
|
||||
WHERE user_id = :user_id AND contact_profile_id = :profile_id LIMIT 1
|
||||
WHERE user_id = ? AND contact_profile_id = ? LIMIT 1
|
||||
)
|
||||
AND 1 NOT IN (
|
||||
SELECT 1 FROM contact_requests
|
||||
WHERE user_id = :user_id AND contact_profile_id = :profile_id LIMIT 1
|
||||
WHERE user_id = ? AND contact_profile_id = ? LIMIT 1
|
||||
)
|
||||
AND 1 NOT IN (
|
||||
SELECT 1 FROM group_members
|
||||
WHERE user_id = :user_id
|
||||
AND (member_profile_id = :profile_id OR contact_profile_id = :profile_id)
|
||||
WHERE user_id = ?
|
||||
AND (member_profile_id = ? OR contact_profile_id = ?)
|
||||
LIMIT 1
|
||||
)
|
||||
|]
|
||||
[":user_id" := userId, ":profile_id" := profileId]
|
||||
( (userId, profileId, userId, profileId, userId, profileId)
|
||||
:. (userId, profileId, userId, profileId, profileId)
|
||||
)
|
||||
|
||||
updateContactProfile :: DB.Connection -> User -> Contact -> Profile -> ExceptT StoreError IO Contact
|
||||
updateContactProfile db user@User {userId} c p'
|
||||
@@ -465,14 +475,14 @@ updateContactUsed db User {userId} Contact {contactId} = do
|
||||
updateContactUnreadChat :: DB.Connection -> User -> Contact -> Bool -> IO ()
|
||||
updateContactUnreadChat db User {userId} Contact {contactId} unreadChat = do
|
||||
updatedAt <- getCurrentTime
|
||||
DB.execute db "UPDATE contacts SET unread_chat = ?, updated_at = ? WHERE user_id = ? AND contact_id = ?" (unreadChat, updatedAt, userId, contactId)
|
||||
DB.execute db "UPDATE contacts SET unread_chat = ?, updated_at = ? WHERE user_id = ? AND contact_id = ?" (BI unreadChat, updatedAt, userId, contactId)
|
||||
|
||||
setUserChatsRead :: DB.Connection -> User -> IO ()
|
||||
setUserChatsRead db User {userId} = do
|
||||
updatedAt <- getCurrentTime
|
||||
DB.execute db "UPDATE contacts SET unread_chat = ?, updated_at = ? WHERE user_id = ? AND unread_chat = ?" (False, updatedAt, userId, True)
|
||||
DB.execute db "UPDATE groups SET unread_chat = ?, updated_at = ? WHERE user_id = ? AND unread_chat = ?" (False, updatedAt, userId, True)
|
||||
DB.execute db "UPDATE note_folders SET unread_chat = ?, updated_at = ? WHERE user_id = ? AND unread_chat = ?" (False, updatedAt, userId, True)
|
||||
DB.execute db "UPDATE contacts SET unread_chat = ?, updated_at = ? WHERE user_id = ? AND unread_chat = ?" (BI False, updatedAt, userId, BI True)
|
||||
DB.execute db "UPDATE groups SET unread_chat = ?, updated_at = ? WHERE user_id = ? AND unread_chat = ?" (BI False, updatedAt, userId, BI True)
|
||||
DB.execute db "UPDATE note_folders SET unread_chat = ?, updated_at = ? WHERE user_id = ? AND unread_chat = ?" (BI False, updatedAt, userId, BI True)
|
||||
DB.execute db "UPDATE chat_items SET item_status = ?, updated_at = ? WHERE user_id = ? AND item_status = ?" (CISRcvRead, updatedAt, userId, CISRcvNew)
|
||||
|
||||
updateContactStatus :: DB.Connection -> User -> Contact -> ContactStatus -> IO Contact
|
||||
@@ -491,7 +501,7 @@ updateContactStatus db User {userId} ct@Contact {contactId} contactStatus = do
|
||||
updateGroupUnreadChat :: DB.Connection -> User -> GroupInfo -> Bool -> IO ()
|
||||
updateGroupUnreadChat db User {userId} GroupInfo {groupId} unreadChat = do
|
||||
updatedAt <- getCurrentTime
|
||||
DB.execute db "UPDATE groups SET unread_chat = ?, updated_at = ? WHERE user_id = ? AND group_id = ?" (unreadChat, updatedAt, userId, groupId)
|
||||
DB.execute db "UPDATE groups SET unread_chat = ?, updated_at = ? WHERE user_id = ? AND group_id = ?" (BI unreadChat, updatedAt, userId, groupId)
|
||||
|
||||
setConnectionVerified :: DB.Connection -> User -> Int64 -> Maybe Text -> IO ()
|
||||
setConnectionVerified db User {userId} connId code = do
|
||||
@@ -635,40 +645,42 @@ createOrUpdateContactRequest db vr user@User {userId, userContactId} userContact
|
||||
created_at, updated_at, xcontact_id, pq_support)
|
||||
VALUES (?,?,?,?,?,?,?,?,?,?,?)
|
||||
|]
|
||||
( (userContactLinkId, invId, minV, maxV, profileId, ldn, userId)
|
||||
( (userContactLinkId, Binary invId, minV, maxV, profileId, ldn, userId)
|
||||
:. (currentTs, currentTs, xContactId_, pqSup)
|
||||
)
|
||||
insertedRowId db
|
||||
getContact' :: XContactId -> IO (Maybe Contact)
|
||||
getContact' xContactId = do
|
||||
ct_ <- maybeFirstRow (toContact vr user []) $
|
||||
DB.query
|
||||
db
|
||||
[sql|
|
||||
SELECT
|
||||
-- Contact
|
||||
ct.contact_id, ct.contact_profile_id, ct.local_display_name, ct.via_group, cp.display_name, cp.full_name, cp.image, cp.contact_link, cp.local_alias, ct.contact_used, ct.contact_status, ct.enable_ntfs, ct.send_rcpts, ct.favorite,
|
||||
cp.preferences, ct.user_preferences, ct.created_at, ct.updated_at, ct.chat_ts, ct.contact_group_member_id, ct.contact_grp_inv_sent, ct.ui_themes, ct.chat_deleted, ct.custom_data,
|
||||
-- Connection
|
||||
c.connection_id, c.agent_conn_id, c.conn_level, c.via_contact, c.via_user_contact_link, c.via_group_link, c.group_link_id, c.custom_user_profile_id, c.conn_status, c.conn_type, c.contact_conn_initiated, c.local_alias,
|
||||
c.contact_id, c.group_member_id, c.snd_file_id, c.rcv_file_id, c.user_contact_link_id, c.created_at, c.security_code, c.security_code_verified_at, c.pq_support, c.pq_encryption, c.pq_snd_enabled, c.pq_rcv_enabled, c.auth_err_counter, c.quota_err_counter,
|
||||
c.conn_chat_version, c.peer_chat_min_version, c.peer_chat_max_version
|
||||
FROM contacts ct
|
||||
JOIN contact_profiles cp ON ct.contact_profile_id = cp.contact_profile_id
|
||||
LEFT JOIN connections c ON c.contact_id = ct.contact_id
|
||||
WHERE ct.user_id = ? AND ct.xcontact_id = ? AND ct.deleted = 0
|
||||
ORDER BY c.created_at DESC
|
||||
LIMIT 1
|
||||
|]
|
||||
(userId, xContactId)
|
||||
ct_ <-
|
||||
maybeFirstRow (toContact vr user []) $
|
||||
DB.query
|
||||
db
|
||||
[sql|
|
||||
SELECT
|
||||
-- Contact
|
||||
ct.contact_id, ct.contact_profile_id, ct.local_display_name, ct.via_group, cp.display_name, cp.full_name, cp.image, cp.contact_link, cp.local_alias, ct.contact_used, ct.contact_status, ct.enable_ntfs, ct.send_rcpts, ct.favorite,
|
||||
cp.preferences, ct.user_preferences, ct.created_at, ct.updated_at, ct.chat_ts, ct.contact_group_member_id, ct.contact_grp_inv_sent, ct.ui_themes, ct.chat_deleted, ct.custom_data,
|
||||
-- Connection
|
||||
c.connection_id, c.agent_conn_id, c.conn_level, c.via_contact, c.via_user_contact_link, c.via_group_link, c.group_link_id, c.custom_user_profile_id, c.conn_status, c.conn_type, c.contact_conn_initiated, c.local_alias,
|
||||
c.contact_id, c.group_member_id, c.snd_file_id, c.rcv_file_id, c.user_contact_link_id, c.created_at, c.security_code, c.security_code_verified_at, c.pq_support, c.pq_encryption, c.pq_snd_enabled, c.pq_rcv_enabled, c.auth_err_counter, c.quota_err_counter,
|
||||
c.conn_chat_version, c.peer_chat_min_version, c.peer_chat_max_version
|
||||
FROM contacts ct
|
||||
JOIN contact_profiles cp ON ct.contact_profile_id = cp.contact_profile_id
|
||||
LEFT JOIN connections c ON c.contact_id = ct.contact_id
|
||||
WHERE ct.user_id = ? AND ct.xcontact_id = ? AND ct.deleted = 0
|
||||
ORDER BY c.created_at DESC
|
||||
LIMIT 1
|
||||
|]
|
||||
(userId, xContactId)
|
||||
mapM (addDirectChatTags db) ct_
|
||||
getGroupInfo' :: XContactId -> IO (Maybe GroupInfo)
|
||||
getGroupInfo' xContactId = do
|
||||
g_ <- maybeFirstRow (toGroupInfo vr userContactId []) $
|
||||
DB.query
|
||||
db
|
||||
(groupInfoQuery <> " WHERE g.business_xcontact_id = ? AND g.user_id = ? AND mu.contact_id = ?")
|
||||
(xContactId, userId, userContactId)
|
||||
g_ <-
|
||||
maybeFirstRow (toGroupInfo vr userContactId []) $
|
||||
DB.query
|
||||
db
|
||||
(groupInfoQuery <> " WHERE g.business_xcontact_id = ? AND g.user_id = ? AND mu.contact_id = ?")
|
||||
(xContactId, userId, userContactId)
|
||||
mapM (addGroupChatTags db) g_
|
||||
getContactRequestByXContactId :: XContactId -> IO (Maybe UserContactRequest)
|
||||
getContactRequestByXContactId xContactId =
|
||||
@@ -702,7 +714,7 @@ createOrUpdateContactRequest db vr user@User {userId, userContactId} userContact
|
||||
SET agent_invitation_id = ?, pq_support = ?, peer_chat_min_version = ?, peer_chat_max_version = ?, updated_at = ?
|
||||
WHERE user_id = ? AND contact_request_id = ?
|
||||
|]
|
||||
(invId, pqSup, minV, maxV, currentTs, userId, cReqId)
|
||||
(Binary invId, pqSup, minV, maxV, currentTs, userId, cReqId)
|
||||
else withLocalDisplayName db userId displayName $ \ldn ->
|
||||
Right <$> do
|
||||
DB.execute
|
||||
@@ -712,7 +724,7 @@ createOrUpdateContactRequest db vr user@User {userId, userContactId} userContact
|
||||
SET agent_invitation_id = ?, pq_support = ?, peer_chat_min_version = ?, peer_chat_max_version = ?, local_display_name = ?, updated_at = ?
|
||||
WHERE user_id = ? AND contact_request_id = ?
|
||||
|]
|
||||
(invId, pqSup, minV, maxV, ldn, currentTs, userId, cReqId)
|
||||
(Binary invId, pqSup, minV, maxV, ldn, currentTs, userId, cReqId)
|
||||
safeDeleteLDN db user oldLdn
|
||||
where
|
||||
updateProfile currentTs =
|
||||
@@ -803,7 +815,7 @@ createAcceptedContact db user@User {userId, profile = LocalProfile {preferences}
|
||||
DB.execute
|
||||
db
|
||||
"INSERT INTO contacts (user_id, local_display_name, contact_profile_id, enable_ntfs, user_preferences, created_at, updated_at, chat_ts, xcontact_id, contact_used) VALUES (?,?,?,?,?,?,?,?,?,?)"
|
||||
(userId, localDisplayName, profileId, True, userPreferences, createdAt, createdAt, createdAt, xContactId, contactUsed)
|
||||
(userId, localDisplayName, profileId, BI True, userPreferences, createdAt, createdAt, createdAt, xContactId, BI contactUsed)
|
||||
contactId <- insertedRowId db
|
||||
DB.execute db "UPDATE contact_requests SET contact_id = ? WHERE user_id = ? AND local_display_name = ?" (contactId, userId, localDisplayName)
|
||||
conn <- createConnection_ db userId ConnContact (Just contactId) agentConnId ConnNew connChatVersion cReqChatVRange Nothing (Just userContactLinkId) customUserProfileId 0 createdAt subMode pqSup
|
||||
@@ -841,7 +853,7 @@ updateContactAccepted db User {userId} Contact {contactId} contactUsed =
|
||||
DB.execute
|
||||
db
|
||||
"UPDATE contacts SET contact_used = ? WHERE user_id = ? AND contact_id = ?"
|
||||
(contactUsed, userId, contactId)
|
||||
(BI contactUsed, userId, contactId)
|
||||
|
||||
getContactIdByName :: DB.Connection -> User -> ContactName -> ExceptT StoreError IO Int64
|
||||
getContactIdByName db User {userId} cName =
|
||||
@@ -882,12 +894,12 @@ getContact_ db vr user@User {userId} contactId deleted = do
|
||||
WHERE cc.user_id = ct.user_id AND cc.contact_id = ct.contact_id
|
||||
ORDER BY cc_conn_status_ord DESC, cc_created_at DESC
|
||||
LIMIT 1
|
||||
)
|
||||
) cc
|
||||
)
|
||||
OR c.connection_id IS NULL
|
||||
)
|
||||
|]
|
||||
(userId, contactId, deleted, ConnReady, ConnSndReady)
|
||||
(userId, contactId, BI deleted, ConnReady, ConnSndReady)
|
||||
|
||||
getUserByContactRequestId :: DB.Connection -> Int64 -> ExceptT StoreError IO User
|
||||
getUserByContactRequestId db contactRequestId =
|
||||
@@ -897,16 +909,16 @@ getUserByContactRequestId db contactRequestId =
|
||||
getPendingContactConnections :: DB.Connection -> User -> IO [PendingContactConnection]
|
||||
getPendingContactConnections db User {userId} = do
|
||||
map toPendingContactConnection
|
||||
<$> DB.queryNamed
|
||||
<$> DB.query
|
||||
db
|
||||
[sql|
|
||||
SELECT connection_id, agent_conn_id, conn_status, via_contact_uri_hash, via_user_contact_link, group_link_id, custom_user_profile_id, conn_req_inv, local_alias, created_at, updated_at
|
||||
FROM connections
|
||||
WHERE user_id = :user_id
|
||||
AND conn_type = :conn_type
|
||||
WHERE user_id = ?
|
||||
AND conn_type = ?
|
||||
AND contact_id IS NULL
|
||||
|]
|
||||
[":user_id" := userId, ":conn_type" := ConnContact]
|
||||
(userId, ConnContact)
|
||||
|
||||
getContactConnections :: DB.Connection -> VersionRangeChat -> UserId -> Contact -> IO [Connection]
|
||||
getContactConnections db vr userId Contact {contactId} =
|
||||
@@ -945,9 +957,13 @@ getConnectionById db vr User {userId} connId = ExceptT $ do
|
||||
|
||||
getConnectionsContacts :: DB.Connection -> [ConnId] -> IO [ContactRef]
|
||||
getConnectionsContacts db agentConnIds = do
|
||||
DB.execute_ db "DROP TABLE IF EXISTS temp.conn_ids"
|
||||
DB.execute_ db "CREATE TABLE temp.conn_ids (conn_id BLOB)"
|
||||
DB.executeMany db "INSERT INTO temp.conn_ids (conn_id) VALUES (?)" $ map Only agentConnIds
|
||||
DB.execute_ db "DROP TABLE IF EXISTS temp_conn_ids"
|
||||
#if defined(dbPostgres)
|
||||
DB.execute_ db "CREATE TABLE temp_conn_ids (conn_id BYTEA)"
|
||||
#else
|
||||
DB.execute_ db "CREATE TABLE temp_conn_ids (conn_id BLOB)"
|
||||
#endif
|
||||
DB.executeMany db "INSERT INTO temp_conn_ids (conn_id) VALUES (?)" $ map Only agentConnIds
|
||||
conns <-
|
||||
map toContactRef
|
||||
<$> DB.query
|
||||
@@ -956,12 +972,12 @@ getConnectionsContacts db agentConnIds = do
|
||||
SELECT ct.contact_id, c.connection_id, c.agent_conn_id, ct.local_display_name
|
||||
FROM contacts ct
|
||||
JOIN connections c ON c.contact_id = ct.contact_id
|
||||
WHERE c.agent_conn_id IN (SELECT conn_id FROM temp.conn_ids)
|
||||
WHERE c.agent_conn_id IN (SELECT conn_id FROM temp_conn_ids)
|
||||
AND c.conn_type = ?
|
||||
AND ct.deleted = 0
|
||||
|]
|
||||
(Only ConnContact)
|
||||
DB.execute_ db "DROP TABLE temp.conn_ids"
|
||||
DB.execute_ db "DROP TABLE temp_conn_ids"
|
||||
pure conns
|
||||
where
|
||||
toContactRef :: (ContactId, Int64, ConnId, ContactName) -> ContactRef
|
||||
@@ -986,7 +1002,7 @@ updateConnectionStatus_ db connId connStatus = do
|
||||
|
||||
updateContactSettings :: DB.Connection -> User -> Int64 -> ChatSettings -> IO ()
|
||||
updateContactSettings db User {userId} contactId ChatSettings {enableNtfs, sendRcpts, favorite} =
|
||||
DB.execute db "UPDATE contacts SET enable_ntfs = ?, send_rcpts = ?, favorite = ? WHERE user_id = ? AND contact_id = ?" (enableNtfs, sendRcpts, favorite, userId, contactId)
|
||||
DB.execute db "UPDATE contacts SET enable_ntfs = ?, send_rcpts = ?, favorite = ? WHERE user_id = ? AND contact_id = ?" (enableNtfs, BI <$> sendRcpts, BI favorite, userId, contactId)
|
||||
|
||||
setConnConnReqInv :: DB.Connection -> User -> Int64 -> ConnReqInvitation -> IO ()
|
||||
setConnConnReqInv db User {userId} connId connReq = do
|
||||
@@ -1025,7 +1041,7 @@ setContactUIThemes db User {userId} Contact {contactId} uiThemes = do
|
||||
setContactChatDeleted :: DB.Connection -> User -> Contact -> Bool -> IO ()
|
||||
setContactChatDeleted db User {userId} Contact {contactId} chatDeleted = do
|
||||
updatedAt <- getCurrentTime
|
||||
DB.execute db "UPDATE contacts SET chat_deleted = ?, updated_at = ? WHERE user_id = ? AND contact_id = ?" (chatDeleted, updatedAt, userId, contactId)
|
||||
DB.execute db "UPDATE contacts SET chat_deleted = ?, updated_at = ? WHERE user_id = ? AND contact_id = ?" (BI chatDeleted, updatedAt, userId, contactId)
|
||||
|
||||
updateDirectChatTags :: DB.Connection -> ContactId -> [ChatTagId] -> IO ()
|
||||
updateDirectChatTags db contactId tIds = do
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
{-# LANGUAGE CPP #-}
|
||||
{-# LANGUAGE DuplicateRecordFields #-}
|
||||
{-# LANGUAGE FlexibleContexts #-}
|
||||
{-# LANGUAGE GADTs #-}
|
||||
@@ -96,9 +97,6 @@ import Data.Time (addUTCTime)
|
||||
import Data.Time.Clock (UTCTime (..), getCurrentTime, nominalDay)
|
||||
import Data.Type.Equality
|
||||
import Data.Word (Word32)
|
||||
import Database.SQLite.Simple (Only (..), (:.) (..))
|
||||
import Database.SQLite.Simple.QQ (sql)
|
||||
import Database.SQLite.Simple.ToField (ToField)
|
||||
import Simplex.Chat.Messages
|
||||
import Simplex.Chat.Messages.CIContent
|
||||
import Simplex.Chat.Protocol
|
||||
@@ -110,7 +108,8 @@ import Simplex.Chat.Types
|
||||
import Simplex.Chat.Util (week)
|
||||
import Simplex.Messaging.Agent.Protocol (AgentMsgId, ConnId, UserId)
|
||||
import Simplex.Messaging.Agent.Store.AgentStore (firstRow, firstRow', maybeFirstRow)
|
||||
import qualified Simplex.Messaging.Agent.Store.SQLite.DB as DB
|
||||
import Simplex.Messaging.Agent.Store.DB (BoolInt (..))
|
||||
import qualified Simplex.Messaging.Agent.Store.DB as DB
|
||||
import qualified Simplex.Messaging.Crypto as C
|
||||
import Simplex.Messaging.Crypto.File (CryptoFile (..), CryptoFileArgs (..))
|
||||
import qualified Simplex.Messaging.Crypto.File as CF
|
||||
@@ -118,6 +117,15 @@ import Simplex.Messaging.Crypto.Ratchet as CR
|
||||
import Simplex.Messaging.Protocol (SubscriptionMode (..))
|
||||
import Simplex.Messaging.Version
|
||||
import System.FilePath (takeFileName)
|
||||
#if defined(dbPostgres)
|
||||
import Database.PostgreSQL.Simple (Only (..), (:.) (..))
|
||||
import Database.PostgreSQL.Simple.SqlQQ (sql)
|
||||
import Database.PostgreSQL.Simple.ToField (ToField)
|
||||
#else
|
||||
import Database.SQLite.Simple (Only (..), (:.) (..))
|
||||
import Database.SQLite.Simple.QQ (sql)
|
||||
import Database.SQLite.Simple.ToField (ToField)
|
||||
#endif
|
||||
|
||||
getLiveSndFileTransfers :: DB.Connection -> User -> IO [SndFileTransfer]
|
||||
getLiveSndFileTransfers db User {userId} = do
|
||||
@@ -283,7 +291,7 @@ createSndFTDescrXFTP db User {userId} m Connection {connId} FileTransferMeta {fi
|
||||
DB.execute
|
||||
db
|
||||
"INSERT INTO xftp_file_descriptions (user_id, file_descr_text, file_descr_part_no, file_descr_complete, created_at, updated_at) VALUES (?,?,?,?,?,?)"
|
||||
(userId, fileDescrText, fileDescrPartNo, fileDescrComplete, currentTs, currentTs)
|
||||
(userId, fileDescrText, fileDescrPartNo, BI fileDescrComplete, currentTs, currentTs)
|
||||
fileDescrId <- insertedRowId db
|
||||
DB.execute
|
||||
db
|
||||
@@ -308,7 +316,7 @@ updateSndFTDescrXFTP db user@User {userId} sft@SndFileTransfer {fileId, fileDesc
|
||||
SET file_descr_text = ?, file_descr_part_no = ?, file_descr_complete = ?, updated_at = ?
|
||||
WHERE user_id = ? AND file_descr_id = ?
|
||||
|]
|
||||
(rfdText, 1 :: Int, True, currentTs, userId, fileDescrId)
|
||||
(rfdText, 1 :: Int, BI True, currentTs, userId, fileDescrId)
|
||||
updateCIFileStatus db user fileId $ CIFSSndTransfer 1 1
|
||||
updateSndFileStatus db sft FSConnected
|
||||
|
||||
@@ -574,7 +582,7 @@ createRcvFD_ db userId currentTs FileDescr {fileDescrText, fileDescrPartNo, file
|
||||
DB.execute
|
||||
db
|
||||
"INSERT INTO xftp_file_descriptions (user_id, file_descr_text, file_descr_part_no, file_descr_complete, created_at, updated_at) VALUES (?,?,?,?,?,?)"
|
||||
(userId, fileDescrText, fileDescrPartNo, fileDescrComplete, currentTs, currentTs)
|
||||
(userId, fileDescrText, fileDescrPartNo, BI fileDescrComplete, currentTs, currentTs)
|
||||
insertedRowId db
|
||||
pure RcvFileDescr {fileDescrId, fileDescrPartNo, fileDescrText, fileDescrComplete}
|
||||
|
||||
@@ -607,7 +615,7 @@ appendRcvFD db userId fileId fd@FileDescr {fileDescrText, fileDescrPartNo, fileD
|
||||
SET file_descr_text = ?, file_descr_part_no = ?, file_descr_complete = ?
|
||||
WHERE file_descr_id = ?
|
||||
|]
|
||||
(fileDescrText', fileDescrPartNo, fileDescrComplete, fileDescrId)
|
||||
(fileDescrText', fileDescrPartNo, BI fileDescrComplete, fileDescrId)
|
||||
pure RcvFileDescr {fileDescrId, fileDescrText = fileDescrText', fileDescrPartNo, fileDescrComplete}
|
||||
|
||||
getRcvFileDescrByRcvFileId :: DB.Connection -> FileTransferId -> ExceptT StoreError IO RcvFileDescr
|
||||
@@ -650,8 +658,8 @@ getRcvFileDescrBySndFileId_ db fileId =
|
||||
|]
|
||||
(Only fileId)
|
||||
|
||||
toRcvFileDescr :: (Int64, Text, Int, Bool) -> RcvFileDescr
|
||||
toRcvFileDescr (fileDescrId, fileDescrText, fileDescrPartNo, fileDescrComplete) =
|
||||
toRcvFileDescr :: (Int64, Text, Int, BoolInt) -> RcvFileDescr
|
||||
toRcvFileDescr (fileDescrId, fileDescrText, fileDescrPartNo, BI fileDescrComplete) =
|
||||
RcvFileDescr {fileDescrId, fileDescrText, fileDescrPartNo, fileDescrComplete}
|
||||
|
||||
updateRcvFileAgentId :: DB.Connection -> FileTransferId -> Maybe AgentRcvFileId -> IO ()
|
||||
@@ -682,8 +690,8 @@ getRcvFileTransfer_ db userId fileId = do
|
||||
FROM rcv_files r
|
||||
JOIN files f USING (file_id)
|
||||
LEFT JOIN connections c ON r.file_id = c.rcv_file_id
|
||||
LEFT JOIN contacts cs USING (contact_id)
|
||||
LEFT JOIN group_members m USING (group_member_id)
|
||||
LEFT JOIN contacts cs ON cs.contact_id = f.contact_id
|
||||
LEFT JOIN group_members m ON m.group_member_id = r.group_member_id
|
||||
WHERE f.user_id = ? AND f.file_id = ?
|
||||
|]
|
||||
(userId, fileId)
|
||||
@@ -692,9 +700,9 @@ getRcvFileTransfer_ db userId fileId = do
|
||||
where
|
||||
rcvFileTransfer ::
|
||||
Maybe RcvFileDescr ->
|
||||
(FileStatus, Maybe ConnReqInvitation, Maybe Int64, String, Integer, Integer, Maybe Bool) :. (Maybe ContactName, Maybe ContactName, Maybe FilePath, Maybe C.SbKey, Maybe C.CbNonce, Maybe InlineFileMode, Maybe InlineFileMode, Maybe AgentRcvFileId, Bool, Bool) :. (Maybe Int64, Maybe AgentConnId) ->
|
||||
(FileStatus, Maybe ConnReqInvitation, Maybe Int64, String, Integer, Integer, Maybe BoolInt) :. (Maybe ContactName, Maybe ContactName, Maybe FilePath, Maybe C.SbKey, Maybe C.CbNonce, Maybe InlineFileMode, Maybe InlineFileMode, Maybe AgentRcvFileId, BoolInt, BoolInt) :. (Maybe Int64, Maybe AgentConnId) ->
|
||||
ExceptT StoreError IO RcvFileTransfer
|
||||
rcvFileTransfer rfd_ ((fileStatus', fileConnReq, grpMemberId, fileName, fileSize, chunkSize, cancelled_) :. (contactName_, memberName_, filePath_, fileKey, fileNonce, fileInline, rcvFileInline, agentRcvFileId, agentRcvFileDeleted, userApprovedRelays) :. (connId_, agentConnId_)) =
|
||||
rcvFileTransfer rfd_ ((fileStatus', fileConnReq, grpMemberId, fileName, fileSize, chunkSize, cancelled_) :. (contactName_, memberName_, filePath_, fileKey, fileNonce, fileInline, rcvFileInline, agentRcvFileId, BI agentRcvFileDeleted, BI userApprovedRelays) :. (connId_, agentConnId_)) =
|
||||
case contactName_ <|> memberName_ <|> standaloneName_ of
|
||||
Nothing -> throwError $ SERcvFileInvalid fileId
|
||||
Just name ->
|
||||
@@ -717,7 +725,7 @@ getRcvFileTransfer_ db userId fileId = do
|
||||
rfi_ = case (filePath_, connId_, agentConnId_) of
|
||||
(Just filePath, connId, agentConnId) -> pure $ Just RcvFileInfo {filePath, connId, agentConnId}
|
||||
_ -> pure Nothing
|
||||
cancelled = fromMaybe False cancelled_
|
||||
cancelled = maybe False unBI cancelled_
|
||||
|
||||
acceptRcvFileTransfer :: DB.Connection -> VersionRangeChat -> User -> Int64 -> (CommandId, ConnId) -> ConnStatus -> FilePath -> SubscriptionMode -> ExceptT StoreError IO AChatItem
|
||||
acceptRcvFileTransfer db vr user@User {userId} fileId (cmdId, acId) connStatus filePath subMode = ExceptT $ do
|
||||
@@ -726,7 +734,7 @@ acceptRcvFileTransfer db vr user@User {userId} fileId (cmdId, acId) connStatus f
|
||||
DB.execute
|
||||
db
|
||||
"INSERT INTO connections (agent_conn_id, conn_status, conn_type, rcv_file_id, user_id, created_at, updated_at, to_subscribe) VALUES (?,?,?,?,?,?,?,?)"
|
||||
(acId, connStatus, ConnRcvFile, fileId, userId, currentTs, currentTs, subMode == SMOnlyCreate)
|
||||
(acId, connStatus, ConnRcvFile, fileId, userId, currentTs, currentTs, BI (subMode == SMOnlyCreate))
|
||||
connId <- insertedRowId db
|
||||
setCommandConnId db user cmdId connId
|
||||
runExceptT $ getChatItemByFileId db vr user fileId
|
||||
@@ -763,7 +771,7 @@ acceptRcvFT_ db User {userId} fileId filePath userApprovedRelays rcvFileInline c
|
||||
DB.execute
|
||||
db
|
||||
"UPDATE rcv_files SET user_approved_relays = ?, rcv_file_inline = ?, file_status = ?, updated_at = ? WHERE file_id = ?"
|
||||
(userApprovedRelays, rcvFileInline, FSAccepted, currentTs, fileId)
|
||||
(BI userApprovedRelays, rcvFileInline, FSAccepted, currentTs, fileId)
|
||||
|
||||
setRcvFileToReceive :: DB.Connection -> FileTransferId -> Bool -> Maybe CryptoFileArgs -> IO ()
|
||||
setRcvFileToReceive db fileId userApprovedRelays cfArgs_ = do
|
||||
@@ -775,7 +783,7 @@ setRcvFileToReceive db fileId userApprovedRelays cfArgs_ = do
|
||||
SET to_receive = 1, user_approved_relays = ?, updated_at = ?
|
||||
WHERE file_id = ?
|
||||
|]
|
||||
(userApprovedRelays, currentTs, fileId)
|
||||
(BI userApprovedRelays, currentTs, fileId)
|
||||
forM_ cfArgs_ $ \cfArgs -> setFileCryptoArgs_ db fileId cfArgs currentTs
|
||||
|
||||
setFileCryptoArgs :: DB.Connection -> FileTransferId -> CryptoFileArgs -> IO ()
|
||||
@@ -928,8 +936,8 @@ getSndFileTransfers_ db userId fileId =
|
||||
FROM snd_files s
|
||||
JOIN files f USING (file_id)
|
||||
JOIN connections c USING (connection_id)
|
||||
LEFT JOIN contacts cs USING (contact_id)
|
||||
LEFT JOIN group_members m USING (group_member_id)
|
||||
LEFT JOIN contacts cs ON cs.contact_id = f.contact_id
|
||||
LEFT JOIN group_members m ON m.group_member_id = s.group_member_id
|
||||
WHERE f.user_id = ? AND f.file_id = ?
|
||||
|]
|
||||
(userId, fileId)
|
||||
@@ -955,11 +963,11 @@ getFileTransferMeta_ db userId fileId =
|
||||
|]
|
||||
(userId, fileId)
|
||||
where
|
||||
fileTransferMeta :: (String, Integer, Integer, FilePath, Maybe C.SbKey, Maybe C.CbNonce, Maybe InlineFileMode, Maybe AgentSndFileId, Bool, Maybe Text, Maybe Bool, Maybe FileTransferId) -> FileTransferMeta
|
||||
fileTransferMeta (fileName, fileSize, chunkSize, filePath, fileKey, fileNonce, fileInline, aSndFileId_, agentSndFileDeleted, privateSndFileDescr, cancelled_, xftpRedirectFor) =
|
||||
fileTransferMeta :: (String, Integer, Integer, FilePath, Maybe C.SbKey, Maybe C.CbNonce, Maybe InlineFileMode, Maybe AgentSndFileId, BoolInt, Maybe Text, Maybe BoolInt, Maybe FileTransferId) -> FileTransferMeta
|
||||
fileTransferMeta (fileName, fileSize, chunkSize, filePath, fileKey, fileNonce, fileInline, aSndFileId_, BI agentSndFileDeleted, privateSndFileDescr, cancelled_, xftpRedirectFor) =
|
||||
let cryptoArgs = CFArgs <$> fileKey <*> fileNonce
|
||||
xftpSndFile = (\fId -> XFTPSndFile {agentSndFileId = fId, privateSndFileDescr, agentSndFileDeleted, cryptoArgs}) <$> aSndFileId_
|
||||
in FileTransferMeta {fileId, xftpSndFile, xftpRedirectFor, fileName, fileSize, chunkSize, filePath, fileInline, cancelled = fromMaybe False cancelled_}
|
||||
in FileTransferMeta {fileId, xftpSndFile, xftpRedirectFor, fileName, fileSize, chunkSize, filePath, fileInline, cancelled = maybe False unBI cancelled_}
|
||||
|
||||
lookupFileTransferRedirectMeta :: DB.Connection -> User -> Int64 -> IO [FileTransferMeta]
|
||||
lookupFileTransferRedirectMeta db User {userId} fileId = do
|
||||
|
||||
+172
-170
@@ -1,3 +1,4 @@
|
||||
{-# LANGUAGE CPP #-}
|
||||
{-# LANGUAGE DataKinds #-}
|
||||
{-# LANGUAGE DuplicateRecordFields #-}
|
||||
{-# LANGUAGE LambdaCase #-}
|
||||
@@ -141,8 +142,6 @@ import Data.Maybe (catMaybes, fromMaybe, isJust, isNothing)
|
||||
import Data.Ord (Down (..))
|
||||
import Data.Text (Text)
|
||||
import Data.Time.Clock (UTCTime (..), getCurrentTime)
|
||||
import Database.SQLite.Simple (NamedParam (..), Only (..), Query (..), (:.) (..))
|
||||
import Database.SQLite.Simple.QQ (sql)
|
||||
import Simplex.Chat.Messages
|
||||
import Simplex.Chat.Protocol (groupForwardVersion)
|
||||
import Simplex.Chat.Store.Direct
|
||||
@@ -152,16 +151,24 @@ import Simplex.Chat.Types.Preferences
|
||||
import Simplex.Chat.Types.Shared
|
||||
import Simplex.Chat.Types.UITheme
|
||||
import Simplex.Messaging.Agent.Protocol (ConnId, UserId)
|
||||
import Simplex.Messaging.Agent.Store.AgentStore (firstRow, maybeFirstRow)
|
||||
import qualified Simplex.Messaging.Agent.Store.SQLite.DB as DB
|
||||
import Simplex.Messaging.Agent.Store.AgentStore (firstRow, fromOnlyBI, maybeFirstRow)
|
||||
import Simplex.Messaging.Agent.Store.DB (Binary (..), BoolInt (..))
|
||||
import qualified Simplex.Messaging.Agent.Store.DB as DB
|
||||
import qualified Simplex.Messaging.Crypto as C
|
||||
import Simplex.Messaging.Crypto.Ratchet (pattern PQEncOff, pattern PQSupportOff)
|
||||
import Simplex.Messaging.Protocol (SubscriptionMode (..))
|
||||
import Simplex.Messaging.Util (eitherToMaybe, ($>>=), (<$$>))
|
||||
import Simplex.Messaging.Version
|
||||
import UnliftIO.STM
|
||||
#if defined(dbPostgres)
|
||||
import Database.PostgreSQL.Simple (Only (..), Query, (:.) (..))
|
||||
import Database.PostgreSQL.Simple.SqlQQ (sql)
|
||||
#else
|
||||
import Database.SQLite.Simple (Only (..), Query, (:.) (..))
|
||||
import Database.SQLite.Simple.QQ (sql)
|
||||
#endif
|
||||
|
||||
type MaybeGroupMemberRow = ((Maybe Int64, Maybe Int64, Maybe MemberId, Maybe VersionChat, Maybe VersionChat, Maybe GroupMemberRole, Maybe GroupMemberCategory, Maybe GroupMemberStatus, Maybe Bool, Maybe MemberRestrictionStatus) :. (Maybe Int64, Maybe GroupMemberId, Maybe ContactName, Maybe ContactId, Maybe ProfileId, Maybe ProfileId, Maybe ContactName, Maybe Text, Maybe ImageData, Maybe ConnReqContact, Maybe LocalAlias, Maybe Preferences))
|
||||
type MaybeGroupMemberRow = ((Maybe Int64, Maybe Int64, Maybe MemberId, Maybe VersionChat, Maybe VersionChat, Maybe GroupMemberRole, Maybe GroupMemberCategory, Maybe GroupMemberStatus, Maybe BoolInt, Maybe MemberRestrictionStatus) :. (Maybe Int64, Maybe GroupMemberId, Maybe ContactName, Maybe ContactId, Maybe ProfileId, Maybe ProfileId, Maybe ContactName, Maybe Text, Maybe ImageData, Maybe ConnReqContact, Maybe LocalAlias, Maybe Preferences))
|
||||
|
||||
toMaybeGroupMember :: Int64 -> MaybeGroupMemberRow -> Maybe GroupMember
|
||||
toMaybeGroupMember userContactId ((Just groupMemberId, Just groupId, Just memberId, Just minVer, Just maxVer, Just memberRole, Just memberCategory, Just memberStatus, Just showMessages, memberBlocked) :. (invitedById, invitedByGroupMemberId, Just localDisplayName, memberContactId, Just memberContactProfileId, Just profileId, Just displayName, Just fullName, image, contactLink, Just localAlias, contactPreferences)) =
|
||||
@@ -175,7 +182,7 @@ createGroupLink db User {userId} groupInfo@GroupInfo {groupId, localDisplayName}
|
||||
DB.execute
|
||||
db
|
||||
"INSERT INTO user_contact_links (user_id, group_id, group_link_id, local_display_name, conn_req_contact, group_link_member_role, auto_accept, created_at, updated_at) VALUES (?,?,?,?,?,?,?,?,?)"
|
||||
(userId, groupId, groupLinkId, "group_link_" <> localDisplayName, cReq, memberRole, True, currentTs, currentTs)
|
||||
(userId, groupId, groupLinkId, "group_link_" <> localDisplayName, cReq, memberRole, BI True, currentTs, currentTs)
|
||||
userContactLinkId <- insertedRowId db
|
||||
void $ createConnection_ db userId ConnUserContact (Just userContactLinkId) agentConnId ConnNew initialChatVersion chatInitialVRange Nothing Nothing Nothing 0 currentTs subMode PQSupportOff
|
||||
|
||||
@@ -254,41 +261,42 @@ setGroupLinkMemberRole db User {userId} userContactLinkId memberRole =
|
||||
|
||||
getGroupAndMember :: DB.Connection -> User -> Int64 -> VersionRangeChat -> ExceptT StoreError IO (GroupInfo, GroupMember)
|
||||
getGroupAndMember db User {userId, userContactId} groupMemberId vr = do
|
||||
gm <- ExceptT . firstRow toGroupAndMember (SEInternalError "referenced group member not found") $
|
||||
DB.query
|
||||
db
|
||||
[sql|
|
||||
SELECT
|
||||
-- GroupInfo
|
||||
g.group_id, g.local_display_name, gp.display_name, gp.full_name, gp.description, gp.image,
|
||||
g.host_conn_custom_user_profile_id, g.enable_ntfs, g.send_rcpts, g.favorite, gp.preferences,
|
||||
g.created_at, g.updated_at, g.chat_ts, g.user_member_profile_sent_at, g.business_chat, g.business_member_id, g.customer_member_id, g.ui_themes, g.custom_data,
|
||||
-- GroupInfo {membership}
|
||||
mu.group_member_id, mu.group_id, mu.member_id, mu.peer_chat_min_version, mu.peer_chat_max_version, mu.member_role, mu.member_category,
|
||||
mu.member_status, mu.show_messages, mu.member_restriction, mu.invited_by, mu.invited_by_group_member_id, mu.local_display_name, mu.contact_id, mu.contact_profile_id, pu.contact_profile_id,
|
||||
-- GroupInfo {membership = GroupMember {memberProfile}}
|
||||
pu.display_name, pu.full_name, pu.image, pu.contact_link, pu.local_alias, pu.preferences,
|
||||
-- from GroupMember
|
||||
m.group_member_id, m.group_id, m.member_id, m.peer_chat_min_version, m.peer_chat_max_version, m.member_role, m.member_category, m.member_status, m.show_messages, m.member_restriction,
|
||||
m.invited_by, m.invited_by_group_member_id, m.local_display_name, m.contact_id, m.contact_profile_id, p.contact_profile_id, p.display_name, p.full_name, p.image, p.contact_link, p.local_alias, p.preferences,
|
||||
c.connection_id, c.agent_conn_id, c.conn_level, c.via_contact, c.via_user_contact_link, c.via_group_link, c.group_link_id, c.custom_user_profile_id,
|
||||
c.conn_status, c.conn_type, c.contact_conn_initiated, c.local_alias, c.contact_id, c.group_member_id, c.snd_file_id, c.rcv_file_id, c.user_contact_link_id,
|
||||
c.created_at, c.security_code, c.security_code_verified_at, c.pq_support, c.pq_encryption, c.pq_snd_enabled, c.pq_rcv_enabled, c.auth_err_counter, c.quota_err_counter,
|
||||
c.conn_chat_version, c.peer_chat_min_version, c.peer_chat_max_version
|
||||
FROM group_members m
|
||||
JOIN contact_profiles p ON p.contact_profile_id = COALESCE(m.member_profile_id, m.contact_profile_id)
|
||||
JOIN groups g ON g.group_id = m.group_id
|
||||
JOIN group_profiles gp USING (group_profile_id)
|
||||
JOIN group_members mu ON g.group_id = mu.group_id
|
||||
JOIN contact_profiles pu ON pu.contact_profile_id = COALESCE(mu.member_profile_id, mu.contact_profile_id)
|
||||
LEFT JOIN connections c ON c.connection_id = (
|
||||
SELECT max(cc.connection_id)
|
||||
FROM connections cc
|
||||
where cc.user_id = ? AND cc.group_member_id = m.group_member_id
|
||||
)
|
||||
WHERE m.group_member_id = ? AND g.user_id = ? AND mu.contact_id = ?
|
||||
|]
|
||||
(userId, groupMemberId, userId, userContactId)
|
||||
gm <-
|
||||
ExceptT . firstRow toGroupAndMember (SEInternalError "referenced group member not found") $
|
||||
DB.query
|
||||
db
|
||||
[sql|
|
||||
SELECT
|
||||
-- GroupInfo
|
||||
g.group_id, g.local_display_name, gp.display_name, gp.full_name, gp.description, gp.image,
|
||||
g.host_conn_custom_user_profile_id, g.enable_ntfs, g.send_rcpts, g.favorite, gp.preferences,
|
||||
g.created_at, g.updated_at, g.chat_ts, g.user_member_profile_sent_at, g.business_chat, g.business_member_id, g.customer_member_id, g.ui_themes, g.custom_data,
|
||||
-- GroupInfo {membership}
|
||||
mu.group_member_id, mu.group_id, mu.member_id, mu.peer_chat_min_version, mu.peer_chat_max_version, mu.member_role, mu.member_category,
|
||||
mu.member_status, mu.show_messages, mu.member_restriction, mu.invited_by, mu.invited_by_group_member_id, mu.local_display_name, mu.contact_id, mu.contact_profile_id, pu.contact_profile_id,
|
||||
-- GroupInfo {membership = GroupMember {memberProfile}}
|
||||
pu.display_name, pu.full_name, pu.image, pu.contact_link, pu.local_alias, pu.preferences,
|
||||
-- from GroupMember
|
||||
m.group_member_id, m.group_id, m.member_id, m.peer_chat_min_version, m.peer_chat_max_version, m.member_role, m.member_category, m.member_status, m.show_messages, m.member_restriction,
|
||||
m.invited_by, m.invited_by_group_member_id, m.local_display_name, m.contact_id, m.contact_profile_id, p.contact_profile_id, p.display_name, p.full_name, p.image, p.contact_link, p.local_alias, p.preferences,
|
||||
c.connection_id, c.agent_conn_id, c.conn_level, c.via_contact, c.via_user_contact_link, c.via_group_link, c.group_link_id, c.custom_user_profile_id,
|
||||
c.conn_status, c.conn_type, c.contact_conn_initiated, c.local_alias, c.contact_id, c.group_member_id, c.snd_file_id, c.rcv_file_id, c.user_contact_link_id,
|
||||
c.created_at, c.security_code, c.security_code_verified_at, c.pq_support, c.pq_encryption, c.pq_snd_enabled, c.pq_rcv_enabled, c.auth_err_counter, c.quota_err_counter,
|
||||
c.conn_chat_version, c.peer_chat_min_version, c.peer_chat_max_version
|
||||
FROM group_members m
|
||||
JOIN contact_profiles p ON p.contact_profile_id = COALESCE(m.member_profile_id, m.contact_profile_id)
|
||||
JOIN groups g ON g.group_id = m.group_id
|
||||
JOIN group_profiles gp USING (group_profile_id)
|
||||
JOIN group_members mu ON g.group_id = mu.group_id
|
||||
JOIN contact_profiles pu ON pu.contact_profile_id = COALESCE(mu.member_profile_id, mu.contact_profile_id)
|
||||
LEFT JOIN connections c ON c.connection_id = (
|
||||
SELECT max(cc.connection_id)
|
||||
FROM connections cc
|
||||
where cc.user_id = ? AND cc.group_member_id = m.group_member_id
|
||||
)
|
||||
WHERE m.group_member_id = ? AND g.user_id = ? AND mu.contact_id = ?
|
||||
|]
|
||||
(userId, groupMemberId, userId, userContactId)
|
||||
liftIO $ bitraverse (addGroupChatTags db) pure gm
|
||||
where
|
||||
toGroupAndMember :: (GroupInfoRow :. GroupMemberRow :. MaybeConnectionRow) -> (GroupInfo, GroupMember)
|
||||
@@ -319,7 +327,7 @@ createNewGroup db vr gVar user@User {userId} groupProfile incognitoProfile = Exc
|
||||
created_at, updated_at, chat_ts, user_member_profile_sent_at)
|
||||
VALUES (?,?,?,?,?,?,?,?)
|
||||
|]
|
||||
(ldn, userId, profileId, True, currentTs, currentTs, currentTs, currentTs)
|
||||
(ldn, userId, profileId, BI True, currentTs, currentTs, currentTs, currentTs)
|
||||
insertedRowId db
|
||||
memberId <- liftIO $ encodedRandomBytes gVar 12
|
||||
membership <- createContactMemberInv_ db user groupId Nothing user (MemberIdRole (MemberId memberId) GROwner) GCUserMember GSMemCreator IBUser customUserProfileId currentTs vr
|
||||
@@ -387,7 +395,7 @@ createGroupInvitation db vr user@User {userId} contact@Contact {contactId, activ
|
||||
created_at, updated_at, chat_ts, user_member_profile_sent_at, business_chat, business_member_id, customer_member_id)
|
||||
VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?)
|
||||
|]
|
||||
((profileId, localDisplayName, connRequest, customUserProfileId, userId, True, currentTs, currentTs, currentTs, currentTs) :. businessChatInfoRow business)
|
||||
((profileId, localDisplayName, connRequest, customUserProfileId, userId, BI True, currentTs, currentTs, currentTs, currentTs) :. businessChatInfoRow business)
|
||||
insertedRowId db
|
||||
let hostVRange = adjustedMemberVRange vr peerChatVRange
|
||||
GroupMember {groupMemberId} <- createContactMemberInv_ db user groupId Nothing contact fromMember GCHostMember GSMemInvited IBUnknown Nothing currentTs hostVRange
|
||||
@@ -532,7 +540,7 @@ createGroupInvitedViaLink
|
||||
created_at, updated_at, chat_ts, user_member_profile_sent_at, business_chat, business_member_id, customer_member_id)
|
||||
VALUES (?,?,?,?,?,?,?,?,?,?,?,?)
|
||||
|]
|
||||
((profileId, localDisplayName, customUserProfileId, userId, True, currentTs, currentTs, currentTs, currentTs) :. businessChatInfoRow business)
|
||||
((profileId, localDisplayName, customUserProfileId, userId, BI True, currentTs, currentTs, currentTs, currentTs) :. businessChatInfoRow business)
|
||||
insertedRowId db
|
||||
insertHost_ currentTs groupId = do
|
||||
let fromMemberProfile = profileFromName fromMemberName
|
||||
@@ -632,24 +640,28 @@ getUserGroups db vr user@User {userId} = do
|
||||
|
||||
getUserGroupDetails :: DB.Connection -> VersionRangeChat -> User -> Maybe ContactId -> Maybe String -> IO [GroupInfo]
|
||||
getUserGroupDetails db vr User {userId, userContactId} _contactId_ search_ = do
|
||||
g_ <- map (toGroupInfo vr userContactId [])
|
||||
<$> DB.query
|
||||
db
|
||||
[sql|
|
||||
SELECT
|
||||
g.group_id, g.local_display_name, gp.display_name, gp.full_name, gp.description, gp.image,
|
||||
g.host_conn_custom_user_profile_id, g.enable_ntfs, g.send_rcpts, g.favorite, gp.preferences,
|
||||
g.created_at, g.updated_at, g.chat_ts, g.user_member_profile_sent_at, g.business_chat, g.business_member_id, g.customer_member_id, g.ui_themes, g.custom_data,
|
||||
mu.group_member_id, g.group_id, mu.member_id, mu.peer_chat_min_version, mu.peer_chat_max_version, mu.member_role, mu.member_category, mu.member_status, mu.show_messages, mu.member_restriction,
|
||||
mu.invited_by, mu.invited_by_group_member_id, mu.local_display_name, mu.contact_id, mu.contact_profile_id, pu.contact_profile_id, pu.display_name, pu.full_name, pu.image, pu.contact_link, pu.local_alias, pu.preferences
|
||||
FROM groups g
|
||||
JOIN group_profiles gp USING (group_profile_id)
|
||||
JOIN group_members mu USING (group_id)
|
||||
JOIN contact_profiles pu ON pu.contact_profile_id = COALESCE(mu.member_profile_id, mu.contact_profile_id)
|
||||
WHERE g.user_id = ? AND mu.contact_id = ?
|
||||
AND (gp.display_name LIKE '%' || ? || '%' OR gp.full_name LIKE '%' || ? || '%' OR gp.description LIKE '%' || ? || '%')
|
||||
|]
|
||||
(userId, userContactId, search, search, search)
|
||||
g_ <-
|
||||
map (toGroupInfo vr userContactId [])
|
||||
<$> DB.query
|
||||
db
|
||||
[sql|
|
||||
SELECT
|
||||
g.group_id, g.local_display_name, gp.display_name, gp.full_name, gp.description, gp.image,
|
||||
g.host_conn_custom_user_profile_id, g.enable_ntfs, g.send_rcpts, g.favorite, gp.preferences,
|
||||
g.created_at, g.updated_at, g.chat_ts, g.user_member_profile_sent_at, g.business_chat, g.business_member_id, g.customer_member_id, g.ui_themes, g.custom_data,
|
||||
mu.group_member_id, g.group_id, mu.member_id, mu.peer_chat_min_version, mu.peer_chat_max_version, mu.member_role, mu.member_category, mu.member_status, mu.show_messages, mu.member_restriction,
|
||||
mu.invited_by, mu.invited_by_group_member_id, mu.local_display_name, mu.contact_id, mu.contact_profile_id, pu.contact_profile_id, pu.display_name, pu.full_name, pu.image, pu.contact_link, pu.local_alias, pu.preferences
|
||||
FROM groups g
|
||||
JOIN group_profiles gp USING (group_profile_id)
|
||||
JOIN group_members mu USING (group_id)
|
||||
JOIN contact_profiles pu ON pu.contact_profile_id = COALESCE(mu.member_profile_id, mu.contact_profile_id)
|
||||
WHERE g.user_id = ? AND mu.contact_id = ?
|
||||
AND (LOWER(gp.display_name) LIKE '%' || LOWER(?) || '%'
|
||||
OR LOWER(gp.full_name) LIKE '%' || LOWER(?) || '%'
|
||||
OR LOWER(gp.description) LIKE '%' || LOWER(?) || '%'
|
||||
)
|
||||
|]
|
||||
(userId, userContactId, search, search, search)
|
||||
mapM (addGroupChatTags db) g_
|
||||
where
|
||||
search = fromMaybe "" search_
|
||||
@@ -958,7 +970,7 @@ createBusinessRequestGroup
|
||||
created_at, updated_at, chat_ts, user_member_profile_sent_at, business_chat, business_xcontact_id)
|
||||
VALUES (?,?,?,?,?,?,?,?,?,?)
|
||||
|]
|
||||
(profileId, localDisplayName, userId, True, currentTs, currentTs, currentTs, currentTs, BCCustomer, xContactId)
|
||||
(profileId, localDisplayName, userId, BI True, currentTs, currentTs, currentTs, currentTs, BCCustomer, xContactId)
|
||||
insertedRowId db
|
||||
memberId <- liftIO $ encodedRandomBytes gVar 12
|
||||
membership <- createContactMemberInv_ db user groupId Nothing user (MemberIdRole (MemberId memberId) GROwner) GCUserMember GSMemCreator IBUser Nothing currentTs vr
|
||||
@@ -1193,57 +1205,47 @@ createIntroductions db chatV members toMember = do
|
||||
updateIntroStatus :: DB.Connection -> Int64 -> GroupMemberIntroStatus -> IO ()
|
||||
updateIntroStatus db introId introStatus = do
|
||||
currentTs <- getCurrentTime
|
||||
DB.executeNamed
|
||||
DB.execute
|
||||
db
|
||||
[sql|
|
||||
UPDATE group_member_intros
|
||||
SET intro_status = :intro_status, updated_at = :updated_at
|
||||
WHERE group_member_intro_id = :intro_id
|
||||
SET intro_status = ?, updated_at = ?
|
||||
WHERE group_member_intro_id = ?
|
||||
|]
|
||||
[":intro_status" := introStatus, ":updated_at" := currentTs, ":intro_id" := introId]
|
||||
(introStatus, currentTs, introId)
|
||||
|
||||
saveIntroInvitation :: DB.Connection -> GroupMember -> GroupMember -> IntroInvitation -> ExceptT StoreError IO GroupMemberIntro
|
||||
saveIntroInvitation db reMember toMember introInv@IntroInvitation {groupConnReq} = do
|
||||
intro <- getIntroduction db reMember toMember
|
||||
liftIO $ do
|
||||
currentTs <- getCurrentTime
|
||||
DB.executeNamed
|
||||
DB.execute
|
||||
db
|
||||
[sql|
|
||||
UPDATE group_member_intros
|
||||
SET intro_status = :intro_status,
|
||||
group_queue_info = :group_queue_info,
|
||||
direct_queue_info = :direct_queue_info,
|
||||
updated_at = :updated_at
|
||||
WHERE group_member_intro_id = :intro_id
|
||||
SET intro_status = ?,
|
||||
group_queue_info = ?,
|
||||
direct_queue_info = ?,
|
||||
updated_at = ?
|
||||
WHERE group_member_intro_id = ?
|
||||
|]
|
||||
[ ":intro_status" := GMIntroInvReceived,
|
||||
":group_queue_info" := groupConnReq,
|
||||
":direct_queue_info" := directConnReq introInv,
|
||||
":updated_at" := currentTs,
|
||||
":intro_id" := introId intro
|
||||
]
|
||||
(GMIntroInvReceived, groupConnReq, directConnReq introInv, currentTs, introId intro)
|
||||
pure intro {introInvitation = Just introInv, introStatus = GMIntroInvReceived}
|
||||
|
||||
saveMemberInvitation :: DB.Connection -> GroupMember -> IntroInvitation -> IO ()
|
||||
saveMemberInvitation db GroupMember {groupMemberId} IntroInvitation {groupConnReq, directConnReq} = do
|
||||
currentTs <- getCurrentTime
|
||||
DB.executeNamed
|
||||
DB.execute
|
||||
db
|
||||
[sql|
|
||||
UPDATE group_members
|
||||
SET member_status = :member_status,
|
||||
group_queue_info = :group_queue_info,
|
||||
direct_queue_info = :direct_queue_info,
|
||||
updated_at = :updated_at
|
||||
WHERE group_member_id = :group_member_id
|
||||
SET member_status = ?,
|
||||
group_queue_info = ?,
|
||||
direct_queue_info = ?,
|
||||
updated_at = ?
|
||||
WHERE group_member_id = ?
|
||||
|]
|
||||
[ ":member_status" := GSMemIntroInvited,
|
||||
":group_queue_info" := groupConnReq,
|
||||
":direct_queue_info" := directConnReq,
|
||||
":updated_at" := currentTs,
|
||||
":group_member_id" := groupMemberId
|
||||
]
|
||||
(GSMemIntroInvited, groupConnReq, directConnReq, currentTs, groupMemberId)
|
||||
|
||||
getIntroduction :: DB.Connection -> GroupMember -> GroupMember -> ExceptT StoreError IO GroupMemberIntro
|
||||
getIntroduction db reMember toMember = ExceptT $ do
|
||||
@@ -1364,14 +1366,14 @@ createIntroToMemberContact db user@User {userId} GroupMember {memberContactId =
|
||||
pure contactId
|
||||
updateMember_ :: Int64 -> UTCTime -> IO ()
|
||||
updateMember_ contactId ts =
|
||||
DB.executeNamed
|
||||
DB.execute
|
||||
db
|
||||
[sql|
|
||||
UPDATE group_members
|
||||
SET contact_id = :contact_id, updated_at = :updated_at
|
||||
WHERE group_member_id = :group_member_id
|
||||
SET contact_id = ?, updated_at = ?
|
||||
WHERE group_member_id = ?
|
||||
|]
|
||||
[":contact_id" := contactId, ":updated_at" := ts, ":group_member_id" := groupMemberId]
|
||||
(contactId, ts, groupMemberId)
|
||||
|
||||
createMemberConnection_ :: DB.Connection -> UserId -> Int64 -> ConnId -> VersionChat -> VersionRangeChat -> Maybe Int64 -> Int -> UTCTime -> SubscriptionMode -> IO Connection
|
||||
createMemberConnection_ db userId groupMemberId agentConnId chatV peerChatVRange viaContact connLevel currentTs subMode =
|
||||
@@ -1379,42 +1381,43 @@ createMemberConnection_ db userId groupMemberId agentConnId chatV peerChatVRange
|
||||
|
||||
getViaGroupMember :: DB.Connection -> VersionRangeChat -> User -> Contact -> IO (Maybe (GroupInfo, GroupMember))
|
||||
getViaGroupMember db vr User {userId, userContactId} Contact {contactId} = do
|
||||
gm_ <- maybeFirstRow toGroupAndMember $
|
||||
DB.query
|
||||
db
|
||||
[sql|
|
||||
SELECT
|
||||
-- GroupInfo
|
||||
g.group_id, g.local_display_name, gp.display_name, gp.full_name, gp.description, gp.image,
|
||||
g.host_conn_custom_user_profile_id, g.enable_ntfs, g.send_rcpts, g.favorite, gp.preferences,
|
||||
g.created_at, g.updated_at, g.chat_ts, g.user_member_profile_sent_at, g.business_chat, g.business_member_id, g.customer_member_id, g.ui_themes, g.custom_data,
|
||||
-- GroupInfo {membership}
|
||||
mu.group_member_id, mu.group_id, mu.member_id, mu.peer_chat_min_version, mu.peer_chat_max_version, mu.member_role, mu.member_category,
|
||||
mu.member_status, mu.show_messages, mu.member_restriction, mu.invited_by, mu.invited_by_group_member_id, mu.local_display_name, mu.contact_id, mu.contact_profile_id, pu.contact_profile_id,
|
||||
-- GroupInfo {membership = GroupMember {memberProfile}}
|
||||
pu.display_name, pu.full_name, pu.image, pu.contact_link, pu.local_alias, pu.preferences,
|
||||
-- via GroupMember
|
||||
m.group_member_id, m.group_id, m.member_id, m.peer_chat_min_version, m.peer_chat_max_version, m.member_role, m.member_category, m.member_status, m.show_messages, m.member_restriction,
|
||||
m.invited_by, m.invited_by_group_member_id, m.local_display_name, m.contact_id, m.contact_profile_id, p.contact_profile_id, p.display_name, p.full_name, p.image, p.contact_link, p.local_alias, p.preferences,
|
||||
c.connection_id, c.agent_conn_id, c.conn_level, c.via_contact, c.via_user_contact_link, c.via_group_link, c.group_link_id, c.custom_user_profile_id,
|
||||
c.conn_status, c.conn_type, c.contact_conn_initiated, c.local_alias, c.contact_id, c.group_member_id, c.snd_file_id, c.rcv_file_id, c.user_contact_link_id,
|
||||
c.created_at, c.security_code, c.security_code_verified_at, c.pq_support, c.pq_encryption, c.pq_snd_enabled, c.pq_rcv_enabled, c.auth_err_counter, c.quota_err_counter,
|
||||
c.conn_chat_version, c.peer_chat_min_version, c.peer_chat_max_version
|
||||
FROM group_members m
|
||||
JOIN contacts ct ON ct.contact_id = m.contact_id
|
||||
JOIN contact_profiles p ON p.contact_profile_id = COALESCE(m.member_profile_id, m.contact_profile_id)
|
||||
JOIN groups g ON g.group_id = m.group_id AND g.group_id = ct.via_group
|
||||
JOIN group_profiles gp USING (group_profile_id)
|
||||
JOIN group_members mu ON g.group_id = mu.group_id
|
||||
JOIN contact_profiles pu ON pu.contact_profile_id = COALESCE(mu.member_profile_id, mu.contact_profile_id)
|
||||
LEFT JOIN connections c ON c.connection_id = (
|
||||
SELECT max(cc.connection_id)
|
||||
FROM connections cc
|
||||
where cc.user_id = ? AND cc.group_member_id = m.group_member_id
|
||||
)
|
||||
WHERE ct.user_id = ? AND ct.contact_id = ? AND mu.contact_id = ? AND ct.deleted = 0
|
||||
|]
|
||||
(userId, userId, contactId, userContactId)
|
||||
gm_ <-
|
||||
maybeFirstRow toGroupAndMember $
|
||||
DB.query
|
||||
db
|
||||
[sql|
|
||||
SELECT
|
||||
-- GroupInfo
|
||||
g.group_id, g.local_display_name, gp.display_name, gp.full_name, gp.description, gp.image,
|
||||
g.host_conn_custom_user_profile_id, g.enable_ntfs, g.send_rcpts, g.favorite, gp.preferences,
|
||||
g.created_at, g.updated_at, g.chat_ts, g.user_member_profile_sent_at, g.business_chat, g.business_member_id, g.customer_member_id, g.ui_themes, g.custom_data,
|
||||
-- GroupInfo {membership}
|
||||
mu.group_member_id, mu.group_id, mu.member_id, mu.peer_chat_min_version, mu.peer_chat_max_version, mu.member_role, mu.member_category,
|
||||
mu.member_status, mu.show_messages, mu.member_restriction, mu.invited_by, mu.invited_by_group_member_id, mu.local_display_name, mu.contact_id, mu.contact_profile_id, pu.contact_profile_id,
|
||||
-- GroupInfo {membership = GroupMember {memberProfile}}
|
||||
pu.display_name, pu.full_name, pu.image, pu.contact_link, pu.local_alias, pu.preferences,
|
||||
-- via GroupMember
|
||||
m.group_member_id, m.group_id, m.member_id, m.peer_chat_min_version, m.peer_chat_max_version, m.member_role, m.member_category, m.member_status, m.show_messages, m.member_restriction,
|
||||
m.invited_by, m.invited_by_group_member_id, m.local_display_name, m.contact_id, m.contact_profile_id, p.contact_profile_id, p.display_name, p.full_name, p.image, p.contact_link, p.local_alias, p.preferences,
|
||||
c.connection_id, c.agent_conn_id, c.conn_level, c.via_contact, c.via_user_contact_link, c.via_group_link, c.group_link_id, c.custom_user_profile_id,
|
||||
c.conn_status, c.conn_type, c.contact_conn_initiated, c.local_alias, c.contact_id, c.group_member_id, c.snd_file_id, c.rcv_file_id, c.user_contact_link_id,
|
||||
c.created_at, c.security_code, c.security_code_verified_at, c.pq_support, c.pq_encryption, c.pq_snd_enabled, c.pq_rcv_enabled, c.auth_err_counter, c.quota_err_counter,
|
||||
c.conn_chat_version, c.peer_chat_min_version, c.peer_chat_max_version
|
||||
FROM group_members m
|
||||
JOIN contacts ct ON ct.contact_id = m.contact_id
|
||||
JOIN contact_profiles p ON p.contact_profile_id = COALESCE(m.member_profile_id, m.contact_profile_id)
|
||||
JOIN groups g ON g.group_id = m.group_id AND g.group_id = ct.via_group
|
||||
JOIN group_profiles gp USING (group_profile_id)
|
||||
JOIN group_members mu ON g.group_id = mu.group_id
|
||||
JOIN contact_profiles pu ON pu.contact_profile_id = COALESCE(mu.member_profile_id, mu.contact_profile_id)
|
||||
LEFT JOIN connections c ON c.connection_id = (
|
||||
SELECT max(cc.connection_id)
|
||||
FROM connections cc
|
||||
where cc.user_id = ? AND cc.group_member_id = m.group_member_id
|
||||
)
|
||||
WHERE ct.user_id = ? AND ct.contact_id = ? AND mu.contact_id = ? AND ct.deleted = 0
|
||||
|]
|
||||
(userId, userId, contactId, userContactId)
|
||||
mapM (bitraverse (addGroupChatTags db) pure) gm_
|
||||
where
|
||||
toGroupAndMember :: (GroupInfoRow :. GroupMemberRow :. MaybeConnectionRow) -> (GroupInfo, GroupMember)
|
||||
@@ -1650,7 +1653,7 @@ createSentProbe db gVar userId to =
|
||||
DB.execute
|
||||
db
|
||||
"INSERT INTO sent_probes (contact_id, group_member_id, probe, user_id, created_at, updated_at) VALUES (?,?,?,?,?,?)"
|
||||
(ctId, gmId, probe, userId, currentTs, currentTs)
|
||||
(ctId, gmId, Binary probe, userId, currentTs, currentTs)
|
||||
(Probe probe,) <$> insertedRowId db
|
||||
|
||||
createSentProbeHash :: DB.Connection -> UserId -> Int64 -> ContactOrMember -> IO ()
|
||||
@@ -1676,13 +1679,13 @@ matchReceivedProbe db vr user@User {userId} from (Probe probe) = do
|
||||
LEFT JOIN groups g ON g.group_id = m.group_id
|
||||
WHERE r.user_id = ? AND r.probe_hash = ? AND r.probe IS NULL
|
||||
|]
|
||||
(userId, probeHash)
|
||||
(userId, Binary probeHash)
|
||||
currentTs <- getCurrentTime
|
||||
let (ctId, gmId) = contactOrMemberIds from
|
||||
DB.execute
|
||||
db
|
||||
"INSERT INTO received_probes (contact_id, group_member_id, probe, probe_hash, user_id, created_at, updated_at) VALUES (?,?,?,?,?,?,?)"
|
||||
(ctId, gmId, probe, probeHash, userId, currentTs, currentTs)
|
||||
(ctId, gmId, Binary probe, Binary probeHash, userId, currentTs, currentTs)
|
||||
let cgmIds' = filterFirstContactId cgmIds
|
||||
catMaybes <$> mapM (getContactOrMember_ db vr user) cgmIds'
|
||||
where
|
||||
@@ -1708,13 +1711,13 @@ matchReceivedProbeHash db vr user@User {userId} from (ProbeHash probeHash) = do
|
||||
LEFT JOIN groups g ON g.group_id = m.group_id
|
||||
WHERE r.user_id = ? AND r.probe_hash = ? AND r.probe IS NOT NULL
|
||||
|]
|
||||
(userId, probeHash)
|
||||
(userId, Binary probeHash)
|
||||
currentTs <- getCurrentTime
|
||||
let (ctId, gmId) = contactOrMemberIds from
|
||||
DB.execute
|
||||
db
|
||||
"INSERT INTO received_probes (contact_id, group_member_id, probe_hash, user_id, created_at, updated_at) VALUES (?,?,?,?,?,?)"
|
||||
(ctId, gmId, probeHash, userId, currentTs, currentTs)
|
||||
(ctId, gmId, Binary probeHash, userId, currentTs, currentTs)
|
||||
pure probeIds $>>= \(Only probe :. cgmIds) -> (,Probe probe) <$$> getContactOrMember_ db vr user cgmIds
|
||||
|
||||
matchSentProbe :: DB.Connection -> VersionRangeChat -> User -> ContactOrMember -> Probe -> IO (Maybe ContactOrMember)
|
||||
@@ -1736,7 +1739,7 @@ matchSentProbe db vr user@User {userId} _from (Probe probe) = do
|
||||
WHERE s.user_id = ? AND s.probe = ?
|
||||
AND (h.contact_id = ? OR h.group_member_id = ?)
|
||||
|]
|
||||
(userId, probe, ctId, gmId)
|
||||
(userId, Binary probe, ctId, gmId)
|
||||
|
||||
getContactOrMember_ :: DB.Connection -> VersionRangeChat -> User -> (Maybe ContactId, Maybe GroupId, Maybe GroupMemberId) -> IO (Maybe ContactOrMember)
|
||||
getContactOrMember_ db vr user ids =
|
||||
@@ -1777,22 +1780,18 @@ mergeContactRecords db vr user@User {userId} to@Contact {localDisplayName = keep
|
||||
db
|
||||
"UPDATE chat_items SET contact_id = ?, updated_at = ? WHERE contact_id = ? AND user_id = ?"
|
||||
(toContactId, currentTs, fromContactId, userId)
|
||||
DB.executeNamed
|
||||
DB.execute
|
||||
db
|
||||
[sql|
|
||||
UPDATE group_members
|
||||
SET contact_id = :to_contact_id,
|
||||
local_display_name = (SELECT local_display_name FROM contacts WHERE contact_id = :to_contact_id),
|
||||
contact_profile_id = (SELECT contact_profile_id FROM contacts WHERE contact_id = :to_contact_id),
|
||||
updated_at = :updated_at
|
||||
WHERE contact_id = :from_contact_id
|
||||
AND user_id = :user_id
|
||||
SET contact_id = ?,
|
||||
local_display_name = (SELECT local_display_name FROM contacts WHERE contact_id = ?),
|
||||
contact_profile_id = (SELECT contact_profile_id FROM contacts WHERE contact_id = ?),
|
||||
updated_at = ?
|
||||
WHERE contact_id = ?
|
||||
AND user_id = ?
|
||||
|]
|
||||
[ ":to_contact_id" := toContactId,
|
||||
":from_contact_id" := fromContactId,
|
||||
":user_id" := userId,
|
||||
":updated_at" := currentTs
|
||||
]
|
||||
(toContactId, toContactId, toContactId, currentTs, fromContactId, userId)
|
||||
deleteContactProfile_ db userId fromContactId
|
||||
DB.execute db "DELETE FROM contacts WHERE contact_id = ? AND user_id = ?" (fromContactId, userId)
|
||||
deleteUnusedDisplayName_ db userId fromLDN
|
||||
@@ -1867,41 +1866,44 @@ associateContactWithMemberRecord
|
||||
|
||||
deleteUnusedDisplayName_ :: DB.Connection -> UserId -> ContactName -> IO ()
|
||||
deleteUnusedDisplayName_ db userId localDisplayName =
|
||||
DB.executeNamed
|
||||
DB.execute
|
||||
db
|
||||
[sql|
|
||||
DELETE FROM display_names
|
||||
WHERE user_id = :user_id AND local_display_name = :local_display_name
|
||||
WHERE user_id = ? AND local_display_name = ?
|
||||
AND 1 NOT IN (
|
||||
SELECT 1 FROM users
|
||||
WHERE local_display_name = :local_display_name LIMIT 1
|
||||
WHERE local_display_name = ? LIMIT 1
|
||||
)
|
||||
AND 1 NOT IN (
|
||||
SELECT 1 FROM contacts
|
||||
WHERE user_id = :user_id AND local_display_name = :local_display_name LIMIT 1
|
||||
WHERE user_id = ? AND local_display_name = ? LIMIT 1
|
||||
)
|
||||
AND 1 NOT IN (
|
||||
SELECT 1 FROM groups
|
||||
WHERE user_id = :user_id AND local_display_name = :local_display_name LIMIT 1
|
||||
WHERE user_id = ? AND local_display_name = ? LIMIT 1
|
||||
)
|
||||
AND 1 NOT IN (
|
||||
SELECT 1 FROM group_members
|
||||
WHERE user_id = :user_id AND local_display_name = :local_display_name LIMIT 1
|
||||
WHERE user_id = ? AND local_display_name = ? LIMIT 1
|
||||
)
|
||||
AND 1 NOT IN (
|
||||
SELECT 1 FROM user_contact_links
|
||||
WHERE user_id = :user_id AND local_display_name = :local_display_name LIMIT 1
|
||||
WHERE user_id = ? AND local_display_name = ? LIMIT 1
|
||||
)
|
||||
AND 1 NOT IN (
|
||||
SELECT 1 FROM contact_requests
|
||||
WHERE user_id = :user_id AND local_display_name = :local_display_name LIMIT 1
|
||||
WHERE user_id = ? AND local_display_name = ? LIMIT 1
|
||||
)
|
||||
AND 1 NOT IN (
|
||||
SELECT 1 FROM contact_requests
|
||||
WHERE user_id = :user_id AND local_display_name = :local_display_name LIMIT 1
|
||||
WHERE user_id = ? AND local_display_name = ? LIMIT 1
|
||||
)
|
||||
|]
|
||||
[":user_id" := userId, ":local_display_name" := localDisplayName]
|
||||
( (userId, localDisplayName, localDisplayName, userId, localDisplayName, userId, localDisplayName)
|
||||
:. (userId, localDisplayName, userId, localDisplayName, userId, localDisplayName)
|
||||
:. (userId, localDisplayName)
|
||||
)
|
||||
|
||||
deleteOldProbes :: DB.Connection -> UTCTime -> IO ()
|
||||
deleteOldProbes db createdAtCutoff = do
|
||||
@@ -1911,7 +1913,7 @@ deleteOldProbes db createdAtCutoff = do
|
||||
|
||||
updateGroupSettings :: DB.Connection -> User -> Int64 -> ChatSettings -> IO ()
|
||||
updateGroupSettings db User {userId} groupId ChatSettings {enableNtfs, sendRcpts, favorite} =
|
||||
DB.execute db "UPDATE groups SET enable_ntfs = ?, send_rcpts = ?, favorite = ? WHERE user_id = ? AND group_id = ?" (enableNtfs, sendRcpts, favorite, userId, groupId)
|
||||
DB.execute db "UPDATE groups SET enable_ntfs = ?, send_rcpts = ?, favorite = ? WHERE user_id = ? AND group_id = ?" (enableNtfs, BI <$> sendRcpts, BI favorite, userId, groupId)
|
||||
|
||||
updateGroupMemberSettings :: DB.Connection -> User -> GroupId -> GroupMemberId -> GroupMemberSettings -> IO ()
|
||||
updateGroupMemberSettings db User {userId} gId gMemberId GroupMemberSettings {showMessages} = do
|
||||
@@ -1923,7 +1925,7 @@ updateGroupMemberSettings db User {userId} gId gMemberId GroupMemberSettings {sh
|
||||
SET show_messages = ?, updated_at = ?
|
||||
WHERE user_id = ? AND group_id = ? AND group_member_id = ?
|
||||
|]
|
||||
(showMessages, currentTs, userId, gId, gMemberId)
|
||||
(BI showMessages, currentTs, userId, gId, gMemberId)
|
||||
|
||||
updateGroupMemberBlocked :: DB.Connection -> User -> GroupId -> GroupMemberId -> MemberRestrictionStatus -> IO ()
|
||||
updateGroupMemberBlocked db User {userId} gId gMemberId memberBlocked = do
|
||||
@@ -2025,8 +2027,8 @@ createMemberContact
|
||||
contact_group_member_id, contact_grp_inv_sent, created_at, updated_at, chat_ts
|
||||
) VALUES (?,?,?,?,?,?,?,?,?,?,?)
|
||||
|]
|
||||
( (userId, localDisplayName, memberContactProfileId, True, userPreferences, True)
|
||||
:. (groupMemberId, False, currentTs, currentTs, currentTs)
|
||||
( (userId, localDisplayName, memberContactProfileId, BI True, userPreferences, BI True)
|
||||
:. (groupMemberId, BI False, currentTs, currentTs, currentTs)
|
||||
)
|
||||
contactId <- insertedRowId db
|
||||
DB.execute
|
||||
@@ -2041,8 +2043,8 @@ createMemberContact
|
||||
conn_chat_version, peer_chat_min_version, peer_chat_max_version, created_at, updated_at, to_subscribe
|
||||
) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)
|
||||
|]
|
||||
( (userId, acId, cReq, connLevel, ConnNew, ConnContact, True, contactId, customUserProfileId)
|
||||
:. (connChatVersion, minV, maxV, currentTs, currentTs, subMode == SMOnlyCreate)
|
||||
( (userId, acId, cReq, connLevel, ConnNew, ConnContact, BI True, contactId, customUserProfileId)
|
||||
:. (connChatVersion, minV, maxV, currentTs, currentTs, BI (subMode == SMOnlyCreate))
|
||||
)
|
||||
connId <- insertedRowId db
|
||||
let ctConn =
|
||||
@@ -2093,7 +2095,7 @@ setContactGrpInvSent db Contact {contactId} xGrpDirectInvSent = do
|
||||
DB.execute
|
||||
db
|
||||
"UPDATE contacts SET contact_grp_inv_sent = ?, updated_at = ? WHERE contact_id = ?"
|
||||
(xGrpDirectInvSent, currentTs, contactId)
|
||||
(BI xGrpDirectInvSent, currentTs, contactId)
|
||||
|
||||
createMemberContactInvited :: DB.Connection -> User -> (CommandId, ConnId) -> GroupInfo -> GroupMember -> Connection -> SubscriptionMode -> IO (Contact, GroupMember)
|
||||
createMemberContactInvited
|
||||
@@ -2123,7 +2125,7 @@ createMemberContactInvited
|
||||
created_at, updated_at, chat_ts
|
||||
) VALUES (?,?,?,?,?,?,?,?,?)
|
||||
|]
|
||||
( (userId, memberLDN, memberContactProfileId, True, userPreferences, True)
|
||||
( (userId, memberLDN, memberContactProfileId, BI True, userPreferences, BI True)
|
||||
:. (currentTs, currentTs, currentTs)
|
||||
)
|
||||
contactId <- insertedRowId db
|
||||
@@ -2175,7 +2177,7 @@ createMemberContactConn_
|
||||
) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?)
|
||||
|]
|
||||
( (userId, acId, connLevel, ConnJoined, ConnContact, contactId, customUserProfileId)
|
||||
:. (connChatVersion, minV, maxV, currentTs, currentTs, subMode == SMOnlyCreate)
|
||||
:. (connChatVersion, minV, maxV, currentTs, currentTs, BI (subMode == SMOnlyCreate))
|
||||
)
|
||||
connId <- insertedRowId db
|
||||
setCommandConnId db user cmdId connId
|
||||
@@ -2244,7 +2246,7 @@ updateContactMemberProfile db user@User {userId} m ct@Contact {contactId} p'
|
||||
|
||||
getXGrpLinkMemReceived :: DB.Connection -> GroupMemberId -> ExceptT StoreError IO Bool
|
||||
getXGrpLinkMemReceived db mId =
|
||||
ExceptT . firstRow fromOnly (SEGroupMemberNotFound mId) $
|
||||
ExceptT . firstRow fromOnlyBI (SEGroupMemberNotFound mId) $
|
||||
DB.query db "SELECT xgrplinkmem_received FROM group_members WHERE group_member_id = ?" (Only mId)
|
||||
|
||||
setXGrpLinkMemReceived :: DB.Connection -> GroupMemberId -> Bool -> IO ()
|
||||
@@ -2253,7 +2255,7 @@ setXGrpLinkMemReceived db mId xGrpLinkMemReceived = do
|
||||
DB.execute
|
||||
db
|
||||
"UPDATE group_members SET xgrplinkmem_received = ?, updated_at = ? WHERE group_member_id = ?"
|
||||
(xGrpLinkMemReceived, currentTs, mId)
|
||||
(BI xGrpLinkMemReceived, currentTs, mId)
|
||||
|
||||
createNewUnknownGroupMember :: DB.Connection -> VersionRangeChat -> User -> GroupInfo -> MemberId -> Text -> ExceptT StoreError IO GroupMember
|
||||
createNewUnknownGroupMember db vr user@User {userId, userContactId} GroupInfo {groupId} memberId memberName = do
|
||||
|
||||
+188
-140
@@ -1,3 +1,4 @@
|
||||
{-# LANGUAGE CPP #-}
|
||||
{-# LANGUAGE DataKinds #-}
|
||||
{-# LANGUAGE DuplicateRecordFields #-}
|
||||
{-# LANGUAGE GADTs #-}
|
||||
@@ -140,8 +141,6 @@ import Data.Text (Text)
|
||||
import qualified Data.Text as T
|
||||
import Data.Time (addUTCTime)
|
||||
import Data.Time.Clock (UTCTime (..), getCurrentTime)
|
||||
import Database.SQLite.Simple (FromRow, NamedParam (..), Only (..), Query, ToRow, (:.) (..))
|
||||
import Database.SQLite.Simple.QQ (sql)
|
||||
import Simplex.Chat.Controller (ChatListQuery (..), ChatPagination (..), ContentFilter (..), PaginationByTime (..))
|
||||
import Simplex.Chat.Markdown
|
||||
import Simplex.Chat.Messages
|
||||
@@ -160,6 +159,13 @@ import qualified Simplex.Messaging.Crypto as C
|
||||
import Simplex.Messaging.Crypto.File (CryptoFile (..), CryptoFileArgs (..))
|
||||
import Simplex.Messaging.Util (eitherToMaybe)
|
||||
import UnliftIO.STM
|
||||
#if defined(dbPostgres)
|
||||
import Database.PostgreSQL.Simple (FromRow, Only (..), Query, ToRow, (:.) (..))
|
||||
import Database.PostgreSQL.Simple.SqlQQ (sql)
|
||||
#else
|
||||
import Database.SQLite.Simple (FromRow, Only (..), Query, ToRow, (:.) (..))
|
||||
import Database.SQLite.Simple.QQ (sql)
|
||||
#endif
|
||||
|
||||
deleteContactCIs :: DB.Connection -> User -> Contact -> IO ()
|
||||
deleteContactCIs db user@User {userId} ct@Contact {contactId} = do
|
||||
@@ -200,7 +206,7 @@ createNewSndMessage db gVar connOrGroupId chatMsgEvent encodeMessage =
|
||||
shared_msg_id, shared_msg_id_user, created_at, updated_at
|
||||
) VALUES (?,?,?,?,?,?,?,?,?)
|
||||
|]
|
||||
(MDSnd, toCMEventTag chatMsgEvent, msgBody, connId_, groupId_, sharedMsgId, Just True, createdAt, createdAt)
|
||||
(MDSnd, toCMEventTag chatMsgEvent, DB.Binary msgBody, connId_, groupId_, DB.Binary sharedMsgId, Just (BI True), createdAt, createdAt)
|
||||
msgId <- insertedRowId db
|
||||
pure $ Right SndMessage {msgId, sharedMsgId = SharedMsgId sharedMsgId, msgBody}
|
||||
where
|
||||
@@ -285,7 +291,7 @@ createNewRcvMessage db connOrGroupId NewRcvMessage {chatMsgEvent, msgBody} share
|
||||
(msg_sent, chat_msg_event, msg_body, created_at, updated_at, connection_id, group_id, shared_msg_id, author_group_member_id, forwarded_by_group_member_id)
|
||||
VALUES (?,?,?,?,?,?,?,?,?,?)
|
||||
|]
|
||||
(MDRcv, toCMEventTag chatMsgEvent, msgBody, currentTs, currentTs, connId_, groupId_, sharedMsgId_, authorMember, forwardedByMember)
|
||||
(MDRcv, toCMEventTag chatMsgEvent, DB.Binary msgBody, currentTs, currentTs, connId_, groupId_, sharedMsgId_, authorMember, forwardedByMember)
|
||||
msgId <- insertedRowId db
|
||||
pure RcvMessage {msgId, chatMsgEvent = ACME (encoding @e) chatMsgEvent, sharedMsgId_, msgBody, authorMember, forwardedByMember}
|
||||
|
||||
@@ -415,13 +421,14 @@ createNewChatItem_ db User {userId} chatDirection msgId_ sharedMsgId ciContent q
|
||||
fwd_from_tag, fwd_from_chat_name, fwd_from_msg_dir, fwd_from_contact_id, fwd_from_group_id, fwd_from_chat_item_id
|
||||
) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)
|
||||
|]
|
||||
((userId, msgId_) :. idsRow :. itemRow :. quoteRow :. forwardedFromRow)
|
||||
((userId, msgId_) :. idsRow :. itemRow :. quoteRow' :. forwardedFromRow)
|
||||
ciId <- insertedRowId db
|
||||
forM_ msgId_ $ \msgId -> insertChatItemMessage_ db ciId msgId createdAt
|
||||
pure ciId
|
||||
where
|
||||
itemRow :: (SMsgDirection d, UTCTime, CIContent d, Text, Text, CIStatus d, Maybe MsgContentTag, Maybe SharedMsgId, Maybe GroupMemberId) :. (UTCTime, UTCTime, Maybe Bool) :. (Maybe Int, Maybe UTCTime)
|
||||
itemRow = (msgDirection @d, itemTs, ciContent, toCIContentTag ciContent, ciContentToText ciContent, ciCreateStatus ciContent, msgContentTag <$> ciMsgContent ciContent, sharedMsgId, forwardedByMember) :. (createdAt, createdAt, justTrue live) :. ciTimedRow timed
|
||||
itemRow :: (SMsgDirection d, UTCTime, CIContent d, Text, Text, CIStatus d, Maybe MsgContentTag, Maybe SharedMsgId, Maybe GroupMemberId) :. (UTCTime, UTCTime, Maybe BoolInt) :. (Maybe Int, Maybe UTCTime)
|
||||
itemRow = (msgDirection @d, itemTs, ciContent, toCIContentTag ciContent, ciContentToText ciContent, ciCreateStatus ciContent, msgContentTag <$> ciMsgContent ciContent, sharedMsgId, forwardedByMember) :. (createdAt, createdAt, BI <$> (justTrue live)) :. ciTimedRow timed
|
||||
quoteRow' = let (a, b, c, d, e) = quoteRow in (a, b, c, BI <$> d, e)
|
||||
idsRow :: (Maybe Int64, Maybe Int64, Maybe Int64, Maybe Int64)
|
||||
idsRow = case chatDirection of
|
||||
CDDirectRcv Contact {contactId} -> (Just contactId, Nothing, Nothing, Nothing)
|
||||
@@ -452,11 +459,11 @@ getChatItemQuote_ :: ChatTypeQuotable c => DB.Connection -> User -> ChatDirectio
|
||||
getChatItemQuote_ db User {userId, userContactId} chatDirection QuotedMsg {msgRef = MsgRef {msgId, sentAt, sent, memberId}, content} =
|
||||
case chatDirection of
|
||||
CDDirectRcv Contact {contactId} -> getDirectChatItemQuote_ contactId (not sent)
|
||||
CDGroupRcv GroupInfo {groupId, membership = GroupMember {memberId = userMemberId}} sender@GroupMember {memberId = senderMemberId} ->
|
||||
CDGroupRcv GroupInfo {groupId, membership = GroupMember {memberId = userMemberId}} sender@GroupMember {groupMemberId = senderGMId, memberId = senderMemberId} ->
|
||||
case memberId of
|
||||
Just mId
|
||||
| mId == userMemberId -> (`ciQuote` CIQGroupSnd) <$> getUserGroupChatItemId_ groupId
|
||||
| mId == senderMemberId -> (`ciQuote` CIQGroupRcv (Just sender)) <$> getGroupChatItemId_ groupId mId
|
||||
| mId == senderMemberId -> (`ciQuote` CIQGroupRcv (Just sender)) <$> getGroupChatItemId_ groupId senderGMId
|
||||
| otherwise -> getGroupChatItemQuote_ groupId mId
|
||||
_ -> pure . ciQuote Nothing $ CIQGroupRcv Nothing
|
||||
where
|
||||
@@ -468,7 +475,7 @@ getChatItemQuote_ db User {userId, userContactId} chatDirection QuotedMsg {msgRe
|
||||
DB.query
|
||||
db
|
||||
"SELECT chat_item_id FROM chat_items WHERE user_id = ? AND contact_id = ? AND shared_msg_id = ? AND item_sent = ?"
|
||||
(userId, contactId, msgId, userSent)
|
||||
(userId, contactId, msgId, BI userSent)
|
||||
where
|
||||
ciQuoteDirect :: Maybe ChatItemId -> CIQuote 'CTDirect
|
||||
ciQuoteDirect = (`ciQuote` if userSent then CIQDirectSnd else CIQDirectRcv)
|
||||
@@ -479,17 +486,17 @@ getChatItemQuote_ db User {userId, userContactId} chatDirection QuotedMsg {msgRe
|
||||
db
|
||||
"SELECT chat_item_id FROM chat_items WHERE user_id = ? AND group_id = ? AND shared_msg_id = ? AND item_sent = ? AND group_member_id IS NULL"
|
||||
(userId, groupId, msgId, MDSnd)
|
||||
getGroupChatItemId_ :: Int64 -> MemberId -> IO (Maybe ChatItemId)
|
||||
getGroupChatItemId_ groupId mId =
|
||||
getGroupChatItemId_ :: Int64 -> GroupMemberId -> IO (Maybe ChatItemId)
|
||||
getGroupChatItemId_ groupId groupMemberId =
|
||||
maybeFirstRow fromOnly $
|
||||
DB.query
|
||||
db
|
||||
"SELECT chat_item_id FROM chat_items WHERE user_id = ? AND group_id = ? AND shared_msg_id = ? AND item_sent = ? AND group_member_id = ?"
|
||||
(userId, groupId, msgId, MDRcv, mId)
|
||||
(userId, groupId, msgId, MDRcv, groupMemberId)
|
||||
getGroupChatItemQuote_ :: Int64 -> MemberId -> IO (CIQuote 'CTGroup)
|
||||
getGroupChatItemQuote_ groupId mId = do
|
||||
ciQuoteGroup
|
||||
<$> DB.queryNamed
|
||||
<$> DB.query
|
||||
db
|
||||
[sql|
|
||||
SELECT i.chat_item_id,
|
||||
@@ -503,10 +510,10 @@ getChatItemQuote_ db User {userId, userContactId} chatDirection QuotedMsg {msgRe
|
||||
LEFT JOIN chat_items i ON i.user_id = m.user_id
|
||||
AND i.group_id = m.group_id
|
||||
AND m.group_member_id = i.group_member_id
|
||||
AND i.shared_msg_id = :msg_id
|
||||
WHERE m.user_id = :user_id AND m.group_id = :group_id AND m.member_id = :member_id
|
||||
AND i.shared_msg_id = ?
|
||||
WHERE m.user_id = ? AND m.group_id = ? AND m.member_id = ?
|
||||
|]
|
||||
[":user_id" := userId, ":group_id" := groupId, ":member_id" := mId, ":msg_id" := msgId]
|
||||
(msgId, userId, groupId, mId)
|
||||
where
|
||||
ciQuoteGroup :: [Only (Maybe ChatItemId) :. GroupMemberRow] -> CIQuote 'CTGroup
|
||||
ciQuoteGroup [] = ciQuote Nothing $ CIQGroupRcv Nothing
|
||||
@@ -564,14 +571,21 @@ findDirectChatPreviews_ db User {userId} pagination clq =
|
||||
ACPD SCTDirect $ DirectChatPD ts contactId lastItemId_ (toChatStats statsRow)
|
||||
baseQuery =
|
||||
[sql|
|
||||
SELECT ct.contact_id, ct.chat_ts as ts, LastItems.chat_item_id, COALESCE(ChatStats.UnreadCount, 0), 0, COALESCE(ChatStats.MinUnread, 0), ct.unread_chat
|
||||
SELECT
|
||||
ct.contact_id,
|
||||
ct.chat_ts,
|
||||
(
|
||||
SELECT chat_item_id
|
||||
FROM chat_items ci
|
||||
WHERE ci.user_id = ? AND ci.contact_id = ct.contact_id
|
||||
ORDER BY ci.created_at DESC
|
||||
LIMIT 1
|
||||
) AS chat_item_id,
|
||||
COALESCE(ChatStats.UnreadCount, 0),
|
||||
0,
|
||||
COALESCE(ChatStats.MinUnread, 0),
|
||||
ct.unread_chat
|
||||
FROM contacts ct
|
||||
LEFT JOIN (
|
||||
SELECT contact_id, chat_item_id, MAX(created_at)
|
||||
FROM chat_items
|
||||
WHERE user_id = ? AND contact_id IS NOT NULL
|
||||
GROUP BY contact_id
|
||||
) LastItems ON LastItems.contact_id = ct.contact_id
|
||||
LEFT JOIN (
|
||||
SELECT contact_id, COUNT(1) AS UnreadCount, MIN(chat_item_id) AS MinUnread
|
||||
FROM chat_items
|
||||
@@ -582,58 +596,61 @@ findDirectChatPreviews_ db User {userId} pagination clq =
|
||||
baseParams = (userId, userId, CISRcvNew)
|
||||
getPreviews = case clq of
|
||||
CLQFilters {favorite = False, unread = False} -> do
|
||||
let q = baseQuery <> " WHERE ct.user_id = ? AND ct.is_user = 0 AND ct.deleted = 0 AND ct.contact_used"
|
||||
let q = baseQuery <> " WHERE ct.user_id = ? AND ct.is_user = 0 AND ct.deleted = 0 AND ct.contact_used = 1"
|
||||
p = baseParams :. Only userId
|
||||
queryWithPagination db q p pagination
|
||||
queryWithPagination q p
|
||||
CLQFilters {favorite = True, unread = False} -> do
|
||||
let q =
|
||||
baseQuery
|
||||
<> " "
|
||||
<> [sql|
|
||||
WHERE ct.user_id = ? AND ct.is_user = 0 AND ct.deleted = 0 AND ct.contact_used
|
||||
WHERE ct.user_id = ? AND ct.is_user = 0 AND ct.deleted = 0 AND ct.contact_used = 1
|
||||
AND ct.favorite = 1
|
||||
|]
|
||||
|]
|
||||
p = baseParams :. Only userId
|
||||
queryWithPagination db q p pagination
|
||||
queryWithPagination q p
|
||||
CLQFilters {favorite = False, unread = True} -> do
|
||||
let q =
|
||||
baseQuery
|
||||
<> " "
|
||||
<> [sql|
|
||||
WHERE ct.user_id = ? AND ct.is_user = 0 AND ct.deleted = 0 AND ct.contact_used
|
||||
WHERE ct.user_id = ? AND ct.is_user = 0 AND ct.deleted = 0 AND ct.contact_used = 1
|
||||
AND (ct.unread_chat = 1 OR ChatStats.UnreadCount > 0)
|
||||
|]
|
||||
|]
|
||||
p = baseParams :. Only userId
|
||||
queryWithPagination db q p pagination
|
||||
queryWithPagination q p
|
||||
CLQFilters {favorite = True, unread = True} -> do
|
||||
let q =
|
||||
baseQuery
|
||||
<> " "
|
||||
<> [sql|
|
||||
WHERE ct.user_id = ? AND ct.is_user = 0 AND ct.deleted = 0 AND ct.contact_used
|
||||
WHERE ct.user_id = ? AND ct.is_user = 0 AND ct.deleted = 0 AND ct.contact_used = 1
|
||||
AND (ct.favorite = 1
|
||||
OR ct.unread_chat = 1 OR ChatStats.UnreadCount > 0)
|
||||
|]
|
||||
|]
|
||||
p = baseParams :. Only userId
|
||||
queryWithPagination db q p pagination
|
||||
queryWithPagination q p
|
||||
CLQSearch {search} -> do
|
||||
let q =
|
||||
baseQuery
|
||||
<> " "
|
||||
<> [sql|
|
||||
JOIN contact_profiles cp ON ct.contact_profile_id = cp.contact_profile_id
|
||||
WHERE ct.user_id = ? AND ct.is_user = 0 AND ct.deleted = 0 AND ct.contact_used
|
||||
WHERE ct.user_id = ? AND ct.is_user = 0 AND ct.deleted = 0 AND ct.contact_used = 1
|
||||
AND (
|
||||
ct.local_display_name LIKE '%' || ? || '%'
|
||||
OR cp.display_name LIKE '%' || ? || '%'
|
||||
OR cp.full_name LIKE '%' || ? || '%'
|
||||
OR cp.local_alias LIKE '%' || ? || '%'
|
||||
LOWER(ct.local_display_name) LIKE '%' || LOWER(?) || '%'
|
||||
OR LOWER(cp.display_name) LIKE '%' || LOWER(?) || '%'
|
||||
OR LOWER(cp.full_name) LIKE '%' || LOWER(?) || '%'
|
||||
OR LOWER(cp.local_alias) LIKE '%' || LOWER(?) || '%'
|
||||
)
|
||||
|]
|
||||
|]
|
||||
p = baseParams :. (userId, search, search, search, search)
|
||||
queryWithPagination db q p pagination
|
||||
|
||||
queryWithPagination :: ToRow p => DB.Connection -> Query -> p -> PaginationByTime -> IO [(ContactId, UTCTime, Maybe ChatItemId) :. ChatStatsRow]
|
||||
queryWithPagination db query params = \case
|
||||
PTLast count -> DB.query db (query <> " ORDER BY ts DESC LIMIT ?") (params :. Only count)
|
||||
PTAfter ts count -> DB.query db (query <> " AND ts > ? ORDER BY ts ASC LIMIT ?") (params :. (ts, count))
|
||||
PTBefore ts count -> DB.query db (query <> " AND ts < ? ORDER BY ts DESC LIMIT ?") (params :. (ts, count))
|
||||
queryWithPagination q p
|
||||
queryWithPagination :: ToRow p => Query -> p -> IO [(ContactId, UTCTime, Maybe ChatItemId) :. ChatStatsRow]
|
||||
queryWithPagination query params = case pagination of
|
||||
PTLast count -> DB.query db (query <> " ORDER BY ct.chat_ts DESC LIMIT ?") (params :. Only count)
|
||||
PTAfter ts count -> DB.query db (query <> " AND ct.chat_ts > ? ORDER BY ct.chat_ts ASC LIMIT ?") (params :. (ts, count))
|
||||
PTBefore ts count -> DB.query db (query <> " AND ct.chat_ts < ? ORDER BY ct.chat_ts DESC LIMIT ?") (params :. (ts, count))
|
||||
|
||||
getDirectChatPreview_ :: DB.Connection -> VersionRangeChat -> User -> ChatPreviewData 'CTDirect -> ExceptT StoreError IO AChat
|
||||
getDirectChatPreview_ db vr user (DirectChatPD _ contactId lastItemId_ stats) = do
|
||||
@@ -652,14 +669,21 @@ findGroupChatPreviews_ db User {userId} pagination clq =
|
||||
ACPD SCTGroup $ GroupChatPD ts groupId lastItemId_ (toChatStats statsRow)
|
||||
baseQuery =
|
||||
[sql|
|
||||
SELECT g.group_id, g.chat_ts as ts, LastItems.chat_item_id, COALESCE(ChatStats.UnreadCount, 0), COALESCE(ReportCount.Count, 0), COALESCE(ChatStats.MinUnread, 0), g.unread_chat
|
||||
SELECT
|
||||
g.group_id,
|
||||
g.chat_ts,
|
||||
(
|
||||
SELECT chat_item_id
|
||||
FROM chat_items ci
|
||||
WHERE ci.user_id = ? AND ci.group_id = g.group_id
|
||||
ORDER BY ci.item_ts DESC
|
||||
LIMIT 1
|
||||
) AS chat_item_id,
|
||||
COALESCE(ChatStats.UnreadCount, 0),
|
||||
COALESCE(ReportCount.Count, 0),
|
||||
COALESCE(ChatStats.MinUnread, 0),
|
||||
g.unread_chat
|
||||
FROM groups g
|
||||
LEFT JOIN (
|
||||
SELECT group_id, chat_item_id, MAX(item_ts)
|
||||
FROM chat_items
|
||||
WHERE user_id = ? AND group_id IS NOT NULL
|
||||
GROUP BY group_id
|
||||
) LastItems ON LastItems.group_id = g.group_id
|
||||
LEFT JOIN (
|
||||
SELECT group_id, COUNT(1) AS UnreadCount, MIN(chat_item_id) AS MinUnread
|
||||
FROM chat_items
|
||||
@@ -679,50 +703,59 @@ findGroupChatPreviews_ db User {userId} pagination clq =
|
||||
CLQFilters {favorite = False, unread = False} -> do
|
||||
let q = baseQuery <> " WHERE g.user_id = ?"
|
||||
p = baseParams :. Only userId
|
||||
queryWithPagination db q p pagination
|
||||
queryWithPagination q p
|
||||
CLQFilters {favorite = True, unread = False} -> do
|
||||
let q =
|
||||
baseQuery
|
||||
<> " "
|
||||
<> [sql|
|
||||
WHERE g.user_id = ?
|
||||
AND g.favorite = 1
|
||||
|]
|
||||
|]
|
||||
p = baseParams :. Only userId
|
||||
queryWithPagination db q p pagination
|
||||
queryWithPagination q p
|
||||
CLQFilters {favorite = False, unread = True} -> do
|
||||
let q =
|
||||
baseQuery
|
||||
<> " "
|
||||
<> [sql|
|
||||
WHERE g.user_id = ?
|
||||
AND (g.unread_chat = 1 OR ChatStats.UnreadCount > 0)
|
||||
|]
|
||||
|]
|
||||
p = baseParams :. Only userId
|
||||
queryWithPagination db q p pagination
|
||||
queryWithPagination q p
|
||||
CLQFilters {favorite = True, unread = True} -> do
|
||||
let q =
|
||||
baseQuery
|
||||
<> " "
|
||||
<> [sql|
|
||||
WHERE g.user_id = ?
|
||||
AND (g.favorite = 1
|
||||
OR g.unread_chat = 1 OR ChatStats.UnreadCount > 0)
|
||||
|]
|
||||
|]
|
||||
p = baseParams :. Only userId
|
||||
queryWithPagination db q p pagination
|
||||
queryWithPagination q p
|
||||
CLQSearch {search} -> do
|
||||
let q =
|
||||
baseQuery
|
||||
<> " "
|
||||
<> [sql|
|
||||
JOIN group_profiles gp ON gp.group_profile_id = g.group_profile_id
|
||||
WHERE g.user_id = ?
|
||||
AND (
|
||||
g.local_display_name LIKE '%' || ? || '%'
|
||||
OR gp.display_name LIKE '%' || ? || '%'
|
||||
OR gp.full_name LIKE '%' || ? || '%'
|
||||
OR gp.description LIKE '%' || ? || '%'
|
||||
LOWER(g.local_display_name) LIKE '%' || LOWER(?) || '%'
|
||||
OR LOWER(gp.display_name) LIKE '%' || LOWER(?) || '%'
|
||||
OR LOWER(gp.full_name) LIKE '%' || LOWER(?) || '%'
|
||||
OR LOWER(gp.description) LIKE '%' || LOWER(?) || '%'
|
||||
)
|
||||
|]
|
||||
|]
|
||||
p = baseParams :. (userId, search, search, search, search)
|
||||
queryWithPagination db q p pagination
|
||||
queryWithPagination q p
|
||||
queryWithPagination :: ToRow p => Query -> p -> IO [(GroupId, UTCTime, Maybe ChatItemId) :. ChatStatsRow]
|
||||
queryWithPagination query params = case pagination of
|
||||
PTLast count -> DB.query db (query <> " ORDER BY g.chat_ts DESC LIMIT ?") (params :. Only count)
|
||||
PTAfter ts count -> DB.query db (query <> " AND g.chat_ts > ? ORDER BY g.chat_ts ASC LIMIT ?") (params :. (ts, count))
|
||||
PTBefore ts count -> DB.query db (query <> " AND g.chat_ts < ? ORDER BY g.chat_ts DESC LIMIT ?") (params :. (ts, count))
|
||||
|
||||
getGroupChatPreview_ :: DB.Connection -> VersionRangeChat -> User -> ChatPreviewData 'CTGroup -> ExceptT StoreError IO AChat
|
||||
getGroupChatPreview_ db vr user (GroupChatPD _ groupId lastItemId_ stats) = do
|
||||
@@ -741,14 +774,21 @@ findLocalChatPreviews_ db User {userId} pagination clq =
|
||||
ACPD SCTLocal $ LocalChatPD ts noteFolderId lastItemId_ (toChatStats statsRow)
|
||||
baseQuery =
|
||||
[sql|
|
||||
SELECT nf.note_folder_id, nf.chat_ts as ts, LastItems.chat_item_id, COALESCE(ChatStats.UnreadCount, 0), 0, COALESCE(ChatStats.MinUnread, 0), nf.unread_chat
|
||||
SELECT
|
||||
nf.note_folder_id,
|
||||
nf.chat_ts,
|
||||
(
|
||||
SELECT chat_item_id
|
||||
FROM chat_items ci
|
||||
WHERE ci.user_id = ? AND ci.note_folder_id = nf.note_folder_id
|
||||
ORDER BY ci.created_at DESC
|
||||
LIMIT 1
|
||||
) AS chat_item_id,
|
||||
COALESCE(ChatStats.UnreadCount, 0),
|
||||
0,
|
||||
COALESCE(ChatStats.MinUnread, 0),
|
||||
nf.unread_chat
|
||||
FROM note_folders nf
|
||||
LEFT JOIN (
|
||||
SELECT note_folder_id, chat_item_id, MAX(created_at)
|
||||
FROM chat_items
|
||||
WHERE user_id = ? AND note_folder_id IS NOT NULL
|
||||
GROUP BY note_folder_id
|
||||
) LastItems ON LastItems.note_folder_id = nf.note_folder_id
|
||||
LEFT JOIN (
|
||||
SELECT note_folder_id, COUNT(1) AS UnreadCount, MIN(chat_item_id) AS MinUnread
|
||||
FROM chat_items
|
||||
@@ -761,36 +801,44 @@ findLocalChatPreviews_ db User {userId} pagination clq =
|
||||
CLQFilters {favorite = False, unread = False} -> do
|
||||
let q = baseQuery <> " WHERE nf.user_id = ?"
|
||||
p = baseParams :. Only userId
|
||||
queryWithPagination db q p pagination
|
||||
queryWithPagination q p
|
||||
CLQFilters {favorite = True, unread = False} -> do
|
||||
let q =
|
||||
baseQuery
|
||||
<> " "
|
||||
<> [sql|
|
||||
WHERE nf.user_id = ?
|
||||
AND nf.favorite = 1
|
||||
|]
|
||||
|]
|
||||
p = baseParams :. Only userId
|
||||
queryWithPagination db q p pagination
|
||||
queryWithPagination q p
|
||||
CLQFilters {favorite = False, unread = True} -> do
|
||||
let q =
|
||||
baseQuery
|
||||
<> " "
|
||||
<> [sql|
|
||||
WHERE nf.user_id = ?
|
||||
AND (nf.unread_chat = 1 OR ChatStats.UnreadCount > 0)
|
||||
|]
|
||||
|]
|
||||
p = baseParams :. Only userId
|
||||
queryWithPagination db q p pagination
|
||||
queryWithPagination q p
|
||||
CLQFilters {favorite = True, unread = True} -> do
|
||||
let q =
|
||||
baseQuery
|
||||
<> " "
|
||||
<> [sql|
|
||||
WHERE nf.user_id = ?
|
||||
AND (nf.favorite = 1
|
||||
OR nf.unread_chat = 1 OR ChatStats.UnreadCount > 0)
|
||||
|]
|
||||
|]
|
||||
p = baseParams :. Only userId
|
||||
queryWithPagination db q p pagination
|
||||
queryWithPagination q p
|
||||
CLQSearch {} -> pure []
|
||||
queryWithPagination :: ToRow p => Query -> p -> IO [(NoteFolderId, UTCTime, Maybe ChatItemId) :. ChatStatsRow]
|
||||
queryWithPagination query params = case pagination of
|
||||
PTLast count -> DB.query db (query <> " ORDER BY nf.chat_ts DESC LIMIT ?") (params :. Only count)
|
||||
PTAfter ts count -> DB.query db (query <> " AND nf.chat_ts > ? ORDER BY nf.chat_ts ASC LIMIT ?") (params :. (ts, count))
|
||||
PTBefore ts count -> DB.query db (query <> " AND nf.chat_ts < ? ORDER BY nf.chat_ts DESC LIMIT ?") (params :. (ts, count))
|
||||
|
||||
getLocalChatPreview_ :: DB.Connection -> User -> ChatPreviewData 'CTLocal -> ExceptT StoreError IO AChat
|
||||
getLocalChatPreview_ db user (LocalChatPD _ noteFolderId lastItemId_ stats) = do
|
||||
@@ -833,9 +881,9 @@ toLocalChatItem currentTs ((itemId, itemTs, AMsgDirection msgDir, itemContentTex
|
||||
let itemDeleted' = case itemDeleted of
|
||||
DBCINotDeleted -> Nothing
|
||||
_ -> Just (CIDeleted @'CTLocal deletedTs)
|
||||
itemEdited' = fromMaybe False itemEdited
|
||||
itemEdited' = maybe False unBI itemEdited
|
||||
itemForwarded = toCIForwardedFrom forwardedFromRow
|
||||
in mkCIMeta itemId content itemText status sentViaProxy sharedMsgId itemForwarded itemDeleted' itemEdited' ciTimed itemLive currentTs itemTs Nothing createdAt updatedAt
|
||||
in mkCIMeta itemId content itemText status (unBI <$> sentViaProxy) sharedMsgId itemForwarded itemDeleted' itemEdited' ciTimed (unBI <$> itemLive) currentTs itemTs Nothing createdAt updatedAt
|
||||
ciTimed :: Maybe CITimed
|
||||
ciTimed = timedTTL >>= \ttl -> Just CITimed {ttl, deleteAt = timedDeleteAt}
|
||||
|
||||
@@ -852,7 +900,7 @@ getContactRequestChatPreviews_ db User {userId} pagination clq = case clq of
|
||||
SELECT
|
||||
cr.contact_request_id, cr.local_display_name, cr.agent_invitation_id, cr.contact_id, cr.user_contact_link_id,
|
||||
c.agent_conn_id, cr.contact_profile_id, p.display_name, p.full_name, p.image, p.contact_link, cr.xcontact_id, cr.pq_support, p.preferences,
|
||||
cr.created_at, cr.updated_at as ts,
|
||||
cr.created_at, cr.updated_at,
|
||||
cr.peer_chat_min_version, cr.peer_chat_max_version
|
||||
FROM contact_requests cr
|
||||
JOIN connections c ON c.user_contact_link_id = cr.user_contact_link_id
|
||||
@@ -863,16 +911,16 @@ getContactRequestChatPreviews_ db User {userId} pagination clq = case clq of
|
||||
AND uc.local_display_name = ''
|
||||
AND uc.group_id IS NULL
|
||||
AND (
|
||||
cr.local_display_name LIKE '%' || ? || '%'
|
||||
OR p.display_name LIKE '%' || ? || '%'
|
||||
OR p.full_name LIKE '%' || ? || '%'
|
||||
LOWER(cr.local_display_name) LIKE '%' || LOWER(?) || '%'
|
||||
OR LOWER(p.display_name) LIKE '%' || LOWER(?) || '%'
|
||||
OR LOWER(p.full_name) LIKE '%' || LOWER(?) || '%'
|
||||
)
|
||||
|]
|
||||
params search = (userId, userId, search, search, search)
|
||||
getPreviews search = case pagination of
|
||||
PTLast count -> DB.query db (query <> " ORDER BY ts DESC LIMIT ?") (params search :. Only count)
|
||||
PTAfter ts count -> DB.query db (query <> " AND ts > ? ORDER BY ts ASC LIMIT ?") (params search :. (ts, count))
|
||||
PTBefore ts count -> DB.query db (query <> " AND ts < ? ORDER BY ts DESC LIMIT ?") (params search :. (ts, count))
|
||||
PTLast count -> DB.query db (query <> " ORDER BY cr.updated_at DESC LIMIT ?") (params search :. Only count)
|
||||
PTAfter ts count -> DB.query db (query <> " AND cr.updated_at > ? ORDER BY cr.updated_at ASC LIMIT ?") (params search :. (ts, count))
|
||||
PTBefore ts count -> DB.query db (query <> " AND cr.updated_at < ? ORDER BY cr.updated_at DESC LIMIT ?") (params search :. (ts, count))
|
||||
toPreview :: ContactRequestRow -> AChatPreviewData
|
||||
toPreview cReqRow =
|
||||
let cReq@UserContactRequest {updatedAt} = toContactRequest cReqRow
|
||||
@@ -891,7 +939,7 @@ getContactConnectionChatPreviews_ db User {userId} pagination clq = case clq of
|
||||
[sql|
|
||||
SELECT
|
||||
connection_id, agent_conn_id, conn_status, via_contact_uri_hash, via_user_contact_link, group_link_id,
|
||||
custom_user_profile_id, conn_req_inv, local_alias, created_at, updated_at as ts
|
||||
custom_user_profile_id, conn_req_inv, local_alias, created_at, updated_at
|
||||
FROM connections
|
||||
WHERE user_id = ?
|
||||
AND conn_type = ?
|
||||
@@ -899,14 +947,14 @@ getContactConnectionChatPreviews_ db User {userId} pagination clq = case clq of
|
||||
AND contact_id IS NULL
|
||||
AND conn_level = 0
|
||||
AND via_contact IS NULL
|
||||
AND (via_group_link = 0 || (via_group_link = 1 AND group_link_id IS NOT NULL))
|
||||
AND local_alias LIKE '%' || ? || '%'
|
||||
AND (via_group_link = 0 OR (via_group_link = 1 AND group_link_id IS NOT NULL))
|
||||
AND LOWER(local_alias) LIKE '%' || LOWER(?) || '%'
|
||||
|]
|
||||
params search = (userId, ConnContact, ConnPrepared, search)
|
||||
getPreviews search = case pagination of
|
||||
PTLast count -> DB.query db (query <> " ORDER BY ts DESC LIMIT ?") (params search :. Only count)
|
||||
PTAfter ts count -> DB.query db (query <> " AND ts > ? ORDER BY ts ASC LIMIT ?") (params search :. (ts, count))
|
||||
PTBefore ts count -> DB.query db (query <> " AND ts < ? ORDER BY ts DESC LIMIT ?") (params search :. (ts, count))
|
||||
PTLast count -> DB.query db (query <> " ORDER BY updated_at DESC LIMIT ?") (params search :. Only count)
|
||||
PTAfter ts count -> DB.query db (query <> " AND updated_at > ? ORDER BY updated_at ASC LIMIT ?") (params search :. (ts, count))
|
||||
PTBefore ts count -> DB.query db (query <> " AND updated_at < ? ORDER BY updated_at DESC LIMIT ?") (params search :. (ts, count))
|
||||
toPreview :: (Int64, ConnId, ConnStatus, Maybe ByteString, Maybe Int64, Maybe GroupLinkId, Maybe Int64, Maybe ConnReqInvitation, LocalAlias, UTCTime, UTCTime) -> AChatPreviewData
|
||||
toPreview connRow =
|
||||
let conn@PendingContactConnection {updatedAt} = toPendingContactConnection connRow
|
||||
@@ -942,7 +990,7 @@ getDirectChatItemIdsLast_ db User {userId} Contact {contactId} count search =
|
||||
[sql|
|
||||
SELECT chat_item_id
|
||||
FROM chat_items
|
||||
WHERE user_id = ? AND contact_id = ? AND item_text LIKE '%' || ? || '%'
|
||||
WHERE user_id = ? AND contact_id = ? AND LOWER(item_text) LIKE '%' || LOWER(?) || '%'
|
||||
ORDER BY created_at DESC, chat_item_id DESC
|
||||
LIMIT ?
|
||||
|]
|
||||
@@ -1006,7 +1054,7 @@ getDirectCIsAfter_ db User {userId} Contact {contactId} afterCI count search =
|
||||
[sql|
|
||||
SELECT chat_item_id
|
||||
FROM chat_items
|
||||
WHERE user_id = ? AND contact_id = ? AND item_text LIKE '%' || ? || '%'
|
||||
WHERE user_id = ? AND contact_id = ? AND LOWER(item_text) LIKE '%' || LOWER(?) || '%'
|
||||
AND (created_at > ? OR (created_at = ? AND chat_item_id > ?))
|
||||
ORDER BY created_at ASC, chat_item_id ASC
|
||||
LIMIT ?
|
||||
@@ -1029,7 +1077,7 @@ getDirectCIsBefore_ db User {userId} Contact {contactId} beforeCI count search =
|
||||
[sql|
|
||||
SELECT chat_item_id
|
||||
FROM chat_items
|
||||
WHERE user_id = ? AND contact_id = ? AND item_text LIKE '%' || ? || '%'
|
||||
WHERE user_id = ? AND contact_id = ? AND LOWER(item_text) LIKE '%' || LOWER(?) || '%'
|
||||
AND (created_at < ? OR (created_at = ? AND chat_item_id < ?))
|
||||
ORDER BY created_at DESC, chat_item_id DESC
|
||||
LIMIT ?
|
||||
@@ -1121,7 +1169,7 @@ getContactNavInfo_ db User {userId} Contact {contactId} afterCI = do
|
||||
FROM chat_items
|
||||
WHERE user_id = ? AND contact_id = ? AND item_status = ?
|
||||
AND created_at = ? AND chat_item_id > ?
|
||||
)
|
||||
) ci
|
||||
|]
|
||||
( (userId, contactId, CISRcvNew, ciCreatedAt afterCI)
|
||||
:. (userId, contactId, CISRcvNew, ciCreatedAt afterCI, cChatItemId afterCI)
|
||||
@@ -1143,7 +1191,7 @@ getContactNavInfo_ db User {userId} Contact {contactId} afterCI = do
|
||||
FROM chat_items
|
||||
WHERE user_id = ? AND contact_id = ?
|
||||
AND created_at = ? AND chat_item_id > ?
|
||||
)
|
||||
) ci
|
||||
|]
|
||||
( (userId, contactId, ciCreatedAt afterCI)
|
||||
:. (userId, contactId, ciCreatedAt afterCI, cChatItemId afterCI)
|
||||
@@ -1199,7 +1247,7 @@ getGroupChatItemIDs db User {userId} GroupInfo {groupId} contentFilter range cou
|
||||
rangeQuery :: ToRow p => Query -> p -> Query -> IO [ChatItemId]
|
||||
rangeQuery c p ob
|
||||
| null search = searchQuery "" ()
|
||||
| otherwise = searchQuery " AND item_text LIKE '%' || ? || '%' " (Only search)
|
||||
| otherwise = searchQuery " AND LOWER(item_text) LIKE '%' || LOWER(?) || '%' " (Only search)
|
||||
where
|
||||
searchQuery :: ToRow p' => Query -> p' -> IO [ChatItemId]
|
||||
searchQuery c' p' =
|
||||
@@ -1313,7 +1361,7 @@ getGroupMinUnreadId_ db user g contentFilter =
|
||||
queryUnreadGroupItems db user g contentFilter baseQuery orderLimit
|
||||
where
|
||||
baseQuery = "SELECT chat_item_id FROM chat_items WHERE user_id = ? AND group_id = ? "
|
||||
orderLimit = " ORDER BY item_ts ASC, chat_item_id ASC LIMIT 1"
|
||||
orderLimit = " ORDER BY item_ts ASC, chat_item_id ASC LIMIT 1"
|
||||
|
||||
getGroupUnreadCount_ :: DB.Connection -> User -> GroupInfo -> Maybe ContentFilter -> IO Int
|
||||
getGroupUnreadCount_ db user g contentFilter =
|
||||
@@ -1372,7 +1420,7 @@ getGroupNavInfo_ db User {userId} GroupInfo {groupId} afterCI = do
|
||||
FROM chat_items
|
||||
WHERE user_id = ? AND group_id = ? AND item_status = ?
|
||||
AND item_ts = ? AND chat_item_id > ?
|
||||
)
|
||||
) ci
|
||||
|]
|
||||
( (userId, groupId, CISRcvNew, chatItemTs afterCI)
|
||||
:. (userId, groupId, CISRcvNew, chatItemTs afterCI, cChatItemId afterCI)
|
||||
@@ -1394,7 +1442,7 @@ getGroupNavInfo_ db User {userId} GroupInfo {groupId} afterCI = do
|
||||
FROM chat_items
|
||||
WHERE user_id = ? AND group_id = ?
|
||||
AND item_ts = ? AND chat_item_id > ?
|
||||
)
|
||||
) ci
|
||||
|]
|
||||
( (userId, groupId, chatItemTs afterCI)
|
||||
:. (userId, groupId, chatItemTs afterCI, cChatItemId afterCI)
|
||||
@@ -1428,7 +1476,7 @@ getLocalChatItemIdsLast_ db User {userId} NoteFolder {noteFolderId} count search
|
||||
[sql|
|
||||
SELECT chat_item_id
|
||||
FROM chat_items
|
||||
WHERE user_id = ? AND note_folder_id = ? AND item_text LIKE '%' || ? || '%'
|
||||
WHERE user_id = ? AND note_folder_id = ? AND LOWER(item_text) LIKE '%' || LOWER(?) || '%'
|
||||
ORDER BY created_at DESC, chat_item_id DESC
|
||||
LIMIT ?
|
||||
|]
|
||||
@@ -1476,7 +1524,7 @@ getLocalCIsAfter_ db User {userId} NoteFolder {noteFolderId} afterCI count searc
|
||||
[sql|
|
||||
SELECT chat_item_id
|
||||
FROM chat_items
|
||||
WHERE user_id = ? AND note_folder_id = ? AND item_text LIKE '%' || ? || '%'
|
||||
WHERE user_id = ? AND note_folder_id = ? AND LOWER(item_text) LIKE '%' || LOWER(?) || '%'
|
||||
AND (created_at > ? OR (created_at = ? AND chat_item_id > ?))
|
||||
ORDER BY created_at ASC, chat_item_id ASC
|
||||
LIMIT ?
|
||||
@@ -1499,7 +1547,7 @@ getLocalCIsBefore_ db User {userId} NoteFolder {noteFolderId} beforeCI count sea
|
||||
[sql|
|
||||
SELECT chat_item_id
|
||||
FROM chat_items
|
||||
WHERE user_id = ? AND note_folder_id = ? AND item_text LIKE '%' || ? || '%'
|
||||
WHERE user_id = ? AND note_folder_id = ? AND LOWER(item_text) LIKE '%' || LOWER(?) || '%'
|
||||
AND (created_at < ? OR (created_at = ? AND chat_item_id < ?))
|
||||
ORDER BY created_at DESC, chat_item_id DESC
|
||||
LIMIT ?
|
||||
@@ -1591,7 +1639,7 @@ getLocalNavInfo_ db User {userId} NoteFolder {noteFolderId} afterCI = do
|
||||
FROM chat_items
|
||||
WHERE user_id = ? AND note_folder_id = ? AND item_status = ?
|
||||
AND created_at = ? AND chat_item_id > ?
|
||||
)
|
||||
) ci
|
||||
|]
|
||||
( (userId, noteFolderId, CISRcvNew, ciCreatedAt afterCI)
|
||||
:. (userId, noteFolderId, CISRcvNew, ciCreatedAt afterCI, cChatItemId afterCI)
|
||||
@@ -1613,7 +1661,7 @@ getLocalNavInfo_ db User {userId} NoteFolder {noteFolderId} afterCI = do
|
||||
FROM chat_items
|
||||
WHERE user_id = ? AND note_folder_id = ?
|
||||
AND created_at = ? AND chat_item_id > ?
|
||||
)
|
||||
) ci
|
||||
|]
|
||||
( (userId, noteFolderId, ciCreatedAt afterCI)
|
||||
:. (userId, noteFolderId, ciCreatedAt afterCI, cChatItemId afterCI)
|
||||
@@ -1763,21 +1811,21 @@ updateLocalChatItemsRead db User {userId} noteFolderId = do
|
||||
|
||||
type MaybeCIFIleRow = (Maybe Int64, Maybe String, Maybe Integer, Maybe FilePath, Maybe C.SbKey, Maybe C.CbNonce, Maybe ACIFileStatus, Maybe FileProtocol)
|
||||
|
||||
type ChatItemModeRow = (Maybe Int, Maybe UTCTime, Maybe Bool)
|
||||
type ChatItemModeRow = (Maybe Int, Maybe UTCTime, Maybe BoolInt)
|
||||
|
||||
type ChatItemForwardedFromRow = (Maybe CIForwardedFromTag, Maybe Text, Maybe MsgDirection, Maybe Int64, Maybe Int64, Maybe Int64)
|
||||
|
||||
type ChatItemRow =
|
||||
(Int64, ChatItemTs, AMsgDirection, Text, Text, ACIStatus, Maybe Bool, Maybe SharedMsgId)
|
||||
:. (Int, Maybe UTCTime, Maybe Bool, UTCTime, UTCTime)
|
||||
(Int64, ChatItemTs, AMsgDirection, Text, Text, ACIStatus, Maybe BoolInt, Maybe SharedMsgId)
|
||||
:. (Int, Maybe UTCTime, Maybe BoolInt, UTCTime, UTCTime)
|
||||
:. ChatItemForwardedFromRow
|
||||
:. ChatItemModeRow
|
||||
:. MaybeCIFIleRow
|
||||
|
||||
type QuoteRow = (Maybe ChatItemId, Maybe SharedMsgId, Maybe UTCTime, Maybe MsgContent, Maybe Bool)
|
||||
type QuoteRow = (Maybe ChatItemId, Maybe SharedMsgId, Maybe UTCTime, Maybe MsgContent, Maybe BoolInt)
|
||||
|
||||
toDirectQuote :: QuoteRow -> Maybe (CIQuote 'CTDirect)
|
||||
toDirectQuote qr@(_, _, _, _, quotedSent) = toQuote qr $ direction <$> quotedSent
|
||||
toDirectQuote qr@(_, _, _, _, quotedSent) = toQuote qr $ direction . unBI <$> quotedSent
|
||||
where
|
||||
direction sent = if sent then CIQDirectSnd else CIQDirectRcv
|
||||
|
||||
@@ -1818,9 +1866,9 @@ toDirectChatItem currentTs (((itemId, itemTs, AMsgDirection msgDir, itemContentT
|
||||
let itemDeleted' = case itemDeleted of
|
||||
DBCINotDeleted -> Nothing
|
||||
_ -> Just (CIDeleted @'CTDirect deletedTs)
|
||||
itemEdited' = fromMaybe False itemEdited
|
||||
itemEdited' = maybe False unBI itemEdited
|
||||
itemForwarded = toCIForwardedFrom forwardedFromRow
|
||||
in mkCIMeta itemId content itemText status sentViaProxy sharedMsgId itemForwarded itemDeleted' itemEdited' ciTimed itemLive currentTs itemTs Nothing createdAt updatedAt
|
||||
in mkCIMeta itemId content itemText status (unBI <$> sentViaProxy) sharedMsgId itemForwarded itemDeleted' itemEdited' ciTimed (unBI <$> itemLive) currentTs itemTs Nothing createdAt updatedAt
|
||||
ciTimed :: Maybe CITimed
|
||||
ciTimed = timedTTL >>= \ttl -> Just CITimed {ttl, deleteAt = timedDeleteAt}
|
||||
|
||||
@@ -1837,9 +1885,9 @@ type GroupQuoteRow = QuoteRow :. MaybeGroupMemberRow
|
||||
toGroupQuote :: QuoteRow -> Maybe GroupMember -> Maybe (CIQuote 'CTGroup)
|
||||
toGroupQuote qr@(_, _, _, _, quotedSent) quotedMember_ = toQuote qr $ direction quotedSent quotedMember_
|
||||
where
|
||||
direction (Just True) _ = Just CIQGroupSnd
|
||||
direction (Just False) (Just member) = Just . CIQGroupRcv $ Just member
|
||||
direction (Just False) Nothing = Just $ CIQGroupRcv Nothing
|
||||
direction (Just (BI True)) _ = Just CIQGroupSnd
|
||||
direction (Just (BI False)) (Just member) = Just . CIQGroupRcv $ Just member
|
||||
direction (Just (BI False)) Nothing = Just $ CIQGroupRcv Nothing
|
||||
direction _ _ = Nothing
|
||||
|
||||
-- this function can be changed so it never fails, not only avoid failure on invalid json
|
||||
@@ -1880,9 +1928,9 @@ toGroupChatItem currentTs userContactId (((itemId, itemTs, AMsgDirection msgDir,
|
||||
DBCIBlocked -> Just (CIBlocked deletedTs)
|
||||
DBCIBlockedByAdmin -> Just (CIBlockedByAdmin deletedTs)
|
||||
_ -> Just (maybe (CIDeleted @'CTGroup deletedTs) (CIModerated deletedTs) deletedByGroupMember_)
|
||||
itemEdited' = fromMaybe False itemEdited
|
||||
itemEdited' = maybe False unBI itemEdited
|
||||
itemForwarded = toCIForwardedFrom forwardedFromRow
|
||||
in mkCIMeta itemId content itemText status sentViaProxy sharedMsgId itemForwarded itemDeleted' itemEdited' ciTimed itemLive currentTs itemTs forwardedByMember createdAt updatedAt
|
||||
in mkCIMeta itemId content itemText status (unBI <$> sentViaProxy) sharedMsgId itemForwarded itemDeleted' itemEdited' ciTimed (unBI <$> itemLive) currentTs itemTs forwardedByMember createdAt updatedAt
|
||||
ciTimed :: Maybe CITimed
|
||||
ciTimed = timedTTL >>= \ttl -> Just CITimed {ttl, deleteAt = timedDeleteAt}
|
||||
|
||||
@@ -1912,7 +1960,7 @@ getAllChatItems db vr user@User {userId} pagination search_ = do
|
||||
[sql|
|
||||
SELECT chat_item_id, contact_id, group_id, note_folder_id
|
||||
FROM chat_items
|
||||
WHERE user_id = ? AND item_text LIKE '%' || ? || '%'
|
||||
WHERE user_id = ? AND LOWER(item_text) LIKE '%' || LOWER(?) || '%'
|
||||
ORDER BY item_ts DESC, chat_item_id DESC
|
||||
LIMIT ?
|
||||
|]
|
||||
@@ -1923,7 +1971,7 @@ getAllChatItems db vr user@User {userId} pagination search_ = do
|
||||
[sql|
|
||||
SELECT chat_item_id, contact_id, group_id, note_folder_id
|
||||
FROM chat_items
|
||||
WHERE user_id = ? AND item_text LIKE '%' || ? || '%'
|
||||
WHERE user_id = ? AND LOWER(item_text) LIKE '%' || LOWER(?) || '%'
|
||||
AND (item_ts > ? OR (item_ts = ? AND chat_item_id > ?))
|
||||
ORDER BY item_ts ASC, chat_item_id ASC
|
||||
LIMIT ?
|
||||
@@ -1936,7 +1984,7 @@ getAllChatItems db vr user@User {userId} pagination search_ = do
|
||||
[sql|
|
||||
SELECT chat_item_id, contact_id, group_id, note_folder_id
|
||||
FROM chat_items
|
||||
WHERE user_id = ? AND item_text LIKE '%' || ? || '%'
|
||||
WHERE user_id = ? AND LOWER(item_text) LIKE '%' || LOWER(?) || '%'
|
||||
AND (item_ts < ? OR (item_ts = ? AND chat_item_id < ?))
|
||||
ORDER BY item_ts DESC, chat_item_id DESC
|
||||
LIMIT ?
|
||||
@@ -1992,7 +2040,7 @@ updateDirectChatItemStatus db user@User {userId} ct@Contact {contactId} itemId i
|
||||
|
||||
setDirectSndChatItemViaProxy :: DB.Connection -> User -> Contact -> ChatItem 'CTDirect 'MDSnd -> Bool -> IO (ChatItem 'CTDirect 'MDSnd)
|
||||
setDirectSndChatItemViaProxy db User {userId} Contact {contactId} ci viaProxy = do
|
||||
DB.execute db "UPDATE chat_items SET via_proxy = ? WHERE user_id = ? AND contact_id = ? AND chat_item_id = ?" (viaProxy, userId, contactId, chatItemId' ci)
|
||||
DB.execute db "UPDATE chat_items SET via_proxy = ? WHERE user_id = ? AND contact_id = ? AND chat_item_id = ?" (BI viaProxy, userId, contactId, chatItemId' ci)
|
||||
pure ci {meta = (meta ci) {sentViaProxy = Just viaProxy}}
|
||||
|
||||
updateDirectChatItem :: MsgDirectionI d => DB.Connection -> User -> Contact -> ChatItemId -> CIContent d -> Bool -> Bool -> Maybe CITimed -> Maybe MessageId -> ExceptT StoreError IO (ChatItem 'CTDirect d)
|
||||
@@ -2044,7 +2092,7 @@ updateDirectChatItem_ db userId contactId ChatItem {meta, content} msgId_ = do
|
||||
SET item_content = ?, item_text = ?, item_status = ?, item_deleted = ?, item_deleted_ts = ?, item_edited = ?, item_live = ?, updated_at = ?, timed_ttl = ?, timed_delete_at = ?
|
||||
WHERE user_id = ? AND contact_id = ? AND chat_item_id = ?
|
||||
|]
|
||||
((content, itemText, itemStatus, itemDeleted', itemDeletedTs', itemEdited, itemLive, updatedAt) :. ciTimedRow itemTimed :. (userId, contactId, itemId))
|
||||
((content, itemText, itemStatus, BI itemDeleted', itemDeletedTs', BI itemEdited, BI <$> itemLive, updatedAt) :. ciTimedRow itemTimed :. (userId, contactId, itemId))
|
||||
forM_ msgId_ $ \msgId -> liftIO $ insertChatItemMessage_ db itemId msgId updatedAt
|
||||
|
||||
addInitialAndNewCIVersions :: DB.Connection -> ChatItemId -> (UTCTime, MsgContent) -> (UTCTime, MsgContent) -> IO ()
|
||||
@@ -2235,7 +2283,7 @@ updateGroupChatItem_ db User {userId} groupId ChatItem {content, meta} msgId_ =
|
||||
SET item_content = ?, item_text = ?, item_status = ?, item_deleted = ?, item_deleted_ts = ?, item_edited = ?, item_live = ?, updated_at = ?, timed_ttl = ?, timed_delete_at = ?
|
||||
WHERE user_id = ? AND group_id = ? AND chat_item_id = ?
|
||||
|]
|
||||
((content, itemText, itemStatus, itemDeleted', itemDeletedTs', itemEdited, itemLive, updatedAt) :. ciTimedRow itemTimed :. (userId, groupId, itemId))
|
||||
((content, itemText, itemStatus, BI itemDeleted', itemDeletedTs', BI itemEdited, BI <$> itemLive, updatedAt) :. ciTimedRow itemTimed :. (userId, groupId, itemId))
|
||||
forM_ msgId_ $ \msgId -> insertChatItemMessage_ db itemId msgId updatedAt
|
||||
|
||||
deleteGroupChatItem :: DB.Connection -> User -> GroupInfo -> ChatItem 'CTGroup d -> IO ()
|
||||
@@ -2573,7 +2621,7 @@ updateLocalChatItem_ db userId noteFolderId ChatItem {meta, content} = do
|
||||
SET item_content = ?, item_text = ?, item_status = ?, item_deleted = ?, item_deleted_ts = ?, item_edited = ?, updated_at = ?
|
||||
WHERE user_id = ? AND note_folder_id = ? AND chat_item_id = ?
|
||||
|]
|
||||
((content, itemText, itemStatus, itemDeleted', itemDeletedTs', itemEdited, updatedAt) :. (userId, noteFolderId, itemId))
|
||||
((content, itemText, itemStatus, BI itemDeleted', itemDeletedTs', BI itemEdited, updatedAt) :. (userId, noteFolderId, itemId))
|
||||
|
||||
deleteLocalChatItem :: DB.Connection -> User -> NoteFolder -> ChatItem 'CTLocal d -> IO ()
|
||||
deleteLocalChatItem db User {userId} NoteFolder {noteFolderId} ci = do
|
||||
@@ -2740,8 +2788,8 @@ deleteGroupCIReactions_ db g@GroupInfo {groupId} ci@ChatItem {meta = CIMeta {ite
|
||||
"DELETE FROM chat_item_reactions WHERE group_id = ? AND shared_msg_id = ? AND item_member_id = ?"
|
||||
(groupId, itemSharedMId, memberId)
|
||||
|
||||
toCIReaction :: (MsgReaction, Bool, Int) -> CIReactionCount
|
||||
toCIReaction (reaction, userReacted, totalReacted) = CIReactionCount {reaction, userReacted, totalReacted}
|
||||
toCIReaction :: (MsgReaction, BoolInt, Int) -> CIReactionCount
|
||||
toCIReaction (reaction, BI userReacted, totalReacted) = CIReactionCount {reaction, userReacted, totalReacted}
|
||||
|
||||
getDirectReactions :: DB.Connection -> Contact -> SharedMsgId -> Bool -> IO [MsgReaction]
|
||||
getDirectReactions db ct itemSharedMId sent =
|
||||
@@ -2753,7 +2801,7 @@ getDirectReactions db ct itemSharedMId sent =
|
||||
FROM chat_item_reactions
|
||||
WHERE contact_id = ? AND shared_msg_id = ? AND reaction_sent = ?
|
||||
|]
|
||||
(contactId' ct, itemSharedMId, sent)
|
||||
(contactId' ct, itemSharedMId, BI sent)
|
||||
|
||||
setDirectReaction :: DB.Connection -> Contact -> SharedMsgId -> Bool -> MsgReaction -> Bool -> MessageId -> UTCTime -> IO ()
|
||||
setDirectReaction db ct itemSharedMId sent reaction add msgId reactionTs
|
||||
@@ -2765,7 +2813,7 @@ setDirectReaction db ct itemSharedMId sent reaction add msgId reactionTs
|
||||
(contact_id, shared_msg_id, reaction_sent, reaction, created_by_msg_id, reaction_ts)
|
||||
VALUES (?,?,?,?,?,?)
|
||||
|]
|
||||
(contactId' ct, itemSharedMId, sent, reaction, msgId, reactionTs)
|
||||
(contactId' ct, itemSharedMId, BI sent, reaction, msgId, reactionTs)
|
||||
| otherwise =
|
||||
DB.execute
|
||||
db
|
||||
@@ -2773,7 +2821,7 @@ setDirectReaction db ct itemSharedMId sent reaction add msgId reactionTs
|
||||
DELETE FROM chat_item_reactions
|
||||
WHERE contact_id = ? AND shared_msg_id = ? AND reaction_sent = ? AND reaction = ?
|
||||
|]
|
||||
(contactId' ct, itemSharedMId, sent, reaction)
|
||||
(contactId' ct, itemSharedMId, BI sent, reaction)
|
||||
|
||||
getGroupReactions :: DB.Connection -> GroupInfo -> GroupMember -> MemberId -> SharedMsgId -> Bool -> IO [MsgReaction]
|
||||
getGroupReactions db GroupInfo {groupId} m itemMemberId itemSharedMId sent =
|
||||
@@ -2785,7 +2833,7 @@ getGroupReactions db GroupInfo {groupId} m itemMemberId itemSharedMId sent =
|
||||
FROM chat_item_reactions
|
||||
WHERE group_id = ? AND group_member_id = ? AND item_member_id = ? AND shared_msg_id = ? AND reaction_sent = ?
|
||||
|]
|
||||
(groupId, groupMemberId' m, itemMemberId, itemSharedMId, sent)
|
||||
(groupId, groupMemberId' m, itemMemberId, itemSharedMId, BI sent)
|
||||
|
||||
setGroupReaction :: DB.Connection -> GroupInfo -> GroupMember -> MemberId -> SharedMsgId -> Bool -> MsgReaction -> Bool -> MessageId -> UTCTime -> IO ()
|
||||
setGroupReaction db GroupInfo {groupId} m itemMemberId itemSharedMId sent reaction add msgId reactionTs
|
||||
@@ -2797,7 +2845,7 @@ setGroupReaction db GroupInfo {groupId} m itemMemberId itemSharedMId sent reacti
|
||||
(group_id, group_member_id, item_member_id, shared_msg_id, reaction_sent, reaction, created_by_msg_id, reaction_ts)
|
||||
VALUES (?,?,?,?,?,?,?,?)
|
||||
|]
|
||||
(groupId, groupMemberId' m, itemMemberId, itemSharedMId, sent, reaction, msgId, reactionTs)
|
||||
(groupId, groupMemberId' m, itemMemberId, itemSharedMId, BI sent, reaction, msgId, reactionTs)
|
||||
| otherwise =
|
||||
DB.execute
|
||||
db
|
||||
@@ -2805,7 +2853,7 @@ setGroupReaction db GroupInfo {groupId} m itemMemberId itemSharedMId sent reacti
|
||||
DELETE FROM chat_item_reactions
|
||||
WHERE group_id = ? AND group_member_id = ? AND shared_msg_id = ? AND item_member_id = ? AND reaction_sent = ? AND reaction = ?
|
||||
|]
|
||||
(groupId, groupMemberId' m, itemSharedMId, itemMemberId, sent, reaction)
|
||||
(groupId, groupMemberId' m, itemSharedMId, itemMemberId, BI sent, reaction)
|
||||
|
||||
getReactionMembers :: DB.Connection -> VersionRangeChat -> User -> GroupId -> SharedMsgId -> MsgReaction -> IO [MemberReaction]
|
||||
getReactionMembers db vr user groupId itemSharedMId reaction = do
|
||||
@@ -2974,7 +3022,7 @@ setGroupSndViaProxy db itemId memberId viaProxy =
|
||||
SET via_proxy = ?
|
||||
WHERE chat_item_id = ? AND group_member_id = ?
|
||||
|]
|
||||
(viaProxy, itemId, memberId)
|
||||
(BI viaProxy, itemId, memberId)
|
||||
|
||||
getGroupSndStatuses :: DB.Connection -> ChatItemId -> IO [MemberDeliveryStatus]
|
||||
getGroupSndStatuses db itemId =
|
||||
@@ -2989,7 +3037,7 @@ getGroupSndStatuses db itemId =
|
||||
(Only itemId)
|
||||
where
|
||||
memStatus (groupMemberId, memberDeliveryStatus, sentViaProxy) =
|
||||
MemberDeliveryStatus {groupMemberId, memberDeliveryStatus, sentViaProxy}
|
||||
MemberDeliveryStatus {groupMemberId, memberDeliveryStatus, sentViaProxy = unBI <$> sentViaProxy}
|
||||
|
||||
getGroupSndStatusCounts :: DB.Connection -> ChatItemId -> IO [(GroupSndStatus, Int)]
|
||||
getGroupSndStatusCounts db itemId =
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
{-# LANGUAGE CPP #-}
|
||||
{-# LANGUAGE DuplicateRecordFields #-}
|
||||
{-# LANGUAGE LambdaCase #-}
|
||||
{-# LANGUAGE NamedFieldPuns #-}
|
||||
@@ -10,13 +11,19 @@ module Simplex.Chat.Store.NoteFolders where
|
||||
import Control.Monad.Except (ExceptT (..), throwError)
|
||||
import Control.Monad.IO.Class (liftIO)
|
||||
import Data.Time (getCurrentTime)
|
||||
import Database.SQLite.Simple (Only (..))
|
||||
import Database.SQLite.Simple.QQ (sql)
|
||||
import Simplex.Chat.Store.Shared (StoreError (..))
|
||||
import Simplex.Chat.Types (NoteFolder (..), NoteFolderId, User (..))
|
||||
import Simplex.Messaging.Agent.Protocol (UserId)
|
||||
import Simplex.Messaging.Agent.Store.AgentStore (firstRow)
|
||||
import qualified Simplex.Messaging.Agent.Store.SQLite.DB as DB
|
||||
import Simplex.Messaging.Agent.Store.DB (BoolInt (..))
|
||||
import qualified Simplex.Messaging.Agent.Store.DB as DB
|
||||
#if defined(dbPostgres)
|
||||
import Database.PostgreSQL.Simple (Only (..))
|
||||
import Database.PostgreSQL.Simple.SqlQQ (sql)
|
||||
#else
|
||||
import Database.SQLite.Simple (Only (..))
|
||||
import Database.SQLite.Simple.QQ (sql)
|
||||
#endif
|
||||
|
||||
createNoteFolder :: DB.Connection -> User -> ExceptT StoreError IO ()
|
||||
createNoteFolder db User {userId} = do
|
||||
@@ -43,13 +50,13 @@ getNoteFolder db User {userId} noteFolderId =
|
||||
|]
|
||||
(userId, noteFolderId)
|
||||
where
|
||||
toNoteFolder (createdAt, updatedAt, chatTs, favorite, unread) =
|
||||
toNoteFolder (createdAt, updatedAt, chatTs, BI favorite, BI unread) =
|
||||
NoteFolder {noteFolderId, userId, createdAt, updatedAt, chatTs, favorite, unread}
|
||||
|
||||
updateNoteFolderUnreadChat :: DB.Connection -> User -> NoteFolder -> Bool -> IO ()
|
||||
updateNoteFolderUnreadChat db User {userId} NoteFolder {noteFolderId} unreadChat = do
|
||||
updatedAt <- getCurrentTime
|
||||
DB.execute db "UPDATE note_folders SET unread_chat = ?, updated_at = ? WHERE user_id = ? AND note_folder_id = ?" (unreadChat, updatedAt, userId, noteFolderId)
|
||||
DB.execute db "UPDATE note_folders SET unread_chat = ?, updated_at = ? WHERE user_id = ? AND note_folder_id = ?" (BI unreadChat, updatedAt, userId, noteFolderId)
|
||||
|
||||
deleteNoteFolderFiles :: DB.Connection -> UserId -> NoteFolder -> IO ()
|
||||
deleteNoteFolderFiles db userId NoteFolder {noteFolderId} = do
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
{-# LANGUAGE NamedFieldPuns #-}
|
||||
|
||||
module Simplex.Chat.Store.Postgres.Migrations (migrations) where
|
||||
|
||||
import Data.List (sortOn)
|
||||
import Data.Text (Text)
|
||||
import Simplex.Chat.Store.Postgres.Migrations.M20241220_initial
|
||||
import Simplex.Messaging.Agent.Store.Shared (Migration (..))
|
||||
|
||||
schemaMigrations :: [(String, Text, Maybe Text)]
|
||||
schemaMigrations =
|
||||
[ ("20241220_initial", m20241220_initial, Nothing)
|
||||
]
|
||||
|
||||
-- | The list of migrations in ascending order by date
|
||||
migrations :: [Migration]
|
||||
migrations = sortOn name $ map migration schemaMigrations
|
||||
where
|
||||
migration (name, up, down) = Migration {name, up, down}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,3 +1,4 @@
|
||||
{-# LANGUAGE CPP #-}
|
||||
{-# LANGUAGE DataKinds #-}
|
||||
{-# LANGUAGE DeriveAnyClass #-}
|
||||
{-# LANGUAGE DuplicateRecordFields #-}
|
||||
@@ -86,8 +87,6 @@ import Data.Text (Text)
|
||||
import qualified Data.Text as T
|
||||
import Data.Text.Encoding (decodeLatin1, encodeUtf8)
|
||||
import Data.Time.Clock (UTCTime (..), getCurrentTime)
|
||||
import Database.SQLite.Simple (NamedParam (..), Only (..), Query, (:.) (..))
|
||||
import Database.SQLite.Simple.QQ (sql)
|
||||
import Simplex.Chat.Call
|
||||
import Simplex.Chat.Messages
|
||||
import Simplex.Chat.Operators
|
||||
@@ -101,7 +100,8 @@ import Simplex.Chat.Types.UITheme
|
||||
import Simplex.Messaging.Agent.Env.SQLite (ServerRoles (..))
|
||||
import Simplex.Messaging.Agent.Protocol (ACorrId, ConnId, UserId)
|
||||
import Simplex.Messaging.Agent.Store.AgentStore (firstRow, maybeFirstRow)
|
||||
import qualified Simplex.Messaging.Agent.Store.SQLite.DB as DB
|
||||
import Simplex.Messaging.Agent.Store.DB (BoolInt (..))
|
||||
import qualified Simplex.Messaging.Agent.Store.DB as DB
|
||||
import qualified Simplex.Messaging.Crypto as C
|
||||
import qualified Simplex.Messaging.Crypto.Ratchet as CR
|
||||
import Simplex.Messaging.Encoding.String
|
||||
@@ -109,6 +109,13 @@ import Simplex.Messaging.Parsers (defaultJSON)
|
||||
import Simplex.Messaging.Protocol (BasicAuth (..), ProtoServerWithAuth (..), ProtocolServer (..), ProtocolType (..), ProtocolTypeI (..), SProtocolType (..), SubscriptionMode)
|
||||
import Simplex.Messaging.Transport.Client (TransportHost)
|
||||
import Simplex.Messaging.Util (eitherToMaybe, safeDecodeUtf8)
|
||||
#if defined(dbPostgres)
|
||||
import Database.PostgreSQL.Simple (Only (..), Query, (:.) (..))
|
||||
import Database.PostgreSQL.Simple.SqlQQ (sql)
|
||||
#else
|
||||
import Database.SQLite.Simple (Only (..), Query, (:.) (..))
|
||||
import Database.SQLite.Simple.QQ (sql)
|
||||
#endif
|
||||
|
||||
createUserRecord :: DB.Connection -> AgentUserId -> Profile -> Bool -> ExceptT StoreError IO User
|
||||
createUserRecord db auId p activeUser = createUserRecordAt db auId p activeUser =<< liftIO getCurrentTime
|
||||
@@ -124,7 +131,7 @@ createUserRecordAt db (AgentUserId auId) Profile {displayName, fullName, image,
|
||||
DB.execute
|
||||
db
|
||||
"INSERT INTO users (agent_user_id, local_display_name, active_user, active_order, contact_id, show_ntfs, send_rcpts_contacts, send_rcpts_small_groups, created_at, updated_at) VALUES (?,?,?,?,0,?,?,?,?,?)"
|
||||
(auId, displayName, activeUser, order, showNtfs, sendRcptsContacts, sendRcptsSmallGroups, currentTs, currentTs)
|
||||
(auId, displayName, BI activeUser, order, BI showNtfs, BI sendRcptsContacts, BI sendRcptsSmallGroups, currentTs, currentTs)
|
||||
userId <- insertedRowId db
|
||||
DB.execute
|
||||
db
|
||||
@@ -138,10 +145,10 @@ createUserRecordAt db (AgentUserId auId) Profile {displayName, fullName, image,
|
||||
DB.execute
|
||||
db
|
||||
"INSERT INTO contacts (contact_profile_id, local_display_name, user_id, is_user, created_at, updated_at, chat_ts) VALUES (?,?,?,?,?,?,?)"
|
||||
(profileId, displayName, userId, True, currentTs, currentTs, currentTs)
|
||||
(profileId, displayName, userId, BI True, currentTs, currentTs, currentTs)
|
||||
contactId <- insertedRowId db
|
||||
DB.execute db "UPDATE users SET contact_id = ? WHERE user_id = ?" (contactId, userId)
|
||||
pure $ toUser $ (userId, auId, contactId, profileId, activeUser, order, displayName, fullName, image, Nothing, userPreferences) :. (showNtfs, sendRcptsContacts, sendRcptsSmallGroups, Nothing, Nothing, Nothing, Nothing)
|
||||
pure $ toUser $ (userId, auId, contactId, profileId, BI activeUser, order, displayName, fullName, image, Nothing, userPreferences) :. (BI showNtfs, BI sendRcptsContacts, BI sendRcptsSmallGroups, Nothing, Nothing, Nothing, Nothing)
|
||||
|
||||
getUsersInfo :: DB.Connection -> IO [UserInfo]
|
||||
getUsersInfo db = getUsers db >>= mapM getUserInfo
|
||||
@@ -253,7 +260,7 @@ updateUserPrivacy db User {userId, showNtfs, viewPwdHash} =
|
||||
SET view_pwd_hash = ?, view_pwd_salt = ?, show_ntfs = ?
|
||||
WHERE user_id = ?
|
||||
|]
|
||||
(hashSalt viewPwdHash :. (showNtfs, userId))
|
||||
(hashSalt viewPwdHash :. (BI showNtfs, userId))
|
||||
where
|
||||
hashSalt = L.unzip . fmap (\UserPwdHash {hash, salt} -> (hash, salt))
|
||||
|
||||
@@ -262,16 +269,16 @@ updateAllContactReceipts db onOff =
|
||||
DB.execute
|
||||
db
|
||||
"UPDATE users SET send_rcpts_contacts = ?, send_rcpts_small_groups = ? WHERE view_pwd_hash IS NULL"
|
||||
(onOff, onOff)
|
||||
(BI onOff, BI onOff)
|
||||
|
||||
updateUserContactReceipts :: DB.Connection -> User -> UserMsgReceiptSettings -> IO ()
|
||||
updateUserContactReceipts db User {userId} UserMsgReceiptSettings {enable, clearOverrides} = do
|
||||
DB.execute db "UPDATE users SET send_rcpts_contacts = ? WHERE user_id = ?" (enable, userId)
|
||||
DB.execute db "UPDATE users SET send_rcpts_contacts = ? WHERE user_id = ?" (BI enable, userId)
|
||||
when clearOverrides $ DB.execute_ db "UPDATE contacts SET send_rcpts = NULL"
|
||||
|
||||
updateUserGroupReceipts :: DB.Connection -> User -> UserMsgReceiptSettings -> IO ()
|
||||
updateUserGroupReceipts db User {userId} UserMsgReceiptSettings {enable, clearOverrides} = do
|
||||
DB.execute db "UPDATE users SET send_rcpts_small_groups = ? WHERE user_id = ?" (enable, userId)
|
||||
DB.execute db "UPDATE users SET send_rcpts_small_groups = ? WHERE user_id = ?" (BI enable, userId)
|
||||
when clearOverrides $ DB.execute_ db "UPDATE groups SET send_rcpts = NULL"
|
||||
|
||||
updateUserProfile :: DB.Connection -> User -> Profile -> ExceptT StoreError IO User
|
||||
@@ -403,21 +410,21 @@ deleteUserAddress db user@User {userId} = do
|
||||
)
|
||||
|]
|
||||
(Only userId)
|
||||
DB.executeNamed
|
||||
DB.execute
|
||||
db
|
||||
[sql|
|
||||
DELETE FROM display_names
|
||||
WHERE user_id = :user_id
|
||||
WHERE user_id = ?
|
||||
AND local_display_name in (
|
||||
SELECT cr.local_display_name
|
||||
FROM contact_requests cr
|
||||
JOIN user_contact_links uc USING (user_contact_link_id)
|
||||
WHERE uc.user_id = :user_id AND uc.local_display_name = '' AND uc.group_id IS NULL
|
||||
WHERE uc.user_id = ? AND uc.local_display_name = '' AND uc.group_id IS NULL
|
||||
)
|
||||
AND local_display_name NOT IN (SELECT local_display_name FROM users WHERE user_id = :user_id)
|
||||
AND local_display_name NOT IN (SELECT local_display_name FROM users WHERE user_id = ?)
|
||||
|]
|
||||
[":user_id" := userId]
|
||||
DB.executeNamed
|
||||
(userId, userId, userId)
|
||||
DB.execute
|
||||
db
|
||||
[sql|
|
||||
DELETE FROM contact_profiles
|
||||
@@ -425,10 +432,10 @@ deleteUserAddress db user@User {userId} = do
|
||||
SELECT cr.contact_profile_id
|
||||
FROM contact_requests cr
|
||||
JOIN user_contact_links uc USING (user_contact_link_id)
|
||||
WHERE uc.user_id = :user_id AND uc.local_display_name = '' AND uc.group_id IS NULL
|
||||
WHERE uc.user_id = ? AND uc.local_display_name = '' AND uc.group_id IS NULL
|
||||
)
|
||||
|]
|
||||
[":user_id" := userId]
|
||||
(Only userId)
|
||||
void $ setUserProfileContactLink db user Nothing
|
||||
DB.execute db "DELETE FROM user_contact_links WHERE user_id = ? AND local_display_name = '' AND group_id IS NULL" (Only userId)
|
||||
|
||||
@@ -455,8 +462,8 @@ $(J.deriveJSON defaultJSON ''AutoAccept)
|
||||
|
||||
$(J.deriveJSON defaultJSON ''UserContactLink)
|
||||
|
||||
toUserContactLink :: (ConnReqContact, Bool, Bool, IncognitoEnabled, Maybe MsgContent) -> UserContactLink
|
||||
toUserContactLink (connReq, autoAccept, businessAddress, acceptIncognito, autoReply) =
|
||||
toUserContactLink :: (ConnReqContact, BoolInt, BoolInt, BoolInt, Maybe MsgContent) -> UserContactLink
|
||||
toUserContactLink (connReq, BI autoAccept, BI businessAddress, BI acceptIncognito, autoReply) =
|
||||
UserContactLink connReq $
|
||||
if autoAccept then Just AutoAccept {businessAddress, acceptIncognito, autoReply} else Nothing
|
||||
|
||||
@@ -528,8 +535,8 @@ updateUserAddressAutoAccept db user@User {userId} autoAccept = do
|
||||
|]
|
||||
(ucl :. Only userId)
|
||||
ucl = case autoAccept of
|
||||
Just AutoAccept {businessAddress, acceptIncognito, autoReply} -> (True, businessAddress, acceptIncognito, autoReply)
|
||||
_ -> (False, False, False, Nothing)
|
||||
Just AutoAccept {businessAddress, acceptIncognito, autoReply} -> (BI True, BI businessAddress, BI acceptIncognito, autoReply)
|
||||
_ -> (BI False, BI False, BI False, Nothing)
|
||||
|
||||
getProtocolServers :: forall p. ProtocolTypeI p => DB.Connection -> SProtocolType p -> User -> IO [UserServer p]
|
||||
getProtocolServers db p User {userId} =
|
||||
@@ -543,10 +550,10 @@ getProtocolServers db p User {userId} =
|
||||
|]
|
||||
(userId, decodeLatin1 $ strEncode p)
|
||||
where
|
||||
toUserServer :: (DBEntityId, NonEmpty TransportHost, String, C.KeyHash, Maybe Text, Bool, Maybe Bool, Bool) -> UserServer p
|
||||
toUserServer (serverId, host, port, keyHash, auth_, preset, tested, enabled) =
|
||||
toUserServer :: (DBEntityId, NonEmpty TransportHost, String, C.KeyHash, Maybe Text, BoolInt, Maybe BoolInt, BoolInt) -> UserServer p
|
||||
toUserServer (serverId, host, port, keyHash, auth_, BI preset, tested, BI enabled) =
|
||||
let server = ProtoServerWithAuth (ProtocolServer p host port keyHash) (BasicAuth . encodeUtf8 <$> auth_)
|
||||
in UserServer {serverId, server, preset, tested, enabled, deleted = False}
|
||||
in UserServer {serverId, server, preset, tested = unBI <$> tested, enabled, deleted = False}
|
||||
|
||||
insertProtocolServer :: forall p. ProtocolTypeI p => DB.Connection -> SProtocolType p -> User -> UTCTime -> NewUserServer p -> IO (UserServer p)
|
||||
insertProtocolServer db p User {userId} ts srv@UserServer {server, preset, tested, enabled} = do
|
||||
@@ -557,7 +564,7 @@ insertProtocolServer db p User {userId} ts srv@UserServer {server, preset, teste
|
||||
(protocol, host, port, key_hash, basic_auth, preset, tested, enabled, user_id, created_at, updated_at)
|
||||
VALUES (?,?,?,?,?,?,?,?,?,?,?)
|
||||
|]
|
||||
(serverColumns p server :. (preset, tested, enabled, userId, ts, ts))
|
||||
(serverColumns p server :. (BI preset, BI <$> tested, BI enabled, userId, ts, ts))
|
||||
sId <- insertedRowId db
|
||||
pure (srv :: NewUserServer p) {serverId = DBEntityId sId}
|
||||
|
||||
@@ -571,7 +578,7 @@ updateProtocolServer db p ts UserServer {serverId, server, preset, tested, enabl
|
||||
preset = ?, tested = ?, enabled = ?, updated_at = ?
|
||||
WHERE smp_server_id = ?
|
||||
|]
|
||||
(serverColumns p server :. (preset, tested, enabled, ts, serverId))
|
||||
(serverColumns p server :. (BI preset, BI <$> tested, BI enabled, ts, serverId))
|
||||
|
||||
serverColumns :: ProtocolTypeI p => SProtocolType p -> ProtoServerWithAuth p -> (Text, NonEmpty TransportHost, String, C.KeyHash, Maybe Text)
|
||||
serverColumns p (ProtoServerWithAuth ProtocolServer {host, port, keyHash} auth_) =
|
||||
@@ -611,7 +618,7 @@ updateServerOperator db currentTs ServerOperator {operatorId, enabled, smpRoles,
|
||||
SET enabled = ?, smp_role_storage = ?, smp_role_proxy = ?, xftp_role_storage = ?, xftp_role_proxy = ?, updated_at = ?
|
||||
WHERE server_operator_id = ?
|
||||
|]
|
||||
(enabled, storage smpRoles, proxy smpRoles, storage xftpRoles, proxy xftpRoles, currentTs, operatorId)
|
||||
(BI enabled, BI (storage smpRoles), BI (proxy smpRoles), BI (storage xftpRoles), BI (proxy xftpRoles), currentTs, operatorId)
|
||||
|
||||
getUpdateServerOperators :: DB.Connection -> NonEmpty PresetOperator -> Bool -> IO [(Maybe PresetOperator, Maybe ServerOperator)]
|
||||
getUpdateServerOperators db presetOps newUser = do
|
||||
@@ -649,7 +656,7 @@ getUpdateServerOperators db presetOps newUser = do
|
||||
SET trade_name = ?, legal_name = ?, server_domains = ?, enabled = ?, smp_role_storage = ?, smp_role_proxy = ?, xftp_role_storage = ?, xftp_role_proxy = ?
|
||||
WHERE server_operator_id = ?
|
||||
|]
|
||||
(tradeName, legalName, T.intercalate "," serverDomains, enabled, storage smpRoles, proxy smpRoles, storage xftpRoles, proxy xftpRoles, operatorId)
|
||||
(tradeName, legalName, T.intercalate "," serverDomains, BI enabled, BI (storage smpRoles), BI (proxy smpRoles), BI (storage xftpRoles), BI (proxy xftpRoles), operatorId)
|
||||
insertOperator :: NewServerOperator -> IO ServerOperator
|
||||
insertOperator op@ServerOperator {operatorTag, tradeName, legalName, serverDomains, enabled, smpRoles, xftpRoles} = do
|
||||
DB.execute
|
||||
@@ -659,7 +666,7 @@ getUpdateServerOperators db presetOps newUser = do
|
||||
(server_operator_tag, trade_name, legal_name, server_domains, enabled, smp_role_storage, smp_role_proxy, xftp_role_storage, xftp_role_proxy)
|
||||
VALUES (?,?,?,?,?,?,?,?,?)
|
||||
|]
|
||||
(operatorTag, tradeName, legalName, T.intercalate "," serverDomains, enabled, storage smpRoles, proxy smpRoles, storage xftpRoles, proxy xftpRoles)
|
||||
(operatorTag, tradeName, legalName, T.intercalate "," serverDomains, BI enabled, BI (storage smpRoles), BI (proxy smpRoles), BI (storage xftpRoles), BI (proxy xftpRoles))
|
||||
opId <- insertedRowId db
|
||||
pure op {operatorId = DBEntityId opId}
|
||||
autoAcceptConditions op UsageConditions {conditionsCommit} now =
|
||||
@@ -677,8 +684,8 @@ serverOperatorQuery =
|
||||
getServerOperators_ :: DB.Connection -> IO [ServerOperator]
|
||||
getServerOperators_ db = map toServerOperator <$> DB.query_ db serverOperatorQuery
|
||||
|
||||
toServerOperator :: (DBEntityId, Maybe OperatorTag, Text, Maybe Text, Text, Bool) :. (Bool, Bool) :. (Bool, Bool) -> ServerOperator
|
||||
toServerOperator ((operatorId, operatorTag, tradeName, legalName, domains, enabled) :. smpRoles' :. xftpRoles') =
|
||||
toServerOperator :: (DBEntityId, Maybe OperatorTag, Text, Maybe Text, Text, BoolInt) :. (BoolInt, BoolInt) :. (BoolInt, BoolInt) -> ServerOperator
|
||||
toServerOperator ((operatorId, operatorTag, tradeName, legalName, domains, BI enabled) :. smpRoles' :. xftpRoles') =
|
||||
ServerOperator
|
||||
{ operatorId,
|
||||
operatorTag,
|
||||
@@ -691,7 +698,7 @@ toServerOperator ((operatorId, operatorTag, tradeName, legalName, domains, enabl
|
||||
xftpRoles = serverRoles xftpRoles'
|
||||
}
|
||||
where
|
||||
serverRoles (storage, proxy) = ServerRoles {storage, proxy}
|
||||
serverRoles (BI storage, BI proxy) = ServerRoles {storage, proxy}
|
||||
|
||||
getOperatorConditions_ :: DB.Connection -> ServerOperator -> UsageConditions -> Maybe UsageConditions -> UTCTime -> IO ConditionsAcceptance
|
||||
getOperatorConditions_ db ServerOperator {operatorId} UsageConditions {conditionsCommit = currentCommit, createdAt, notifiedAt} latestAcceptedConds_ now = do
|
||||
@@ -711,7 +718,7 @@ getOperatorConditions_ db ServerOperator {operatorId} UsageConditions {condition
|
||||
|]
|
||||
(Only operatorId)
|
||||
pure $ case operatorAcceptedConds_ of
|
||||
Just (operatorCommit, acceptedAt_, autoAccept)
|
||||
Just (operatorCommit, acceptedAt_, BI autoAccept)
|
||||
| operatorCommit /= latestAcceptedCommit -> CARequired Nothing -- TODO should we consider this operator disabled?
|
||||
| currentCommit /= latestAcceptedCommit -> CARequired $ conditionsRequiredOrDeadline createdAt (fromMaybe now notifiedAt)
|
||||
| otherwise -> CAAccepted acceptedAt_ autoAccept
|
||||
@@ -767,23 +774,23 @@ acceptConditions db condId opIds acceptedAt = do
|
||||
|
||||
acceptConditions_ :: DB.Connection -> ServerOperator -> Text -> UTCTime -> Bool -> IO ()
|
||||
acceptConditions_ db ServerOperator {operatorId, operatorTag} conditionsCommit acceptedAt autoAccepted = do
|
||||
acceptedAt_ :: Maybe (Maybe UTCTime) <- maybeFirstRow fromOnly $ DB.query db "SELECT accepted_at FROM operator_usage_conditions WHERE server_operator_id = ? AND conditions_commit == ?" (operatorId, conditionsCommit)
|
||||
acceptedAt_ :: Maybe (Maybe UTCTime) <- maybeFirstRow fromOnly $ DB.query db "SELECT accepted_at FROM operator_usage_conditions WHERE server_operator_id = ? AND conditions_commit = ?" (operatorId, conditionsCommit)
|
||||
case acceptedAt_ of
|
||||
Just Nothing ->
|
||||
DB.execute
|
||||
db
|
||||
(q <> "ON CONFLICT (server_operator_id, conditions_commit) DO UPDATE SET accepted_at = ?, auto_accepted = ?")
|
||||
(operatorId, operatorTag, conditionsCommit, acceptedAt, autoAccepted, acceptedAt, autoAccepted)
|
||||
Just (Just _) ->
|
||||
DB.execute
|
||||
db
|
||||
(q <> "ON CONFLICT (server_operator_id, conditions_commit) DO NOTHING")
|
||||
(operatorId, operatorTag, conditionsCommit, acceptedAt, autoAccepted)
|
||||
Nothing ->
|
||||
DB.execute
|
||||
db
|
||||
q
|
||||
(operatorId, operatorTag, conditionsCommit, acceptedAt, autoAccepted)
|
||||
Just Nothing ->
|
||||
DB.execute
|
||||
db
|
||||
(q <> "ON CONFLICT (server_operator_id, conditions_commit) DO UPDATE SET accepted_at = ?, auto_accepted = ?")
|
||||
(operatorId, operatorTag, conditionsCommit, acceptedAt, BI autoAccepted, acceptedAt, BI autoAccepted)
|
||||
Just (Just _) ->
|
||||
DB.execute
|
||||
db
|
||||
(q <> "ON CONFLICT (server_operator_id, conditions_commit) DO NOTHING")
|
||||
(operatorId, operatorTag, conditionsCommit, acceptedAt, BI autoAccepted)
|
||||
Nothing ->
|
||||
DB.execute
|
||||
db
|
||||
q
|
||||
(operatorId, operatorTag, conditionsCommit, acceptedAt, BI autoAccepted)
|
||||
where
|
||||
q =
|
||||
[sql|
|
||||
@@ -820,7 +827,7 @@ setUserServers' db user@User {userId} ts UpdatedUserOperatorServers {operator, s
|
||||
| deleted -> pure Nothing
|
||||
| otherwise -> Just <$> insertProtocolServer db p user ts s
|
||||
DBEntityId srvId
|
||||
| deleted -> Nothing <$ DB.execute db "DELETE FROM protocol_servers WHERE user_id = ? AND smp_server_id = ? AND preset = ?" (userId, srvId, False)
|
||||
| deleted -> Nothing <$ DB.execute db "DELETE FROM protocol_servers WHERE user_id = ? AND smp_server_id = ? AND preset = ?" (userId, srvId, BI False)
|
||||
| otherwise -> Just s <$ updateProtocolServer db p ts s
|
||||
|
||||
createCall :: DB.Connection -> User -> Call -> UTCTime -> IO ()
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
{-# LANGUAGE CPP #-}
|
||||
{-# LANGUAGE DuplicateRecordFields #-}
|
||||
{-# LANGUAGE NamedFieldPuns #-}
|
||||
{-# LANGUAGE OverloadedStrings #-}
|
||||
@@ -8,19 +9,23 @@ module Simplex.Chat.Store.Remote where
|
||||
import Control.Monad.Except
|
||||
import Data.Int (Int64)
|
||||
import Data.Text (Text)
|
||||
import Data.Text.Encoding (encodeUtf8, decodeASCII)
|
||||
import Data.Text.Encoding (decodeASCII, encodeUtf8)
|
||||
import Data.Word (Word16)
|
||||
import Database.SQLite.Simple (Only (..))
|
||||
import qualified Database.SQLite.Simple as SQL
|
||||
import Database.SQLite.Simple.QQ (sql)
|
||||
import Simplex.Chat.Remote.Types
|
||||
import Simplex.Chat.Store.Shared
|
||||
import Simplex.Messaging.Agent.Store.AgentStore (firstRow, maybeFirstRow)
|
||||
import qualified Simplex.Messaging.Agent.Store.SQLite.DB as DB
|
||||
import qualified Simplex.Messaging.Agent.Store.DB as DB
|
||||
import qualified Simplex.Messaging.Crypto as C
|
||||
import Simplex.Messaging.Encoding.String (StrEncoding (..))
|
||||
import Simplex.RemoteControl.Types
|
||||
import UnliftIO
|
||||
#if defined(dbPostgres)
|
||||
import Database.PostgreSQL.Simple (Only (..), Query)
|
||||
import Database.PostgreSQL.Simple.SqlQQ (sql)
|
||||
#else
|
||||
import Database.SQLite.Simple (Only (..), Query)
|
||||
import Database.SQLite.Simple.QQ (sql)
|
||||
#endif
|
||||
|
||||
insertRemoteHost :: DB.Connection -> Text -> FilePath -> Maybe RCCtrlAddress -> Maybe Word16 -> RCHostPairing -> ExceptT StoreError IO RemoteHostId
|
||||
insertRemoteHost db hostDeviceName storePath rcAddr_ bindPort_ RCHostPairing {caKey, caCert, idPrivKey, knownHost = kh_} = do
|
||||
@@ -54,7 +59,7 @@ getRemoteHostByFingerprint db fingerprint =
|
||||
maybeFirstRow toRemoteHost $
|
||||
DB.query db (remoteHostQuery <> " WHERE host_fingerprint = ?") (Only fingerprint)
|
||||
|
||||
remoteHostQuery :: SQL.Query
|
||||
remoteHostQuery :: Query
|
||||
remoteHostQuery =
|
||||
[sql|
|
||||
SELECT remote_host_id, host_device_name, store_path, ca_key, ca_cert, id_key, host_fingerprint, host_dh_pub, bind_iface, bind_addr, bind_port
|
||||
@@ -117,7 +122,7 @@ getRemoteCtrlByFingerprint db fingerprint =
|
||||
maybeFirstRow toRemoteCtrl $
|
||||
DB.query db (remoteCtrlQuery <> " WHERE ctrl_fingerprint = ?") (Only fingerprint)
|
||||
|
||||
remoteCtrlQuery :: SQL.Query
|
||||
remoteCtrlQuery :: Query
|
||||
remoteCtrlQuery =
|
||||
[sql|
|
||||
SELECT remote_ctrl_id, ctrl_device_name, ca_key, ca_cert, ctrl_fingerprint, id_pub, dh_priv_key, prev_dh_priv_key
|
||||
|
||||
+120
-120
@@ -1,128 +1,128 @@
|
||||
{-# LANGUAGE NamedFieldPuns #-}
|
||||
|
||||
module Simplex.Chat.Store.Migrations (migrations) where
|
||||
module Simplex.Chat.Store.SQLite.Migrations (migrations) where
|
||||
|
||||
import Data.List (sortOn)
|
||||
import Database.SQLite.Simple (Query (..))
|
||||
import Simplex.Chat.Migrations.M20220101_initial
|
||||
import Simplex.Chat.Migrations.M20220122_v1_1
|
||||
import Simplex.Chat.Migrations.M20220205_chat_item_status
|
||||
import Simplex.Chat.Migrations.M20220210_deduplicate_contact_requests
|
||||
import Simplex.Chat.Migrations.M20220224_messages_fks
|
||||
import Simplex.Chat.Migrations.M20220301_smp_servers
|
||||
import Simplex.Chat.Migrations.M20220302_profile_images
|
||||
import Simplex.Chat.Migrations.M20220304_msg_quotes
|
||||
import Simplex.Chat.Migrations.M20220321_chat_item_edited
|
||||
import Simplex.Chat.Migrations.M20220404_files_status_fields
|
||||
import Simplex.Chat.Migrations.M20220514_profiles_user_id
|
||||
import Simplex.Chat.Migrations.M20220626_auto_reply
|
||||
import Simplex.Chat.Migrations.M20220702_calls
|
||||
import Simplex.Chat.Migrations.M20220715_groups_chat_item_id
|
||||
import Simplex.Chat.Migrations.M20220811_chat_items_indices
|
||||
import Simplex.Chat.Migrations.M20220812_incognito_profiles
|
||||
import Simplex.Chat.Migrations.M20220818_chat_notifications
|
||||
import Simplex.Chat.Migrations.M20220822_groups_host_conn_custom_user_profile_id
|
||||
import Simplex.Chat.Migrations.M20220823_delete_broken_group_event_chat_items
|
||||
import Simplex.Chat.Migrations.M20220824_profiles_local_alias
|
||||
import Simplex.Chat.Migrations.M20220909_commands
|
||||
import Simplex.Chat.Migrations.M20220926_connection_alias
|
||||
import Simplex.Chat.Migrations.M20220928_settings
|
||||
import Simplex.Chat.Migrations.M20221001_shared_msg_id_indices
|
||||
import Simplex.Chat.Migrations.M20221003_delete_broken_integrity_error_chat_items
|
||||
import Simplex.Chat.Migrations.M20221004_idx_msg_deliveries_message_id
|
||||
import Simplex.Chat.Migrations.M20221011_user_contact_links_group_id
|
||||
import Simplex.Chat.Migrations.M20221012_inline_files
|
||||
import Simplex.Chat.Migrations.M20221019_unread_chat
|
||||
import Simplex.Chat.Migrations.M20221021_auto_accept__group_links
|
||||
import Simplex.Chat.Migrations.M20221024_contact_used
|
||||
import Simplex.Chat.Migrations.M20221025_chat_settings
|
||||
import Simplex.Chat.Migrations.M20221029_group_link_id
|
||||
import Simplex.Chat.Migrations.M20221112_server_password
|
||||
import Simplex.Chat.Migrations.M20221115_server_cfg
|
||||
import Simplex.Chat.Migrations.M20221129_delete_group_feature_items
|
||||
import Simplex.Chat.Migrations.M20221130_delete_item_deleted
|
||||
import Simplex.Chat.Migrations.M20221209_verified_connection
|
||||
import Simplex.Chat.Migrations.M20221210_idxs
|
||||
import Simplex.Chat.Migrations.M20221211_group_description
|
||||
import Simplex.Chat.Migrations.M20221212_chat_items_timed
|
||||
import Simplex.Chat.Migrations.M20221214_live_message
|
||||
import Simplex.Chat.Migrations.M20221222_chat_ts
|
||||
import Simplex.Chat.Migrations.M20221223_idx_chat_items_item_status
|
||||
import Simplex.Chat.Migrations.M20221230_idxs
|
||||
import Simplex.Chat.Migrations.M20230107_connections_auth_err_counter
|
||||
import Simplex.Chat.Migrations.M20230111_users_agent_user_id
|
||||
import Simplex.Chat.Migrations.M20230117_fkey_indexes
|
||||
import Simplex.Chat.Migrations.M20230118_recreate_smp_servers
|
||||
import Simplex.Chat.Migrations.M20230129_drop_chat_items_group_idx
|
||||
import Simplex.Chat.Migrations.M20230206_item_deleted_by_group_member_id
|
||||
import Simplex.Chat.Migrations.M20230303_group_link_role
|
||||
import Simplex.Chat.Migrations.M20230317_hidden_profiles
|
||||
import Simplex.Chat.Migrations.M20230318_file_description
|
||||
import Simplex.Chat.Migrations.M20230321_agent_file_deleted
|
||||
import Simplex.Chat.Migrations.M20230328_files_protocol
|
||||
import Simplex.Chat.Migrations.M20230402_protocol_servers
|
||||
import Simplex.Chat.Migrations.M20230411_extra_xftp_file_descriptions
|
||||
import Simplex.Chat.Migrations.M20230420_rcv_files_to_receive
|
||||
import Simplex.Chat.Migrations.M20230422_profile_contact_links
|
||||
import Simplex.Chat.Migrations.M20230504_recreate_msg_delivery_events_cleanup_messages
|
||||
import Simplex.Chat.Migrations.M20230505_chat_item_versions
|
||||
import Simplex.Chat.Migrations.M20230511_reactions
|
||||
import Simplex.Chat.Migrations.M20230519_item_deleted_ts
|
||||
import Simplex.Chat.Migrations.M20230526_indexes
|
||||
import Simplex.Chat.Migrations.M20230529_indexes
|
||||
import Simplex.Chat.Migrations.M20230608_deleted_contacts
|
||||
import Simplex.Chat.Migrations.M20230618_favorite_chats
|
||||
import Simplex.Chat.Migrations.M20230621_chat_item_moderations
|
||||
import Simplex.Chat.Migrations.M20230705_delivery_receipts
|
||||
import Simplex.Chat.Migrations.M20230721_group_snd_item_statuses
|
||||
import Simplex.Chat.Migrations.M20230814_indexes
|
||||
import Simplex.Chat.Migrations.M20230827_file_encryption
|
||||
import Simplex.Chat.Migrations.M20230829_connections_chat_vrange
|
||||
import Simplex.Chat.Migrations.M20230903_connections_to_subscribe
|
||||
import Simplex.Chat.Migrations.M20230913_member_contacts
|
||||
import Simplex.Chat.Migrations.M20230914_member_probes
|
||||
import Simplex.Chat.Migrations.M20230926_contact_status
|
||||
import Simplex.Chat.Migrations.M20231002_conn_initiated
|
||||
import Simplex.Chat.Migrations.M20231009_via_group_link_uri_hash
|
||||
import Simplex.Chat.Migrations.M20231010_member_settings
|
||||
import Simplex.Chat.Migrations.M20231019_indexes
|
||||
import Simplex.Chat.Migrations.M20231030_xgrplinkmem_received
|
||||
import Simplex.Chat.Migrations.M20231107_indexes
|
||||
import Simplex.Chat.Migrations.M20231113_group_forward
|
||||
import Simplex.Chat.Migrations.M20231114_remote_control
|
||||
import Simplex.Chat.Migrations.M20231126_remote_ctrl_address
|
||||
import Simplex.Chat.Migrations.M20231207_chat_list_pagination
|
||||
import Simplex.Chat.Migrations.M20231214_item_content_tag
|
||||
import Simplex.Chat.Migrations.M20231215_recreate_msg_deliveries
|
||||
import Simplex.Chat.Migrations.M20240102_note_folders
|
||||
import Simplex.Chat.Migrations.M20240104_members_profile_update
|
||||
import Simplex.Chat.Migrations.M20240115_block_member_for_all
|
||||
import Simplex.Chat.Migrations.M20240122_indexes
|
||||
import Simplex.Chat.Migrations.M20240214_redirect_file_id
|
||||
import Simplex.Chat.Migrations.M20240222_app_settings
|
||||
import Simplex.Chat.Migrations.M20240226_users_restrict
|
||||
import Simplex.Chat.Migrations.M20240228_pq
|
||||
import Simplex.Chat.Migrations.M20240313_drop_agent_ack_cmd_id
|
||||
import Simplex.Chat.Migrations.M20240324_custom_data
|
||||
import Simplex.Chat.Migrations.M20240402_item_forwarded
|
||||
import Simplex.Chat.Migrations.M20240430_ui_theme
|
||||
import Simplex.Chat.Migrations.M20240501_chat_deleted
|
||||
import Simplex.Chat.Migrations.M20240510_chat_items_via_proxy
|
||||
import Simplex.Chat.Migrations.M20240515_rcv_files_user_approved_relays
|
||||
import Simplex.Chat.Migrations.M20240528_quota_err_counter
|
||||
import Simplex.Chat.Migrations.M20240827_calls_uuid
|
||||
import Simplex.Chat.Migrations.M20240920_user_order
|
||||
import Simplex.Chat.Migrations.M20241008_indexes
|
||||
import Simplex.Chat.Migrations.M20241010_contact_requests_contact_id
|
||||
import Simplex.Chat.Migrations.M20241023_chat_item_autoincrement_id
|
||||
import Simplex.Chat.Migrations.M20241027_server_operators
|
||||
import Simplex.Chat.Migrations.M20241125_indexes
|
||||
import Simplex.Chat.Migrations.M20241128_business_chats
|
||||
import Simplex.Chat.Migrations.M20241205_business_chat_members
|
||||
import Simplex.Chat.Migrations.M20241222_operator_conditions
|
||||
import Simplex.Chat.Migrations.M20241223_chat_tags
|
||||
import Simplex.Chat.Migrations.M20241230_reports
|
||||
import Simplex.Chat.Migrations.M20250105_indexes
|
||||
import Simplex.Chat.Store.SQLite.Migrations.M20220101_initial
|
||||
import Simplex.Chat.Store.SQLite.Migrations.M20220122_v1_1
|
||||
import Simplex.Chat.Store.SQLite.Migrations.M20220205_chat_item_status
|
||||
import Simplex.Chat.Store.SQLite.Migrations.M20220210_deduplicate_contact_requests
|
||||
import Simplex.Chat.Store.SQLite.Migrations.M20220224_messages_fks
|
||||
import Simplex.Chat.Store.SQLite.Migrations.M20220301_smp_servers
|
||||
import Simplex.Chat.Store.SQLite.Migrations.M20220302_profile_images
|
||||
import Simplex.Chat.Store.SQLite.Migrations.M20220304_msg_quotes
|
||||
import Simplex.Chat.Store.SQLite.Migrations.M20220321_chat_item_edited
|
||||
import Simplex.Chat.Store.SQLite.Migrations.M20220404_files_status_fields
|
||||
import Simplex.Chat.Store.SQLite.Migrations.M20220514_profiles_user_id
|
||||
import Simplex.Chat.Store.SQLite.Migrations.M20220626_auto_reply
|
||||
import Simplex.Chat.Store.SQLite.Migrations.M20220702_calls
|
||||
import Simplex.Chat.Store.SQLite.Migrations.M20220715_groups_chat_item_id
|
||||
import Simplex.Chat.Store.SQLite.Migrations.M20220811_chat_items_indices
|
||||
import Simplex.Chat.Store.SQLite.Migrations.M20220812_incognito_profiles
|
||||
import Simplex.Chat.Store.SQLite.Migrations.M20220818_chat_notifications
|
||||
import Simplex.Chat.Store.SQLite.Migrations.M20220822_groups_host_conn_custom_user_profile_id
|
||||
import Simplex.Chat.Store.SQLite.Migrations.M20220823_delete_broken_group_event_chat_items
|
||||
import Simplex.Chat.Store.SQLite.Migrations.M20220824_profiles_local_alias
|
||||
import Simplex.Chat.Store.SQLite.Migrations.M20220909_commands
|
||||
import Simplex.Chat.Store.SQLite.Migrations.M20220926_connection_alias
|
||||
import Simplex.Chat.Store.SQLite.Migrations.M20220928_settings
|
||||
import Simplex.Chat.Store.SQLite.Migrations.M20221001_shared_msg_id_indices
|
||||
import Simplex.Chat.Store.SQLite.Migrations.M20221003_delete_broken_integrity_error_chat_items
|
||||
import Simplex.Chat.Store.SQLite.Migrations.M20221004_idx_msg_deliveries_message_id
|
||||
import Simplex.Chat.Store.SQLite.Migrations.M20221011_user_contact_links_group_id
|
||||
import Simplex.Chat.Store.SQLite.Migrations.M20221012_inline_files
|
||||
import Simplex.Chat.Store.SQLite.Migrations.M20221019_unread_chat
|
||||
import Simplex.Chat.Store.SQLite.Migrations.M20221021_auto_accept__group_links
|
||||
import Simplex.Chat.Store.SQLite.Migrations.M20221024_contact_used
|
||||
import Simplex.Chat.Store.SQLite.Migrations.M20221025_chat_settings
|
||||
import Simplex.Chat.Store.SQLite.Migrations.M20221029_group_link_id
|
||||
import Simplex.Chat.Store.SQLite.Migrations.M20221112_server_password
|
||||
import Simplex.Chat.Store.SQLite.Migrations.M20221115_server_cfg
|
||||
import Simplex.Chat.Store.SQLite.Migrations.M20221129_delete_group_feature_items
|
||||
import Simplex.Chat.Store.SQLite.Migrations.M20221130_delete_item_deleted
|
||||
import Simplex.Chat.Store.SQLite.Migrations.M20221209_verified_connection
|
||||
import Simplex.Chat.Store.SQLite.Migrations.M20221210_idxs
|
||||
import Simplex.Chat.Store.SQLite.Migrations.M20221211_group_description
|
||||
import Simplex.Chat.Store.SQLite.Migrations.M20221212_chat_items_timed
|
||||
import Simplex.Chat.Store.SQLite.Migrations.M20221214_live_message
|
||||
import Simplex.Chat.Store.SQLite.Migrations.M20221222_chat_ts
|
||||
import Simplex.Chat.Store.SQLite.Migrations.M20221223_idx_chat_items_item_status
|
||||
import Simplex.Chat.Store.SQLite.Migrations.M20221230_idxs
|
||||
import Simplex.Chat.Store.SQLite.Migrations.M20230107_connections_auth_err_counter
|
||||
import Simplex.Chat.Store.SQLite.Migrations.M20230111_users_agent_user_id
|
||||
import Simplex.Chat.Store.SQLite.Migrations.M20230117_fkey_indexes
|
||||
import Simplex.Chat.Store.SQLite.Migrations.M20230118_recreate_smp_servers
|
||||
import Simplex.Chat.Store.SQLite.Migrations.M20230129_drop_chat_items_group_idx
|
||||
import Simplex.Chat.Store.SQLite.Migrations.M20230206_item_deleted_by_group_member_id
|
||||
import Simplex.Chat.Store.SQLite.Migrations.M20230303_group_link_role
|
||||
import Simplex.Chat.Store.SQLite.Migrations.M20230317_hidden_profiles
|
||||
import Simplex.Chat.Store.SQLite.Migrations.M20230318_file_description
|
||||
import Simplex.Chat.Store.SQLite.Migrations.M20230321_agent_file_deleted
|
||||
import Simplex.Chat.Store.SQLite.Migrations.M20230328_files_protocol
|
||||
import Simplex.Chat.Store.SQLite.Migrations.M20230402_protocol_servers
|
||||
import Simplex.Chat.Store.SQLite.Migrations.M20230411_extra_xftp_file_descriptions
|
||||
import Simplex.Chat.Store.SQLite.Migrations.M20230420_rcv_files_to_receive
|
||||
import Simplex.Chat.Store.SQLite.Migrations.M20230422_profile_contact_links
|
||||
import Simplex.Chat.Store.SQLite.Migrations.M20230504_recreate_msg_delivery_events_cleanup_messages
|
||||
import Simplex.Chat.Store.SQLite.Migrations.M20230505_chat_item_versions
|
||||
import Simplex.Chat.Store.SQLite.Migrations.M20230511_reactions
|
||||
import Simplex.Chat.Store.SQLite.Migrations.M20230519_item_deleted_ts
|
||||
import Simplex.Chat.Store.SQLite.Migrations.M20230526_indexes
|
||||
import Simplex.Chat.Store.SQLite.Migrations.M20230529_indexes
|
||||
import Simplex.Chat.Store.SQLite.Migrations.M20230608_deleted_contacts
|
||||
import Simplex.Chat.Store.SQLite.Migrations.M20230618_favorite_chats
|
||||
import Simplex.Chat.Store.SQLite.Migrations.M20230621_chat_item_moderations
|
||||
import Simplex.Chat.Store.SQLite.Migrations.M20230705_delivery_receipts
|
||||
import Simplex.Chat.Store.SQLite.Migrations.M20230721_group_snd_item_statuses
|
||||
import Simplex.Chat.Store.SQLite.Migrations.M20230814_indexes
|
||||
import Simplex.Chat.Store.SQLite.Migrations.M20230827_file_encryption
|
||||
import Simplex.Chat.Store.SQLite.Migrations.M20230829_connections_chat_vrange
|
||||
import Simplex.Chat.Store.SQLite.Migrations.M20230903_connections_to_subscribe
|
||||
import Simplex.Chat.Store.SQLite.Migrations.M20230913_member_contacts
|
||||
import Simplex.Chat.Store.SQLite.Migrations.M20230914_member_probes
|
||||
import Simplex.Chat.Store.SQLite.Migrations.M20230926_contact_status
|
||||
import Simplex.Chat.Store.SQLite.Migrations.M20231002_conn_initiated
|
||||
import Simplex.Chat.Store.SQLite.Migrations.M20231009_via_group_link_uri_hash
|
||||
import Simplex.Chat.Store.SQLite.Migrations.M20231010_member_settings
|
||||
import Simplex.Chat.Store.SQLite.Migrations.M20231019_indexes
|
||||
import Simplex.Chat.Store.SQLite.Migrations.M20231030_xgrplinkmem_received
|
||||
import Simplex.Chat.Store.SQLite.Migrations.M20231107_indexes
|
||||
import Simplex.Chat.Store.SQLite.Migrations.M20231113_group_forward
|
||||
import Simplex.Chat.Store.SQLite.Migrations.M20231114_remote_control
|
||||
import Simplex.Chat.Store.SQLite.Migrations.M20231126_remote_ctrl_address
|
||||
import Simplex.Chat.Store.SQLite.Migrations.M20231207_chat_list_pagination
|
||||
import Simplex.Chat.Store.SQLite.Migrations.M20231214_item_content_tag
|
||||
import Simplex.Chat.Store.SQLite.Migrations.M20231215_recreate_msg_deliveries
|
||||
import Simplex.Chat.Store.SQLite.Migrations.M20240102_note_folders
|
||||
import Simplex.Chat.Store.SQLite.Migrations.M20240104_members_profile_update
|
||||
import Simplex.Chat.Store.SQLite.Migrations.M20240115_block_member_for_all
|
||||
import Simplex.Chat.Store.SQLite.Migrations.M20240122_indexes
|
||||
import Simplex.Chat.Store.SQLite.Migrations.M20240214_redirect_file_id
|
||||
import Simplex.Chat.Store.SQLite.Migrations.M20240222_app_settings
|
||||
import Simplex.Chat.Store.SQLite.Migrations.M20240226_users_restrict
|
||||
import Simplex.Chat.Store.SQLite.Migrations.M20240228_pq
|
||||
import Simplex.Chat.Store.SQLite.Migrations.M20240313_drop_agent_ack_cmd_id
|
||||
import Simplex.Chat.Store.SQLite.Migrations.M20240324_custom_data
|
||||
import Simplex.Chat.Store.SQLite.Migrations.M20240402_item_forwarded
|
||||
import Simplex.Chat.Store.SQLite.Migrations.M20240430_ui_theme
|
||||
import Simplex.Chat.Store.SQLite.Migrations.M20240501_chat_deleted
|
||||
import Simplex.Chat.Store.SQLite.Migrations.M20240510_chat_items_via_proxy
|
||||
import Simplex.Chat.Store.SQLite.Migrations.M20240515_rcv_files_user_approved_relays
|
||||
import Simplex.Chat.Store.SQLite.Migrations.M20240528_quota_err_counter
|
||||
import Simplex.Chat.Store.SQLite.Migrations.M20240827_calls_uuid
|
||||
import Simplex.Chat.Store.SQLite.Migrations.M20240920_user_order
|
||||
import Simplex.Chat.Store.SQLite.Migrations.M20241008_indexes
|
||||
import Simplex.Chat.Store.SQLite.Migrations.M20241010_contact_requests_contact_id
|
||||
import Simplex.Chat.Store.SQLite.Migrations.M20241023_chat_item_autoincrement_id
|
||||
import Simplex.Chat.Store.SQLite.Migrations.M20241027_server_operators
|
||||
import Simplex.Chat.Store.SQLite.Migrations.M20241125_indexes
|
||||
import Simplex.Chat.Store.SQLite.Migrations.M20241128_business_chats
|
||||
import Simplex.Chat.Store.SQLite.Migrations.M20241205_business_chat_members
|
||||
import Simplex.Chat.Store.SQLite.Migrations.M20241222_operator_conditions
|
||||
import Simplex.Chat.Store.SQLite.Migrations.M20241223_chat_tags
|
||||
import Simplex.Chat.Store.SQLite.Migrations.M20241230_reports
|
||||
import Simplex.Chat.Store.SQLite.Migrations.M20250105_indexes
|
||||
import Simplex.Messaging.Agent.Store.Shared (Migration (..))
|
||||
|
||||
schemaMigrations :: [(String, Query, Maybe Query)]
|
||||
@@ -0,0 +1,271 @@
|
||||
{-# LANGUAGE QuasiQuotes #-}
|
||||
|
||||
module Simplex.Chat.Store.SQLite.Migrations.M20220101_initial where
|
||||
|
||||
import Database.SQLite.Simple (Query)
|
||||
import Database.SQLite.Simple.QQ (sql)
|
||||
|
||||
m20220101_initial :: Query
|
||||
m20220101_initial =
|
||||
[sql|
|
||||
CREATE TABLE contact_profiles ( -- remote user profile
|
||||
contact_profile_id INTEGER PRIMARY KEY,
|
||||
display_name TEXT NOT NULL, -- contact name set by remote user (not unique), this name must not contain spaces
|
||||
full_name TEXT NOT NULL,
|
||||
properties TEXT NOT NULL DEFAULT '{}' -- JSON with contact profile properties
|
||||
);
|
||||
|
||||
CREATE INDEX contact_profiles_index ON contact_profiles (display_name, full_name);
|
||||
|
||||
CREATE TABLE users (
|
||||
user_id INTEGER PRIMARY KEY,
|
||||
contact_id INTEGER NOT NULL UNIQUE REFERENCES contacts ON DELETE CASCADE
|
||||
DEFERRABLE INITIALLY DEFERRED,
|
||||
local_display_name TEXT NOT NULL UNIQUE,
|
||||
active_user INTEGER NOT NULL DEFAULT 0, -- 1 for active user
|
||||
FOREIGN KEY (user_id, local_display_name)
|
||||
REFERENCES display_names (user_id, local_display_name)
|
||||
ON DELETE CASCADE
|
||||
ON UPDATE CASCADE
|
||||
DEFERRABLE INITIALLY DEFERRED
|
||||
);
|
||||
|
||||
CREATE TABLE display_names (
|
||||
user_id INTEGER NOT NULL REFERENCES users ON DELETE CASCADE,
|
||||
local_display_name TEXT NOT NULL,
|
||||
ldn_base TEXT NOT NULL,
|
||||
ldn_suffix INTEGER NOT NULL DEFAULT 0,
|
||||
PRIMARY KEY (user_id, local_display_name) ON CONFLICT FAIL,
|
||||
UNIQUE (user_id, ldn_base, ldn_suffix) ON CONFLICT FAIL
|
||||
) WITHOUT ROWID;
|
||||
|
||||
CREATE TABLE contacts (
|
||||
contact_id INTEGER PRIMARY KEY,
|
||||
contact_profile_id INTEGER REFERENCES contact_profiles ON DELETE SET NULL,
|
||||
user_id INTEGER NOT NULL REFERENCES users ON DELETE CASCADE,
|
||||
local_display_name TEXT NOT NULL,
|
||||
is_user INTEGER NOT NULL DEFAULT 0, -- 1 if this contact is a user
|
||||
via_group INTEGER REFERENCES groups (group_id) ON DELETE SET NULL,
|
||||
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
||||
FOREIGN KEY (user_id, local_display_name)
|
||||
REFERENCES display_names (user_id, local_display_name)
|
||||
ON DELETE CASCADE
|
||||
ON UPDATE CASCADE,
|
||||
UNIQUE (user_id, local_display_name),
|
||||
UNIQUE (user_id, contact_profile_id)
|
||||
);
|
||||
|
||||
CREATE TABLE sent_probes (
|
||||
sent_probe_id INTEGER PRIMARY KEY,
|
||||
contact_id INTEGER NOT NULL UNIQUE REFERENCES contacts ON DELETE CASCADE,
|
||||
probe BLOB NOT NULL,
|
||||
user_id INTEGER NOT NULL REFERENCES users ON DELETE CASCADE,
|
||||
UNIQUE (user_id, probe)
|
||||
);
|
||||
|
||||
CREATE TABLE sent_probe_hashes (
|
||||
sent_probe_hash_id INTEGER PRIMARY KEY,
|
||||
sent_probe_id INTEGER NOT NULL REFERENCES sent_probes ON DELETE CASCADE,
|
||||
contact_id INTEGER NOT NULL REFERENCES contacts ON DELETE CASCADE,
|
||||
user_id INTEGER NOT NULL REFERENCES users ON DELETE CASCADE,
|
||||
UNIQUE (sent_probe_id, contact_id)
|
||||
);
|
||||
|
||||
CREATE TABLE received_probes (
|
||||
received_probe_id INTEGER PRIMARY KEY,
|
||||
contact_id INTEGER NOT NULL REFERENCES contacts ON DELETE CASCADE,
|
||||
probe BLOB,
|
||||
probe_hash BLOB NOT NULL,
|
||||
user_id INTEGER NOT NULL REFERENCES users ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE TABLE known_servers(
|
||||
server_id INTEGER PRIMARY KEY,
|
||||
host TEXT NOT NULL,
|
||||
port TEXT NOT NULL,
|
||||
key_hash BLOB,
|
||||
user_id INTEGER NOT NULL REFERENCES users ON DELETE CASCADE,
|
||||
UNIQUE (user_id, host, port)
|
||||
) WITHOUT ROWID;
|
||||
|
||||
CREATE TABLE group_profiles ( -- shared group profiles
|
||||
group_profile_id INTEGER PRIMARY KEY,
|
||||
display_name TEXT NOT NULL, -- this name must not contain spaces
|
||||
full_name TEXT NOT NULL,
|
||||
properties TEXT NOT NULL DEFAULT '{}' -- JSON with user or contact profile
|
||||
);
|
||||
|
||||
CREATE TABLE groups (
|
||||
group_id INTEGER PRIMARY KEY, -- local group ID
|
||||
user_id INTEGER NOT NULL REFERENCES users ON DELETE CASCADE,
|
||||
local_display_name TEXT NOT NULL, -- local group name without spaces
|
||||
group_profile_id INTEGER REFERENCES group_profiles ON DELETE SET NULL, -- shared group profile
|
||||
inv_queue_info BLOB, -- received
|
||||
FOREIGN KEY (user_id, local_display_name)
|
||||
REFERENCES display_names (user_id, local_display_name)
|
||||
ON DELETE CASCADE
|
||||
ON UPDATE CASCADE,
|
||||
UNIQUE (user_id, local_display_name),
|
||||
UNIQUE (user_id, group_profile_id)
|
||||
);
|
||||
|
||||
CREATE INDEX idx_groups_inv_queue_info ON groups (inv_queue_info);
|
||||
|
||||
CREATE TABLE group_members ( -- group members, excluding the local user
|
||||
group_member_id INTEGER PRIMARY KEY,
|
||||
group_id INTEGER NOT NULL REFERENCES groups ON DELETE CASCADE,
|
||||
member_id BLOB NOT NULL, -- shared member ID, unique per group
|
||||
member_role TEXT NOT NULL, -- owner, admin, member
|
||||
member_category TEXT NOT NULL, -- see GroupMemberCategory
|
||||
member_status TEXT NOT NULL, -- see GroupMemberStatus
|
||||
invited_by INTEGER REFERENCES contacts (contact_id) ON DELETE SET NULL, -- NULL for the members who joined before the current user and for the group creator
|
||||
sent_inv_queue_info BLOB, -- sent
|
||||
group_queue_info BLOB, -- received
|
||||
direct_queue_info BLOB, -- received
|
||||
user_id INTEGER NOT NULL REFERENCES users ON DELETE CASCADE,
|
||||
local_display_name TEXT NOT NULL, -- should be the same as contact
|
||||
contact_profile_id INTEGER NOT NULL REFERENCES contact_profiles ON DELETE CASCADE,
|
||||
contact_id INTEGER REFERENCES contacts ON DELETE CASCADE,
|
||||
FOREIGN KEY (user_id, local_display_name)
|
||||
REFERENCES display_names (user_id, local_display_name)
|
||||
ON DELETE CASCADE
|
||||
ON UPDATE CASCADE,
|
||||
UNIQUE (group_id, member_id)
|
||||
);
|
||||
|
||||
CREATE TABLE group_member_intros (
|
||||
group_member_intro_id INTEGER PRIMARY KEY,
|
||||
re_group_member_id INTEGER NOT NULL REFERENCES group_members (group_member_id) ON DELETE CASCADE,
|
||||
to_group_member_id INTEGER NOT NULL REFERENCES group_members (group_member_id) ON DELETE CASCADE,
|
||||
group_queue_info BLOB,
|
||||
direct_queue_info BLOB,
|
||||
intro_status TEXT NOT NULL, -- see GroupMemberIntroStatus
|
||||
UNIQUE (re_group_member_id, to_group_member_id)
|
||||
);
|
||||
|
||||
CREATE TABLE files (
|
||||
file_id INTEGER PRIMARY KEY,
|
||||
contact_id INTEGER REFERENCES contacts ON DELETE CASCADE,
|
||||
group_id INTEGER REFERENCES groups ON DELETE CASCADE,
|
||||
file_name TEXT NOT NULL,
|
||||
file_path TEXT,
|
||||
file_size INTEGER NOT NULL,
|
||||
chunk_size INTEGER NOT NULL,
|
||||
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
||||
user_id INTEGER NOT NULL REFERENCES users ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE TABLE snd_files (
|
||||
file_id INTEGER NOT NULL REFERENCES files ON DELETE CASCADE,
|
||||
connection_id INTEGER NOT NULL REFERENCES connections ON DELETE CASCADE,
|
||||
file_status TEXT NOT NULL, -- new, accepted, connected, completed
|
||||
group_member_id INTEGER REFERENCES group_members ON DELETE CASCADE,
|
||||
PRIMARY KEY (file_id, connection_id)
|
||||
) WITHOUT ROWID;
|
||||
|
||||
CREATE TABLE rcv_files (
|
||||
file_id INTEGER PRIMARY KEY REFERENCES files ON DELETE CASCADE,
|
||||
file_status TEXT NOT NULL, -- new, accepted, connected, completed
|
||||
group_member_id INTEGER REFERENCES group_members ON DELETE CASCADE,
|
||||
file_queue_info BLOB
|
||||
);
|
||||
|
||||
CREATE TABLE snd_file_chunks (
|
||||
file_id INTEGER NOT NULL,
|
||||
connection_id INTEGER NOT NULL,
|
||||
chunk_number INTEGER NOT NULL,
|
||||
chunk_agent_msg_id INTEGER,
|
||||
chunk_sent INTEGER NOT NULL DEFAULT 0, -- 0 (sent to agent), 1 (sent to server)
|
||||
FOREIGN KEY (file_id, connection_id) REFERENCES snd_files ON DELETE CASCADE,
|
||||
PRIMARY KEY (file_id, connection_id, chunk_number)
|
||||
) WITHOUT ROWID;
|
||||
|
||||
CREATE TABLE rcv_file_chunks (
|
||||
file_id INTEGER NOT NULL REFERENCES rcv_files ON DELETE CASCADE,
|
||||
chunk_number INTEGER NOT NULL,
|
||||
chunk_agent_msg_id INTEGER NOT NULL,
|
||||
chunk_stored INTEGER NOT NULL DEFAULT 0, -- 0 (received), 1 (appended to file)
|
||||
PRIMARY KEY (file_id, chunk_number)
|
||||
) WITHOUT ROWID;
|
||||
|
||||
CREATE TABLE connections ( -- all SMP agent connections
|
||||
connection_id INTEGER PRIMARY KEY,
|
||||
agent_conn_id BLOB NOT NULL UNIQUE,
|
||||
conn_level INTEGER NOT NULL DEFAULT 0,
|
||||
via_contact INTEGER REFERENCES contacts (contact_id) ON DELETE SET NULL,
|
||||
conn_status TEXT NOT NULL,
|
||||
conn_type TEXT NOT NULL, -- contact, member, rcv_file, snd_file
|
||||
user_contact_link_id INTEGER REFERENCES user_contact_links ON DELETE CASCADE,
|
||||
contact_id INTEGER REFERENCES contacts ON DELETE CASCADE,
|
||||
group_member_id INTEGER REFERENCES group_members ON DELETE CASCADE,
|
||||
snd_file_id INTEGER,
|
||||
rcv_file_id INTEGER REFERENCES rcv_files (file_id) ON DELETE CASCADE,
|
||||
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
||||
user_id INTEGER NOT NULL REFERENCES users ON DELETE CASCADE,
|
||||
FOREIGN KEY (snd_file_id, connection_id)
|
||||
REFERENCES snd_files (file_id, connection_id)
|
||||
ON DELETE CASCADE
|
||||
DEFERRABLE INITIALLY DEFERRED
|
||||
);
|
||||
|
||||
CREATE TABLE user_contact_links (
|
||||
user_contact_link_id INTEGER PRIMARY KEY,
|
||||
conn_req_contact BLOB NOT NULL,
|
||||
local_display_name TEXT NOT NULL DEFAULT '',
|
||||
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
||||
user_id INTEGER NOT NULL REFERENCES users ON DELETE CASCADE,
|
||||
UNIQUE (user_id, local_display_name)
|
||||
);
|
||||
|
||||
CREATE TABLE contact_requests (
|
||||
contact_request_id INTEGER PRIMARY KEY,
|
||||
user_contact_link_id INTEGER NOT NULL REFERENCES user_contact_links
|
||||
ON UPDATE CASCADE ON DELETE CASCADE,
|
||||
agent_invitation_id BLOB NOT NULL,
|
||||
contact_profile_id INTEGER REFERENCES contact_profiles
|
||||
ON DELETE SET NULL
|
||||
DEFERRABLE INITIALLY DEFERRED,
|
||||
local_display_name TEXT NOT NULL,
|
||||
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
||||
user_id INTEGER NOT NULL REFERENCES users ON DELETE CASCADE,
|
||||
FOREIGN KEY (user_id, local_display_name)
|
||||
REFERENCES display_names (user_id, local_display_name)
|
||||
ON UPDATE CASCADE
|
||||
ON DELETE CASCADE
|
||||
DEFERRABLE INITIALLY DEFERRED,
|
||||
UNIQUE (user_id, local_display_name),
|
||||
UNIQUE (user_id, contact_profile_id)
|
||||
);
|
||||
|
||||
-- all message events as received or sent, append only
|
||||
-- maps to message deliveries as one-to-many for group messages
|
||||
CREATE TABLE messages (
|
||||
message_id INTEGER PRIMARY KEY,
|
||||
msg_sent INTEGER NOT NULL, -- 0 for received, 1 for sent
|
||||
chat_msg_event TEXT NOT NULL, -- message event tag (the constructor of CMEventTag)
|
||||
msg_body BLOB, -- agent message body as received or sent
|
||||
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
||||
);
|
||||
|
||||
-- TODO ? agent_msg_id could be NOT NULL now that pending_group_messages are separate
|
||||
-- message deliveries communicated with the agent, append only
|
||||
CREATE TABLE msg_deliveries (
|
||||
msg_delivery_id INTEGER PRIMARY KEY,
|
||||
message_id INTEGER NOT NULL REFERENCES messages ON DELETE CASCADE, -- non UNIQUE for group messages
|
||||
connection_id INTEGER NOT NULL REFERENCES connections ON DELETE CASCADE,
|
||||
agent_msg_id INTEGER, -- internal agent message ID (NULL while pending)
|
||||
agent_msg_meta TEXT, -- JSON with timestamps etc. sent in MSG, NULL for sent
|
||||
chat_ts TEXT NOT NULL DEFAULT (datetime('now')), -- broker_ts for received, created_at for sent
|
||||
UNIQUE (connection_id, agent_msg_id)
|
||||
);
|
||||
|
||||
-- TODO recovery for received messages with "rcv_agent" status - acknowledge to agent
|
||||
-- changes of message delivery status, append only
|
||||
CREATE TABLE msg_delivery_events (
|
||||
msg_delivery_event_id INTEGER PRIMARY KEY,
|
||||
msg_delivery_id INTEGER NOT NULL REFERENCES msg_deliveries ON DELETE CASCADE, -- non UNIQUE for multiple events per msg delivery
|
||||
delivery_status TEXT NOT NULL, -- see MsgDeliveryStatus for allowed values
|
||||
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
||||
UNIQUE (msg_delivery_id, delivery_status)
|
||||
);
|
||||
|]
|
||||
@@ -0,0 +1,221 @@
|
||||
{-# LANGUAGE QuasiQuotes #-}
|
||||
|
||||
module Simplex.Chat.Store.SQLite.Migrations.M20220122_v1_1 where
|
||||
|
||||
import Database.SQLite.Simple (Query)
|
||||
import Database.SQLite.Simple.QQ (sql)
|
||||
|
||||
m20220122_v1_1 :: Query
|
||||
m20220122_v1_1 =
|
||||
[sql|
|
||||
-- * pending group messages
|
||||
|
||||
-- pending messages for announced (memberCurrent) but not yet connected (memberActive) group members
|
||||
CREATE TABLE pending_group_messages (
|
||||
pending_group_message_id INTEGER PRIMARY KEY,
|
||||
group_member_id INTEGER NOT NULL REFERENCES group_members ON DELETE CASCADE,
|
||||
message_id INTEGER NOT NULL REFERENCES messages ON DELETE CASCADE,
|
||||
group_member_intro_id INTEGER REFERENCES group_member_intros ON DELETE CASCADE,
|
||||
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
||||
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
||||
);
|
||||
|
||||
-- * chat items
|
||||
|
||||
-- mutable chat_items presented to user
|
||||
CREATE TABLE chat_items (
|
||||
chat_item_id INTEGER PRIMARY KEY,
|
||||
user_id INTEGER NOT NULL REFERENCES users ON DELETE CASCADE,
|
||||
contact_id INTEGER REFERENCES contacts ON DELETE CASCADE,
|
||||
group_id INTEGER REFERENCES groups ON DELETE CASCADE,
|
||||
group_member_id INTEGER REFERENCES group_members ON DELETE SET NULL, -- NULL for sent even if group_id is not
|
||||
chat_msg_id INTEGER, -- sent as part of the message that created the item
|
||||
created_by_msg_id INTEGER UNIQUE REFERENCES messages (message_id) ON DELETE SET NULL,
|
||||
item_sent INTEGER NOT NULL, -- 0 for received, 1 for sent
|
||||
item_ts TEXT NOT NULL, -- broker_ts of creating message for received, created_at for sent
|
||||
item_deleted INTEGER NOT NULL DEFAULT 0, -- 1 for deleted
|
||||
item_content TEXT NOT NULL, -- JSON
|
||||
item_text TEXT NOT NULL, -- textual representation
|
||||
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
||||
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
||||
);
|
||||
|
||||
CREATE TABLE chat_item_messages (
|
||||
chat_item_id INTEGER NOT NULL REFERENCES chat_items ON DELETE CASCADE,
|
||||
message_id INTEGER NOT NULL UNIQUE REFERENCES messages ON DELETE CASCADE,
|
||||
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
||||
updated_at TEXT NOT NULL DEFAULT (datetime('now')),
|
||||
UNIQUE (chat_item_id, message_id)
|
||||
);
|
||||
|
||||
ALTER TABLE files ADD COLUMN chat_item_id INTEGER DEFAULT NULL REFERENCES chat_items ON DELETE CASCADE;
|
||||
|
||||
-- * created_at & updated_at for all tables
|
||||
|
||||
PRAGMA ignore_check_constraints=ON;
|
||||
|
||||
-- ** contact_profiles
|
||||
|
||||
ALTER TABLE contact_profiles ADD COLUMN created_at TEXT CHECK (created_at NOT NULL);
|
||||
UPDATE contact_profiles SET created_at = '1970-01-01 00:00:00';
|
||||
|
||||
ALTER TABLE contact_profiles ADD COLUMN updated_at TEXT CHECK (updated_at NOT NULL);
|
||||
UPDATE contact_profiles SET updated_at = '1970-01-01 00:00:00';
|
||||
|
||||
-- ** users
|
||||
|
||||
ALTER TABLE users ADD COLUMN created_at TEXT CHECK (created_at NOT NULL);
|
||||
UPDATE users SET created_at = '1970-01-01 00:00:00';
|
||||
|
||||
ALTER TABLE users ADD COLUMN updated_at TEXT CHECK (updated_at NOT NULL);
|
||||
UPDATE users SET updated_at = '1970-01-01 00:00:00';
|
||||
|
||||
-- ** display_names
|
||||
|
||||
ALTER TABLE display_names ADD COLUMN created_at TEXT CHECK (created_at NOT NULL);
|
||||
UPDATE display_names SET created_at = '1970-01-01 00:00:00';
|
||||
|
||||
ALTER TABLE display_names ADD COLUMN updated_at TEXT CHECK (updated_at NOT NULL);
|
||||
UPDATE display_names SET updated_at = '1970-01-01 00:00:00';
|
||||
|
||||
-- ** contacts
|
||||
|
||||
ALTER TABLE contacts ADD COLUMN updated_at TEXT CHECK (updated_at NOT NULL);
|
||||
UPDATE contacts SET updated_at = '1970-01-01 00:00:00';
|
||||
|
||||
-- ** sent_probes
|
||||
|
||||
ALTER TABLE sent_probes ADD COLUMN created_at TEXT CHECK (created_at NOT NULL);
|
||||
UPDATE sent_probes SET created_at = '1970-01-01 00:00:00';
|
||||
|
||||
ALTER TABLE sent_probes ADD COLUMN updated_at TEXT CHECK (updated_at NOT NULL);
|
||||
UPDATE sent_probes SET updated_at = '1970-01-01 00:00:00';
|
||||
|
||||
-- ** sent_probe_hashes
|
||||
|
||||
ALTER TABLE sent_probe_hashes ADD COLUMN created_at TEXT CHECK (created_at NOT NULL);
|
||||
UPDATE sent_probe_hashes SET created_at = '1970-01-01 00:00:00';
|
||||
|
||||
ALTER TABLE sent_probe_hashes ADD COLUMN updated_at TEXT CHECK (updated_at NOT NULL);
|
||||
UPDATE sent_probe_hashes SET updated_at = '1970-01-01 00:00:00';
|
||||
|
||||
-- ** received_probes
|
||||
|
||||
ALTER TABLE received_probes ADD COLUMN created_at TEXT CHECK (created_at NOT NULL);
|
||||
UPDATE received_probes SET created_at = '1970-01-01 00:00:00';
|
||||
|
||||
ALTER TABLE received_probes ADD COLUMN updated_at TEXT CHECK (updated_at NOT NULL);
|
||||
UPDATE received_probes SET updated_at = '1970-01-01 00:00:00';
|
||||
|
||||
-- ** known_servers
|
||||
|
||||
ALTER TABLE known_servers ADD COLUMN created_at TEXT CHECK (created_at NOT NULL);
|
||||
UPDATE known_servers SET created_at = '1970-01-01 00:00:00';
|
||||
|
||||
ALTER TABLE known_servers ADD COLUMN updated_at TEXT CHECK (updated_at NOT NULL);
|
||||
UPDATE known_servers SET updated_at = '1970-01-01 00:00:00';
|
||||
|
||||
-- ** group_profiles
|
||||
|
||||
ALTER TABLE group_profiles ADD COLUMN created_at TEXT CHECK (created_at NOT NULL);
|
||||
UPDATE group_profiles SET created_at = '1970-01-01 00:00:00';
|
||||
|
||||
ALTER TABLE group_profiles ADD COLUMN updated_at TEXT CHECK (updated_at NOT NULL);
|
||||
UPDATE group_profiles SET updated_at = '1970-01-01 00:00:00';
|
||||
|
||||
-- ** groups
|
||||
|
||||
ALTER TABLE groups ADD COLUMN created_at TEXT CHECK (created_at NOT NULL);
|
||||
UPDATE groups SET created_at = '1970-01-01 00:00:00';
|
||||
|
||||
ALTER TABLE groups ADD COLUMN updated_at TEXT CHECK (updated_at NOT NULL);
|
||||
UPDATE groups SET updated_at = '1970-01-01 00:00:00';
|
||||
|
||||
-- ** group_members
|
||||
|
||||
ALTER TABLE group_members ADD COLUMN created_at TEXT CHECK (created_at NOT NULL);
|
||||
UPDATE group_members SET created_at = '1970-01-01 00:00:00';
|
||||
|
||||
ALTER TABLE group_members ADD COLUMN updated_at TEXT CHECK (updated_at NOT NULL);
|
||||
UPDATE group_members SET updated_at = '1970-01-01 00:00:00';
|
||||
|
||||
-- ** group_member_intros
|
||||
|
||||
ALTER TABLE group_member_intros ADD COLUMN created_at TEXT CHECK (created_at NOT NULL);
|
||||
UPDATE group_member_intros SET created_at = '1970-01-01 00:00:00';
|
||||
|
||||
ALTER TABLE group_member_intros ADD COLUMN updated_at TEXT CHECK (updated_at NOT NULL);
|
||||
UPDATE group_member_intros SET updated_at = '1970-01-01 00:00:00';
|
||||
|
||||
-- ** files
|
||||
|
||||
ALTER TABLE files ADD COLUMN updated_at TEXT CHECK (updated_at NOT NULL);
|
||||
UPDATE files SET updated_at = '1970-01-01 00:00:00';
|
||||
|
||||
-- ** snd_files
|
||||
|
||||
ALTER TABLE snd_files ADD COLUMN created_at TEXT CHECK (created_at NOT NULL);
|
||||
UPDATE snd_files SET created_at = '1970-01-01 00:00:00';
|
||||
|
||||
ALTER TABLE snd_files ADD COLUMN updated_at TEXT CHECK (updated_at NOT NULL);
|
||||
UPDATE snd_files SET updated_at = '1970-01-01 00:00:00';
|
||||
|
||||
-- ** rcv_files
|
||||
|
||||
ALTER TABLE rcv_files ADD COLUMN created_at TEXT CHECK (created_at NOT NULL);
|
||||
UPDATE rcv_files SET created_at = '1970-01-01 00:00:00';
|
||||
|
||||
ALTER TABLE rcv_files ADD COLUMN updated_at TEXT CHECK (updated_at NOT NULL);
|
||||
UPDATE rcv_files SET updated_at = '1970-01-01 00:00:00';
|
||||
|
||||
-- ** snd_file_chunks
|
||||
|
||||
ALTER TABLE snd_file_chunks ADD COLUMN created_at TEXT CHECK (created_at NOT NULL);
|
||||
UPDATE snd_file_chunks SET created_at = '1970-01-01 00:00:00';
|
||||
|
||||
ALTER TABLE snd_file_chunks ADD COLUMN updated_at TEXT CHECK (updated_at NOT NULL);
|
||||
UPDATE snd_file_chunks SET updated_at = '1970-01-01 00:00:00';
|
||||
|
||||
-- ** rcv_file_chunks
|
||||
|
||||
ALTER TABLE rcv_file_chunks ADD COLUMN created_at TEXT CHECK (created_at NOT NULL);
|
||||
UPDATE rcv_file_chunks SET created_at = '1970-01-01 00:00:00';
|
||||
|
||||
ALTER TABLE rcv_file_chunks ADD COLUMN updated_at TEXT CHECK (updated_at NOT NULL);
|
||||
UPDATE rcv_file_chunks SET updated_at = '1970-01-01 00:00:00';
|
||||
|
||||
-- ** connections
|
||||
|
||||
ALTER TABLE connections ADD COLUMN updated_at TEXT CHECK (updated_at NOT NULL);
|
||||
UPDATE connections SET updated_at = '1970-01-01 00:00:00';
|
||||
|
||||
-- ** user_contact_links
|
||||
|
||||
ALTER TABLE user_contact_links ADD COLUMN updated_at TEXT CHECK (updated_at NOT NULL);
|
||||
UPDATE user_contact_links SET updated_at = '1970-01-01 00:00:00';
|
||||
|
||||
-- ** contact_requests
|
||||
|
||||
ALTER TABLE contact_requests ADD COLUMN updated_at TEXT CHECK (updated_at NOT NULL);
|
||||
UPDATE contact_requests SET updated_at = '1970-01-01 00:00:00';
|
||||
|
||||
-- ** messages
|
||||
|
||||
ALTER TABLE messages ADD COLUMN updated_at TEXT CHECK (updated_at NOT NULL);
|
||||
UPDATE messages SET updated_at = '1970-01-01 00:00:00';
|
||||
|
||||
-- ** msg_deliveries
|
||||
|
||||
ALTER TABLE msg_deliveries ADD COLUMN created_at TEXT CHECK (created_at NOT NULL);
|
||||
UPDATE msg_deliveries SET created_at = '1970-01-01 00:00:00';
|
||||
|
||||
ALTER TABLE msg_deliveries ADD COLUMN updated_at TEXT CHECK (updated_at NOT NULL);
|
||||
UPDATE msg_deliveries SET updated_at = '1970-01-01 00:00:00';
|
||||
|
||||
-- ** msg_delivery_events
|
||||
|
||||
ALTER TABLE msg_delivery_events ADD COLUMN updated_at TEXT CHECK (updated_at NOT NULL);
|
||||
UPDATE msg_delivery_events SET updated_at = '1970-01-01 00:00:00';
|
||||
|
||||
PRAGMA ignore_check_constraints=OFF;
|
||||
|]
|
||||
@@ -0,0 +1,20 @@
|
||||
{-# LANGUAGE QuasiQuotes #-}
|
||||
|
||||
module Simplex.Chat.Store.SQLite.Migrations.M20220205_chat_item_status where
|
||||
|
||||
import Database.SQLite.Simple (Query)
|
||||
import Database.SQLite.Simple.QQ (sql)
|
||||
|
||||
m20220205_chat_item_status :: Query
|
||||
m20220205_chat_item_status =
|
||||
[sql|
|
||||
PRAGMA ignore_check_constraints=ON;
|
||||
|
||||
ALTER TABLE chat_items ADD COLUMN item_status TEXT CHECK (item_status NOT NULL);
|
||||
|
||||
UPDATE chat_items SET item_status = 'rcv_read' WHERE item_sent = 0;
|
||||
|
||||
UPDATE chat_items SET item_status = 'snd_sent' WHERE item_sent = 1;
|
||||
|
||||
PRAGMA ignore_check_constraints=OFF;
|
||||
|]
|
||||
@@ -0,0 +1,25 @@
|
||||
{-# LANGUAGE QuasiQuotes #-}
|
||||
|
||||
module Simplex.Chat.Store.SQLite.Migrations.M20220210_deduplicate_contact_requests where
|
||||
|
||||
import Database.SQLite.Simple (Query)
|
||||
import Database.SQLite.Simple.QQ (sql)
|
||||
|
||||
m20220210_deduplicate_contact_requests :: Query
|
||||
m20220210_deduplicate_contact_requests =
|
||||
[sql|
|
||||
-- hash of contact address uri used by contact request sender to connect,
|
||||
-- null for contact request recipient and for both parties when using one-off invitation
|
||||
ALTER TABLE connections ADD COLUMN via_contact_uri_hash BLOB;
|
||||
CREATE INDEX idx_connections_via_contact_uri_hash ON connections (via_contact_uri_hash);
|
||||
|
||||
ALTER TABLE connections ADD COLUMN xcontact_id BLOB;
|
||||
|
||||
ALTER TABLE contact_requests ADD COLUMN xcontact_id BLOB;
|
||||
CREATE INDEX idx_contact_requests_xcontact_id ON contact_requests (xcontact_id);
|
||||
|
||||
ALTER TABLE contacts ADD COLUMN xcontact_id BLOB;
|
||||
CREATE INDEX idx_contacts_xcontact_id ON contacts (xcontact_id);
|
||||
|
||||
ALTER TABLE user_contact_links ADD column auto_accept INTEGER DEFAULT 0;
|
||||
|]
|
||||
@@ -0,0 +1,13 @@
|
||||
{-# LANGUAGE QuasiQuotes #-}
|
||||
|
||||
module Simplex.Chat.Store.SQLite.Migrations.M20220224_messages_fks where
|
||||
|
||||
import Database.SQLite.Simple (Query)
|
||||
import Database.SQLite.Simple.QQ (sql)
|
||||
|
||||
m20220224_messages_fks :: Query
|
||||
m20220224_messages_fks =
|
||||
[sql|
|
||||
ALTER TABLE messages ADD COLUMN connection_id INTEGER DEFAULT NULL REFERENCES connections ON DELETE CASCADE;
|
||||
ALTER TABLE messages ADD COLUMN group_id INTEGER DEFAULT NULL REFERENCES groups ON DELETE CASCADE;
|
||||
|]
|
||||
@@ -0,0 +1,21 @@
|
||||
{-# LANGUAGE QuasiQuotes #-}
|
||||
|
||||
module Simplex.Chat.Store.SQLite.Migrations.M20220301_smp_servers where
|
||||
|
||||
import Database.SQLite.Simple (Query)
|
||||
import Database.SQLite.Simple.QQ (sql)
|
||||
|
||||
m20220301_smp_servers :: Query
|
||||
m20220301_smp_servers =
|
||||
[sql|
|
||||
CREATE TABLE smp_servers (
|
||||
smp_server_id INTEGER PRIMARY KEY,
|
||||
host TEXT NOT NULL,
|
||||
port TEXT NOT NULL,
|
||||
key_hash BLOB NOT NULL,
|
||||
user_id INTEGER NOT NULL REFERENCES users ON DELETE CASCADE,
|
||||
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
||||
updated_at TEXT NOT NULL DEFAULT (datetime('now')),
|
||||
UNIQUE (host, port)
|
||||
);
|
||||
|]
|
||||
@@ -0,0 +1,13 @@
|
||||
{-# LANGUAGE QuasiQuotes #-}
|
||||
|
||||
module Simplex.Chat.Store.SQLite.Migrations.M20220302_profile_images where
|
||||
|
||||
import Database.SQLite.Simple (Query)
|
||||
import Database.SQLite.Simple.QQ (sql)
|
||||
|
||||
m20220302_profile_images :: Query
|
||||
m20220302_profile_images =
|
||||
[sql|
|
||||
ALTER TABLE contact_profiles ADD COLUMN image TEXT;
|
||||
ALTER TABLE group_profiles ADD COLUMN image TEXT;
|
||||
|]
|
||||
@@ -0,0 +1,24 @@
|
||||
{-# LANGUAGE QuasiQuotes #-}
|
||||
|
||||
module Simplex.Chat.Store.SQLite.Migrations.M20220304_msg_quotes where
|
||||
|
||||
import Database.SQLite.Simple (Query)
|
||||
import Database.SQLite.Simple.QQ (sql)
|
||||
|
||||
m20220304_msg_quotes :: Query
|
||||
m20220304_msg_quotes =
|
||||
[sql|
|
||||
ALTER TABLE messages ADD COLUMN shared_msg_id BLOB;
|
||||
ALTER TABLE messages ADD COLUMN shared_msg_id_user INTEGER; -- 1 for user messages, NULL for received messages
|
||||
CREATE INDEX idx_messages_shared_msg_id ON messages (shared_msg_id);
|
||||
CREATE UNIQUE INDEX idx_messages_direct_shared_msg_id ON messages (connection_id, shared_msg_id_user, shared_msg_id);
|
||||
CREATE UNIQUE INDEX idx_messages_group_shared_msg_id ON messages (group_id, shared_msg_id_user, shared_msg_id);
|
||||
|
||||
ALTER TABLE chat_items ADD COLUMN shared_msg_id BLOB;
|
||||
ALTER TABLE chat_items ADD COLUMN quoted_shared_msg_id BLOB; -- from MessageRef in QuotedMsg
|
||||
ALTER TABLE chat_items ADD COLUMN quoted_sent_at TEXT; -- from MessageRef in QuotedMsg
|
||||
ALTER TABLE chat_items ADD COLUMN quoted_content TEXT; -- from MsgContent in QuotedMsg (JSON)
|
||||
ALTER TABLE chat_items ADD COLUMN quoted_sent INTEGER; -- from MessageRef, 1 for sent, 0 for received, NULL for messages without quote
|
||||
ALTER TABLE chat_items ADD COLUMN quoted_member_id BLOB; -- from MessageRef
|
||||
CREATE INDEX idx_chat_items_shared_msg_id ON chat_items (shared_msg_id);
|
||||
|]
|
||||
@@ -0,0 +1,12 @@
|
||||
{-# LANGUAGE QuasiQuotes #-}
|
||||
|
||||
module Simplex.Chat.Store.SQLite.Migrations.M20220321_chat_item_edited where
|
||||
|
||||
import Database.SQLite.Simple (Query)
|
||||
import Database.SQLite.Simple.QQ (sql)
|
||||
|
||||
m20220321_chat_item_edited :: Query
|
||||
m20220321_chat_item_edited =
|
||||
[sql|
|
||||
ALTER TABLE chat_items ADD COLUMN item_edited INTEGER; -- 1 for edited
|
||||
|]
|
||||
@@ -0,0 +1,19 @@
|
||||
{-# LANGUAGE QuasiQuotes #-}
|
||||
|
||||
module Simplex.Chat.Store.SQLite.Migrations.M20220404_files_status_fields where
|
||||
|
||||
import Database.SQLite.Simple (Query)
|
||||
import Database.SQLite.Simple.QQ (sql)
|
||||
|
||||
m20220404_files_status_fields :: Query
|
||||
m20220404_files_status_fields =
|
||||
[sql|
|
||||
ALTER TABLE files ADD COLUMN cancelled INTEGER; -- 1 for cancelled
|
||||
ALTER TABLE files ADD COLUMN ci_file_status TEXT; -- CIFileStatus
|
||||
|
||||
DELETE FROM chat_items
|
||||
WHERE chat_item_id IN (
|
||||
SELECT chat_item_id
|
||||
FROM files
|
||||
);
|
||||
|]
|
||||
@@ -0,0 +1,26 @@
|
||||
{-# LANGUAGE QuasiQuotes #-}
|
||||
|
||||
module Simplex.Chat.Store.SQLite.Migrations.M20220514_profiles_user_id where
|
||||
|
||||
import Database.SQLite.Simple (Query)
|
||||
import Database.SQLite.Simple.QQ (sql)
|
||||
|
||||
m20220514_profiles_user_id :: Query
|
||||
m20220514_profiles_user_id =
|
||||
[sql|
|
||||
ALTER TABLE contact_profiles ADD COLUMN user_id INTEGER DEFAULT NULL REFERENCES users ON DELETE CASCADE;
|
||||
UPDATE contact_profiles SET user_id = (
|
||||
SELECT user_id
|
||||
FROM users
|
||||
WHERE active_user = 1
|
||||
LIMIT 1
|
||||
);
|
||||
|
||||
ALTER TABLE group_profiles ADD COLUMN user_id INTEGER DEFAULT NULL REFERENCES users ON DELETE CASCADE;
|
||||
UPDATE group_profiles SET user_id = (
|
||||
SELECT user_id
|
||||
FROM users
|
||||
WHERE active_user = 1
|
||||
LIMIT 1
|
||||
);
|
||||
|]
|
||||
@@ -0,0 +1,15 @@
|
||||
{-# LANGUAGE QuasiQuotes #-}
|
||||
|
||||
module Simplex.Chat.Store.SQLite.Migrations.M20220626_auto_reply where
|
||||
|
||||
import Database.SQLite.Simple (Query)
|
||||
import Database.SQLite.Simple.QQ (sql)
|
||||
|
||||
m20220626_auto_reply :: Query
|
||||
m20220626_auto_reply =
|
||||
[sql|
|
||||
ALTER TABLE user_contact_links ADD COLUMN auto_reply_msg_content TEXT DEFAULT NULL;
|
||||
|
||||
ALTER TABLE connections ADD COLUMN via_user_contact_link INTEGER DEFAULT NULL
|
||||
REFERENCES user_contact_links (user_contact_link_id) ON DELETE SET NULL;
|
||||
|]
|
||||
@@ -0,0 +1,22 @@
|
||||
{-# LANGUAGE QuasiQuotes #-}
|
||||
|
||||
module Simplex.Chat.Store.SQLite.Migrations.M20220702_calls where
|
||||
|
||||
import Database.SQLite.Simple (Query)
|
||||
import Database.SQLite.Simple.QQ (sql)
|
||||
|
||||
m20220702_calls :: Query
|
||||
m20220702_calls =
|
||||
[sql|
|
||||
CREATE TABLE calls ( -- stores call invitations state for communicating state between NSE and app when call notification comes
|
||||
call_id INTEGER PRIMARY KEY,
|
||||
contact_id INTEGER NOT NULL REFERENCES contacts ON DELETE CASCADE,
|
||||
shared_call_id BLOB NOT NULL,
|
||||
chat_item_id INTEGER NOT NULL REFERENCES chat_items ON DELETE CASCADE,
|
||||
call_state BLOB NOT NULL,
|
||||
call_ts TEXT NOT NULL,
|
||||
user_id INTEGER NOT NULL REFERENCES users ON DELETE CASCADE,
|
||||
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
||||
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
||||
);
|
||||
|]
|
||||
@@ -0,0 +1,12 @@
|
||||
{-# LANGUAGE QuasiQuotes #-}
|
||||
|
||||
module Simplex.Chat.Store.SQLite.Migrations.M20220715_groups_chat_item_id where
|
||||
|
||||
import Database.SQLite.Simple (Query)
|
||||
import Database.SQLite.Simple.QQ (sql)
|
||||
|
||||
m20220715_groups_chat_item_id :: Query
|
||||
m20220715_groups_chat_item_id =
|
||||
[sql|
|
||||
ALTER TABLE groups ADD COLUMN chat_item_id INTEGER DEFAULT NULL REFERENCES chat_items ON DELETE SET NULL;
|
||||
|]
|
||||
@@ -0,0 +1,13 @@
|
||||
{-# LANGUAGE QuasiQuotes #-}
|
||||
|
||||
module Simplex.Chat.Store.SQLite.Migrations.M20220811_chat_items_indices where
|
||||
|
||||
import Database.SQLite.Simple (Query)
|
||||
import Database.SQLite.Simple.QQ (sql)
|
||||
|
||||
m20220811_chat_items_indices :: Query
|
||||
m20220811_chat_items_indices =
|
||||
[sql|
|
||||
CREATE INDEX idx_chat_items_groups ON chat_items(user_id, group_id, item_ts, chat_item_id);
|
||||
CREATE INDEX idx_chat_items_contacts ON chat_items(user_id, contact_id, chat_item_id);
|
||||
|]
|
||||
@@ -0,0 +1,16 @@
|
||||
{-# LANGUAGE QuasiQuotes #-}
|
||||
|
||||
module Simplex.Chat.Store.SQLite.Migrations.M20220812_incognito_profiles where
|
||||
|
||||
import Database.SQLite.Simple (Query)
|
||||
import Database.SQLite.Simple.QQ (sql)
|
||||
|
||||
m20220812_incognito_profiles :: Query
|
||||
m20220812_incognito_profiles =
|
||||
[sql|
|
||||
ALTER TABLE connections ADD COLUMN custom_user_profile_id INTEGER REFERENCES contact_profiles ON DELETE SET NULL; -- only set for direct connections
|
||||
|
||||
ALTER TABLE group_members ADD COLUMN member_profile_id INTEGER REFERENCES contact_profiles ON DELETE SET NULL; -- member profile id if incognito profile was saved for member (used when invitation is received via incognito direct connection with host)
|
||||
|
||||
ALTER TABLE contact_profiles ADD COLUMN incognito INTEGER; -- 1 for incognito
|
||||
|]
|
||||
@@ -0,0 +1,14 @@
|
||||
{-# LANGUAGE QuasiQuotes #-}
|
||||
|
||||
module Simplex.Chat.Store.SQLite.Migrations.M20220818_chat_notifications where
|
||||
|
||||
import Database.SQLite.Simple (Query)
|
||||
import Database.SQLite.Simple.QQ (sql)
|
||||
|
||||
m20220818_chat_notifications :: Query
|
||||
m20220818_chat_notifications =
|
||||
[sql|
|
||||
ALTER TABLE contacts ADD COLUMN enable_ntfs INTEGER;
|
||||
|
||||
ALTER TABLE groups ADD COLUMN enable_ntfs INTEGER;
|
||||
|]
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
{-# LANGUAGE QuasiQuotes #-}
|
||||
|
||||
module Simplex.Chat.Store.SQLite.Migrations.M20220822_groups_host_conn_custom_user_profile_id where
|
||||
|
||||
import Database.SQLite.Simple (Query)
|
||||
import Database.SQLite.Simple.QQ (sql)
|
||||
|
||||
m20220822_groups_host_conn_custom_user_profile_id :: Query
|
||||
m20220822_groups_host_conn_custom_user_profile_id =
|
||||
[sql|
|
||||
ALTER TABLE groups ADD COLUMN host_conn_custom_user_profile_id INTEGER REFERENCES contact_profiles ON DELETE SET NULL; -- id of custom user profile used in direct connection with host
|
||||
|]
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
{-# LANGUAGE QuasiQuotes #-}
|
||||
|
||||
module Simplex.Chat.Store.SQLite.Migrations.M20220823_delete_broken_group_event_chat_items where
|
||||
|
||||
import Database.SQLite.Simple (Query)
|
||||
import Database.SQLite.Simple.QQ (sql)
|
||||
|
||||
m20220823_delete_broken_group_event_chat_items :: Query
|
||||
m20220823_delete_broken_group_event_chat_items =
|
||||
[sql|
|
||||
DELETE FROM chat_items WHERE item_content LIKE '%{"rcvGroupEvent":{"rcvGroupEvent":{%';
|
||||
DELETE FROM chat_items WHERE item_content LIKE '%{"sndGroupEvent":{"sndGroupEvent":{%';
|
||||
|]
|
||||
@@ -0,0 +1,17 @@
|
||||
{-# LANGUAGE QuasiQuotes #-}
|
||||
|
||||
module Simplex.Chat.Store.SQLite.Migrations.M20220824_profiles_local_alias where
|
||||
|
||||
import Database.SQLite.Simple (Query)
|
||||
import Database.SQLite.Simple.QQ (sql)
|
||||
|
||||
m20220824_profiles_local_alias :: Query
|
||||
m20220824_profiles_local_alias =
|
||||
[sql|
|
||||
PRAGMA ignore_check_constraints=ON;
|
||||
|
||||
ALTER TABLE contact_profiles ADD COLUMN local_alias TEXT DEFAULT '' CHECK (local_alias NOT NULL);
|
||||
UPDATE contact_profiles SET local_alias = '';
|
||||
|
||||
PRAGMA ignore_check_constraints=OFF;
|
||||
|]
|
||||
@@ -0,0 +1,24 @@
|
||||
{-# LANGUAGE QuasiQuotes #-}
|
||||
|
||||
module Simplex.Chat.Store.SQLite.Migrations.M20220909_commands where
|
||||
|
||||
import Database.SQLite.Simple (Query)
|
||||
import Database.SQLite.Simple.QQ (sql)
|
||||
|
||||
m20220909_commands :: Query
|
||||
m20220909_commands =
|
||||
[sql|
|
||||
CREATE TABLE commands (
|
||||
command_id INTEGER PRIMARY KEY AUTOINCREMENT, -- used as ACorrId
|
||||
connection_id INTEGER REFERENCES connections ON DELETE CASCADE,
|
||||
command_function TEXT NOT NULL,
|
||||
command_status TEXT NOT NULL,
|
||||
user_id INTEGER NOT NULL REFERENCES users ON DELETE CASCADE,
|
||||
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
||||
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
||||
);
|
||||
|
||||
ALTER TABLE msg_deliveries ADD COLUMN agent_ack_cmd_id INTEGER; -- correlation id
|
||||
|
||||
ALTER TABLE connections ADD COLUMN conn_req_inv BLOB;
|
||||
|]
|
||||
@@ -0,0 +1,17 @@
|
||||
{-# LANGUAGE QuasiQuotes #-}
|
||||
|
||||
module Simplex.Chat.Store.SQLite.Migrations.M20220926_connection_alias where
|
||||
|
||||
import Database.SQLite.Simple (Query)
|
||||
import Database.SQLite.Simple.QQ (sql)
|
||||
|
||||
m20220926_connection_alias :: Query
|
||||
m20220926_connection_alias =
|
||||
[sql|
|
||||
PRAGMA ignore_check_constraints=ON;
|
||||
|
||||
ALTER TABLE connections ADD COLUMN local_alias DEFAULT '' CHECK (local_alias NOT NULL);
|
||||
UPDATE connections SET local_alias = '';
|
||||
|
||||
PRAGMA ignore_check_constraints=OFF;
|
||||
|]
|
||||
@@ -0,0 +1,18 @@
|
||||
{-# LANGUAGE QuasiQuotes #-}
|
||||
|
||||
module Simplex.Chat.Store.SQLite.Migrations.M20220928_settings where
|
||||
|
||||
import Database.SQLite.Simple (Query)
|
||||
import Database.SQLite.Simple.QQ (sql)
|
||||
|
||||
m20220928_settings :: Query
|
||||
m20220928_settings =
|
||||
[sql|
|
||||
CREATE TABLE settings (
|
||||
settings_id INTEGER PRIMARY KEY,
|
||||
chat_item_ttl INTEGER,
|
||||
user_id INTEGER NOT NULL REFERENCES users ON DELETE CASCADE,
|
||||
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
||||
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
||||
);
|
||||
|]
|
||||
@@ -0,0 +1,25 @@
|
||||
{-# LANGUAGE QuasiQuotes #-}
|
||||
|
||||
module Simplex.Chat.Store.SQLite.Migrations.M20221001_shared_msg_id_indices where
|
||||
|
||||
import Database.SQLite.Simple (Query)
|
||||
import Database.SQLite.Simple.QQ (sql)
|
||||
|
||||
m20221001_shared_msg_id_indices :: Query
|
||||
m20221001_shared_msg_id_indices =
|
||||
[sql|
|
||||
DROP INDEX idx_messages_group_shared_msg_id;
|
||||
|
||||
CREATE UNIQUE INDEX idx_chat_items_direct_shared_msg_id ON chat_items(
|
||||
user_id,
|
||||
contact_id,
|
||||
shared_msg_id
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX idx_chat_items_group_shared_msg_id ON chat_items(
|
||||
user_id,
|
||||
group_id,
|
||||
group_member_id,
|
||||
shared_msg_id
|
||||
);
|
||||
|]
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
{-# LANGUAGE QuasiQuotes #-}
|
||||
|
||||
module Simplex.Chat.Store.SQLite.Migrations.M20221003_delete_broken_integrity_error_chat_items where
|
||||
|
||||
import Database.SQLite.Simple (Query)
|
||||
import Database.SQLite.Simple.QQ (sql)
|
||||
|
||||
m20221003_delete_broken_integrity_error_chat_items :: Query
|
||||
m20221003_delete_broken_integrity_error_chat_items =
|
||||
[sql|
|
||||
DELETE FROM chat_items WHERE item_content LIKE '%{"rcvIntegrityError":{%';
|
||||
|]
|
||||
@@ -0,0 +1,12 @@
|
||||
{-# LANGUAGE QuasiQuotes #-}
|
||||
|
||||
module Simplex.Chat.Store.SQLite.Migrations.M20221004_idx_msg_deliveries_message_id where
|
||||
|
||||
import Database.SQLite.Simple (Query)
|
||||
import Database.SQLite.Simple.QQ (sql)
|
||||
|
||||
m20221004_idx_msg_deliveries_message_id :: Query
|
||||
m20221004_idx_msg_deliveries_message_id =
|
||||
[sql|
|
||||
CREATE INDEX idx_msg_deliveries_message_id ON msg_deliveries(message_id);
|
||||
|]
|
||||
@@ -0,0 +1,14 @@
|
||||
{-# LANGUAGE QuasiQuotes #-}
|
||||
|
||||
module Simplex.Chat.Store.SQLite.Migrations.M20221011_user_contact_links_group_id where
|
||||
|
||||
import Database.SQLite.Simple (Query)
|
||||
import Database.SQLite.Simple.QQ (sql)
|
||||
|
||||
m20221011_user_contact_links_group_id :: Query
|
||||
m20221011_user_contact_links_group_id =
|
||||
[sql|
|
||||
ALTER TABLE user_contact_links ADD COLUMN group_id INTEGER REFERENCES groups ON DELETE CASCADE;
|
||||
|
||||
CREATE UNIQUE INDEX idx_user_contact_links_group_id ON user_contact_links(group_id);
|
||||
|]
|
||||
@@ -0,0 +1,20 @@
|
||||
{-# LANGUAGE QuasiQuotes #-}
|
||||
|
||||
module Simplex.Chat.Store.SQLite.Migrations.M20221012_inline_files where
|
||||
|
||||
import Database.SQLite.Simple (Query)
|
||||
import Database.SQLite.Simple.QQ (sql)
|
||||
|
||||
m20221012_inline_files :: Query
|
||||
m20221012_inline_files =
|
||||
[sql|
|
||||
DROP INDEX idx_messages_direct_shared_msg_id;
|
||||
|
||||
ALTER TABLE files ADD COLUMN file_inline TEXT; -- based on offer, determined by file sender for both sides
|
||||
ALTER TABLE rcv_files ADD COLUMN rcv_file_inline TEXT; -- actual mode when receiving file, determined when invitation is accepted
|
||||
ALTER TABLE rcv_files ADD COLUMN file_inline TEXT; -- based on offer, determined when invitation is processed
|
||||
ALTER TABLE snd_files ADD COLUMN file_inline TEXT; -- actual mode when sending file, determined when invitation is accepted
|
||||
ALTER TABLE snd_files ADD COLUMN last_inline_msg_delivery_id INTEGER;
|
||||
|
||||
CREATE UNIQUE INDEX idx_snd_files_last_inline_msg_delivery_id ON snd_files(last_inline_msg_delivery_id);
|
||||
|]
|
||||
@@ -0,0 +1,20 @@
|
||||
{-# LANGUAGE QuasiQuotes #-}
|
||||
|
||||
module Simplex.Chat.Store.SQLite.Migrations.M20221019_unread_chat where
|
||||
|
||||
import Database.SQLite.Simple (Query)
|
||||
import Database.SQLite.Simple.QQ (sql)
|
||||
|
||||
m20221019_unread_chat :: Query
|
||||
m20221019_unread_chat =
|
||||
[sql|
|
||||
PRAGMA ignore_check_constraints=ON;
|
||||
|
||||
ALTER TABLE contacts ADD COLUMN unread_chat INTEGER DEFAULT 0 CHECK (unread_chat NOT NULL);
|
||||
UPDATE contacts SET unread_chat = 0;
|
||||
ALTER TABLE groups ADD COLUMN unread_chat INTEGER DEFAULT 0 CHECK (unread_chat NOT NULL);
|
||||
UPDATE groups SET unread_chat = 0;
|
||||
|
||||
PRAGMA ignore_check_constraints=OFF;
|
||||
|]
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
{-# LANGUAGE QuasiQuotes #-}
|
||||
|
||||
module Simplex.Chat.Store.SQLite.Migrations.M20221021_auto_accept__group_links where
|
||||
|
||||
import Database.SQLite.Simple (Query)
|
||||
import Database.SQLite.Simple.QQ (sql)
|
||||
|
||||
m20221021_auto_accept__group_links :: Query
|
||||
m20221021_auto_accept__group_links =
|
||||
[sql|
|
||||
PRAGMA ignore_check_constraints=ON;
|
||||
|
||||
ALTER TABLE connections ADD COLUMN via_group_link INTEGER DEFAULT 0 CHECK (via_group_link NOT NULL); -- flag, 1 for connections via group link
|
||||
UPDATE connections SET via_group_link = 0;
|
||||
|
||||
ALTER TABLE user_contact_links ADD column auto_accept_incognito INTEGER DEFAULT 0 CHECK (auto_accept_incognito NOT NULL);
|
||||
UPDATE user_contact_links SET auto_accept_incognito = 0;
|
||||
|
||||
PRAGMA ignore_check_constraints=OFF;
|
||||
|]
|
||||
@@ -0,0 +1,22 @@
|
||||
{-# LANGUAGE QuasiQuotes #-}
|
||||
|
||||
module Simplex.Chat.Store.SQLite.Migrations.M20221024_contact_used where
|
||||
|
||||
import Database.SQLite.Simple (Query)
|
||||
import Database.SQLite.Simple.QQ (sql)
|
||||
|
||||
m20221024_contact_used :: Query
|
||||
m20221024_contact_used =
|
||||
[sql|
|
||||
PRAGMA ignore_check_constraints=ON;
|
||||
|
||||
ALTER TABLE contacts ADD COLUMN contact_used INTEGER DEFAULT 0 CHECK (contact_used NOT NULL);
|
||||
|
||||
UPDATE contacts SET contact_used = 0;
|
||||
|
||||
UPDATE contacts SET contact_used = 1 WHERE contact_id IN (
|
||||
SELECT DISTINCT contact_id FROM chat_items WHERE contact_id IS NOT NULL
|
||||
);
|
||||
|
||||
PRAGMA ignore_check_constraints=OFF;
|
||||
|]
|
||||
@@ -0,0 +1,21 @@
|
||||
{-# LANGUAGE QuasiQuotes #-}
|
||||
|
||||
module Simplex.Chat.Store.SQLite.Migrations.M20221025_chat_settings where
|
||||
|
||||
import Database.SQLite.Simple (Query)
|
||||
import Database.SQLite.Simple.QQ (sql)
|
||||
|
||||
m20221025_chat_settings :: Query
|
||||
m20221025_chat_settings =
|
||||
[sql|
|
||||
PRAGMA ignore_check_constraints=ON;
|
||||
|
||||
ALTER TABLE group_profiles ADD COLUMN preferences TEXT;
|
||||
|
||||
ALTER TABLE contact_profiles ADD COLUMN preferences TEXT;
|
||||
|
||||
ALTER TABLE contacts ADD COLUMN user_preferences TEXT DEFAULT '{}' CHECK (user_preferences NOT NULL);
|
||||
UPDATE contacts SET user_preferences = '{}';
|
||||
|
||||
PRAGMA ignore_check_constraints=OFF;
|
||||
|]
|
||||
@@ -0,0 +1,14 @@
|
||||
{-# LANGUAGE QuasiQuotes #-}
|
||||
|
||||
module Simplex.Chat.Store.SQLite.Migrations.M20221029_group_link_id where
|
||||
|
||||
import Database.SQLite.Simple (Query)
|
||||
import Database.SQLite.Simple.QQ (sql)
|
||||
|
||||
m20221029_group_link_id :: Query
|
||||
m20221029_group_link_id =
|
||||
[sql|
|
||||
ALTER TABLE user_contact_links ADD COLUMN group_link_id BLOB;
|
||||
|
||||
ALTER TABLE connections ADD COLUMN group_link_id BLOB;
|
||||
|]
|
||||
@@ -0,0 +1,12 @@
|
||||
{-# LANGUAGE QuasiQuotes #-}
|
||||
|
||||
module Simplex.Chat.Store.SQLite.Migrations.M20221112_server_password where
|
||||
|
||||
import Database.SQLite.Simple (Query)
|
||||
import Database.SQLite.Simple.QQ (sql)
|
||||
|
||||
m20221112_server_password :: Query
|
||||
m20221112_server_password =
|
||||
[sql|
|
||||
ALTER TABLE smp_servers ADD COLUMN basic_auth TEXT;
|
||||
|]
|
||||
@@ -0,0 +1,19 @@
|
||||
{-# LANGUAGE QuasiQuotes #-}
|
||||
|
||||
module Simplex.Chat.Store.SQLite.Migrations.M20221115_server_cfg where
|
||||
|
||||
import Database.SQLite.Simple (Query)
|
||||
import Database.SQLite.Simple.QQ (sql)
|
||||
|
||||
m20221115_server_cfg :: Query
|
||||
m20221115_server_cfg =
|
||||
[sql|
|
||||
PRAGMA ignore_check_constraints=ON;
|
||||
|
||||
ALTER TABLE smp_servers ADD COLUMN preset INTEGER DEFAULT 0 CHECK (preset NOT NULL);
|
||||
ALTER TABLE smp_servers ADD COLUMN tested INTEGER;
|
||||
ALTER TABLE smp_servers ADD COLUMN enabled INTEGER DEFAULT 1 CHECK (enabled NOT NULL);
|
||||
UPDATE smp_servers SET preset = 0, enabled = 1;
|
||||
|
||||
PRAGMA ignore_check_constraints=OFF;
|
||||
|]
|
||||
@@ -0,0 +1,13 @@
|
||||
{-# LANGUAGE QuasiQuotes #-}
|
||||
|
||||
module Simplex.Chat.Store.SQLite.Migrations.M20221129_delete_group_feature_items where
|
||||
|
||||
import Database.SQLite.Simple (Query)
|
||||
import Database.SQLite.Simple.QQ (sql)
|
||||
|
||||
m20221129_delete_group_feature_items :: Query
|
||||
m20221129_delete_group_feature_items =
|
||||
[sql|
|
||||
DELETE FROM chat_items WHERE item_content LIKE '%{"rcvGroupFeature":{%';
|
||||
DELETE FROM chat_items WHERE item_content LIKE '%{"sndGroupFeature":{%';
|
||||
|]
|
||||
@@ -0,0 +1,12 @@
|
||||
{-# LANGUAGE QuasiQuotes #-}
|
||||
|
||||
module Simplex.Chat.Store.SQLite.Migrations.M20221130_delete_item_deleted where
|
||||
|
||||
import Database.SQLite.Simple (Query)
|
||||
import Database.SQLite.Simple.QQ (sql)
|
||||
|
||||
m20221130_delete_item_deleted :: Query
|
||||
m20221130_delete_item_deleted =
|
||||
[sql|
|
||||
DELETE FROM chat_items WHERE item_deleted = 1; -- clean up legacy not fully deleted group chat items
|
||||
|]
|
||||
@@ -0,0 +1,13 @@
|
||||
{-# LANGUAGE QuasiQuotes #-}
|
||||
|
||||
module Simplex.Chat.Store.SQLite.Migrations.M20221209_verified_connection where
|
||||
|
||||
import Database.SQLite.Simple (Query)
|
||||
import Database.SQLite.Simple.QQ (sql)
|
||||
|
||||
m20221209_verified_connection :: Query
|
||||
m20221209_verified_connection =
|
||||
[sql|
|
||||
ALTER TABLE connections ADD COLUMN security_code TEXT NULL;
|
||||
ALTER TABLE connections ADD COLUMN security_code_verified_at TEXT NULL;
|
||||
|]
|
||||
@@ -0,0 +1,16 @@
|
||||
{-# LANGUAGE QuasiQuotes #-}
|
||||
|
||||
module Simplex.Chat.Store.SQLite.Migrations.M20221210_idxs where
|
||||
|
||||
import Database.SQLite.Simple (Query)
|
||||
import Database.SQLite.Simple.QQ (sql)
|
||||
|
||||
m20221210_idxs :: Query
|
||||
m20221210_idxs =
|
||||
[sql|
|
||||
CREATE INDEX idx_messages_connection_id ON messages(connection_id);
|
||||
|
||||
CREATE INDEX idx_chat_items_group_member_id ON chat_items(group_member_id);
|
||||
|
||||
CREATE INDEX idx_chat_items_contact_id ON chat_items(contact_id);
|
||||
|]
|
||||
@@ -0,0 +1,12 @@
|
||||
{-# LANGUAGE QuasiQuotes #-}
|
||||
|
||||
module Simplex.Chat.Store.SQLite.Migrations.M20221211_group_description where
|
||||
|
||||
import Database.SQLite.Simple (Query)
|
||||
import Database.SQLite.Simple.QQ (sql)
|
||||
|
||||
m20221211_group_description :: Query
|
||||
m20221211_group_description =
|
||||
[sql|
|
||||
ALTER TABLE group_profiles ADD COLUMN description TEXT NULL;
|
||||
|]
|
||||
@@ -0,0 +1,16 @@
|
||||
{-# LANGUAGE QuasiQuotes #-}
|
||||
|
||||
module Simplex.Chat.Store.SQLite.Migrations.M20221212_chat_items_timed where
|
||||
|
||||
import Database.SQLite.Simple (Query)
|
||||
import Database.SQLite.Simple.QQ (sql)
|
||||
|
||||
m20221212_chat_items_timed :: Query
|
||||
m20221212_chat_items_timed =
|
||||
[sql|
|
||||
ALTER TABLE chat_items ADD COLUMN timed_ttl INTEGER;
|
||||
|
||||
ALTER TABLE chat_items ADD COLUMN timed_delete_at TEXT;
|
||||
|
||||
CREATE INDEX idx_chat_items_timed_delete_at ON chat_items(timed_delete_at);
|
||||
|]
|
||||
@@ -0,0 +1,12 @@
|
||||
{-# LANGUAGE QuasiQuotes #-}
|
||||
|
||||
module Simplex.Chat.Store.SQLite.Migrations.M20221214_live_message where
|
||||
|
||||
import Database.SQLite.Simple (Query)
|
||||
import Database.SQLite.Simple.QQ (sql)
|
||||
|
||||
m20221214_live_message :: Query
|
||||
m20221214_live_message =
|
||||
[sql|
|
||||
ALTER TABLE chat_items ADD COLUMN item_live INTEGER;
|
||||
|]
|
||||
@@ -0,0 +1,14 @@
|
||||
{-# LANGUAGE QuasiQuotes #-}
|
||||
|
||||
module Simplex.Chat.Store.SQLite.Migrations.M20221222_chat_ts where
|
||||
|
||||
import Database.SQLite.Simple (Query)
|
||||
import Database.SQLite.Simple.QQ (sql)
|
||||
|
||||
m20221222_chat_ts :: Query
|
||||
m20221222_chat_ts =
|
||||
[sql|
|
||||
ALTER TABLE contacts ADD COLUMN chat_ts TEXT; -- must be not NULL
|
||||
|
||||
ALTER TABLE groups ADD COLUMN chat_ts TEXT; -- must be not NULL
|
||||
|]
|
||||
@@ -0,0 +1,12 @@
|
||||
{-# LANGUAGE QuasiQuotes #-}
|
||||
|
||||
module Simplex.Chat.Store.SQLite.Migrations.M20221223_idx_chat_items_item_status where
|
||||
|
||||
import Database.SQLite.Simple (Query)
|
||||
import Database.SQLite.Simple.QQ (sql)
|
||||
|
||||
m20221223_idx_chat_items_item_status :: Query
|
||||
m20221223_idx_chat_items_item_status =
|
||||
[sql|
|
||||
CREATE INDEX idx_chat_items_item_status ON chat_items(item_status);
|
||||
|]
|
||||
@@ -0,0 +1,14 @@
|
||||
{-# LANGUAGE QuasiQuotes #-}
|
||||
|
||||
module Simplex.Chat.Store.SQLite.Migrations.M20221230_idxs where
|
||||
|
||||
import Database.SQLite.Simple (Query)
|
||||
import Database.SQLite.Simple.QQ (sql)
|
||||
|
||||
m20221230_idxs :: Query
|
||||
m20221230_idxs =
|
||||
[sql|
|
||||
CREATE INDEX idx_connections_group_member ON connections(user_id, group_member_id);
|
||||
|
||||
CREATE INDEX idx_commands_connection_id ON commands(connection_id);
|
||||
|]
|
||||
@@ -0,0 +1,17 @@
|
||||
{-# LANGUAGE QuasiQuotes #-}
|
||||
|
||||
module Simplex.Chat.Store.SQLite.Migrations.M20230107_connections_auth_err_counter where
|
||||
|
||||
import Database.SQLite.Simple (Query)
|
||||
import Database.SQLite.Simple.QQ (sql)
|
||||
|
||||
m20230107_connections_auth_err_counter :: Query
|
||||
m20230107_connections_auth_err_counter =
|
||||
[sql|
|
||||
PRAGMA ignore_check_constraints=ON;
|
||||
|
||||
ALTER TABLE connections ADD COLUMN auth_err_counter INTEGER DEFAULT 0 CHECK (auth_err_counter NOT NULL);
|
||||
UPDATE connections SET auth_err_counter = 0;
|
||||
|
||||
PRAGMA ignore_check_constraints=OFF;
|
||||
|]
|
||||
@@ -0,0 +1,17 @@
|
||||
{-# LANGUAGE QuasiQuotes #-}
|
||||
|
||||
module Simplex.Chat.Store.SQLite.Migrations.M20230111_users_agent_user_id where
|
||||
|
||||
import Database.SQLite.Simple (Query)
|
||||
import Database.SQLite.Simple.QQ (sql)
|
||||
|
||||
m20230111_users_agent_user_id :: Query
|
||||
m20230111_users_agent_user_id =
|
||||
[sql|
|
||||
PRAGMA ignore_check_constraints=ON;
|
||||
|
||||
ALTER TABLE users ADD COLUMN agent_user_id INTEGER CHECK (agent_user_id NOT NULL);
|
||||
UPDATE users SET agent_user_id = 1;
|
||||
|
||||
PRAGMA ignore_check_constraints=OFF;
|
||||
|]
|
||||
@@ -0,0 +1,59 @@
|
||||
{-# LANGUAGE QuasiQuotes #-}
|
||||
|
||||
module Simplex.Chat.Store.SQLite.Migrations.M20230117_fkey_indexes where
|
||||
|
||||
import Database.SQLite.Simple (Query)
|
||||
import Database.SQLite.Simple.QQ (sql)
|
||||
|
||||
-- .lint fkey-indexes
|
||||
m20230117_fkey_indexes :: Query
|
||||
m20230117_fkey_indexes =
|
||||
[sql|
|
||||
CREATE INDEX idx_calls_user_id ON calls(user_id);
|
||||
CREATE INDEX idx_calls_chat_item_id ON calls(chat_item_id);
|
||||
CREATE INDEX idx_calls_contact_id ON calls(contact_id);
|
||||
CREATE INDEX idx_chat_items_group_id ON chat_items(group_id);
|
||||
CREATE INDEX idx_commands_user_id ON commands(user_id);
|
||||
CREATE INDEX idx_connections_custom_user_profile_id ON connections(custom_user_profile_id);
|
||||
CREATE INDEX idx_connections_via_user_contact_link ON connections(via_user_contact_link);
|
||||
CREATE INDEX idx_connections_rcv_file_id ON connections(rcv_file_id);
|
||||
CREATE INDEX idx_connections_contact_id ON connections(contact_id);
|
||||
CREATE INDEX idx_connections_user_contact_link_id ON connections(user_contact_link_id);
|
||||
CREATE INDEX idx_connections_via_contact ON connections(via_contact);
|
||||
CREATE INDEX idx_contact_profiles_user_id ON contact_profiles(user_id);
|
||||
CREATE INDEX idx_contact_requests_contact_profile_id ON contact_requests(contact_profile_id);
|
||||
CREATE INDEX idx_contact_requests_user_contact_link_id ON contact_requests(user_contact_link_id);
|
||||
CREATE INDEX idx_contacts_via_group ON contacts(via_group);
|
||||
CREATE INDEX idx_contacts_contact_profile_id ON contacts(contact_profile_id);
|
||||
CREATE INDEX idx_files_chat_item_id ON files(chat_item_id);
|
||||
CREATE INDEX idx_files_user_id ON files(user_id);
|
||||
CREATE INDEX idx_files_group_id ON files(group_id);
|
||||
CREATE INDEX idx_files_contact_id ON files(contact_id);
|
||||
CREATE INDEX idx_group_member_intros_to_group_member_id ON group_member_intros(to_group_member_id);
|
||||
CREATE INDEX idx_group_members_user_id_local_display_name ON group_members(user_id, local_display_name);
|
||||
CREATE INDEX idx_group_members_member_profile_id ON group_members(member_profile_id);
|
||||
CREATE INDEX idx_group_members_contact_id ON group_members(contact_id);
|
||||
CREATE INDEX idx_group_members_contact_profile_id ON group_members(contact_profile_id);
|
||||
CREATE INDEX idx_group_members_user_id ON group_members(user_id);
|
||||
CREATE INDEX idx_group_members_invited_by ON group_members(invited_by);
|
||||
CREATE INDEX idx_group_profiles_user_id ON group_profiles(user_id);
|
||||
CREATE INDEX idx_groups_host_conn_custom_user_profile_id ON groups(host_conn_custom_user_profile_id);
|
||||
CREATE INDEX idx_groups_chat_item_id ON groups(chat_item_id);
|
||||
CREATE INDEX idx_groups_group_profile_id ON groups(group_profile_id);
|
||||
CREATE INDEX idx_messages_group_id ON messages(group_id);
|
||||
CREATE INDEX idx_pending_group_messages_group_member_intro_id ON pending_group_messages(group_member_intro_id);
|
||||
CREATE INDEX idx_pending_group_messages_message_id ON pending_group_messages(message_id);
|
||||
CREATE INDEX idx_pending_group_messages_group_member_id ON pending_group_messages(group_member_id);
|
||||
CREATE INDEX idx_rcv_file_chunks_file_id ON rcv_file_chunks(file_id);
|
||||
CREATE INDEX idx_rcv_files_group_member_id ON rcv_files(group_member_id);
|
||||
CREATE INDEX idx_received_probes_user_id ON received_probes(user_id);
|
||||
CREATE INDEX idx_received_probes_contact_id ON received_probes(contact_id);
|
||||
CREATE INDEX idx_sent_probe_hashes_user_id ON sent_probe_hashes(user_id);
|
||||
CREATE INDEX idx_sent_probe_hashes_contact_id ON sent_probe_hashes(contact_id);
|
||||
CREATE INDEX idx_settings_user_id ON settings(user_id);
|
||||
CREATE INDEX idx_smp_servers_user_id ON smp_servers(user_id);
|
||||
CREATE INDEX idx_snd_file_chunks_file_id_connection_id ON snd_file_chunks(file_id, connection_id);
|
||||
CREATE INDEX idx_snd_files_group_member_id ON snd_files(group_member_id);
|
||||
CREATE INDEX idx_snd_files_connection_id ON snd_files(connection_id);
|
||||
CREATE INDEX idx_snd_files_file_id ON snd_files(file_id);
|
||||
|]
|
||||
@@ -0,0 +1,39 @@
|
||||
{-# LANGUAGE QuasiQuotes #-}
|
||||
|
||||
module Simplex.Chat.Store.SQLite.Migrations.M20230118_recreate_smp_servers where
|
||||
|
||||
import Database.SQLite.Simple (Query)
|
||||
import Database.SQLite.Simple.QQ (sql)
|
||||
|
||||
-- UNIQUE constraint includes user_id
|
||||
m20230118_recreate_smp_servers :: Query
|
||||
m20230118_recreate_smp_servers =
|
||||
[sql|
|
||||
DROP INDEX idx_smp_servers_user_id;
|
||||
|
||||
CREATE TABLE new_smp_servers (
|
||||
smp_server_id INTEGER PRIMARY KEY,
|
||||
host TEXT NOT NULL,
|
||||
port TEXT NOT NULL,
|
||||
key_hash BLOB NOT NULL,
|
||||
basic_auth TEXT,
|
||||
preset INTEGER NOT NULL DEFAULT 0,
|
||||
tested INTEGER,
|
||||
enabled INTEGER NOT NULL DEFAULT 1,
|
||||
user_id INTEGER NOT NULL REFERENCES users ON DELETE CASCADE,
|
||||
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
||||
updated_at TEXT NOT NULL DEFAULT (datetime('now')),
|
||||
UNIQUE (user_id, host, port)
|
||||
);
|
||||
|
||||
INSERT INTO new_smp_servers
|
||||
(smp_server_id, host, port, key_hash, basic_auth, preset, tested, enabled, user_id, created_at, updated_at)
|
||||
SELECT
|
||||
smp_server_id, host, port, key_hash, basic_auth, preset, tested, enabled, user_id, created_at, updated_at
|
||||
FROM smp_servers;
|
||||
|
||||
DROP TABLE smp_servers;
|
||||
ALTER TABLE new_smp_servers RENAME TO smp_servers;
|
||||
|
||||
CREATE INDEX idx_smp_servers_user_id ON "smp_servers"(user_id);
|
||||
|]
|
||||
@@ -0,0 +1,12 @@
|
||||
{-# LANGUAGE QuasiQuotes #-}
|
||||
|
||||
module Simplex.Chat.Store.SQLite.Migrations.M20230129_drop_chat_items_group_idx where
|
||||
|
||||
import Database.SQLite.Simple (Query)
|
||||
import Database.SQLite.Simple.QQ (sql)
|
||||
|
||||
m20230129_drop_chat_items_group_idx :: Query
|
||||
m20230129_drop_chat_items_group_idx =
|
||||
[sql|
|
||||
DROP INDEX idx_chat_items_group_id;
|
||||
|]
|
||||
@@ -0,0 +1,14 @@
|
||||
{-# LANGUAGE QuasiQuotes #-}
|
||||
|
||||
module Simplex.Chat.Store.SQLite.Migrations.M20230206_item_deleted_by_group_member_id where
|
||||
|
||||
import Database.SQLite.Simple (Query)
|
||||
import Database.SQLite.Simple.QQ (sql)
|
||||
|
||||
m20230206_item_deleted_by_group_member_id :: Query
|
||||
m20230206_item_deleted_by_group_member_id =
|
||||
[sql|
|
||||
ALTER TABLE chat_items ADD COLUMN item_deleted_by_group_member_id INTEGER REFERENCES group_members ON DELETE SET NULL;
|
||||
|
||||
CREATE INDEX idx_chat_items_item_deleted_by_group_member_id ON chat_items(item_deleted_by_group_member_id);
|
||||
|]
|
||||
@@ -0,0 +1,12 @@
|
||||
{-# LANGUAGE QuasiQuotes #-}
|
||||
|
||||
module Simplex.Chat.Store.SQLite.Migrations.M20230303_group_link_role where
|
||||
|
||||
import Database.SQLite.Simple (Query)
|
||||
import Database.SQLite.Simple.QQ (sql)
|
||||
|
||||
m20230303_group_link_role :: Query
|
||||
m20230303_group_link_role =
|
||||
[sql|
|
||||
ALTER TABLE user_contact_links ADD COLUMN group_link_member_role TEXT NULL; -- member or observer
|
||||
|]
|
||||
@@ -0,0 +1,22 @@
|
||||
{-# LANGUAGE QuasiQuotes #-}
|
||||
|
||||
module Simplex.Chat.Store.SQLite.Migrations.M20230317_hidden_profiles where
|
||||
|
||||
import Database.SQLite.Simple (Query)
|
||||
import Database.SQLite.Simple.QQ (sql)
|
||||
|
||||
m20230317_hidden_profiles :: Query
|
||||
m20230317_hidden_profiles =
|
||||
[sql|
|
||||
ALTER TABLE users ADD COLUMN view_pwd_hash BLOB;
|
||||
ALTER TABLE users ADD COLUMN view_pwd_salt BLOB;
|
||||
ALTER TABLE users ADD COLUMN show_ntfs INTEGER NOT NULL DEFAULT 1;
|
||||
|]
|
||||
|
||||
down_m20230317_hidden_profiles :: Query
|
||||
down_m20230317_hidden_profiles =
|
||||
[sql|
|
||||
ALTER TABLE users DROP COLUMN view_pwd_hash;
|
||||
ALTER TABLE users DROP COLUMN view_pwd_salt;
|
||||
ALTER TABLE users DROP COLUMN show_ntfs;
|
||||
|]
|
||||
@@ -0,0 +1,56 @@
|
||||
{-# LANGUAGE QuasiQuotes #-}
|
||||
|
||||
module Simplex.Chat.Store.SQLite.Migrations.M20230318_file_description where
|
||||
|
||||
import Database.SQLite.Simple (Query)
|
||||
import Database.SQLite.Simple.QQ (sql)
|
||||
|
||||
-- this table includes file descriptions for the recipients for both sent and received files
|
||||
-- in the latter case the user is the recipient
|
||||
|
||||
m20230318_file_description :: Query
|
||||
m20230318_file_description =
|
||||
[sql|
|
||||
CREATE TABLE xftp_file_descriptions (
|
||||
file_descr_id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
user_id INTEGER NOT NULL REFERENCES users ON DELETE CASCADE,
|
||||
file_descr_text TEXT NOT NULL,
|
||||
file_descr_part_no INTEGER NOT NULL DEFAULT(0),
|
||||
file_descr_complete INTEGER NOT NULL DEFAULT(0),
|
||||
created_at TEXT NOT NULL DEFAULT(datetime('now')),
|
||||
updated_at TEXT NOT NULL DEFAULT(datetime('now'))
|
||||
);
|
||||
|
||||
ALTER TABLE files ADD COLUMN agent_snd_file_id BLOB NULL;
|
||||
|
||||
ALTER TABLE files ADD COLUMN private_snd_file_descr TEXT NULL;
|
||||
|
||||
ALTER TABLE snd_files ADD COLUMN file_descr_id INTEGER NULL
|
||||
REFERENCES xftp_file_descriptions ON DELETE SET NULL;
|
||||
|
||||
CREATE INDEX idx_snd_files_file_descr_id ON snd_files(file_descr_id);
|
||||
|
||||
ALTER TABLE rcv_files ADD COLUMN file_descr_id INTEGER NULL
|
||||
REFERENCES xftp_file_descriptions ON DELETE SET NULL;
|
||||
|
||||
CREATE INDEX idx_rcv_files_file_descr_id ON rcv_files(file_descr_id);
|
||||
|
||||
ALTER TABLE rcv_files ADD COLUMN agent_rcv_file_id BLOB NULL;
|
||||
|]
|
||||
|
||||
down_m20230318_file_description :: Query
|
||||
down_m20230318_file_description =
|
||||
[sql|
|
||||
ALTER TABLE rcv_files DROP COLUMN agent_rcv_file_id;
|
||||
|
||||
DROP INDEX idx_rcv_files_file_descr_id;
|
||||
ALTER TABLE rcv_files DROP COLUMN file_descr_id;
|
||||
|
||||
DROP INDEX idx_snd_files_file_descr_id;
|
||||
ALTER TABLE snd_files DROP COLUMN file_descr_id;
|
||||
|
||||
ALTER TABLE files DROP COLUMN private_snd_file_descr;
|
||||
ALTER TABLE files DROP COLUMN agent_snd_file_id;
|
||||
|
||||
DROP TABLE xftp_file_descriptions;
|
||||
|]
|
||||
@@ -0,0 +1,28 @@
|
||||
{-# LANGUAGE QuasiQuotes #-}
|
||||
|
||||
module Simplex.Chat.Store.SQLite.Migrations.M20230321_agent_file_deleted where
|
||||
|
||||
import Database.SQLite.Simple (Query)
|
||||
import Database.SQLite.Simple.QQ (sql)
|
||||
|
||||
m20230321_agent_file_deleted :: Query
|
||||
m20230321_agent_file_deleted =
|
||||
[sql|
|
||||
PRAGMA ignore_check_constraints=ON;
|
||||
|
||||
ALTER TABLE files ADD COLUMN agent_snd_file_deleted INTEGER DEFAULT 0 CHECK (agent_snd_file_deleted NOT NULL);
|
||||
UPDATE files SET agent_snd_file_deleted = 0;
|
||||
|
||||
ALTER TABLE rcv_files ADD COLUMN agent_rcv_file_deleted INTEGER DEFAULT 0 CHECK (agent_rcv_file_deleted NOT NULL);
|
||||
UPDATE rcv_files SET agent_rcv_file_deleted = 0;
|
||||
|
||||
PRAGMA ignore_check_constraints=OFF;
|
||||
|]
|
||||
|
||||
down_m20230321_agent_file_deleted :: Query
|
||||
down_m20230321_agent_file_deleted =
|
||||
[sql|
|
||||
ALTER TABLE rcv_files DROP COLUMN agent_rcv_file_deleted;
|
||||
|
||||
ALTER TABLE files DROP COLUMN agent_snd_file_deleted;
|
||||
|]
|
||||
@@ -0,0 +1,18 @@
|
||||
{-# LANGUAGE QuasiQuotes #-}
|
||||
|
||||
module Simplex.Chat.Store.SQLite.Migrations.M20230328_files_protocol where
|
||||
|
||||
import Database.SQLite.Simple (Query)
|
||||
import Database.SQLite.Simple.QQ (sql)
|
||||
|
||||
m20230328_files_protocol :: Query
|
||||
m20230328_files_protocol =
|
||||
[sql|
|
||||
ALTER TABLE files ADD COLUMN protocol TEXT NOT NULL DEFAULT 'smp';
|
||||
|]
|
||||
|
||||
down_m20230328_files_protocol :: Query
|
||||
down_m20230328_files_protocol =
|
||||
[sql|
|
||||
ALTER TABLE files DROP COLUMN protocol;
|
||||
|]
|
||||
@@ -0,0 +1,20 @@
|
||||
{-# LANGUAGE QuasiQuotes #-}
|
||||
|
||||
module Simplex.Chat.Store.SQLite.Migrations.M20230402_protocol_servers where
|
||||
|
||||
import Database.SQLite.Simple (Query)
|
||||
import Database.SQLite.Simple.QQ (sql)
|
||||
|
||||
m20230402_protocol_servers :: Query
|
||||
m20230402_protocol_servers =
|
||||
[sql|
|
||||
ALTER TABLE smp_servers RENAME TO protocol_servers;
|
||||
ALTER TABLE protocol_servers ADD COLUMN protocol TEXT NOT NULL DEFAULT 'smp';
|
||||
|]
|
||||
|
||||
down_m20230402_protocol_servers :: Query
|
||||
down_m20230402_protocol_servers =
|
||||
[sql|
|
||||
ALTER TABLE protocol_servers DROP COLUMN protocol;
|
||||
ALTER TABLE protocol_servers RENAME TO smp_servers;
|
||||
|]
|
||||
@@ -0,0 +1,35 @@
|
||||
{-# LANGUAGE QuasiQuotes #-}
|
||||
|
||||
module Simplex.Chat.Store.SQLite.Migrations.M20230411_extra_xftp_file_descriptions where
|
||||
|
||||
import Database.SQLite.Simple (Query)
|
||||
import Database.SQLite.Simple.QQ (sql)
|
||||
|
||||
m20230411_extra_xftp_file_descriptions :: Query
|
||||
m20230411_extra_xftp_file_descriptions =
|
||||
[sql|
|
||||
CREATE TABLE extra_xftp_file_descriptions (
|
||||
extra_file_descr_id INTEGER PRIMARY KEY,
|
||||
file_id INTEGER NOT NULL REFERENCES files ON DELETE CASCADE,
|
||||
user_id INTEGER NOT NULL REFERENCES users ON DELETE CASCADE,
|
||||
file_descr_text TEXT NOT NULL,
|
||||
created_at TEXT NOT NULL DEFAULT(datetime('now')),
|
||||
updated_at TEXT NOT NULL DEFAULT(datetime('now'))
|
||||
);
|
||||
|
||||
CREATE INDEX idx_extra_xftp_file_descriptions_file_id ON extra_xftp_file_descriptions(file_id);
|
||||
CREATE INDEX idx_extra_xftp_file_descriptions_user_id ON extra_xftp_file_descriptions(user_id);
|
||||
|
||||
CREATE INDEX idx_xftp_file_descriptions_user_id ON xftp_file_descriptions(user_id);
|
||||
|]
|
||||
|
||||
down_m20230411_extra_xftp_file_descriptions :: Query
|
||||
down_m20230411_extra_xftp_file_descriptions =
|
||||
[sql|
|
||||
DROP INDEX idx_xftp_file_descriptions_user_id;
|
||||
|
||||
DROP INDEX idx_extra_xftp_file_descriptions_user_id;
|
||||
DROP INDEX idx_extra_xftp_file_descriptions_file_id;
|
||||
|
||||
DROP TABLE extra_xftp_file_descriptions;
|
||||
|]
|
||||
@@ -0,0 +1,18 @@
|
||||
{-# LANGUAGE QuasiQuotes #-}
|
||||
|
||||
module Simplex.Chat.Store.SQLite.Migrations.M20230420_rcv_files_to_receive where
|
||||
|
||||
import Database.SQLite.Simple (Query)
|
||||
import Database.SQLite.Simple.QQ (sql)
|
||||
|
||||
m20230420_rcv_files_to_receive :: Query
|
||||
m20230420_rcv_files_to_receive =
|
||||
[sql|
|
||||
ALTER TABLE rcv_files ADD COLUMN to_receive INTEGER;
|
||||
|]
|
||||
|
||||
down_m20230420_rcv_files_to_receive :: Query
|
||||
down_m20230420_rcv_files_to_receive =
|
||||
[sql|
|
||||
ALTER TABLE rcv_files DROP COLUMN to_receive;
|
||||
|]
|
||||
@@ -0,0 +1,18 @@
|
||||
{-# LANGUAGE QuasiQuotes #-}
|
||||
|
||||
module Simplex.Chat.Store.SQLite.Migrations.M20230422_profile_contact_links where
|
||||
|
||||
import Database.SQLite.Simple (Query)
|
||||
import Database.SQLite.Simple.QQ (sql)
|
||||
|
||||
m20230422_profile_contact_links :: Query
|
||||
m20230422_profile_contact_links =
|
||||
[sql|
|
||||
ALTER TABLE contact_profiles ADD COLUMN contact_link BLOB;
|
||||
|]
|
||||
|
||||
down_m20230422_profile_contact_links :: Query
|
||||
down_m20230422_profile_contact_links =
|
||||
[sql|
|
||||
ALTER TABLE contact_profiles DROP COLUMN contact_link;
|
||||
|]
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
{-# LANGUAGE QuasiQuotes #-}
|
||||
|
||||
module Simplex.Chat.Store.SQLite.Migrations.M20230504_recreate_msg_delivery_events_cleanup_messages where
|
||||
|
||||
import Database.SQLite.Simple (Query)
|
||||
import Database.SQLite.Simple.QQ (sql)
|
||||
|
||||
m20230504_recreate_msg_delivery_events_cleanup_messages :: Query
|
||||
m20230504_recreate_msg_delivery_events_cleanup_messages =
|
||||
[sql|
|
||||
DROP TABLE msg_delivery_events;
|
||||
|
||||
CREATE TABLE msg_delivery_events (
|
||||
msg_delivery_event_id INTEGER PRIMARY KEY,
|
||||
msg_delivery_id INTEGER NOT NULL REFERENCES msg_deliveries ON DELETE CASCADE,
|
||||
delivery_status TEXT NOT NULL,
|
||||
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
||||
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
||||
);
|
||||
|
||||
DELETE FROM messages WHERE created_at < datetime('now', '-30 days');
|
||||
|]
|
||||
|
||||
down_m20230504_recreate_msg_delivery_events_cleanup_messages :: Query
|
||||
down_m20230504_recreate_msg_delivery_events_cleanup_messages =
|
||||
[sql|
|
||||
DROP TABLE msg_delivery_events;
|
||||
|
||||
CREATE TABLE msg_delivery_events (
|
||||
msg_delivery_event_id INTEGER PRIMARY KEY,
|
||||
msg_delivery_id INTEGER NOT NULL REFERENCES msg_deliveries ON DELETE CASCADE, -- non UNIQUE for multiple events per msg delivery
|
||||
delivery_status TEXT NOT NULL, -- see MsgDeliveryStatus for allowed values
|
||||
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
||||
updated_at TEXT NOT NULL DEFAULT (datetime('now')),
|
||||
UNIQUE (msg_delivery_id, delivery_status)
|
||||
);
|
||||
|]
|
||||
@@ -0,0 +1,29 @@
|
||||
{-# LANGUAGE QuasiQuotes #-}
|
||||
|
||||
module Simplex.Chat.Store.SQLite.Migrations.M20230505_chat_item_versions where
|
||||
|
||||
import Database.SQLite.Simple (Query)
|
||||
import Database.SQLite.Simple.QQ (sql)
|
||||
|
||||
m20230505_chat_item_versions :: Query
|
||||
m20230505_chat_item_versions =
|
||||
[sql|
|
||||
CREATE TABLE chat_item_versions ( -- contains versions only for edited chat items, including current version
|
||||
chat_item_version_id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
chat_item_id INTEGER NOT NULL REFERENCES chat_items ON DELETE CASCADE,
|
||||
msg_content TEXT NOT NULL,
|
||||
item_version_ts TEXT NOT NULL,
|
||||
created_at TEXT NOT NULL DEFAULT(datetime('now')),
|
||||
updated_at TEXT NOT NULL DEFAULT(datetime('now'))
|
||||
);
|
||||
|
||||
CREATE INDEX idx_chat_item_versions_chat_item_id ON chat_item_versions(chat_item_id);
|
||||
|]
|
||||
|
||||
down_m20230505_chat_item_versions :: Query
|
||||
down_m20230505_chat_item_versions =
|
||||
[sql|
|
||||
DROP INDEX idx_chat_item_versions_chat_item_id;
|
||||
|
||||
DROP TABLE chat_item_versions;
|
||||
|]
|
||||
@@ -0,0 +1,47 @@
|
||||
{-# LANGUAGE QuasiQuotes #-}
|
||||
|
||||
module Simplex.Chat.Store.SQLite.Migrations.M20230511_reactions where
|
||||
|
||||
import Database.SQLite.Simple (Query)
|
||||
import Database.SQLite.Simple.QQ (sql)
|
||||
|
||||
m20230511_reactions :: Query
|
||||
m20230511_reactions =
|
||||
[sql|
|
||||
CREATE TABLE chat_item_reactions (
|
||||
chat_item_reaction_id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
item_member_id BLOB, -- member that created item, NULL for items in direct chats
|
||||
shared_msg_id BLOB NOT NULL,
|
||||
contact_id INTEGER REFERENCES contacts ON DELETE CASCADE,
|
||||
group_id INTEGER REFERENCES groups ON DELETE CASCADE,
|
||||
group_member_id INTEGER REFERENCES group_members ON DELETE SET NULL, -- member that sent reaction, NULL for items in direct chats
|
||||
created_by_msg_id INTEGER REFERENCES messages(message_id) ON DELETE SET NULL,
|
||||
reaction TEXT NOT NULL, -- JSON of MsgReaction
|
||||
reaction_sent INTEGER NOT NULL, -- 0 for received, 1 for sent
|
||||
reaction_ts TEXT NOT NULL, -- broker_ts of creating message for received, created_at for sent
|
||||
created_at TEXT NOT NULL DEFAULT(datetime('now')),
|
||||
updated_at TEXT NOT NULL DEFAULT(datetime('now'))
|
||||
);
|
||||
|
||||
CREATE INDEX idx_chat_item_reactions_shared_msg_id ON chat_item_reactions(shared_msg_id);
|
||||
CREATE INDEX idx_chat_item_reactions_contact_id ON chat_item_reactions(contact_id);
|
||||
CREATE INDEX idx_chat_item_reactions_group_id ON chat_item_reactions(group_id);
|
||||
CREATE INDEX idx_chat_item_reactions_group_member_id ON chat_item_reactions(group_member_id);
|
||||
|
||||
CREATE INDEX idx_chat_item_reactions_contact ON chat_item_reactions(contact_id, shared_msg_id);
|
||||
CREATE INDEX idx_chat_item_reactions_group ON chat_item_reactions(group_id, shared_msg_id);
|
||||
|]
|
||||
|
||||
down_m20230511_reactions :: Query
|
||||
down_m20230511_reactions =
|
||||
[sql|
|
||||
DROP INDEX idx_chat_item_reactions_group;
|
||||
DROP INDEX idx_chat_item_reactions_contact;
|
||||
|
||||
DROP INDEX idx_chat_item_reactions_group_member_id;
|
||||
DROP INDEX idx_chat_item_reactions_group_id;
|
||||
DROP INDEX idx_chat_item_reactions_contact_id;
|
||||
DROP INDEX idx_chat_item_reactions_shared_msg_id;
|
||||
|
||||
DROP TABLE chat_item_reactions;
|
||||
|]
|
||||
@@ -0,0 +1,18 @@
|
||||
{-# LANGUAGE QuasiQuotes #-}
|
||||
|
||||
module Simplex.Chat.Store.SQLite.Migrations.M20230519_item_deleted_ts where
|
||||
|
||||
import Database.SQLite.Simple (Query)
|
||||
import Database.SQLite.Simple.QQ (sql)
|
||||
|
||||
m20230519_item_deleted_ts :: Query
|
||||
m20230519_item_deleted_ts =
|
||||
[sql|
|
||||
ALTER TABLE chat_items ADD COLUMN item_deleted_ts TEXT;
|
||||
|]
|
||||
|
||||
down_m20230519_item_deleted_ts :: Query
|
||||
down_m20230519_item_deleted_ts =
|
||||
[sql|
|
||||
ALTER TABLE chat_items DROP COLUMN item_deleted_ts;
|
||||
|]
|
||||
@@ -0,0 +1,22 @@
|
||||
{-# LANGUAGE QuasiQuotes #-}
|
||||
|
||||
module Simplex.Chat.Store.SQLite.Migrations.M20230526_indexes where
|
||||
|
||||
import Database.SQLite.Simple (Query)
|
||||
import Database.SQLite.Simple.QQ (sql)
|
||||
|
||||
m20230526_indexes :: Query
|
||||
m20230526_indexes =
|
||||
[sql|
|
||||
CREATE INDEX idx_messages_created_at ON messages(created_at);
|
||||
|
||||
CREATE INDEX idx_chat_item_reactions_created_by_msg_id ON chat_item_reactions(created_by_msg_id);
|
||||
|]
|
||||
|
||||
down_m20230526_indexes :: Query
|
||||
down_m20230526_indexes =
|
||||
[sql|
|
||||
DROP INDEX idx_chat_item_reactions_created_by_msg_id;
|
||||
|
||||
DROP INDEX idx_messages_created_at;
|
||||
|]
|
||||
@@ -0,0 +1,30 @@
|
||||
{-# LANGUAGE QuasiQuotes #-}
|
||||
|
||||
module Simplex.Chat.Store.SQLite.Migrations.M20230529_indexes where
|
||||
|
||||
import Database.SQLite.Simple (Query)
|
||||
import Database.SQLite.Simple.QQ (sql)
|
||||
|
||||
m20230529_indexes :: Query
|
||||
m20230529_indexes =
|
||||
[sql|
|
||||
DROP INDEX idx_chat_items_timed_delete_at;
|
||||
|
||||
CREATE INDEX idx_chat_items_timed_delete_at ON chat_items(user_id, timed_delete_at);
|
||||
|
||||
CREATE INDEX idx_group_members_group_id ON group_members(user_id, group_id);
|
||||
|
||||
CREATE INDEX idx_msg_deliveries_agent_ack_cmd_id ON msg_deliveries(connection_id, agent_ack_cmd_id);
|
||||
|]
|
||||
|
||||
down_m20230529_indexes :: Query
|
||||
down_m20230529_indexes =
|
||||
[sql|
|
||||
DROP INDEX idx_msg_deliveries_agent_ack_cmd_id;
|
||||
|
||||
DROP INDEX idx_group_members_group_id;
|
||||
|
||||
DROP INDEX idx_chat_items_timed_delete_at;
|
||||
|
||||
CREATE INDEX idx_chat_items_timed_delete_at ON chat_items(timed_delete_at);
|
||||
|]
|
||||
@@ -0,0 +1,22 @@
|
||||
{-# LANGUAGE QuasiQuotes #-}
|
||||
|
||||
module Simplex.Chat.Store.SQLite.Migrations.M20230608_deleted_contacts where
|
||||
|
||||
import Database.SQLite.Simple (Query)
|
||||
import Database.SQLite.Simple.QQ (sql)
|
||||
|
||||
m20230608_deleted_contacts :: Query
|
||||
m20230608_deleted_contacts =
|
||||
[sql|
|
||||
ALTER TABLE contacts ADD COLUMN deleted INTEGER NOT NULL DEFAULT 0;
|
||||
|
||||
CREATE INDEX msg_delivery_events_msg_delivery_id ON msg_delivery_events(msg_delivery_id);
|
||||
|]
|
||||
|
||||
down_m20230608_deleted_contacts :: Query
|
||||
down_m20230608_deleted_contacts =
|
||||
[sql|
|
||||
DROP INDEX msg_delivery_events_msg_delivery_id;
|
||||
|
||||
ALTER TABLE contacts DROP COLUMN deleted;
|
||||
|]
|
||||
@@ -0,0 +1,20 @@
|
||||
{-# LANGUAGE QuasiQuotes #-}
|
||||
|
||||
module Simplex.Chat.Store.SQLite.Migrations.M20230618_favorite_chats where
|
||||
|
||||
import Database.SQLite.Simple (Query)
|
||||
import Database.SQLite.Simple.QQ (sql)
|
||||
|
||||
m20230618_favorite_chats :: Query
|
||||
m20230618_favorite_chats =
|
||||
[sql|
|
||||
ALTER TABLE contacts ADD COLUMN favorite INTEGER NOT NULL DEFAULT 0;
|
||||
ALTER TABLE groups ADD COLUMN favorite INTEGER NOT NULL DEFAULT 0;
|
||||
|]
|
||||
|
||||
down_m20230618_favorite_chats :: Query
|
||||
down_m20230618_favorite_chats =
|
||||
[sql|
|
||||
ALTER TABLE contacts DROP COLUMN favorite;
|
||||
ALTER TABLE groups DROP COLUMN favorite;
|
||||
|]
|
||||
@@ -0,0 +1,41 @@
|
||||
{-# LANGUAGE QuasiQuotes #-}
|
||||
|
||||
module Simplex.Chat.Store.SQLite.Migrations.M20230621_chat_item_moderations where
|
||||
|
||||
import Database.SQLite.Simple (Query)
|
||||
import Database.SQLite.Simple.QQ (sql)
|
||||
|
||||
-- moderations that could not be applied - for messages that haven't been received at the time of moderation
|
||||
m20230621_chat_item_moderations :: Query
|
||||
m20230621_chat_item_moderations =
|
||||
[sql|
|
||||
CREATE TABLE chat_item_moderations (
|
||||
chat_item_moderation_id INTEGER PRIMARY KEY,
|
||||
group_id INTEGER NOT NULL REFERENCES groups ON DELETE CASCADE,
|
||||
moderator_member_id INTEGER NOT NULL REFERENCES group_members ON DELETE CASCADE,
|
||||
item_member_id BLOB NOT NULL,
|
||||
shared_msg_id BLOB NOT NULL,
|
||||
created_by_msg_id INTEGER REFERENCES messages(message_id) ON DELETE SET NULL,
|
||||
moderated_at TEXT NOT NULL, -- broker_ts of creating message
|
||||
created_at TEXT NOT NULL DEFAULT(datetime('now')),
|
||||
updated_at TEXT NOT NULL DEFAULT(datetime('now'))
|
||||
);
|
||||
|
||||
CREATE INDEX idx_chat_item_moderations_group_id ON chat_item_moderations(group_id);
|
||||
CREATE INDEX idx_chat_item_moderations_moderator_member_id ON chat_item_moderations(moderator_member_id);
|
||||
CREATE INDEX idx_chat_item_moderations_created_by_msg_id ON chat_item_moderations(created_by_msg_id);
|
||||
|
||||
CREATE INDEX idx_chat_item_moderations_group ON chat_item_moderations(group_id, item_member_id, shared_msg_id);
|
||||
|]
|
||||
|
||||
down_m20230621_chat_item_moderations :: Query
|
||||
down_m20230621_chat_item_moderations =
|
||||
[sql|
|
||||
DROP INDEX idx_chat_item_moderations_group;
|
||||
|
||||
DROP INDEX idx_chat_item_moderations_created_by_msg_id;
|
||||
DROP INDEX idx_chat_item_moderations_moderator_member_id;
|
||||
DROP INDEX idx_chat_item_moderations_group_id;
|
||||
|
||||
DROP TABLE chat_item_moderations;
|
||||
|]
|
||||
@@ -0,0 +1,24 @@
|
||||
{-# LANGUAGE QuasiQuotes #-}
|
||||
|
||||
module Simplex.Chat.Store.SQLite.Migrations.M20230705_delivery_receipts where
|
||||
|
||||
import Database.SQLite.Simple (Query)
|
||||
import Database.SQLite.Simple.QQ (sql)
|
||||
|
||||
m20230705_delivery_receipts :: Query
|
||||
m20230705_delivery_receipts =
|
||||
[sql|
|
||||
ALTER TABLE users ADD COLUMN send_rcpts_contacts INTEGER NOT NULL DEFAULT 0;
|
||||
ALTER TABLE users ADD COLUMN send_rcpts_small_groups INTEGER NOT NULL DEFAULT 0;
|
||||
ALTER TABLE contacts ADD COLUMN send_rcpts INTEGER;
|
||||
ALTER TABLE groups ADD COLUMN send_rcpts INTEGER;
|
||||
|]
|
||||
|
||||
down_m20230705_delivery_receipts :: Query
|
||||
down_m20230705_delivery_receipts =
|
||||
[sql|
|
||||
ALTER TABLE users DROP COLUMN send_rcpts_contacts;
|
||||
ALTER TABLE users DROP COLUMN send_rcpts_small_groups;
|
||||
ALTER TABLE contacts DROP COLUMN send_rcpts;
|
||||
ALTER TABLE groups DROP COLUMN send_rcpts;
|
||||
|]
|
||||
@@ -0,0 +1,33 @@
|
||||
{-# LANGUAGE QuasiQuotes #-}
|
||||
|
||||
module Simplex.Chat.Store.SQLite.Migrations.M20230721_group_snd_item_statuses where
|
||||
|
||||
import Database.SQLite.Simple (Query)
|
||||
import Database.SQLite.Simple.QQ (sql)
|
||||
|
||||
m20230721_group_snd_item_statuses :: Query
|
||||
m20230721_group_snd_item_statuses =
|
||||
[sql|
|
||||
CREATE TABLE group_snd_item_statuses (
|
||||
group_snd_item_status_id INTEGER PRIMARY KEY,
|
||||
chat_item_id INTEGER NOT NULL REFERENCES chat_items ON DELETE CASCADE,
|
||||
group_member_id INTEGER NOT NULL REFERENCES group_members ON DELETE CASCADE,
|
||||
group_snd_item_status TEXT NOT NULL,
|
||||
created_at TEXT NOT NULL DEFAULT(datetime('now')),
|
||||
updated_at TEXT NOT NULL DEFAULT(datetime('now'))
|
||||
);
|
||||
|
||||
CREATE INDEX idx_group_snd_item_statuses_chat_item_id ON group_snd_item_statuses(chat_item_id);
|
||||
CREATE INDEX idx_group_snd_item_statuses_group_member_id ON group_snd_item_statuses(group_member_id);
|
||||
|
||||
UPDATE users SET send_rcpts_small_groups = 1 WHERE send_rcpts_contacts = 1;
|
||||
|]
|
||||
|
||||
down_m20230721_group_snd_item_statuses :: Query
|
||||
down_m20230721_group_snd_item_statuses =
|
||||
[sql|
|
||||
DROP INDEX idx_group_snd_item_statuses_group_member_id;
|
||||
DROP INDEX idx_group_snd_item_statuses_chat_item_id;
|
||||
|
||||
DROP TABLE group_snd_item_statuses;
|
||||
|]
|
||||
@@ -0,0 +1,18 @@
|
||||
{-# LANGUAGE QuasiQuotes #-}
|
||||
|
||||
module Simplex.Chat.Store.SQLite.Migrations.M20230814_indexes where
|
||||
|
||||
import Database.SQLite.Simple (Query)
|
||||
import Database.SQLite.Simple.QQ (sql)
|
||||
|
||||
m20230814_indexes :: Query
|
||||
m20230814_indexes =
|
||||
[sql|
|
||||
CREATE INDEX idx_chat_items_user_id_item_status ON chat_items(user_id, item_status);
|
||||
|]
|
||||
|
||||
down_m20230814_indexes :: Query
|
||||
down_m20230814_indexes =
|
||||
[sql|
|
||||
DROP INDEX idx_chat_items_user_id_item_status;
|
||||
|]
|
||||
@@ -0,0 +1,20 @@
|
||||
{-# LANGUAGE QuasiQuotes #-}
|
||||
|
||||
module Simplex.Chat.Store.SQLite.Migrations.M20230827_file_encryption where
|
||||
|
||||
import Database.SQLite.Simple (Query)
|
||||
import Database.SQLite.Simple.QQ (sql)
|
||||
|
||||
m20230827_file_encryption :: Query
|
||||
m20230827_file_encryption =
|
||||
[sql|
|
||||
ALTER TABLE files ADD COLUMN file_crypto_key BLOB;
|
||||
ALTER TABLE files ADD COLUMN file_crypto_nonce BLOB;
|
||||
|]
|
||||
|
||||
down_m20230827_file_encryption :: Query
|
||||
down_m20230827_file_encryption =
|
||||
[sql|
|
||||
ALTER TABLE files DROP COLUMN file_crypto_key;
|
||||
ALTER TABLE files DROP COLUMN file_crypto_nonce;
|
||||
|]
|
||||
@@ -0,0 +1,26 @@
|
||||
{-# LANGUAGE QuasiQuotes #-}
|
||||
|
||||
module Simplex.Chat.Store.SQLite.Migrations.M20230829_connections_chat_vrange where
|
||||
|
||||
import Database.SQLite.Simple (Query)
|
||||
import Database.SQLite.Simple.QQ (sql)
|
||||
|
||||
m20230829_connections_chat_vrange :: Query
|
||||
m20230829_connections_chat_vrange =
|
||||
[sql|
|
||||
ALTER TABLE connections ADD COLUMN peer_chat_min_version INTEGER NOT NULL DEFAULT 1;
|
||||
ALTER TABLE connections ADD COLUMN peer_chat_max_version INTEGER NOT NULL DEFAULT 1;
|
||||
|
||||
ALTER TABLE contact_requests ADD COLUMN peer_chat_min_version INTEGER NOT NULL DEFAULT 1;
|
||||
ALTER TABLE contact_requests ADD COLUMN peer_chat_max_version INTEGER NOT NULL DEFAULT 1;
|
||||
|]
|
||||
|
||||
down_m20230829_connections_chat_vrange :: Query
|
||||
down_m20230829_connections_chat_vrange =
|
||||
[sql|
|
||||
ALTER TABLE contact_requests DROP COLUMN peer_chat_max_version;
|
||||
ALTER TABLE contact_requests DROP COLUMN peer_chat_min_version;
|
||||
|
||||
ALTER TABLE connections DROP COLUMN peer_chat_max_version;
|
||||
ALTER TABLE connections DROP COLUMN peer_chat_min_version;
|
||||
|]
|
||||
@@ -0,0 +1,20 @@
|
||||
{-# LANGUAGE QuasiQuotes #-}
|
||||
|
||||
module Simplex.Chat.Store.SQLite.Migrations.M20230903_connections_to_subscribe where
|
||||
|
||||
import Database.SQLite.Simple (Query)
|
||||
import Database.SQLite.Simple.QQ (sql)
|
||||
|
||||
m20230903_connections_to_subscribe :: Query
|
||||
m20230903_connections_to_subscribe =
|
||||
[sql|
|
||||
ALTER TABLE connections ADD COLUMN to_subscribe INTEGER DEFAULT 0 NOT NULL;
|
||||
CREATE INDEX idx_connections_to_subscribe ON connections(to_subscribe);
|
||||
|]
|
||||
|
||||
down_m20230903_connections_to_subscribe :: Query
|
||||
down_m20230903_connections_to_subscribe =
|
||||
[sql|
|
||||
DROP INDEX idx_connections_to_subscribe;
|
||||
ALTER TABLE connections DROP COLUMN to_subscribe;
|
||||
|]
|
||||
@@ -0,0 +1,27 @@
|
||||
{-# LANGUAGE QuasiQuotes #-}
|
||||
|
||||
module Simplex.Chat.Store.SQLite.Migrations.M20230913_member_contacts where
|
||||
|
||||
import Database.SQLite.Simple (Query)
|
||||
import Database.SQLite.Simple.QQ (sql)
|
||||
|
||||
m20230913_member_contacts :: Query
|
||||
m20230913_member_contacts =
|
||||
[sql|
|
||||
ALTER TABLE contacts ADD COLUMN contact_group_member_id INTEGER
|
||||
REFERENCES group_members(group_member_id) ON DELETE SET NULL;
|
||||
|
||||
CREATE INDEX idx_contacts_contact_group_member_id ON contacts(contact_group_member_id);
|
||||
|
||||
ALTER TABLE contacts ADD COLUMN contact_grp_inv_sent INTEGER NOT NULL DEFAULT 0;
|
||||
|]
|
||||
|
||||
down_m20230913_member_contacts :: Query
|
||||
down_m20230913_member_contacts =
|
||||
[sql|
|
||||
ALTER TABLE contacts DROP COLUMN contact_grp_inv_sent;
|
||||
|
||||
DROP INDEX idx_contacts_contact_group_member_id;
|
||||
|
||||
ALTER TABLE contacts DROP COLUMN contact_group_member_id;
|
||||
|]
|
||||
@@ -0,0 +1,169 @@
|
||||
{-# LANGUAGE QuasiQuotes #-}
|
||||
|
||||
module Simplex.Chat.Store.SQLite.Migrations.M20230914_member_probes where
|
||||
|
||||
import Database.SQLite.Simple (Query)
|
||||
import Database.SQLite.Simple.QQ (sql)
|
||||
|
||||
m20230914_member_probes :: Query
|
||||
m20230914_member_probes =
|
||||
[sql|
|
||||
CREATE TABLE new__sent_probes(
|
||||
sent_probe_id INTEGER PRIMARY KEY,
|
||||
contact_id INTEGER REFERENCES contacts ON DELETE CASCADE,
|
||||
group_member_id INTEGER REFERENCES group_members ON DELETE CASCADE,
|
||||
probe BLOB NOT NULL,
|
||||
user_id INTEGER NOT NULL REFERENCES users ON DELETE CASCADE,
|
||||
created_at TEXT CHECK(created_at NOT NULL),
|
||||
updated_at TEXT CHECK(updated_at NOT NULL),
|
||||
UNIQUE(user_id, probe)
|
||||
);
|
||||
|
||||
CREATE TABLE new__sent_probe_hashes(
|
||||
sent_probe_hash_id INTEGER PRIMARY KEY,
|
||||
sent_probe_id INTEGER NOT NULL REFERENCES new__sent_probes ON DELETE CASCADE,
|
||||
contact_id INTEGER REFERENCES contacts ON DELETE CASCADE,
|
||||
group_member_id INTEGER REFERENCES group_members ON DELETE CASCADE,
|
||||
user_id INTEGER NOT NULL REFERENCES users ON DELETE CASCADE,
|
||||
created_at TEXT CHECK(created_at NOT NULL),
|
||||
updated_at TEXT CHECK(updated_at NOT NULL)
|
||||
);
|
||||
|
||||
CREATE TABLE new__received_probes(
|
||||
received_probe_id INTEGER PRIMARY KEY,
|
||||
contact_id INTEGER REFERENCES contacts ON DELETE CASCADE,
|
||||
group_member_id INTEGER REFERENCES group_members ON DELETE CASCADE,
|
||||
probe BLOB,
|
||||
probe_hash BLOB NOT NULL,
|
||||
user_id INTEGER NOT NULL REFERENCES users ON DELETE CASCADE,
|
||||
created_at TEXT CHECK(created_at NOT NULL),
|
||||
updated_at TEXT CHECK(updated_at NOT NULL)
|
||||
);
|
||||
|
||||
INSERT INTO new__sent_probes
|
||||
(sent_probe_id, contact_id, probe, user_id, created_at, updated_at)
|
||||
SELECT
|
||||
sent_probe_id, contact_id, probe, user_id, created_at, updated_at
|
||||
FROM sent_probes;
|
||||
|
||||
INSERT INTO new__sent_probe_hashes
|
||||
(sent_probe_hash_id, sent_probe_id, contact_id, user_id, created_at, updated_at)
|
||||
SELECT
|
||||
sent_probe_hash_id, sent_probe_id, contact_id, user_id, created_at, updated_at
|
||||
FROM sent_probe_hashes;
|
||||
|
||||
INSERT INTO new__received_probes
|
||||
(received_probe_id, contact_id, probe, probe_hash, user_id, created_at, updated_at)
|
||||
SELECT
|
||||
received_probe_id, contact_id, probe, probe_hash, user_id, created_at, updated_at
|
||||
FROM received_probes;
|
||||
|
||||
DROP INDEX idx_sent_probe_hashes_user_id;
|
||||
DROP INDEX idx_sent_probe_hashes_contact_id;
|
||||
DROP INDEX idx_received_probes_user_id;
|
||||
DROP INDEX idx_received_probes_contact_id;
|
||||
|
||||
DROP TABLE sent_probes;
|
||||
DROP TABLE sent_probe_hashes;
|
||||
DROP TABLE received_probes;
|
||||
|
||||
ALTER TABLE new__sent_probes RENAME TO sent_probes;
|
||||
ALTER TABLE new__sent_probe_hashes RENAME TO sent_probe_hashes;
|
||||
ALTER TABLE new__received_probes RENAME TO received_probes;
|
||||
|
||||
CREATE INDEX idx_sent_probes_user_id ON sent_probes(user_id);
|
||||
CREATE INDEX idx_sent_probes_contact_id ON sent_probes(contact_id);
|
||||
CREATE INDEX idx_sent_probes_group_member_id ON sent_probes(group_member_id);
|
||||
|
||||
CREATE INDEX idx_sent_probe_hashes_user_id ON sent_probe_hashes(user_id);
|
||||
CREATE INDEX idx_sent_probe_hashes_sent_probe_id ON sent_probe_hashes(sent_probe_id);
|
||||
CREATE INDEX idx_sent_probe_hashes_contact_id ON sent_probe_hashes(contact_id);
|
||||
CREATE INDEX idx_sent_probe_hashes_group_member_id ON sent_probe_hashes(group_member_id);
|
||||
|
||||
CREATE INDEX idx_received_probes_user_id ON received_probes(user_id);
|
||||
CREATE INDEX idx_received_probes_contact_id ON received_probes(contact_id);
|
||||
CREATE INDEX idx_received_probes_probe ON received_probes(probe);
|
||||
CREATE INDEX idx_received_probes_probe_hash ON received_probes(probe_hash);
|
||||
|]
|
||||
|
||||
down_m20230914_member_probes :: Query
|
||||
down_m20230914_member_probes =
|
||||
[sql|
|
||||
CREATE TABLE old__sent_probes(
|
||||
sent_probe_id INTEGER PRIMARY KEY,
|
||||
contact_id INTEGER NOT NULL REFERENCES contacts ON DELETE CASCADE,
|
||||
probe BLOB NOT NULL,
|
||||
user_id INTEGER NOT NULL REFERENCES users ON DELETE CASCADE,
|
||||
created_at TEXT CHECK(created_at NOT NULL),
|
||||
updated_at TEXT CHECK(updated_at NOT NULL),
|
||||
UNIQUE(user_id, probe)
|
||||
);
|
||||
|
||||
CREATE TABLE old__sent_probe_hashes(
|
||||
sent_probe_hash_id INTEGER PRIMARY KEY,
|
||||
sent_probe_id INTEGER NOT NULL REFERENCES old__sent_probes ON DELETE CASCADE,
|
||||
contact_id INTEGER NOT NULL REFERENCES contacts ON DELETE CASCADE,
|
||||
user_id INTEGER NOT NULL REFERENCES users ON DELETE CASCADE,
|
||||
created_at TEXT CHECK(created_at NOT NULL),
|
||||
updated_at TEXT CHECK(updated_at NOT NULL)
|
||||
);
|
||||
|
||||
CREATE TABLE old__received_probes(
|
||||
received_probe_id INTEGER PRIMARY KEY,
|
||||
contact_id INTEGER NOT NULL REFERENCES contacts ON DELETE CASCADE,
|
||||
probe BLOB,
|
||||
probe_hash BLOB NOT NULL,
|
||||
user_id INTEGER NOT NULL REFERENCES users ON DELETE CASCADE,
|
||||
created_at TEXT CHECK(created_at NOT NULL),
|
||||
updated_at TEXT CHECK(updated_at NOT NULL)
|
||||
);
|
||||
|
||||
DELETE FROM sent_probes WHERE contact_id IS NULL;
|
||||
DELETE FROM sent_probe_hashes WHERE contact_id IS NULL;
|
||||
DELETE FROM received_probes WHERE contact_id IS NULL;
|
||||
|
||||
INSERT INTO old__sent_probes
|
||||
(sent_probe_id, contact_id, probe, user_id, created_at, updated_at)
|
||||
SELECT
|
||||
sent_probe_id, contact_id, probe, user_id, created_at, updated_at
|
||||
FROM sent_probes;
|
||||
|
||||
INSERT INTO old__sent_probe_hashes
|
||||
(sent_probe_hash_id, sent_probe_id, contact_id, user_id, created_at, updated_at)
|
||||
SELECT
|
||||
sent_probe_hash_id, sent_probe_id, contact_id, user_id, created_at, updated_at
|
||||
FROM sent_probe_hashes;
|
||||
|
||||
INSERT INTO old__received_probes
|
||||
(received_probe_id, contact_id, probe, probe_hash, user_id, created_at, updated_at)
|
||||
SELECT
|
||||
received_probe_id, contact_id, probe, probe_hash, user_id, created_at, updated_at
|
||||
FROM received_probes;
|
||||
|
||||
DROP INDEX idx_sent_probes_user_id;
|
||||
DROP INDEX idx_sent_probes_contact_id;
|
||||
DROP INDEX idx_sent_probes_group_member_id;
|
||||
|
||||
DROP INDEX idx_sent_probe_hashes_user_id;
|
||||
DROP INDEX idx_sent_probe_hashes_sent_probe_id;
|
||||
DROP INDEX idx_sent_probe_hashes_contact_id;
|
||||
DROP INDEX idx_sent_probe_hashes_group_member_id;
|
||||
|
||||
DROP INDEX idx_received_probes_user_id;
|
||||
DROP INDEX idx_received_probes_contact_id;
|
||||
DROP INDEX idx_received_probes_probe;
|
||||
DROP INDEX idx_received_probes_probe_hash;
|
||||
|
||||
DROP TABLE sent_probes;
|
||||
DROP TABLE sent_probe_hashes;
|
||||
DROP TABLE received_probes;
|
||||
|
||||
ALTER TABLE old__sent_probes RENAME TO sent_probes;
|
||||
ALTER TABLE old__sent_probe_hashes RENAME TO sent_probe_hashes;
|
||||
ALTER TABLE old__received_probes RENAME TO received_probes;
|
||||
|
||||
CREATE INDEX idx_received_probes_user_id ON received_probes(user_id);
|
||||
CREATE INDEX idx_received_probes_contact_id ON received_probes(contact_id);
|
||||
CREATE INDEX idx_sent_probe_hashes_user_id ON sent_probe_hashes(user_id);
|
||||
CREATE INDEX idx_sent_probe_hashes_contact_id ON sent_probe_hashes(contact_id);
|
||||
|]
|
||||
@@ -0,0 +1,18 @@
|
||||
{-# LANGUAGE QuasiQuotes #-}
|
||||
|
||||
module Simplex.Chat.Store.SQLite.Migrations.M20230926_contact_status where
|
||||
|
||||
import Database.SQLite.Simple (Query)
|
||||
import Database.SQLite.Simple.QQ (sql)
|
||||
|
||||
m20230926_contact_status :: Query
|
||||
m20230926_contact_status =
|
||||
[sql|
|
||||
ALTER TABLE contacts ADD COLUMN contact_status TEXT NOT NULL DEFAULT 'active';
|
||||
|]
|
||||
|
||||
down_m20230926_contact_status :: Query
|
||||
down_m20230926_contact_status =
|
||||
[sql|
|
||||
ALTER TABLE contacts DROP COLUMN contact_status;
|
||||
|]
|
||||
@@ -0,0 +1,28 @@
|
||||
{-# LANGUAGE QuasiQuotes #-}
|
||||
|
||||
module Simplex.Chat.Store.SQLite.Migrations.M20231002_conn_initiated where
|
||||
|
||||
import Database.SQLite.Simple (Query)
|
||||
import Database.SQLite.Simple.QQ (sql)
|
||||
|
||||
m20231002_conn_initiated :: Query
|
||||
m20231002_conn_initiated =
|
||||
[sql|
|
||||
ALTER TABLE connections ADD COLUMN contact_conn_initiated INTEGER NOT NULL DEFAULT 0;
|
||||
|
||||
UPDATE connections SET conn_req_inv = NULL WHERE conn_status IN ('ready', 'deleted');
|
||||
|
||||
CREATE INDEX idx_sent_probes_created_at ON sent_probes(created_at);
|
||||
CREATE INDEX idx_sent_probe_hashes_created_at ON sent_probe_hashes(created_at);
|
||||
CREATE INDEX idx_received_probes_created_at ON received_probes(created_at);
|
||||
|]
|
||||
|
||||
down_m20231002_conn_initiated :: Query
|
||||
down_m20231002_conn_initiated =
|
||||
[sql|
|
||||
DROP INDEX idx_sent_probes_created_at;
|
||||
DROP INDEX idx_sent_probe_hashes_created_at;
|
||||
DROP INDEX idx_received_probes_created_at;
|
||||
|
||||
ALTER TABLE connections DROP COLUMN contact_conn_initiated;
|
||||
|]
|
||||
@@ -0,0 +1,24 @@
|
||||
{-# LANGUAGE QuasiQuotes #-}
|
||||
|
||||
module Simplex.Chat.Store.SQLite.Migrations.M20231009_via_group_link_uri_hash where
|
||||
|
||||
import Database.SQLite.Simple (Query)
|
||||
import Database.SQLite.Simple.QQ (sql)
|
||||
|
||||
m20231009_via_group_link_uri_hash :: Query
|
||||
m20231009_via_group_link_uri_hash =
|
||||
[sql|
|
||||
CREATE INDEX idx_connections_conn_req_inv ON connections(conn_req_inv);
|
||||
|
||||
ALTER TABLE groups ADD COLUMN via_group_link_uri_hash BLOB;
|
||||
CREATE INDEX idx_groups_via_group_link_uri_hash ON groups(via_group_link_uri_hash);
|
||||
|]
|
||||
|
||||
down_m20231009_via_group_link_uri_hash :: Query
|
||||
down_m20231009_via_group_link_uri_hash =
|
||||
[sql|
|
||||
DROP INDEX idx_groups_via_group_link_uri_hash;
|
||||
ALTER TABLE groups DROP COLUMN via_group_link_uri_hash;
|
||||
|
||||
DROP INDEX idx_connections_conn_req_inv;
|
||||
|]
|
||||
@@ -0,0 +1,18 @@
|
||||
{-# LANGUAGE QuasiQuotes #-}
|
||||
|
||||
module Simplex.Chat.Store.SQLite.Migrations.M20231010_member_settings where
|
||||
|
||||
import Database.SQLite.Simple (Query)
|
||||
import Database.SQLite.Simple.QQ (sql)
|
||||
|
||||
m20231010_member_settings :: Query
|
||||
m20231010_member_settings =
|
||||
[sql|
|
||||
ALTER TABLE group_members ADD COLUMN show_messages INTEGER NOT NULL DEFAULT 1;
|
||||
|]
|
||||
|
||||
down_m20231010_member_settings :: Query
|
||||
down_m20231010_member_settings =
|
||||
[sql|
|
||||
ALTER TABLE group_members DROP COLUMN show_messages;
|
||||
|]
|
||||
@@ -0,0 +1,32 @@
|
||||
{-# LANGUAGE QuasiQuotes #-}
|
||||
|
||||
module Simplex.Chat.Store.SQLite.Migrations.M20231019_indexes where
|
||||
|
||||
import Database.SQLite.Simple (Query)
|
||||
import Database.SQLite.Simple.QQ (sql)
|
||||
|
||||
m20231019_indexes :: Query
|
||||
m20231019_indexes =
|
||||
[sql|
|
||||
DROP INDEX idx_connections_conn_req_inv;
|
||||
CREATE INDEX idx_connections_conn_req_inv ON connections(user_id, conn_req_inv);
|
||||
|
||||
DROP INDEX idx_groups_via_group_link_uri_hash;
|
||||
CREATE INDEX idx_groups_via_group_link_uri_hash ON groups(user_id, via_group_link_uri_hash);
|
||||
|
||||
DROP INDEX idx_connections_via_contact_uri_hash;
|
||||
CREATE INDEX idx_connections_via_contact_uri_hash ON connections(user_id, via_contact_uri_hash);
|
||||
|]
|
||||
|
||||
down_m20231019_indexes :: Query
|
||||
down_m20231019_indexes =
|
||||
[sql|
|
||||
DROP INDEX idx_connections_conn_req_inv;
|
||||
CREATE INDEX idx_connections_conn_req_inv ON connections(conn_req_inv);
|
||||
|
||||
DROP INDEX idx_groups_via_group_link_uri_hash;
|
||||
CREATE INDEX idx_groups_via_group_link_uri_hash ON groups(via_group_link_uri_hash);
|
||||
|
||||
DROP INDEX idx_connections_via_contact_uri_hash;
|
||||
CREATE INDEX idx_connections_via_contact_uri_hash ON connections(via_contact_uri_hash);
|
||||
|]
|
||||
@@ -0,0 +1,18 @@
|
||||
{-# LANGUAGE QuasiQuotes #-}
|
||||
|
||||
module Simplex.Chat.Store.SQLite.Migrations.M20231030_xgrplinkmem_received where
|
||||
|
||||
import Database.SQLite.Simple (Query)
|
||||
import Database.SQLite.Simple.QQ (sql)
|
||||
|
||||
m20231030_xgrplinkmem_received :: Query
|
||||
m20231030_xgrplinkmem_received =
|
||||
[sql|
|
||||
ALTER TABLE group_members ADD COLUMN xgrplinkmem_received INTEGER NOT NULL DEFAULT 0;
|
||||
|]
|
||||
|
||||
down_m20231030_xgrplinkmem_received :: Query
|
||||
down_m20231030_xgrplinkmem_received =
|
||||
[sql|
|
||||
ALTER TABLE group_members DROP COLUMN xgrplinkmem_received;
|
||||
|]
|
||||
@@ -0,0 +1,18 @@
|
||||
{-# LANGUAGE QuasiQuotes #-}
|
||||
|
||||
module Simplex.Chat.Store.SQLite.Migrations.M20231107_indexes where
|
||||
|
||||
import Database.SQLite.Simple (Query)
|
||||
import Database.SQLite.Simple.QQ (sql)
|
||||
|
||||
m20231107_indexes :: Query
|
||||
m20231107_indexes =
|
||||
[sql|
|
||||
CREATE INDEX idx_contact_profiles_contact_link ON contact_profiles(user_id, contact_link);
|
||||
|]
|
||||
|
||||
down_m20231107_indexes :: Query
|
||||
down_m20231107_indexes =
|
||||
[sql|
|
||||
DROP INDEX idx_contact_profiles_contact_link;
|
||||
|]
|
||||
@@ -0,0 +1,53 @@
|
||||
{-# LANGUAGE QuasiQuotes #-}
|
||||
|
||||
module Simplex.Chat.Store.SQLite.Migrations.M20231113_group_forward where
|
||||
|
||||
import Database.SQLite.Simple (Query)
|
||||
import Database.SQLite.Simple.QQ (sql)
|
||||
|
||||
m20231113_group_forward :: Query
|
||||
m20231113_group_forward =
|
||||
[sql|
|
||||
ALTER TABLE group_member_intros ADD COLUMN intro_chat_protocol_version INTEGER NOT NULL DEFAULT 3;
|
||||
CREATE INDEX idx_group_member_intros_re_group_member_id ON group_member_intros(re_group_member_id);
|
||||
|
||||
ALTER TABLE group_members ADD COLUMN invited_by_group_member_id INTEGER REFERENCES group_members ON DELETE SET NULL;
|
||||
ALTER TABLE group_members ADD COLUMN peer_chat_min_version INTEGER NOT NULL DEFAULT 1;
|
||||
ALTER TABLE group_members ADD COLUMN peer_chat_max_version INTEGER NOT NULL DEFAULT 1;
|
||||
CREATE INDEX idx_group_members_invited_by_group_member_id ON group_members(invited_by_group_member_id);
|
||||
|
||||
UPDATE group_members
|
||||
SET (peer_chat_min_version, peer_chat_max_version) = (c.peer_chat_min_version, c.peer_chat_max_version)
|
||||
FROM connections c
|
||||
WHERE c.group_member_id = group_members.group_member_id;
|
||||
|
||||
ALTER TABLE messages ADD COLUMN author_group_member_id INTEGER REFERENCES group_members ON DELETE SET NULL;
|
||||
ALTER TABLE messages ADD COLUMN forwarded_by_group_member_id INTEGER REFERENCES group_members ON DELETE SET NULL;
|
||||
CREATE INDEX idx_messages_author_group_member_id ON messages(author_group_member_id);
|
||||
CREATE INDEX idx_messages_forwarded_by_group_member_id ON messages(forwarded_by_group_member_id);
|
||||
CREATE INDEX idx_messages_group_id_shared_msg_id ON messages(group_id, shared_msg_id);
|
||||
|
||||
ALTER TABLE chat_items ADD COLUMN forwarded_by_group_member_id INTEGER REFERENCES group_members ON DELETE SET NULL;
|
||||
CREATE INDEX idx_chat_items_forwarded_by_group_member_id ON chat_items(forwarded_by_group_member_id);
|
||||
|]
|
||||
|
||||
down_m20231113_group_forward :: Query
|
||||
down_m20231113_group_forward =
|
||||
[sql|
|
||||
DROP INDEX idx_chat_items_forwarded_by_group_member_id;
|
||||
ALTER TABLE chat_items DROP COLUMN forwarded_by_group_member_id;
|
||||
|
||||
DROP INDEX idx_messages_group_id_shared_msg_id;
|
||||
DROP INDEX idx_messages_forwarded_by_group_member_id;
|
||||
DROP INDEX idx_messages_author_group_member_id;
|
||||
ALTER TABLE messages DROP COLUMN forwarded_by_group_member_id;
|
||||
ALTER TABLE messages DROP COLUMN author_group_member_id;
|
||||
|
||||
DROP INDEX idx_group_members_invited_by_group_member_id;
|
||||
ALTER TABLE group_members DROP COLUMN peer_chat_max_version;
|
||||
ALTER TABLE group_members DROP COLUMN peer_chat_min_version;
|
||||
ALTER TABLE group_members DROP COLUMN invited_by_group_member_id;
|
||||
|
||||
DROP INDEX idx_group_member_intros_re_group_member_id;
|
||||
ALTER TABLE group_member_intros DROP COLUMN intro_chat_protocol_version;
|
||||
|]
|
||||
@@ -0,0 +1,45 @@
|
||||
{-# LANGUAGE QuasiQuotes #-}
|
||||
|
||||
module Simplex.Chat.Store.SQLite.Migrations.M20231114_remote_control where
|
||||
|
||||
import Database.SQLite.Simple (Query)
|
||||
import Database.SQLite.Simple.QQ (sql)
|
||||
|
||||
m20231114_remote_control :: Query
|
||||
m20231114_remote_control =
|
||||
[sql|
|
||||
CREATE TABLE remote_hosts ( -- e.g., mobiles known to a desktop app
|
||||
remote_host_id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
host_device_name TEXT NOT NULL,
|
||||
store_path TEXT NOT NULL, -- relative folder name for host files
|
||||
ca_key BLOB NOT NULL,
|
||||
ca_cert BLOB NOT NULL,
|
||||
id_key BLOB NOT NULL, -- long-term/identity signing key
|
||||
host_fingerprint BLOB NOT NULL, -- remote host CA cert fingerprint, set when connected
|
||||
host_dh_pub BLOB NOT NULL -- last session DH key
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX idx_remote_hosts_host_fingerprint ON remote_hosts(host_fingerprint);
|
||||
|
||||
CREATE TABLE remote_controllers ( -- e.g., desktops known to a mobile app
|
||||
remote_ctrl_id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
ctrl_device_name TEXT NOT NULL,
|
||||
ca_key BLOB NOT NULL,
|
||||
ca_cert BLOB NOT NULL,
|
||||
ctrl_fingerprint BLOB NOT NULL, -- remote controller CA cert fingerprint, set when connected
|
||||
id_pub BLOB NOT NULL, -- remote controller long-term/identity key to verify signatures
|
||||
dh_priv_key BLOB NOT NULL, -- last session DH key
|
||||
prev_dh_priv_key BLOB -- previous session DH key
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX idx_remote_controllers_ctrl_fingerprint ON remote_controllers(ctrl_fingerprint);
|
||||
|]
|
||||
|
||||
down_m20231114_remote_control :: Query
|
||||
down_m20231114_remote_control =
|
||||
[sql|
|
||||
DROP INDEX idx_remote_hosts_host_fingerprint;
|
||||
DROP INDEX idx_remote_controllers_ctrl_fingerprint;
|
||||
DROP TABLE remote_hosts;
|
||||
DROP TABLE remote_controllers;
|
||||
|]
|
||||
@@ -0,0 +1,22 @@
|
||||
{-# LANGUAGE QuasiQuotes #-}
|
||||
|
||||
module Simplex.Chat.Store.SQLite.Migrations.M20231126_remote_ctrl_address where
|
||||
|
||||
import Database.SQLite.Simple (Query)
|
||||
import Database.SQLite.Simple.QQ (sql)
|
||||
|
||||
m20231126_remote_ctrl_address :: Query
|
||||
m20231126_remote_ctrl_address =
|
||||
[sql|
|
||||
ALTER TABLE remote_hosts ADD COLUMN bind_addr TEXT;
|
||||
ALTER TABLE remote_hosts ADD COLUMN bind_iface TEXT;
|
||||
ALTER TABLE remote_hosts ADD COLUMN bind_port INTEGER;
|
||||
|]
|
||||
|
||||
down_m20231126_remote_ctrl_address :: Query
|
||||
down_m20231126_remote_ctrl_address =
|
||||
[sql|
|
||||
ALTER TABLE remote_hosts DROP COLUMN bind_addr;
|
||||
ALTER TABLE remote_hosts DROP COLUMN bind_iface;
|
||||
ALTER TABLE remote_hosts DROP COLUMN bind_port;
|
||||
|]
|
||||
@@ -0,0 +1,38 @@
|
||||
{-# LANGUAGE QuasiQuotes #-}
|
||||
|
||||
module Simplex.Chat.Store.SQLite.Migrations.M20231207_chat_list_pagination where
|
||||
|
||||
import Database.SQLite.Simple (Query)
|
||||
import Database.SQLite.Simple.QQ (sql)
|
||||
|
||||
m20231207_chat_list_pagination :: Query
|
||||
m20231207_chat_list_pagination =
|
||||
[sql|
|
||||
UPDATE contacts SET contact_used = 1
|
||||
WHERE contact_id = (
|
||||
SELECT contact_id FROM connections
|
||||
WHERE conn_level = 0 AND via_group_link = 0
|
||||
);
|
||||
|
||||
UPDATE contacts
|
||||
SET chat_ts = updated_at
|
||||
WHERE chat_ts IS NULL;
|
||||
|
||||
UPDATE groups
|
||||
SET chat_ts = updated_at
|
||||
WHERE chat_ts IS NULL;
|
||||
|
||||
CREATE INDEX idx_contacts_chat_ts ON contacts(user_id, chat_ts);
|
||||
CREATE INDEX idx_groups_chat_ts ON groups(user_id, chat_ts);
|
||||
CREATE INDEX idx_contact_requests_updated_at ON contact_requests(user_id, updated_at);
|
||||
CREATE INDEX idx_connections_updated_at ON connections(user_id, updated_at);
|
||||
|]
|
||||
|
||||
down_m20231207_chat_list_pagination :: Query
|
||||
down_m20231207_chat_list_pagination =
|
||||
[sql|
|
||||
DROP INDEX idx_contacts_chat_ts;
|
||||
DROP INDEX idx_groups_chat_ts;
|
||||
DROP INDEX idx_contact_requests_updated_at;
|
||||
DROP INDEX idx_connections_updated_at;
|
||||
|]
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user