invert relay fkey

This commit is contained in:
spaced4ndy
2025-11-12 16:34:58 +04:00
parent 4439f7e58b
commit 2465382aff
11 changed files with 80 additions and 94 deletions
+5 -1
View File
@@ -2223,7 +2223,6 @@ Known:
- updatedAt: UTCTime
- supportChat: [GroupSupportChat](#groupsupportchat)?
- isRelay: bool
- relayData: [GroupRelay](#grouprelay)?
---
@@ -2345,6 +2344,7 @@ Known:
**Record type**:
- groupRelayId: int64
- groupMemberId: int64
- userChatRelayId: int64
- relayStatus: [RelayStatus](#relaystatus)
- relayLink: string?
@@ -3598,6 +3598,10 @@ GroupRelayNotFound:
- type: "groupRelayNotFound"
- groupRelayId: int64
GroupRelayNotFoundByMemberId:
- type: "groupRelayNotFoundByMemberId"
- groupMemberId: int64
InvalidQuote:
- type: "invalidQuote"
@@ -2513,7 +2513,6 @@ export interface GroupMember {
updatedAt: string // ISO-8601 timestamp
supportChat?: GroupSupportChat
isRelay: boolean
relayData?: GroupRelay
}
export interface GroupMemberAdmission {
@@ -2595,6 +2594,7 @@ export interface GroupProfile {
export interface GroupRelay {
groupRelayId: number // int64
groupMemberId: number // int64
userChatRelayId: number // int64
relayStatus: RelayStatus
relayLink?: string
@@ -3816,6 +3816,7 @@ export type StoreError =
| StoreError.UsageConditionsNotFound
| StoreError.UserChatRelayNotFound
| StoreError.GroupRelayNotFound
| StoreError.GroupRelayNotFoundByMemberId
| StoreError.InvalidQuote
| StoreError.InvalidMention
| StoreError.InvalidDeliveryTask
@@ -3903,6 +3904,7 @@ export namespace StoreError {
| "usageConditionsNotFound"
| "userChatRelayNotFound"
| "groupRelayNotFound"
| "groupRelayNotFoundByMemberId"
| "invalidQuote"
| "invalidMention"
| "invalidDeliveryTask"
@@ -4290,6 +4292,11 @@ export namespace StoreError {
groupRelayId: number // int64
}
export interface GroupRelayNotFoundByMemberId extends Interface {
type: "groupRelayNotFoundByMemberId"
groupMemberId: number // int64
}
export interface InvalidQuote extends Interface {
type: "invalidQuote"
}
+2 -2
View File
@@ -3625,8 +3625,8 @@ processChatCommand vr nm = \case
-- TODO - or make "add relays" api retriable, via prepared connection
connId <- withAgent $ \a -> prepareConnectionToJoin a (aUserId user) True cReq PQSupportOff
(relayMember, conn, groupRelay) <- withFastStore $ \db -> do
groupRelay <- createGroupRelayRecord db gInfo relay
relayMember <- createRelayForOwner db vr gVar user gInfo relay groupRelay
relayMember <- createRelayForOwner db vr gVar user gInfo relay
groupRelay <- createGroupRelayRecord db gInfo relayMember relay
conn <- createRelayConnection db vr user (groupMemberId' relayMember) connId ConnPrepared chatV subMode
pure (relayMember, conn, groupRelay)
let GroupMember {memberRole = userRole, memberId = userMemberId} = membership
+15 -22
View File
@@ -728,18 +728,13 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage =
-- [async agent commands] no continuation needed, but command should be asynchronous for stability
allowAgentConnectionAsync user conn' confId XOk
| otherwise -> messageError "x.grp.acpt: memberId is different from expected"
-- TODO [relays] owner: XGrpRelayAcpt processing branch
-- TODO - TBC relay category
XGrpRelayAcpt relayLink
| memberRole' membership == GROwner ->
case relayData m of
Just relay -> do
-- TODO [relays] owner: check current relay status?
withStore' $ \db -> do
updateGroupMemberStatus db userId m GSMemAccepted
void $ setRelayLinkAccepted db relay relayLink
allowAgentConnectionAsync user conn' confId XOk
Nothing -> messageError "x.grp.relay.acpt: member is not saved as relay"
| memberRole' membership == GROwner && isRelay' m -> do
withStore $ \db -> do
relay <- getGroupRelayByGMId db (groupMemberId' m)
liftIO $ updateGroupMemberStatus db userId m GSMemAccepted
void $ liftIO $ setRelayLinkAccepted db relay relayLink
allowAgentConnectionAsync user conn' confId XOk
| otherwise -> messageError "x.grp.relay.acpt: only owner can add relay"
_ -> messageError "CONF from invited member must have x.grp.acpt"
GCHostMember ->
@@ -813,23 +808,21 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage =
memberConnectedChatItem gInfo'' scopeInfo m''
let welcomeMsgId_ = (\PreparedGroup {welcomeSharedMsgId = mId} -> mId) <$> prepared
unless (memberPending membership || isJust welcomeMsgId_) $ maybeCreateGroupDescrLocal gInfo'' m''
GCInviteeMember ->
case relayData m of
-- TODO [relays] owner: relay CON processing branch
-- TODO - TBC relay category
Just relay -> do
-- TODO [relays] owner: agent async setConnShortLink api
GCInviteeMember
| isRelay' m -> do
withStore' $ \db -> updateGroupMemberStatus db userId m GSMemConnected
let m' = m {memberStatus = GSMemConnected}
-- TODO [relays] owner: agent async setConnShortLink api
setGroupLinkData' NRMBackground user gInfo >>= \case
Just gLink -> do
(relay', relays) <- withStore' $ \db ->
(,) <$> updateRelayStatus db relay RSActive <*> getGroupRelays db gInfo
let m'' = m' {relayData = Just relay'}
relays <- withStore $ \db -> do
relay <- getGroupRelayByGMId db (groupMemberId' m)
void $ liftIO $ updateRelayStatus db relay RSActive
liftIO $ getGroupRelays db gInfo
-- TODO [relays] owner: relay added chat item?
toView $ CEvtRelayJoined user gInfo m'' gLink relays
toView $ CEvtRelayJoined user gInfo m' gLink relays
Nothing -> messageError "x.grp.relay.acpt: group link not updated"
Nothing -> do
| otherwise -> do
(gInfo', mStatus) <-
if not (memberPending m)
then do
+2 -3
View File
@@ -149,16 +149,15 @@ getConnectionEntity db vr user@User {userId, userContactId} agentConnId = do
pu.display_name, pu.full_name, pu.short_descr, pu.image, pu.contact_link, pu.chat_peer_type, pu.local_alias, pu.preferences,
mu.created_at, mu.updated_at,
mu.support_chat_ts, mu.support_chat_items_unread, mu.support_chat_items_member_attention, mu.support_chat_items_mentions, mu.support_chat_last_msg_from_member_ts,
mu.is_relay, NULL, NULL, NULL, NULL,
mu.is_relay,
-- 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.short_descr, p.image, p.contact_link, p.chat_peer_type, p.local_alias, p.preferences,
m.created_at, m.updated_at,
m.support_chat_ts, m.support_chat_items_unread, m.support_chat_items_member_attention, m.support_chat_items_mentions, m.support_chat_last_msg_from_member_ts,
m.is_relay, r.group_relay_id, r.chat_relay_id, r.relay_status, r.relay_link
m.is_relay
FROM group_members m
JOIN contact_profiles p ON p.contact_profile_id = COALESCE(m.member_profile_id, m.contact_profile_id)
LEFT JOIN group_relays r ON r.group_relay_id = m.group_relay_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
+29 -26
View File
@@ -76,6 +76,7 @@ module Simplex.Chat.Store.Groups
createNewContactMember,
createGroupRelayRecord,
getGroupRelayById,
getGroupRelayByGMId,
getGroupRelays,
createRelayForOwner,
createRelayForMember,
@@ -204,11 +205,11 @@ 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 BoolInt, Maybe MemberRestrictionStatus) :. (Maybe Int64, Maybe GroupMemberId, Maybe ContactName, Maybe ContactId, Maybe ProfileId) :. (Maybe ProfileId, Maybe ContactName, Maybe Text, Maybe Text, Maybe ImageData, Maybe ConnLinkContact, Maybe ChatPeerType, Maybe LocalAlias, Maybe Preferences) :. (Maybe UTCTime, Maybe UTCTime) :. (Maybe UTCTime, Maybe Int64, Maybe Int64, Maybe Int64, Maybe UTCTime) :. (Maybe BoolInt, Maybe Int64, Maybe Int64, Maybe RelayStatus, Maybe ShortLinkContact)
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 Text, Maybe ImageData, Maybe ConnLinkContact, Maybe ChatPeerType, Maybe LocalAlias, Maybe Preferences) :. (Maybe UTCTime, Maybe UTCTime) :. (Maybe UTCTime, Maybe Int64, Maybe Int64, Maybe Int64, Maybe UTCTime, Maybe BoolInt)
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, shortDescr, image, contactLink, peerType, Just localAlias, contactPreferences) :. (Just createdAt, Just updatedAt) :. (supportChatTs, Just supportChatUnread, Just supportChatUnanswered, Just supportChatMentions, supportChatLastMsgFromMemberTs) :. (Just isRelay, groupRelayId, chatRelayId, relayStatus, relayLink)) =
Just $ toGroupMember userContactId ((groupMemberId, groupId, memberId, minVer, maxVer, memberRole, memberCategory, memberStatus, showMessages, memberBlocked') :. (invitedById, invitedByGroupMemberId, localDisplayName, memberContactId, memberContactProfileId) :. (profileId, displayName, fullName, shortDescr, image, contactLink, peerType, localAlias, contactPreferences) :. (createdAt, updatedAt) :. (supportChatTs, supportChatUnread, supportChatUnanswered, supportChatMentions, supportChatLastMsgFromMemberTs) :. (isRelay, groupRelayId, chatRelayId, relayStatus, relayLink))
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, shortDescr, image, contactLink, peerType, Just localAlias, contactPreferences) :. (Just createdAt, Just updatedAt) :. (supportChatTs, Just supportChatUnread, Just supportChatUnanswered, Just supportChatMentions, supportChatLastMsgFromMemberTs, Just isRelay)) =
Just $ toGroupMember userContactId ((groupMemberId, groupId, memberId, minVer, maxVer, memberRole, memberCategory, memberStatus, showMessages, memberBlocked') :. (invitedById, invitedByGroupMemberId, localDisplayName, memberContactId, memberContactProfileId) :. (profileId, displayName, fullName, shortDescr, image, contactLink, peerType, localAlias, contactPreferences) :. (createdAt, updatedAt) :. (supportChatTs, supportChatUnread, supportChatUnanswered, supportChatMentions, supportChatLastMsgFromMemberTs, isRelay))
toMaybeGroupMember _ _ = Nothing
createGroupLink :: DB.Connection -> TVar ChaChaDRG -> User -> GroupInfo -> ConnId -> CreatedLinkContact -> GroupLinkId -> GroupMemberRole -> SubscriptionMode -> ExceptT StoreError IO GroupLink
@@ -497,8 +498,7 @@ createContactMemberInv_ db User {userId, userContactId} groupId invitedByGroupMe
createdAt,
updatedAt = createdAt,
supportChat = Nothing,
isRelay = BoolDef isRelay,
relayData = Nothing
isRelay = BoolDef isRelay
}
where
memberChatVRange@(VersionRange minV maxV) = vr
@@ -1116,8 +1116,7 @@ createNewContactMember db gVar User {userId, userContactId} GroupInfo {groupId,
createdAt,
updatedAt = createdAt,
supportChat = Nothing,
isRelay = BoolDef False,
relayData = Nothing
isRelay = BoolDef False
}
where
insertMember_ =
@@ -1135,18 +1134,18 @@ createNewContactMember db gVar User {userId, userContactId} GroupInfo {groupId,
:. (minV, maxV)
)
createGroupRelayRecord :: DB.Connection -> GroupInfo -> UserChatRelay -> ExceptT StoreError IO GroupRelay
createGroupRelayRecord db GroupInfo {groupId} UserChatRelay {chatRelayId} = do
createGroupRelayRecord :: DB.Connection -> GroupInfo -> GroupMember -> UserChatRelay -> ExceptT StoreError IO GroupRelay
createGroupRelayRecord db GroupInfo {groupId} GroupMember {groupMemberId} UserChatRelay {chatRelayId} = do
currentTs <- liftIO getCurrentTime
liftIO $
DB.execute
db
[sql|
INSERT INTO group_relays
(group_id, chat_relay_id, relay_status, created_at, updated_at)
VALUES (?,?,?,?,?)
(group_id, group_member_id, chat_relay_id, relay_status, created_at, updated_at)
VALUES (?,?,?,?,?,?)
|]
(groupId, chatRelayId, RSNew, currentTs, currentTs)
(groupId, groupMemberId, chatRelayId, RSNew, currentTs, currentTs)
relayId <- liftIO $ insertedRowId db
getGroupRelayById db relayId
@@ -1158,6 +1157,14 @@ getGroupRelayById db relayId =
(groupRelayQuery <> " WHERE group_relay_id = ?")
(Only relayId)
getGroupRelayByGMId :: DB.Connection -> GroupMemberId -> ExceptT StoreError IO GroupRelay
getGroupRelayByGMId db groupMemberId =
ExceptT . firstRow toGroupRelay (SEGroupRelayNotFoundByMemberId groupMemberId) $
DB.query
db
(groupRelayQuery <> " WHERE group_member_id = ?")
(Only groupMemberId)
getGroupRelays :: DB.Connection -> GroupInfo -> IO [GroupRelay]
getGroupRelays db GroupInfo {groupId} =
map toGroupRelay
@@ -1169,21 +1176,21 @@ getGroupRelays db GroupInfo {groupId} =
groupRelayQuery :: Query
groupRelayQuery =
[sql|
SELECT group_relay_id, chat_relay_id, relay_status, relay_link
SELECT group_relay_id, group_member_id, chat_relay_id, relay_status, relay_link
FROM group_relays
|]
toGroupRelay :: (Int64, Int64, RelayStatus, Maybe ShortLinkContact) -> GroupRelay
toGroupRelay (groupRelayId, userChatRelayId, relayStatus, relayLink) =
GroupRelay {groupRelayId, userChatRelayId, relayStatus, relayLink}
toGroupRelay :: (Int64, GroupMemberId, Int64, RelayStatus, Maybe ShortLinkContact) -> GroupRelay
toGroupRelay (groupRelayId, groupMemberId, userChatRelayId, relayStatus, relayLink) =
GroupRelay {groupRelayId, groupMemberId, userChatRelayId, relayStatus, relayLink}
-- TODO [relays] TBC role, category, relay profile
-- TODO - GCInviteeMember -> GCRelayMember?
-- TODO - GRMember -> GRRelay?
-- TODO - create 1 profile per relay, link to chat_relays?
-- TODO - retrieve profile from relay address
createRelayForOwner :: DB.Connection -> VersionRangeChat -> TVar ChaChaDRG -> User -> GroupInfo -> UserChatRelay -> GroupRelay -> ExceptT StoreError IO GroupMember
createRelayForOwner db vr gVar user@User {userId, userContactId} GroupInfo {groupId, membership} UserChatRelay {name} GroupRelay {groupRelayId} = do
createRelayForOwner :: DB.Connection -> VersionRangeChat -> TVar ChaChaDRG -> User -> GroupInfo -> UserChatRelay -> ExceptT StoreError IO GroupMember
createRelayForOwner db vr gVar user@User {userId, userContactId} GroupInfo {groupId, membership} UserChatRelay {name} = do
currentTs <- liftIO getCurrentTime
let relayProfile = profileFromName name
(localDisplayName, memProfileId) <- createNewMemberProfile_ db user relayProfile currentTs
@@ -1193,14 +1200,11 @@ createRelayForOwner db vr gVar user@User {userId, userContactId} GroupInfo {grou
[sql|
INSERT INTO group_members
( group_id, member_id, member_role, member_category, member_status, invited_by, invited_by_group_member_id,
user_id, local_display_name, contact_profile_id, created_at, updated_at,
is_relay, group_relay_id
)
VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?)
user_id, local_display_name, contact_profile_id, created_at, updated_at, is_relay)
VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?)
|]
( (groupId, MemberId memId, GRMember, GCInviteeMember, GSMemInvited, fromInvitedBy userContactId IBUser, groupMemberId' membership)
:. (userId, localDisplayName, memProfileId, currentTs, currentTs)
:. (BI True, groupRelayId)
:. (userId, localDisplayName, memProfileId, currentTs, currentTs, BI True)
)
insertedRowId db
getGroupMemberById db vr user groupMemberId
@@ -1689,8 +1693,7 @@ createNewMember_
createdAt,
updatedAt = createdAt,
supportChat = Nothing,
isRelay = BoolDef isRelay,
relayData = Nothing
isRelay = BoolDef isRelay
}
checkGroupMemberHasItems :: DB.Connection -> User -> GroupMember -> IO (Maybe ChatItemId)
+4 -8
View File
@@ -679,10 +679,9 @@ getChatItemQuote_ db User {userId, userContactId} chatDirection QuotedMsg {msgRe
p.display_name, p.full_name, p.short_descr, p.image, p.contact_link, p.chat_peer_type, p.local_alias, p.preferences,
m.created_at, m.updated_at,
m.support_chat_ts, m.support_chat_items_unread, m.support_chat_items_member_attention, m.support_chat_items_mentions, m.support_chat_last_msg_from_member_ts,
m.is_relay, r.group_relay_id, r.chat_relay_id, r.relay_status, r.relay_link
m.is_relay
FROM group_members m
JOIN contact_profiles p ON p.contact_profile_id = COALESCE(m.member_profile_id, m.contact_profile_id)
LEFT JOIN group_relays r ON r.group_relay_id = m.group_relay_id
LEFT JOIN contacts c ON m.contact_id = c.contact_id
LEFT JOIN chat_items i ON i.user_id = m.user_id
AND i.group_id = m.group_id
@@ -3005,7 +3004,7 @@ getGroupChatItem db User {userId, userContactId} groupId itemId = ExceptT $ do
p.display_name, p.full_name, p.short_descr, p.image, p.contact_link, p.chat_peer_type, p.local_alias, p.preferences,
m.created_at, m.updated_at,
m.support_chat_ts, m.support_chat_items_unread, m.support_chat_items_member_attention, m.support_chat_items_mentions, m.support_chat_last_msg_from_member_ts,
m.is_relay, r.group_relay_id, r.chat_relay_id, r.relay_status, r.relay_link,
m.is_relay,
-- quoted ChatItem
ri.chat_item_id, i.quoted_shared_msg_id, i.quoted_sent_at, i.quoted_content, i.quoted_sent,
-- quoted GroupMember
@@ -3014,26 +3013,23 @@ getGroupChatItem db User {userId, userContactId} groupId itemId = ExceptT $ do
rp.display_name, rp.full_name, rp.short_descr, rp.image, rp.contact_link, rp.chat_peer_type, rp.local_alias, rp.preferences,
rm.created_at, rm.updated_at,
rm.support_chat_ts, rm.support_chat_items_unread, rm.support_chat_items_member_attention, rm.support_chat_items_mentions, rm.support_chat_last_msg_from_member_ts,
rm.is_relay, rr.group_relay_id, rr.chat_relay_id, rr.relay_status, rr.relay_link,
rm.is_relay,
-- deleted by GroupMember
dbm.group_member_id, dbm.group_id, dbm.member_id, dbm.peer_chat_min_version, dbm.peer_chat_max_version, dbm.member_role, dbm.member_category,
dbm.member_status, dbm.show_messages, dbm.member_restriction, dbm.invited_by, dbm.invited_by_group_member_id, dbm.local_display_name, dbm.contact_id, dbm.contact_profile_id, dbp.contact_profile_id,
dbp.display_name, dbp.full_name, dbp.short_descr, dbp.image, dbp.contact_link, dbp.chat_peer_type, dbp.local_alias, dbp.preferences,
dbm.created_at, dbm.updated_at,
dbm.support_chat_ts, dbm.support_chat_items_unread, dbm.support_chat_items_member_attention, dbm.support_chat_items_mentions, dbm.support_chat_last_msg_from_member_ts,
dbm.is_relay, dbr.group_relay_id, dbr.chat_relay_id, dbr.relay_status, dbr.relay_link
dbm.is_relay
FROM chat_items i
LEFT JOIN files f ON f.chat_item_id = i.chat_item_id
LEFT JOIN group_members m ON m.group_member_id = i.group_member_id
LEFT JOIN contact_profiles p ON p.contact_profile_id = COALESCE(m.member_profile_id, m.contact_profile_id)
LEFT JOIN group_relays r ON r.group_relay_id = m.group_relay_id
LEFT JOIN chat_items ri ON ri.shared_msg_id = i.quoted_shared_msg_id AND ri.group_id = i.group_id
LEFT JOIN group_members rm ON rm.group_member_id = ri.group_member_id
LEFT JOIN contact_profiles rp ON rp.contact_profile_id = COALESCE(rm.member_profile_id, rm.contact_profile_id)
LEFT JOIN group_relays rr ON rr.group_relay_id = rm.group_relay_id
LEFT JOIN group_members dbm ON dbm.group_member_id = i.item_deleted_by_group_member_id
LEFT JOIN contact_profiles dbp ON dbp.contact_profile_id = COALESCE(dbm.member_profile_id, dbm.contact_profile_id)
LEFT JOIN group_relays dbr ON dbr.group_relay_id = dbm.group_relay_id
WHERE i.user_id = ? AND i.group_id = ? AND i.chat_item_id = ?
|]
(userId, groupId, itemId)
@@ -5,13 +5,6 @@ module Simplex.Chat.Store.SQLite.Migrations.M20251018_chat_relays where
import Database.SQLite.Simple (Query)
import Database.SQLite.Simple.QQ (sql)
-- TODO [relays] owner: consider flipping group_members.group_relay_id fkey to group_relays.group_member_id
-- TODO - pros: less joins in all member queries,
-- TODO - can discern relay by is_relay,
-- TODO - little places need additional relay data
-- TODO - cons: in some places will need extra query:
-- TODO - SELECT ... FROM group_relays WHERE group_member_id = ?
-- - chat_relays - user's list of chat relays to choose from (similar to protocol_servers)
-- - users.is_user_chat_relay - indicates that the user can serve as a chat relay
-- (TBC usage, e.g. agree to invitations to be relay)
@@ -21,11 +14,7 @@ import Database.SQLite.Simple.QQ (sql)
-- - group_relays.chat_relay_id - associates group_relays record with a chat_relays record,
-- chat_relays.deleted is to keep associated record if user removes chat relay from configuration,
-- but has group relays using it
-- - group_members.is_relay - indicates that the member is a chat relay (to all group members)
-- - group_members.group_relay_id - associates group_members record with a group_relays record for a group owner;
-- receiving event to member connection, owner can match it to the relay;
-- TBC inverse association - from group_relays to group_members?
-- - TBC also inverse link from group_relays to group_members? (group_relays.group_member_id)
-- - group_members.is_relay - indicates that the member is a chat relay
-- - groups.relay_own_status - indicates for a relay client that it is chat relay for the group (RelayStatus)
m20251018_chat_relays :: Query
m20251018_chat_relays =
@@ -58,6 +47,7 @@ ALTER TABLE group_profiles ADD COLUMN group_link BLOB;
CREATE TABLE group_relays(
group_relay_id INTEGER PRIMARY KEY,
group_id INTEGER NOT NULL REFERENCES groups ON DELETE CASCADE,
group_member_id INTEGER NOT NULL REFERENCES group_members ON DELETE CASCADE,
chat_relay_id INTEGER NOT NULL REFERENCES chat_relays ON DELETE CASCADE,
relay_status TEXT NOT NULL,
relay_link BLOB,
@@ -65,12 +55,10 @@ CREATE TABLE group_relays(
updated_at TEXT NOT NULL DEFAULT(datetime('now'))
);
CREATE INDEX idx_group_relays_group_id ON group_relays(group_id);
CREATE UNIQUE INDEX idx_group_relays_group_member_id ON group_relays(group_member_id);
CREATE INDEX idx_group_relays_chat_relay_id ON group_relays(chat_relay_id);
ALTER TABLE group_members ADD COLUMN is_relay INTEGER NOT NULL DEFAULT 0;
ALTER TABLE group_members ADD COLUMN group_relay_id INTEGER REFERENCES group_relays ON DELETE SET NULL;
CREATE INDEX idx_group_members_group_relay_id ON group_members(group_relay_id);
|]
down_m20251018_chat_relays :: Query
@@ -88,11 +76,9 @@ ALTER TABLE groups DROP COLUMN relay_own_status;
ALTER TABLE group_profiles DROP COLUMN group_link;
DROP INDEX idx_group_relays_group_id;
DROP INDEX idx_group_relays_group_member_id;
DROP INDEX idx_group_relays_chat_relay_id;
DROP TABLE group_relays;
ALTER TABLE group_members DROP COLUMN is_relay;
DROP INDEX idx_group_members_group_relay_id;
ALTER TABLE group_members DROP COLUMN group_relay_id;
|]
@@ -200,7 +200,6 @@ CREATE TABLE group_members(
member_xcontact_id BLOB,
member_welcome_shared_msg_id BLOB,
is_relay INTEGER NOT NULL DEFAULT 0,
group_relay_id INTEGER REFERENCES group_relays ON DELETE SET NULL,
FOREIGN KEY(user_id, local_display_name)
REFERENCES display_names(user_id, local_display_name)
ON DELETE CASCADE
@@ -745,6 +744,7 @@ CREATE TABLE chat_relays(
CREATE TABLE group_relays(
group_relay_id INTEGER PRIMARY KEY,
group_id INTEGER NOT NULL REFERENCES groups ON DELETE CASCADE,
group_member_id INTEGER NOT NULL REFERENCES group_members ON DELETE CASCADE,
chat_relay_id INTEGER NOT NULL REFERENCES chat_relays ON DELETE CASCADE,
relay_status TEXT NOT NULL,
relay_link BLOB,
@@ -1216,8 +1216,10 @@ CREATE INDEX idx_connections_to_subscribe ON connections(
);
CREATE INDEX idx_chat_relays_user_id ON chat_relays(user_id);
CREATE INDEX idx_group_relays_group_id ON group_relays(group_id);
CREATE UNIQUE INDEX idx_group_relays_group_member_id ON group_relays(
group_member_id
);
CREATE INDEX idx_group_relays_chat_relay_id ON group_relays(chat_relay_id);
CREATE INDEX idx_group_members_group_relay_id ON group_members(group_relay_id);
CREATE TRIGGER on_group_members_insert_update_summary
AFTER INSERT ON group_members
FOR EACH ROW
+5 -9
View File
@@ -149,6 +149,7 @@ data StoreError
| SEUsageConditionsNotFound
| SEUserChatRelayNotFound {chatRelayId :: Int64}
| SEGroupRelayNotFound {groupRelayId :: Int64}
| SEGroupRelayNotFoundByMemberId {groupMemberId :: GroupMemberId}
| SEInvalidQuote
| SEInvalidMention
| SEInvalidDeliveryTask {taskId :: Int64}
@@ -658,7 +659,7 @@ type BusinessChatInfoRow = (Maybe BusinessChatType, Maybe MemberId, Maybe Member
type GroupInfoRow = (Int64, GroupName, GroupName, Text, Maybe Text, Text, Maybe Text, Maybe ImageData, Maybe ShortLinkContact) :. (Maybe MsgFilter, Maybe BoolInt, BoolInt, Maybe GroupPreferences, Maybe GroupMemberAdmission) :. (UTCTime, UTCTime, Maybe UTCTime, Maybe UTCTime) :. PreparedGroupRow :. BusinessChatInfoRow :. (BoolInt, Maybe RelayStatus, Maybe UIThemeEntityOverrides, Int64, Maybe CustomData, Maybe Int64, Int, Maybe ConnReqContact) :. GroupMemberRow
type GroupMemberRow = (Int64, Int64, MemberId, VersionChat, VersionChat, GroupMemberRole, GroupMemberCategory, GroupMemberStatus, BoolInt, Maybe MemberRestrictionStatus) :. (Maybe Int64, Maybe GroupMemberId, ContactName, Maybe ContactId, ProfileId) :. ProfileRow :. (UTCTime, UTCTime) :. (Maybe UTCTime, Int64, Int64, Int64, Maybe UTCTime) :. (BoolInt, Maybe Int64, Maybe Int64, Maybe RelayStatus, Maybe ShortLinkContact)
type GroupMemberRow = (Int64, Int64, MemberId, VersionChat, VersionChat, GroupMemberRole, GroupMemberCategory, GroupMemberStatus, BoolInt, Maybe MemberRestrictionStatus) :. (Maybe Int64, Maybe GroupMemberId, ContactName, Maybe ContactId, ProfileId) :. ProfileRow :. (UTCTime, UTCTime) :. (Maybe UTCTime, Int64, Int64, Int64, Maybe UTCTime, BoolInt)
type ProfileRow = (ProfileId, ContactName, Text, Maybe Text, Maybe ImageData, Maybe ConnLinkContact, Maybe ChatPeerType, LocalAlias, Maybe Preferences)
@@ -680,7 +681,7 @@ toPreparedGroup = \case
_ -> Nothing
toGroupMember :: Int64 -> GroupMemberRow -> GroupMember
toGroupMember userContactId ((groupMemberId, groupId, memberId, minVer, maxVer, memberRole, memberCategory, memberStatus, BI showMessages, memberRestriction_) :. (invitedById, invitedByGroupMemberId, localDisplayName, memberContactId, memberContactProfileId) :. profileRow :. (createdAt, updatedAt) :. (supportChatTs_, supportChatUnread, supportChatMemberAttention, supportChatMentions, supportChatLastMsgFromMemberTs) :. (BI isRel, groupRelayId_, chatRelayId_, relayStatus_, relayLink)) =
toGroupMember userContactId ((groupMemberId, groupId, memberId, minVer, maxVer, memberRole, memberCategory, memberStatus, BI showMessages, memberRestriction_) :. (invitedById, invitedByGroupMemberId, localDisplayName, memberContactId, memberContactProfileId) :. profileRow :. (createdAt, updatedAt) :. (supportChatTs_, supportChatUnread, supportChatMemberAttention, supportChatMentions, supportChatLastMsgFromMemberTs, BI isRel)) =
let memberProfile = rowToLocalProfile profileRow
memberSettings = GroupMemberSettings {showMessages}
blockedByAdmin = maybe False mrsBlocked memberRestriction_
@@ -699,9 +700,6 @@ toGroupMember userContactId ((groupMemberId, groupId, memberId, minVer, maxVer,
}
_ -> Nothing
isRelay = BoolDef isRel
relayData = case (groupRelayId_, chatRelayId_, relayStatus_) of
(Just groupRelayId, Just userChatRelayId, Just relayStatus) -> Just GroupRelay {groupRelayId, userChatRelayId, relayStatus, relayLink}
_ -> Nothing
in GroupMember {..}
groupMemberQuery :: Query
@@ -712,14 +710,13 @@ groupMemberQuery =
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.short_descr, p.image, p.contact_link, p.chat_peer_type, p.local_alias, p.preferences,
m.created_at, m.updated_at,
m.support_chat_ts, m.support_chat_items_unread, m.support_chat_items_member_attention, m.support_chat_items_mentions, m.support_chat_last_msg_from_member_ts,
m.is_relay, r.group_relay_id, r.chat_relay_id, r.relay_status, r.relay_link,
m.is_relay,
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.xcontact_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.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)
LEFT JOIN group_relays r ON r.group_relay_id = m.group_relay_id
LEFT JOIN connections c ON c.group_member_id = m.group_member_id
|]
@@ -738,7 +735,6 @@ toBusinessChatInfo _ = Nothing
groupInfoQuery :: Query
groupInfoQuery = groupInfoQueryFields <> " " <> groupInfoQueryFrom
-- membership "member" never references group_relays, therefore `NULL, NULL, NULL, NULL` which avoids extra join
groupInfoQueryFields :: Query
groupInfoQueryFields =
[sql|
@@ -757,7 +753,7 @@ groupInfoQueryFields =
pu.display_name, pu.full_name, pu.short_descr, pu.image, pu.contact_link, pu.chat_peer_type, pu.local_alias, pu.preferences,
mu.created_at, mu.updated_at,
mu.support_chat_ts, mu.support_chat_items_unread, mu.support_chat_items_member_attention, mu.support_chat_items_mentions, mu.support_chat_last_msg_from_member_ts,
mu.is_relay, NULL, NULL, NULL, NULL
mu.is_relay
|]
groupInfoQueryFrom :: Query
+2 -2
View File
@@ -962,8 +962,7 @@ data GroupMember = GroupMember
createdAt :: UTCTime,
updatedAt :: UTCTime,
supportChat :: Maybe GroupSupportChat,
isRelay :: BoolDef, -- marker for all members that this member is a chat relay
relayData :: Maybe GroupRelay -- owner's additional data for a chat relay
isRelay :: BoolDef
}
deriving (Eq, Show)
@@ -972,6 +971,7 @@ memberRole' GroupMember {memberRole} = memberRole
data GroupRelay = GroupRelay
{ groupRelayId :: Int64,
groupMemberId :: GroupMemberId,
userChatRelayId :: Int64, -- ID of configured UserChatRelay
relayStatus :: RelayStatus,
relayLink :: Maybe ShortLinkContact