Compare commits

..

46 Commits

Author SHA1 Message Date
github-actions[bot] 391eafb402 Update version to v3.8.3-patch.7 2025-07-29 09:07:09 +00:00
Gordon 482284a0fb Merge remote-tracking branch 'upstream/3.8.3-patch' into 3.8.3-patch 2025-07-29 16:00:51 +08:00
Gordon a893141ae6 eat: add filtering for invalid messages and invalid conversations to prevent data-fetching exceptions after conversations are deleted. 2025-07-29 16:00:08 +08:00
Monet Lee 9eccfee997 fix: solve redis config db field in v3.8.3-patch (#3490) 2025-07-29 14:11:43 +08:00
Monet Lee f950dbc5e7 fix: import friends send notification in v3.8.3-patch (#3488) 2025-07-29 11:11:42 +08:00
OpenIM-Gordon 55113e5277 feat: add filtering for invalid messages and invalid conversations to prevent data-fetching exceptions after conversations are deleted. (#3239) (#3247) (#3483)
Co-authored-by: OpenIM-Robot <openim4@gmail.com>
2025-07-28 14:43:58 +08:00
OpenIM-Robot 7fdc438500 fix: correctly aggregate read seqs by conversation and user before DB update. [Created by @FGadvancer from #3442] (#3482)
* fix: correctly aggregate read seqs by conversation and user before DB update. (#3442)

* build: docker compose file add some comments.

* fix: correctly aggregate read seqs by conversation and user before DB update.

* solve declarate pkg issue.

---------

Co-authored-by: OpenIM-Gordon <1432970085@qq.com>
Co-authored-by: Monet Lee <monet_lee@163.com>
2025-07-28 12:36:48 +08:00
Monet Lee 2804d90020 fix: use safe submodule init in workflows in v3.8.3-patch. (#3469) 2025-07-22 10:59:16 +08:00
chao 4ca3f2dc0c Merge pull request #3454 from withchao/3.8.3-patch
fix: s3 aws init
2025-07-10 15:03:58 +08:00
withchao b92c5ab821 Merge remote-tracking branch 'origin/3.8.3-patch' into 3.8.3-patch 2025-07-10 14:59:46 +08:00
withchao 9c4b6e50ef fix: s3 aws init 2025-07-10 14:59:33 +08:00
Monet Lee 54e189d80f fix: remove update version file workflows have new line in 3.8.3-patch branch. (#3452) 2025-07-09 17:27:59 +08:00
icey-yu 1b6d70e4a7 Merge pull request #3438 from icey-yu/add-friend
fix: Add friend DB in notification sender
2025-06-25 16:39:23 +08:00
chao 4c9cf55867 fix: add friend db in notification sender 2025-06-25 16:35:53 +08:00
Monet Lee 9be25ab041 build: update gomake version in dockerfile. (#3416) 2025-06-10 17:08:37 +08:00
chao 8a5f8a10ba Merge pull request #3396 from withchao/3.8.3-patch
feat: optimize friend and group applications
2025-05-29 18:35:35 +08:00
chao 1f3b41e379 fix: optimize friend and group applications (#3389)
* pb

* fix: Modifying other fields while setting IsPrivateChat does not take effect

* fix: quote message error revoke

* refactoring scheduled tasks

* refactoring scheduled tasks

* refactoring scheduled tasks

* refactoring scheduled tasks

* refactoring scheduled tasks

* refactoring scheduled tasks

* upgrading pkg tools

* fix

* fix

* optimize log output

* feat: support GetLastMessage

* feat: support GetLastMessage

* feat: s3 switch

* feat: s3 switch

* fix: GetUsersOnline

* feat: SendBusinessNotification supported configuration parameters

* feat: SendBusinessNotification supported configuration parameters

* feat: SendBusinessNotification supported configuration parameters

* feat: seq conversion failed without exiting

* fix: DeleteDoc crash

* fix: fill send time

* fix: fill send time

* fix: crash caused by withdrawing messages from users who have left the group

* fix: user msg timestamp

* seq read config

* seq read config

* fix: the source message of the reference is withdrawn, and the referenced message is deleted

* feat: optimize the default notification.yml

* fix: shouldPushOffline

* fix: the sorting is wrong after canceling the administrator in group settings

* feat: Sending messages supports returning fields modified by webhook

* feat: Sending messages supports returning fields modified by webhook

* feat: Sending messages supports returning fields modified by webhook

* fix: oss specifies content-type when uploading

* fix: the version number contains a line break

* fix: the version number contains a line break

* feat: GetConversationsHasReadAndMaxSeq support pinned

* feat: GetConversationsHasReadAndMaxSeq support pinned

* feat: GetConversationsHasReadAndMaxSeq support pinned

* fix: transferring the group owner to a muted member, incremental version error

* feat: unified conversion code

* feat: update gomake

* fix: in standalone mode, the user online status is wrong

* fix: add permission check

* fix: add permission check

* fix: add rpc interface permission check

* fix: CreateGroupChatConversations

* feat: optimize friend and group applications

* feat: optimize friend and group applications

* feat: optimize friend and group applications

* feat: optimize friend and group applications

* fix: optimize friend and group applications

(cherry picked from commit 812c1e4127)
2025-05-29 18:29:28 +08:00
withchao 1684c82458 feat: optimize friend and group applications (#3384)
* pb

* fix: Modifying other fields while setting IsPrivateChat does not take effect

* fix: quote message error revoke

* refactoring scheduled tasks

* refactoring scheduled tasks

* refactoring scheduled tasks

* refactoring scheduled tasks

* refactoring scheduled tasks

* refactoring scheduled tasks

* upgrading pkg tools

* fix

* fix

* optimize log output

* feat: support GetLastMessage

* feat: support GetLastMessage

* feat: s3 switch

* feat: s3 switch

* fix: GetUsersOnline

* feat: SendBusinessNotification supported configuration parameters

* feat: SendBusinessNotification supported configuration parameters

* feat: SendBusinessNotification supported configuration parameters

* feat: seq conversion failed without exiting

* fix: DeleteDoc crash

* fix: fill send time

* fix: fill send time

* fix: crash caused by withdrawing messages from users who have left the group

* fix: user msg timestamp

* seq read config

* seq read config

* fix: the source message of the reference is withdrawn, and the referenced message is deleted

* feat: optimize the default notification.yml

* fix: shouldPushOffline

* fix: the sorting is wrong after canceling the administrator in group settings

* feat: Sending messages supports returning fields modified by webhook

* feat: Sending messages supports returning fields modified by webhook

* feat: Sending messages supports returning fields modified by webhook

* fix: oss specifies content-type when uploading

* fix: the version number contains a line break

* fix: the version number contains a line break

* feat: GetConversationsHasReadAndMaxSeq support pinned

* feat: GetConversationsHasReadAndMaxSeq support pinned

* feat: GetConversationsHasReadAndMaxSeq support pinned

* fix: transferring the group owner to a muted member, incremental version error

* feat: unified conversion code

* feat: update gomake

* fix: in standalone mode, the user online status is wrong

* fix: add permission check

* fix: add permission check

* fix: add rpc interface permission check

* fix: CreateGroupChatConversations

* feat: optimize friend and group applications

* feat: optimize friend and group applications

* feat: optimize friend and group applications

* feat: optimize friend and group applications

(cherry picked from commit 8e61f30e9c)

# Conflicts:
#	go.mod
#	go.sum
2025-05-29 18:29:19 +08:00
chao bc326704f1 feat: optimize friend and group applications (#3384)
* pb

* fix: Modifying other fields while setting IsPrivateChat does not take effect

* fix: quote message error revoke

* refactoring scheduled tasks

* refactoring scheduled tasks

* refactoring scheduled tasks

* refactoring scheduled tasks

* refactoring scheduled tasks

* refactoring scheduled tasks

* upgrading pkg tools

* fix

* fix

* optimize log output

* feat: support GetLastMessage

* feat: support GetLastMessage

* feat: s3 switch

* feat: s3 switch

* fix: GetUsersOnline

* feat: SendBusinessNotification supported configuration parameters

* feat: SendBusinessNotification supported configuration parameters

* feat: SendBusinessNotification supported configuration parameters

* feat: seq conversion failed without exiting

* fix: DeleteDoc crash

* fix: fill send time

* fix: fill send time

* fix: crash caused by withdrawing messages from users who have left the group

* fix: user msg timestamp

* seq read config

* seq read config

* fix: the source message of the reference is withdrawn, and the referenced message is deleted

* feat: optimize the default notification.yml

* fix: shouldPushOffline

* fix: the sorting is wrong after canceling the administrator in group settings

* feat: Sending messages supports returning fields modified by webhook

* feat: Sending messages supports returning fields modified by webhook

* feat: Sending messages supports returning fields modified by webhook

* fix: oss specifies content-type when uploading

* fix: the version number contains a line break

* fix: the version number contains a line break

* feat: GetConversationsHasReadAndMaxSeq support pinned

* feat: GetConversationsHasReadAndMaxSeq support pinned

* feat: GetConversationsHasReadAndMaxSeq support pinned

* fix: transferring the group owner to a muted member, incremental version error

* feat: unified conversion code

* feat: update gomake

* fix: in standalone mode, the user online status is wrong

* fix: add permission check

* fix: add permission check

* fix: add rpc interface permission check

* fix: CreateGroupChatConversations

* feat: optimize friend and group applications

* feat: optimize friend and group applications

* feat: optimize friend and group applications

* feat: optimize friend and group applications

(cherry picked from commit 8e61f30e9c)

# Conflicts:
#	go.mod
#	go.sum
2025-05-29 18:23:57 +08:00
chao e254bbf1fc Merge branch 'openimsdk:3.8.3-patch' into 3.8.3-patch 2025-05-29 18:08:43 +08:00
withchao 54309b83a2 feat: add group friend rpc interface permission check 2025-05-22 14:52:15 +08:00
withchao d55cb0fa8b feat: add group friend rpc interface permission check 2025-05-22 14:37:54 +08:00
chao 2e58a5cc82 feat: GroupApplicationAgreeMemberEnterNotification splitting (#3297)
* pb

* fix: Modifying other fields while setting IsPrivateChat does not take effect

* fix: quote message error revoke

* refactoring scheduled tasks

* refactoring scheduled tasks

* refactoring scheduled tasks

* refactoring scheduled tasks

* refactoring scheduled tasks

* refactoring scheduled tasks

* upgrading pkg tools

* fix

* fix

* optimize log output

* feat: support GetLastMessage

* feat: support GetLastMessage

* feat: s3 switch

* feat: s3 switch

* fix: GetUsersOnline

* feat: SendBusinessNotification supported configuration parameters

* feat: SendBusinessNotification supported configuration parameters

* feat: SendBusinessNotification supported configuration parameters

* feat: seq conversion failed without exiting

* fix: DeleteDoc crash

* fix: fill send time

* fix: fill send time

* fix: crash caused by withdrawing messages from users who have left the group

* fix: user msg timestamp

* seq read config

* seq read config

* fix: the source message of the reference is withdrawn, and the referenced message is deleted

* feat: optimize the default notification.yml

* fix: shouldPushOffline

* fix: the sorting is wrong after canceling the administrator in group settings

* feat: Sending messages supports returning fields modified by webhook

* feat: Sending messages supports returning fields modified by webhook

* feat: Sending messages supports returning fields modified by webhook

* fix: oss specifies content-type when uploading

* fix: the version number contains a line break

* fix: the version number contains a line break

* feat: GetConversationsHasReadAndMaxSeq support pinned

* feat: GetConversationsHasReadAndMaxSeq support pinned

* feat: GetConversationsHasReadAndMaxSeq support pinned

* fix: transferring the group owner to a muted member, incremental version error

* feat: GroupApplicationAgreeMemberEnterNotification splitting, rpc body size limit

* feat: GroupApplicationAgreeMemberEnterNotification splitting, rpc body size limit

(cherry picked from commit fa3d251dcb)

# Conflicts:
#	config/share.yml
#	pkg/common/config/config.go
#	pkg/common/startrpc/start.go
2025-04-22 17:21:48 +08:00
chao cc957886b1 fix: transferring the group owner to a muted member, incremental version error (#3284)
* pb

* fix: Modifying other fields while setting IsPrivateChat does not take effect

* fix: quote message error revoke

* refactoring scheduled tasks

* refactoring scheduled tasks

* refactoring scheduled tasks

* refactoring scheduled tasks

* refactoring scheduled tasks

* refactoring scheduled tasks

* upgrading pkg tools

* fix

* fix

* optimize log output

* feat: support GetLastMessage

* feat: support GetLastMessage

* feat: s3 switch

* feat: s3 switch

* fix: GetUsersOnline

* feat: SendBusinessNotification supported configuration parameters

* feat: SendBusinessNotification supported configuration parameters

* feat: SendBusinessNotification supported configuration parameters

* feat: seq conversion failed without exiting

* fix: DeleteDoc crash

* fix: fill send time

* fix: fill send time

* fix: crash caused by withdrawing messages from users who have left the group

* fix: user msg timestamp

* seq read config

* seq read config

* fix: the source message of the reference is withdrawn, and the referenced message is deleted

* feat: optimize the default notification.yml

* fix: shouldPushOffline

* fix: the sorting is wrong after canceling the administrator in group settings

* feat: Sending messages supports returning fields modified by webhook

* feat: Sending messages supports returning fields modified by webhook

* feat: Sending messages supports returning fields modified by webhook

* fix: oss specifies content-type when uploading

* fix: the version number contains a line break

* fix: the version number contains a line break

* feat: GetConversationsHasReadAndMaxSeq support pinned

* feat: GetConversationsHasReadAndMaxSeq support pinned

* feat: GetConversationsHasReadAndMaxSeq support pinned

* fix: transferring the group owner to a muted member, incremental version error

(cherry picked from commit 52bd5e8be9)
2025-04-22 17:09:45 +08:00
chao 78c82e08b8 fix: transferring the group owner to a muted member, incremental version error (#3284)
* pb

* fix: Modifying other fields while setting IsPrivateChat does not take effect

* fix: quote message error revoke

* refactoring scheduled tasks

* refactoring scheduled tasks

* refactoring scheduled tasks

* refactoring scheduled tasks

* refactoring scheduled tasks

* refactoring scheduled tasks

* upgrading pkg tools

* fix

* fix

* optimize log output

* feat: support GetLastMessage

* feat: support GetLastMessage

* feat: s3 switch

* feat: s3 switch

* fix: GetUsersOnline

* feat: SendBusinessNotification supported configuration parameters

* feat: SendBusinessNotification supported configuration parameters

* feat: SendBusinessNotification supported configuration parameters

* feat: seq conversion failed without exiting

* fix: DeleteDoc crash

* fix: fill send time

* fix: fill send time

* fix: crash caused by withdrawing messages from users who have left the group

* fix: user msg timestamp

* seq read config

* seq read config

* fix: the source message of the reference is withdrawn, and the referenced message is deleted

* feat: optimize the default notification.yml

* fix: shouldPushOffline

* fix: the sorting is wrong after canceling the administrator in group settings

* feat: Sending messages supports returning fields modified by webhook

* feat: Sending messages supports returning fields modified by webhook

* feat: Sending messages supports returning fields modified by webhook

* fix: oss specifies content-type when uploading

* fix: the version number contains a line break

* fix: the version number contains a line break

* feat: GetConversationsHasReadAndMaxSeq support pinned

* feat: GetConversationsHasReadAndMaxSeq support pinned

* feat: GetConversationsHasReadAndMaxSeq support pinned

* fix: transferring the group owner to a muted member, incremental version error

(cherry picked from commit 52bd5e8be9)
2025-04-22 17:04:15 +08:00
withchao 09cb8336ad Merge remote-tracking branch 'upstream/3.8.3-patch' into 3.8.3-patch 2025-04-22 17:03:34 +08:00
OpenIM-Robot 9a436087cf fix: the source message of the reference is withdrawn, and the referenced message is deleted (#3137) (#3140)
* pb

* fix: Modifying other fields while setting IsPrivateChat does not take effect

* fix: quote message error revoke

* refactoring scheduled tasks

* refactoring scheduled tasks

* refactoring scheduled tasks

* refactoring scheduled tasks

* refactoring scheduled tasks

* refactoring scheduled tasks

* upgrading pkg tools

* fix

* fix

* optimize log output

* feat: support GetLastMessage

* feat: support GetLastMessage

* feat: s3 switch

* feat: s3 switch

* fix: GetUsersOnline

* feat: SendBusinessNotification supported configuration parameters

* feat: SendBusinessNotification supported configuration parameters

* feat: SendBusinessNotification supported configuration parameters

* feat: seq conversion failed without exiting

* fix: DeleteDoc crash

* fix: fill send time

* fix: fill send time

* fix: crash caused by withdrawing messages from users who have left the group

* fix: user msg timestamp

* seq read config

* seq read config

* fix: the source message of the reference is withdrawn, and the referenced message is deleted

Co-authored-by: chao <48119764+withchao@users.noreply.github.com>
2025-04-01 17:20:33 +08:00
OpenIM-Robot 6c7c268b1f fix: the source message of the reference is withdrawn, and the referenced message is deleted (#3137) (#3140)
* pb

* fix: Modifying other fields while setting IsPrivateChat does not take effect

* fix: quote message error revoke

* refactoring scheduled tasks

* refactoring scheduled tasks

* refactoring scheduled tasks

* refactoring scheduled tasks

* refactoring scheduled tasks

* refactoring scheduled tasks

* upgrading pkg tools

* fix

* fix

* optimize log output

* feat: support GetLastMessage

* feat: support GetLastMessage

* feat: s3 switch

* feat: s3 switch

* fix: GetUsersOnline

* feat: SendBusinessNotification supported configuration parameters

* feat: SendBusinessNotification supported configuration parameters

* feat: SendBusinessNotification supported configuration parameters

* feat: seq conversion failed without exiting

* fix: DeleteDoc crash

* fix: fill send time

* fix: fill send time

* fix: crash caused by withdrawing messages from users who have left the group

* fix: user msg timestamp

* seq read config

* seq read config

* fix: the source message of the reference is withdrawn, and the referenced message is deleted

Co-authored-by: chao <48119764+withchao@users.noreply.github.com>
2025-04-01 15:04:25 +08:00
OpenIM-Robot 04b7e6b75e fix: solve unocrrect invite notification (#3213) (#3219)
Co-authored-by: Monet Lee <monet_lee@163.com>
2025-03-12 11:47:02 +08:00
withchao 916d074cf9 resolving conflicts 2025-03-07 15:56:12 +08:00
chao 61b968fbf2 fix: the sorting is wrong after canceling the administrator in group settings (#3185)
* pb

* fix: Modifying other fields while setting IsPrivateChat does not take effect

* fix: quote message error revoke

* refactoring scheduled tasks

* refactoring scheduled tasks

* refactoring scheduled tasks

* refactoring scheduled tasks

* refactoring scheduled tasks

* refactoring scheduled tasks

* upgrading pkg tools

* fix

* fix

* optimize log output

* feat: support GetLastMessage

* feat: support GetLastMessage

* feat: s3 switch

* feat: s3 switch

* fix: GetUsersOnline

* feat: SendBusinessNotification supported configuration parameters

* feat: SendBusinessNotification supported configuration parameters

* feat: SendBusinessNotification supported configuration parameters

* feat: seq conversion failed without exiting

* fix: DeleteDoc crash

* fix: fill send time

* fix: fill send time

* fix: crash caused by withdrawing messages from users who have left the group

* fix: user msg timestamp

* seq read config

* seq read config

* fix: the source message of the reference is withdrawn, and the referenced message is deleted

* feat: optimize the default notification.yml

* fix: shouldPushOffline

* fix: the sorting is wrong after canceling the administrator in group settings
2025-03-07 15:53:39 +08:00
chao efed369c0c feat: optimizing BatchGetIncrementalGroupMember (#3180)
* pb

* fix: Modifying other fields while setting IsPrivateChat does not take effect

* fix: quote message error revoke

* refactoring scheduled tasks

* refactoring scheduled tasks

* refactoring scheduled tasks

* refactoring scheduled tasks

* refactoring scheduled tasks

* refactoring scheduled tasks

* upgrading pkg tools

* fix

* fix

* optimize log output

* feat: support GetLastMessage

* feat: support GetLastMessage

* feat: s3 switch

* feat: s3 switch

* fix: GetUsersOnline

* feat: SendBusinessNotification supported configuration parameters

* feat: SendBusinessNotification supported configuration parameters

* feat: SendBusinessNotification supported configuration parameters

* feat: seq conversion failed without exiting

* fix: DeleteDoc crash

* fix: fill send time

* fix: fill send time

* fix: crash caused by withdrawing messages from users who have left the group

* fix: user msg timestamp

* seq read config

* seq read config

* fix: the source message of the reference is withdrawn, and the referenced message is deleted

* feat: optimize the default notification.yml

* fix: shouldPushOffline

* fix: optimizing BatchGetIncrementalGroupMember
2025-03-07 15:52:35 +08:00
withchao aaf898567f solve uncorrect notification when set group info 2025-03-07 15:51:47 +08:00
Monet Lee 62583b32a7 fix: solve uncorrect notification when set group info (#3172)
* fix: setGroupInfoEx uncorrect call.

* update notification logic.

* update group notification logic.

* update update group announcement notication.

* fix errror.

* refresh

* solve conflict.

* update args.
2025-03-07 15:29:32 +08:00
OpenIM-Gordon 4890487429 refactor: change sendNotification to sendMessage to avoid ambiguity regarding message sending behavior. (#3173)
* feat: add a field to specify whether to send a notification message when creating a group.

* feat: add a field to specify whether to send a notification message when creating a group.

* refactor: change sendNotification to sendMessage to avoid ambiguity regarding message sending behavior.

---------

Co-authored-by: Monet Lee <monet_lee@163.com>
2025-03-07 15:29:18 +08:00
Monet Lee 744f481b9c fix: solve uncorrect GroupMember enter group notification type. (#3188) 2025-03-07 02:39:53 +00:00
chao f242066f30 feat: the default notification.yml is not configured properly (#3168)
* pb

* fix: Modifying other fields while setting IsPrivateChat does not take effect

* fix: quote message error revoke

* refactoring scheduled tasks

* refactoring scheduled tasks

* refactoring scheduled tasks

* refactoring scheduled tasks

* refactoring scheduled tasks

* refactoring scheduled tasks

* upgrading pkg tools

* fix

* fix

* optimize log output

* feat: support GetLastMessage

* feat: support GetLastMessage

* feat: s3 switch

* feat: s3 switch

* fix: GetUsersOnline

* feat: SendBusinessNotification supported configuration parameters

* feat: SendBusinessNotification supported configuration parameters

* feat: SendBusinessNotification supported configuration parameters

* feat: seq conversion failed without exiting

* fix: DeleteDoc crash

* fix: fill send time

* fix: fill send time

* fix: crash caused by withdrawing messages from users who have left the group

* fix: user msg timestamp

* seq read config

* seq read config

* fix: the source message of the reference is withdrawn, and the referenced message is deleted

* feat: optimize the default notification.yml

* fix: shouldPushOffline
2025-02-27 16:33:32 +08:00
withchao 57710bb9e3 resolving merge conflicts 2025-02-25 18:16:57 +08:00
chao 0006bce0a2 fix: seq conversion not reading env in docker environment (#3130)
* pb

* fix: Modifying other fields while setting IsPrivateChat does not take effect

* fix: quote message error revoke

* refactoring scheduled tasks

* refactoring scheduled tasks

* refactoring scheduled tasks

* refactoring scheduled tasks

* refactoring scheduled tasks

* refactoring scheduled tasks

* upgrading pkg tools

* fix

* fix

* optimize log output

* feat: support GetLastMessage

* feat: support GetLastMessage

* feat: s3 switch

* feat: s3 switch

* fix: GetUsersOnline

* feat: SendBusinessNotification supported configuration parameters

* feat: SendBusinessNotification supported configuration parameters

* feat: SendBusinessNotification supported configuration parameters

* feat: seq conversion failed without exiting

* fix: DeleteDoc crash

* fix: fill send time

* fix: fill send time

* fix: crash caused by withdrawing messages from users who have left the group

* fix: user msg timestamp

* seq read config

* seq read config
2025-02-25 18:08:40 +08:00
chao 0bb6f610ef fix: the user sets the conversation timer cleanup timestamp unit incorrectly (#3102)
* pb

* fix: Modifying other fields while setting IsPrivateChat does not take effect

* fix: quote message error revoke

* refactoring scheduled tasks

* refactoring scheduled tasks

* refactoring scheduled tasks

* refactoring scheduled tasks

* refactoring scheduled tasks

* refactoring scheduled tasks

* upgrading pkg tools

* fix

* fix

* optimize log output

* feat: support GetLastMessage

* feat: support GetLastMessage

* feat: s3 switch

* feat: s3 switch

* fix: GetUsersOnline

* feat: SendBusinessNotification supported configuration parameters

* feat: SendBusinessNotification supported configuration parameters

* feat: SendBusinessNotification supported configuration parameters

* feat: seq conversion failed without exiting

* fix: DeleteDoc crash

* fix: fill send time

* fix: fill send time

* fix: crash caused by withdrawing messages from users who have left the group

* fix: user msg timestamp
2025-02-25 18:08:22 +08:00
chao 82d133588e fix: crash caused by withdrawing messages from users who have left the group (#3100)
* pb

* fix: Modifying other fields while setting IsPrivateChat does not take effect

* fix: quote message error revoke

* refactoring scheduled tasks

* refactoring scheduled tasks

* refactoring scheduled tasks

* refactoring scheduled tasks

* refactoring scheduled tasks

* refactoring scheduled tasks

* upgrading pkg tools

* fix

* fix

* optimize log output

* feat: support GetLastMessage

* feat: support GetLastMessage

* feat: s3 switch

* feat: s3 switch

* fix: GetUsersOnline

* feat: SendBusinessNotification supported configuration parameters

* feat: SendBusinessNotification supported configuration parameters

* feat: SendBusinessNotification supported configuration parameters

* feat: seq conversion failed without exiting

* fix: DeleteDoc crash

* fix: fill send time

* fix: fill send time

* fix: crash caused by withdrawing messages from users who have left the group
2025-02-25 18:07:46 +08:00
chao d70dc7b16b fix: the abnormal message has no sending time, causing the SDK to be abnormal (#3087)
* pb

* fix: Modifying other fields while setting IsPrivateChat does not take effect

* fix: quote message error revoke

* refactoring scheduled tasks

* refactoring scheduled tasks

* refactoring scheduled tasks

* refactoring scheduled tasks

* refactoring scheduled tasks

* refactoring scheduled tasks

* upgrading pkg tools

* fix

* fix

* optimize log output

* feat: support GetLastMessage

* feat: support GetLastMessage

* feat: s3 switch

* feat: s3 switch

* fix: GetUsersOnline

* feat: SendBusinessNotification supported configuration parameters

* feat: SendBusinessNotification supported configuration parameters

* feat: SendBusinessNotification supported configuration parameters

* feat: seq conversion failed without exiting

* fix: DeleteDoc crash

* fix: fill send time

* fix: fill send time
2025-02-25 18:07:26 +08:00
chao 9f796e78c1 fix: DeleteDoc crash (#3078)
* pb

* fix: Modifying other fields while setting IsPrivateChat does not take effect

* fix: quote message error revoke

* refactoring scheduled tasks

* refactoring scheduled tasks

* refactoring scheduled tasks

* refactoring scheduled tasks

* refactoring scheduled tasks

* refactoring scheduled tasks

* upgrading pkg tools

* fix

* fix

* optimize log output

* feat: support GetLastMessage

* feat: support GetLastMessage

* feat: s3 switch

* feat: s3 switch

* fix: GetUsersOnline

* feat: SendBusinessNotification supported configuration parameters

* feat: SendBusinessNotification supported configuration parameters

* feat: SendBusinessNotification supported configuration parameters

* feat: seq conversion failed without exiting

* fix: DeleteDoc crash
2025-02-25 18:05:55 +08:00
icey-yu efcd76318e fix: check error in BatchSetTokenMapByUidPid (#3076) 2025-02-25 18:05:43 +08:00
icey-yu d7af353e42 feat: add backup volume && optimize log print (#3066)
* feat: backup

* feat: backup

* feat: backup

* feat: optimize log print
2025-02-25 18:04:25 +08:00
chao ab1691865f fix: seq conversion failed without exiting (#3052)
* pb

* fix: Modifying other fields while setting IsPrivateChat does not take effect

* fix: quote message error revoke

* refactoring scheduled tasks

* refactoring scheduled tasks

* refactoring scheduled tasks

* refactoring scheduled tasks

* refactoring scheduled tasks

* refactoring scheduled tasks

* upgrading pkg tools

* fix

* fix

* optimize log output

* feat: support GetLastMessage

* feat: support GetLastMessage

* feat: s3 switch

* feat: s3 switch

* fix: GetUsersOnline

* feat: SendBusinessNotification supported configuration parameters

* feat: SendBusinessNotification supported configuration parameters

* feat: SendBusinessNotification supported configuration parameters

* feat: seq conversion failed without exiting
2025-02-25 18:03:35 +08:00
110 changed files with 2524 additions and 2412 deletions
@@ -1,4 +1,4 @@
name: Build and release services Docker Images name: Build and release services Images
on: on:
push: push:
@@ -19,26 +19,26 @@ jobs:
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v4 uses: actions/checkout@v3
- name: Set up Docker Buildx - name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3.8.0 uses: docker/setup-buildx-action@v2
- name: Log in to Docker Hub - name: Log in to Docker Hub
uses: docker/login-action@v3.3.0 uses: docker/login-action@v2
with: with:
username: ${{ secrets.DOCKER_USERNAME }} username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }} password: ${{ secrets.DOCKER_PASSWORD }}
- name: Log in to GitHub Container Registry - name: Log in to GitHub Container Registry
uses: docker/login-action@v3.3.0 uses: docker/login-action@v2
with: with:
registry: ghcr.io registry: ghcr.io
username: ${{ github.repository_owner }} username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }} password: ${{ secrets.GITHUB_TOKEN }}
- name: Log in to Aliyun Container Registry - name: Log in to Aliyun Container Registry
uses: docker/login-action@v3.3.0 uses: docker/login-action@v2
with: with:
registry: registry.cn-hangzhou.aliyuncs.com registry: registry.cn-hangzhou.aliyuncs.com
username: ${{ secrets.ALIREGISTRY_USERNAME }} username: ${{ secrets.ALIREGISTRY_USERNAME }}
@@ -46,7 +46,7 @@ jobs:
- name: Extract metadata for Docker (tags, labels) - name: Extract metadata for Docker (tags, labels)
id: meta id: meta
uses: docker/metadata-action@v5.6.0 uses: docker/metadata-action@v5
with: with:
tags: | tags: |
type=ref,event=tag type=ref,event=tag
@@ -54,14 +54,15 @@ jobs:
type=ref,event=branch type=ref,event=branch
type=semver,pattern={{version}} type=semver,pattern={{version}}
type=semver,pattern=v{{version}} type=semver,pattern=v{{version}}
# type=semver,pattern={{major}}.{{minor}}
type=semver,pattern=release-{{raw}} type=semver,pattern=release-{{raw}}
type=sha type=sha
type=raw,value=${{ github.event.inputs.tag }} type=raw,value=${{ github.event.inputs.tag }}
- name: Build and push Docker images - name: Build and push Docker images
run: | run: |
IMG_DIR="build/images" ROOT_DIR="build/images"
for dir in "$IMG_DIR"/*/; do for dir in "$ROOT_DIR"/*/; do
# Find Dockerfile or *.dockerfile in a case-insensitive manner # Find Dockerfile or *.dockerfile in a case-insensitive manner
dockerfile=$(find "$dir" -maxdepth 1 -type f \( -iname 'dockerfile' -o -iname '*.dockerfile' \) | head -n 1) dockerfile=$(find "$dir" -maxdepth 1 -type f \( -iname 'dockerfile' -o -iname '*.dockerfile' \) | head -n 1)
@@ -83,9 +84,8 @@ jobs:
docker buildx build --platform linux/amd64,linux/arm64 \ docker buildx build --platform linux/amd64,linux/arm64 \
--file "$dockerfile" \ --file "$dockerfile" \
"${tag_args[@]}" \ "${tag_args[@]}" \
--push \ --push "$dir"
"."
else else
echo "No valid Dockerfile found in $dir" echo "No valid Dockerfile found in $dir"
fi fi
done done
+38 -103
View File
@@ -4,7 +4,7 @@ on:
push: push:
pull_request: pull_request:
paths-ignore: paths-ignore:
- "**/*.md" - '**/*.md'
workflow_dispatch: workflow_dispatch:
@@ -18,7 +18,7 @@ jobs:
strategy: strategy:
matrix: matrix:
os: [ubuntu-latest] os: [ubuntu-latest]
go_version: ["1.22.x"] go_version: ["1.21.x", "1.22.x"]
steps: steps:
- name: Checkout Server repository - name: Checkout Server repository
@@ -37,20 +37,27 @@ jobs:
- name: Set up infra services - name: Set up infra services
uses: hoverkraft-tech/compose-action@v2.0.1 uses: hoverkraft-tech/compose-action@v2.0.1
# Uncomment and set the correct path to your docker-compose file
with: with:
compose-file: "./docker-compose.yml" compose-file: "./docker-compose.yml"
# - name: Get Internal IP Address # run: |
# id: get-ip # sudo docker compose up -d
# run: | # sudo sleep 30 # Increased sleep time for better stability
# IP=$(hostname -I | awk '{print $1}') # timeout-minutes: 60 # Increased timeout for Docker setup
# echo "The IP Address is: $IP"
# echo "::set-output name=ip::$IP"
# - name: Update .env
# run: | # - name: Get Internal IP Address
# sed -i 's|externalAddress:.*|externalAddress: "http://${{ steps.get-ip.outputs.ip }}:10005"|' config/minio.yml # id: get-ip
# cat config/minio.yml # run: |
# IP=$(hostname -I | awk '{print $1}')
# echo "The IP Address is: $IP"
# echo "::set-output name=ip::$IP"
# - name: Update .env
# run: |
# sed -i 's|externalAddress:.*|externalAddress: "http://${{ steps.get-ip.outputs.ip }}:10005"|' config/minio.yml
# cat config/minio.yml
- name: Build and test Server Services - name: Build and test Server Services
run: | run: |
@@ -78,90 +85,6 @@ jobs:
mage start mage start
mage check mage check
- name: Test Server and Chat
run: |
check_error() {
echo "Response: $1"
errCode=$(echo $1 | jq -r '.errCode')
if [ "$errCode" != "0" ]; then
errMsg=$(echo $1 | jq -r '.errMsg')
echo "Error: $errMsg"
exit 1
fi
}
# Test register
response1=$(curl -X POST -H "Content-Type: application/json" -H "operationID: imAdmin" -d '{
"verifyCode": "666666",
"platform": 3,
"autoLogin": true,
"user":{
"nickname": "test12312",
"areaCode":"+86",
"phoneNumber": "12345678190",
"password":"test123456"
}
}' http://127.0.0.1:10008/account/register)
check_error "$response1"
userID1=$(echo $response1 | jq -r '.data.userID')
echo "userID1: $userID1"
response2=$(curl -X POST -H "Content-Type: application/json" -H "operationID: imAdmin" -d '{
"verifyCode": "666666",
"platform": 3,
"autoLogin": true,
"user":{
"nickname": "test22312",
"areaCode":"+86",
"phoneNumber": "12345678290",
"password":"test123456"
}
}' http://127.0.0.1:10008/account/register)
check_error "$response2"
userID2=$(echo $response2 | jq -r '.data.userID')
echo "userID2: $userID2"
# Test login
login_response=$(curl -X POST -H "Content-Type: application/json" -H "operationID: imAdmin" -d '{
"platform": 3,
"areaCode":"+86",
"phoneNumber": "12345678190",
"password":"test123456"
}' http://localhost:10008/account/login)
check_error "$login_response"
# Test get admin token
get_admin_token_response=$(curl -X POST -H "Content-Type: application/json" -H "operationID: imAdmin" -d '{
"secret": "openIM123",
"platformID": 2,
"userID": "imAdmin"
}' http://127.0.0.1:10002/auth/get_admin_token)
check_error "$get_admin_token_response"
adminToken=$(echo $get_admin_token_response | jq -r '.data.token')
echo "adminToken: $adminToken"
# Test send message
send_msg_response=$(curl -X POST -H "Content-Type: application/json" -H "operationID: imAdmin" -H "token: $adminToken" -d '{
"sendID": "'$userID1'",
"recvID": "'$userID2'",
"senderPlatformID": 3,
"content": {
"content": "hello!!"
},
"contentType": 101,
"sessionType": 1
}' http://127.0.0.1:10002/msg/send_msg)
check_error "$send_msg_response"
# Test get users
get_users_response=$(curl -X POST -H "Content-Type: application/json" -H "operationID: imAdmin" -H "token: $adminToken" -d '{
"pagination": {
"pageNumber": 1,
"showNumber": 100
}
}' http://127.0.0.1:10002/user/get_users)
check_error "$get_users_response"
go-test: go-test:
name: Benchmark Test with go ${{ matrix.go_version }} on ${{ matrix.os }} name: Benchmark Test with go ${{ matrix.go_version }} on ${{ matrix.os }}
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
@@ -170,11 +93,11 @@ jobs:
env: env:
SDK_DIR: openim-sdk-core SDK_DIR: openim-sdk-core
CONFIG_PATH: config/notification.yml CONFIG_PATH: config/notification.yml
# pull-requests: write
strategy: strategy:
matrix: matrix:
os: [ubuntu-latest] os: [ ubuntu-latest ]
go_version: ["1.22.x"] go_version: [ "1.22.x" ]
steps: steps:
- name: Checkout Server repository - name: Checkout Server repository
@@ -183,8 +106,7 @@ jobs:
- name: Checkout SDK repository - name: Checkout SDK repository
uses: actions/checkout@v4 uses: actions/checkout@v4
with: with:
repository: "openimsdk/openim-sdk-core" repository: 'openimsdk/openim-sdk-core'
ref: "release-v3.8"
path: ${{ env.SDK_DIR }} path: ${{ env.SDK_DIR }}
- name: Set up Go ${{ matrix.go_version }} - name: Set up Go ${{ matrix.go_version }}
@@ -197,10 +119,15 @@ jobs:
go install github.com/magefile/mage@latest go install github.com/magefile/mage@latest
go mod download go mod download
- name: Install yq
run: |
sudo wget https://github.com/mikefarah/yq/releases/download/v4.34.1/yq_linux_amd64 -O /usr/bin/yq
sudo chmod +x /usr/bin/yq
- name: Modify Server Configuration - name: Modify Server Configuration
run: | run: |
yq e '.groupCreated.isSendMsg = true' -i ${{ env.CONFIG_PATH }} yq e '.groupCreated.unreadCount = true' -i ${{ env.CONFIG_PATH }}
yq e '.friendApplicationApproved.isSendMsg = true' -i ${{ env.CONFIG_PATH }} yq e '.friendApplicationApproved.unreadCount = true' -i ${{ env.CONFIG_PATH }}
- name: Start Server Services - name: Start Server Services
run: | run: |
@@ -256,3 +183,11 @@ jobs:
run: | run: |
CONTAINER_NAME="${{ github.event.repository.name }}-container" CONTAINER_NAME="${{ github.event.repository.name }}-container"
docker logs $CONTAINER_NAME docker logs $CONTAINER_NAME
# - name: Cleanup Docker Container
# run: |
# CONTAINER_NAME="${{ github.event.repository.name }}-container"
# IMAGE_NAME="${{ github.event.repository.name }}-test"
# docker stop $CONTAINER_NAME
# docker rm $CONTAINER_NAME
# docker rmi $IMAGE_NAME
+126 -54
View File
@@ -1,4 +1,4 @@
name: Create Individual PRs from Milestone name: Create Pre-Release PR from Milestone
permissions: permissions:
contents: write contents: write
@@ -9,24 +9,24 @@ on:
workflow_dispatch: workflow_dispatch:
inputs: inputs:
milestone_name: milestone_name:
description: "Milestone name to collect closed PRs from" description: 'Milestone name to collect closed PRs from'
required: true required: true
default: "v3.8.4" default: 'v3.8.2'
target_branch: target_branch:
description: "Target branch to merge the consolidated PR" description: 'Target branch to merge the consolidated PR'
required: true required: true
default: "pre-release-v3.8.4" default: 'pre-release-v3.8.2'
env: env:
MILESTONE_NAME: ${{ github.event.inputs.milestone_name || 'v3.8.4' }} MILESTONE_NAME: ${{ github.event.inputs.milestone_name || 'v3.8.2' }}
TARGET_BRANCH: ${{ github.event.inputs.target_branch || 'pre-release-v3.8.4' }} TARGET_BRANCH: ${{ github.event.inputs.target_branch || 'pre-release-v3.8.2' }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
BOT_TOKEN: ${{ secrets.BOT_TOKEN }} BOT_TOKEN: ${{ secrets.BOT_TOKEN }}
LABEL_NAME: cherry-picked LABEL_NAME: cherry-picked
TEMP_DIR: /tmp TEMP_DIR: /tmp # Using /tmp as the temporary directory
jobs: jobs:
merge_milestone_prs: cherry_pick_milestone_prs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Setup temp directory - name: Setup temp directory
@@ -47,6 +47,7 @@ jobs:
- name: Setup Git User for OpenIM-Robot - name: Setup Git User for OpenIM-Robot
run: | run: |
# Set up Git credentials for the bot
git config --global user.email "OpenIM-Robot@users.noreply.github.com" git config --global user.email "OpenIM-Robot@users.noreply.github.com"
git config --global user.name "OpenIM-Robot" git config --global user.name "OpenIM-Robot"
@@ -82,65 +83,136 @@ jobs:
if ! echo "$labels" | grep -q "${LABEL_NAME}"; then if ! echo "$labels" | grep -q "${LABEL_NAME}"; then
echo "PR #$pr_number does not have the 'cherry-picked' label. Adding to the list." echo "PR #$pr_number does not have the 'cherry-picked' label. Adding to the list."
echo "$pr_number" >> ${{ env.TEMP_DIR }}/pr_numbers.txt echo "$pr_number" >> ${{ env.TEMP_DIR }}/pr_numbers.txt
else
echo "PR #$pr_number already has the 'cherry-picked' label. Skipping."
fi fi
done done
# Sort the filtered PR numbers
sort -n ${{ env.TEMP_DIR }}/pr_numbers.txt -o ${{ env.TEMP_DIR }}/pr_numbers.txt sort -n ${{ env.TEMP_DIR }}/pr_numbers.txt -o ${{ env.TEMP_DIR }}/pr_numbers.txt
- name: Create Individual PRs echo "Filtered and sorted PR numbers:"
cat ${{ env.TEMP_DIR }}/pr_numbers.txt || echo "No closed PR numbers found for milestone."
- name: Fetch Merge Commits for PRs and Generate Title and Body
run: | run: |
# Ensure the files are initialized
> ${{ env.TEMP_DIR }}/commit_hashes.txt
> ${{ env.TEMP_DIR }}/pr_title.txt
> ${{ env.TEMP_DIR }}/pr_body.txt
# Write description to the PR body
echo "### Description:" >> ${{ env.TEMP_DIR }}/pr_body.txt
echo "Merging PRs from milestone \`$MILESTONE_NAME\` into target branch \`$TARGET_BRANCH\`." >> ${{ env.TEMP_DIR }}/pr_body.txt
echo "" >> ${{ env.TEMP_DIR }}/pr_body.txt
echo "### Need Merge PRs:" >> ${{ env.TEMP_DIR }}/pr_body.txt
pr_numbers_in_title=""
# Process sorted PR numbers and generate commit hashes
for pr_number in $(cat ${{ env.TEMP_DIR }}/pr_numbers.txt); do for pr_number in $(cat ${{ env.TEMP_DIR }}/pr_numbers.txt); do
echo "Processing PR #$pr_number"
pr_details=$(curl -s -H "Authorization: token $BOT_TOKEN" \ pr_details=$(curl -s -H "Authorization: token $BOT_TOKEN" \
-H "Accept: application/vnd.github+json" \ -H "Accept: application/vnd.github+json" \
"https://api.github.com/repos/${{ github.repository }}/pulls/$pr_number") "https://api.github.com/repos/${{ github.repository }}/pulls/$pr_number")
pr_title=$(echo "$pr_details" | jq -r '.title') pr_title=$(echo "$pr_details" | jq -r '.title')
pr_body=$(echo "$pr_details" | jq -r '.body')
pr_creator=$(echo "$pr_details" | jq -r '.user.login')
merge_commit=$(echo "$pr_details" | jq -r '.merge_commit_sha') merge_commit=$(echo "$pr_details" | jq -r '.merge_commit_sha')
short_commit_hash=$(echo "$merge_commit" | cut -c 1-7) short_commit_hash=$(echo "$merge_commit" | cut -c 1-7)
if [ "$merge_commit" != "null" ]; then # Append PR details to the body
git fetch origin echo "- $pr_title: (#$pr_number) ($short_commit_hash)" >> ${{ env.TEMP_DIR }}/pr_body.txt
echo "Checking out target branch: $TARGET_BRANCH"
git checkout $TARGET_BRANCH
echo "Pulling latest changes from target branch: $TARGET_BRANCH" if [ "$merge_commit" != "null" ];then
git pull origin $TARGET_BRANCH echo "$merge_commit" >> ${{ env.TEMP_DIR }}/commit_hashes.txt
echo "#$pr_number" >> ${{ env.TEMP_DIR }}/pr_title.txt
cherry_pick_branch="cherry-pick-${short_commit_hash}" pr_numbers_in_title="$pr_numbers_in_title #$pr_number"
git checkout -b $cherry_pick_branch
echo "Cherry-picking commit: $merge_commit"
if ! git cherry-pick "$merge_commit" --strategy=recursive -X theirs; then
echo "Cherry-pick encountered conflicts, attempting to continue..."
git cherry-pick --continue || { echo "Cherry-pick failed"; exit 1; }
fi
git remote set-url origin "https://${BOT_TOKEN}@github.com/${{ github.repository }}.git"
echo "Pushing branch: $cherry_pick_branch"
git push origin $cherry_pick_branch --force || { echo "Push failed"; exit 1; }
new_pr_title="$pr_title [Created by @$pr_creator from #$pr_number]"
new_pr_body="$pr_body
> This PR is created from original PR #$pr_number."
response=$(curl -s -X POST -H "Authorization: token $BOT_TOKEN" \
-H "Accept: application/vnd.github+json" \
https://api.github.com/repos/${{ github.repository }}/pulls \
-d "$(jq -n --arg title "$new_pr_title" \
--arg head "$cherry_pick_branch" \
--arg base "$TARGET_BRANCH" \
--arg body "$new_pr_body" \
'{title: $title, head: $head, base: $base, body: $body}')")
new_pr_number=$(echo "$response" | jq -r '.number')
echo "Created PR #$new_pr_number"
curl -s -X POST -H "Authorization: token $GITHUB_TOKEN" \
-H "Accept: application/vnd.github+json" \
-d '{"labels": ["milestone-merge"]}' \
"https://api.github.com/repos/${{ github.repository }}/issues/$new_pr_number/labels"
fi fi
done done
commit_hashes=$(cat ${{ env.TEMP_DIR }}/commit_hashes.txt | tr '\n' ' ')
first_commit_hash=$(head -n 1 ${{ env.TEMP_DIR }}/commit_hashes.txt)
cherry_pick_branch="cherry-pick-${first_commit_hash:0:7}"
echo "COMMIT_HASHES=$commit_hashes" >> $GITHUB_ENV
echo "CHERRY_PICK_BRANCH=$cherry_pick_branch" >> $GITHUB_ENV
echo "pr_numbers_in_title=$pr_numbers_in_title" >> $GITHUB_ENV
- name: Pull and Cherry-pick Commits, Then Push
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
BOT_TOKEN: ${{ secrets.BOT_TOKEN }}
run: |
# Fetch and pull the latest changes from the target branch
git fetch origin
git checkout $TARGET_BRANCH
git pull origin $TARGET_BRANCH
# Create a new branch for cherry-picking
git checkout -b $CHERRY_PICK_BRANCH
# Cherry-pick the commits and handle conflicts
for commit_hash in $COMMIT_HASHES; do
echo "Attempting to cherry-pick commit $commit_hash"
if ! git cherry-pick "$commit_hash" --strategy=recursive -X theirs; then
echo "Conflict detected for $commit_hash. Resolving with incoming changes."
conflict_files=$(git diff --name-only --diff-filter=U)
echo "Conflicting files:"
echo "$conflict_files"
for file in $conflict_files; do
if [ -f "$file" ]; then
echo "Resolving conflict for $file"
git add "$file"
else
echo "File $file has been deleted. Skipping."
git rm "$file"
fi
done
echo "Conflicts resolved. Continuing cherry-pick."
git cherry-pick --continue
else
echo "Cherry-pick successful for commit $commit_hash."
fi
done
# Push the cherry-pick branch to the repository
git remote set-url origin "https://${BOT_TOKEN}@github.com/${{ github.repository }}.git"
git push origin $CHERRY_PICK_BRANCH --force
- name: Create Pull Request
run: |
# Prepare and create the PR
pr_title="deps: Merge ${{ env.pr_numbers_in_title }} PRs into $TARGET_BRANCH"
pr_body=$(cat ${{ env.TEMP_DIR }}/pr_body.txt)
echo "Prepared PR title:"
echo "$pr_title"
echo "Prepared PR body:"
echo "$pr_body"
# Create the PR using the GitHub API
response=$(curl -s -X POST -H "Authorization: token $BOT_TOKEN" \
-H "Accept: application/vnd.github+json" \
https://api.github.com/repos/${{ github.repository }}/pulls \
-d "$(jq -n --arg title "$pr_title" \
--arg head "$CHERRY_PICK_BRANCH" \
--arg base "$TARGET_BRANCH" \
--arg body "$pr_body" \
'{title: $title, head: $head, base: $base, body: $body}')")
pr_number=$(echo "$response" | jq -r '.number')
echo "$pr_number" > ${{ env.TEMP_DIR }}/created_pr_number.txt
echo "Created PR #$pr_number"
- name: Add Label to Created Pull Request
run: |
# Add 'milestone-merge' label to the created PR
pr_number=$(cat ${{ env.TEMP_DIR }}/created_pr_number.txt)
echo "Adding label to PR #$pr_number"
curl -s -X POST -H "Authorization: token $GITHUB_TOKEN" \
-H "Accept: application/vnd.github+json" \
-d '{"labels": ["milestone-merge"]}' \
"https://api.github.com/repos/${{ github.repository }}/issues/$pr_number/labels"
echo "Added 'milestone-merge' label to PR #$pr_number."
+47 -36
View File
@@ -25,11 +25,11 @@ jobs:
with: with:
path: main-repo path: main-repo
# - name: Set up QEMU - name: Set up QEMU
# uses: docker/setup-qemu-action@v3.3.0 uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx - name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3.8.0 uses: docker/setup-buildx-action@v3
- name: Build Docker image - name: Build Docker image
id: build id: build
@@ -38,8 +38,11 @@ jobs:
context: ./main-repo context: ./main-repo
load: true load: true
tags: "openim/openim-server:local" tags: "openim/openim-server:local"
cache-from: type=gha,scope=build cache-from: type=gha
cache-to: type=gha,mode=max,scope=build cache-to: type=gha,mode=max
- name: Save Docker image to file
run: docker save -o image.tar openim/openim-server:local
- name: Checkout compose repository - name: Checkout compose repository
uses: actions/checkout@v4 uses: actions/checkout@v4
@@ -63,12 +66,43 @@ jobs:
run: | run: |
cd ${{ github.workspace }}/compose-repo cd ${{ github.workspace }}/compose-repo
docker compose up -d docker compose up -d
sleep 60
docker compose ps # - name: Check openim-server health
# run: |
# timeout=300
# interval=30
# elapsed=0
# while [[ $elapsed -le $timeout ]]; do
# if ! docker exec openim-server mage check; then
# echo "openim-server is not ready, waiting..."
# sleep $interval
# elapsed=$(($elapsed + $interval))
# else
# echo "Health check successful"
# exit 0
# fi
# done
# echo "Health check failed after 5 minutes"
# exit 1
# - name: Check openim-chat health
# if: success()
# run: |
# if ! docker exec openim-chat mage check; then
# echo "openim-chat check failed"
# exit 1
# else
# echo "Health check successful"
# exit 0
# fi
- name: Load Docker image from file
run: docker load -i image.tar
- name: Extract metadata for Docker (tags, labels) - name: Extract metadata for Docker (tags, labels)
id: meta id: meta
uses: docker/metadata-action@v5.6.0 uses: docker/metadata-action@v5.5.1
with: with:
images: | images: |
openim/openim-server openim/openim-server
@@ -78,27 +112,29 @@ jobs:
type=ref,event=tag type=ref,event=tag
type=schedule type=schedule
type=ref,event=branch type=ref,event=branch
# type=semver,pattern={{version}} type=semver,pattern={{version}}
type=semver,pattern=v{{version}} type=semver,pattern=v{{version}}
type=semver,pattern={{major}}.{{minor}}
type=semver,pattern={{major}}
type=semver,pattern=release-{{raw}} type=semver,pattern=release-{{raw}}
type=sha type=sha
type=raw,value=${{ github.event.inputs.tag }} type=raw,value=${{ github.event.inputs.tag }}
- name: Log in to Docker Hub - name: Log in to Docker Hub
uses: docker/login-action@v3.3.0 uses: docker/login-action@v2
with: with:
username: ${{ secrets.DOCKER_USERNAME }} username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }} password: ${{ secrets.DOCKER_PASSWORD }}
- name: Log in to GitHub Container Registry - name: Log in to GitHub Container Registry
uses: docker/login-action@v3.3.0 uses: docker/login-action@v2
with: with:
registry: ghcr.io registry: ghcr.io
username: ${{ github.repository_owner }} username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }} password: ${{ secrets.GITHUB_TOKEN }}
- name: Log in to Aliyun Container Registry - name: Log in to Aliyun Container Registry
uses: docker/login-action@v3.3.0 uses: docker/login-action@v2
with: with:
registry: registry.cn-hangzhou.aliyuncs.com registry: registry.cn-hangzhou.aliyuncs.com
username: ${{ secrets.ALIREGISTRY_USERNAME }} username: ${{ secrets.ALIREGISTRY_USERNAME }}
@@ -112,28 +148,3 @@ jobs:
platforms: linux/amd64,linux/arm64 platforms: linux/amd64,linux/arm64
tags: ${{ steps.meta.outputs.tags }} tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }} labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha,scope=build
cache-to: type=gha,mode=max,scope=build
- name: Verify multi-platform support
run: |
images=("openim/openim-server" "ghcr.io/openimsdk/openim-server" "registry.cn-hangzhou.aliyuncs.com/openimsdk/openim-server")
for image in "${images[@]}"; do
for tag in $(echo "${{ steps.meta.outputs.tags }}" | tr ',' '\n'); do
manifest=$(docker manifest inspect "$image:$tag" || echo "error")
if [[ "$manifest" == "error" ]]; then
echo "Manifest not found for $image:$tag"
exit 1
fi
amd64_found=$(echo "$manifest" | jq '.manifests[] | select(.platform.architecture == "amd64")')
arm64_found=$(echo "$manifest" | jq '.manifests[] | select(.platform.architecture == "arm64")')
if [[ -z "$amd64_found" ]]; then
echo "Multi-platform support check failed for $image:$tag - missing amd64"
exit 1
fi
if [[ -z "$arm64_found" ]]; then
echo "Multi-platform support check failed for $image:$tag - missing arm64"
exit 1
fi
done
done
@@ -8,19 +8,40 @@ jobs:
update-version: update-version:
runs-on: ubuntu-latest runs-on: ubuntu-latest
env: env:
TAG_VERSION: ${{ github.event.release.tag_name }} TAG_VERSION: ${{ github.event.release.tag_name }}
steps: steps:
# Step 1: Checkout the original repository's code # Step 1: Checkout the original repository's code
- name: Checkout code - name: Checkout code
uses: actions/checkout@v4 uses: actions/checkout@v4
with: with:
fetch-depth: 0 fetch-depth: 0
# submodules: "recursive"
- name: Safe submodule initialization
run: |
echo "Checking for submodules..."
if [ -f .gitmodules ]; then
if [ -s .gitmodules ]; then
echo "Initializing submodules..."
if git submodule sync --recursive 2>/dev/null; then
git submodule update --init --force --recursive || {
echo "Warning: Some submodules failed to initialize, continuing anyway..."
}
else
echo "Warning: Submodule sync failed, continuing without submodules..."
fi
else
echo ".gitmodules exists but is empty, skipping submodule initialization"
fi
else
echo "No .gitmodules file found, no submodules to initialize"
fi
# Step 2: Set up Git with official account # Step 2: Set up Git with official account
- name: Set up Git - name: Set up Git
run: | run: |
git config user.name "github-actions[bot]" git config --global user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com" git config --global user.email "github-actions[bot]@users.noreply.github.com"
# Step 3: Check and delete existing tag # Step 3: Check and delete existing tag
- name: Check and delete existing tag - name: Check and delete existing tag
@@ -33,7 +54,8 @@ jobs:
# Step 4: Update version file # Step 4: Update version file
- name: Update version file - name: Update version file
run: | run: |
echo "${{ env.TAG_VERSION }}" > version/version mkdir -p version
echo -n "${{ env.TAG_VERSION }}" > version/version
# Step 5: Commit and push changes # Step 5: Commit and push changes
- name: Commit and push changes - name: Commit and push changes
@@ -42,43 +64,56 @@ jobs:
run: | run: |
git add version/version git add version/version
git commit -m "Update version to ${{ env.TAG_VERSION }}" git commit -m "Update version to ${{ env.TAG_VERSION }}"
git push origin HEAD:${{ github.ref }}
# Step 6: Create and push tag # Step 6: Update tag
- name: Create and push tag - name: Update tag
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: | run: |
git tag ${{ env.TAG_VERSION }} git tag -fa ${{ env.TAG_VERSION }} -m "Update version to ${{ env.TAG_VERSION }}"
git push origin ${{ env.TAG_VERSION }} git push origin ${{ env.TAG_VERSION }} --force
# Step 7: Find and Publish Draft Release # Step 7: Find and Publish Draft Release
- name: Find and Publish Draft Release - name: Find and Publish Draft Release
uses: actions/github-script@v6 uses: actions/github-script@v7
with: with:
github-token: ${{ secrets.GITHUB_TOKEN }} github-token: ${{ secrets.GITHUB_TOKEN }}
script: | script: |
// Get the list of releases const { owner, repo } = context.repo;
const releases = await github.rest.repos.listReleases({ const tagName = process.env.TAG_VERSION;
owner: context.repo.owner,
repo: context.repo.repo
});
// Find the draft release where the title and tag_name are the same try {
const draftRelease = releases.data.find(release => let release;
release.draft && release.name === release.tag_name try {
); const response = await github.rest.repos.getReleaseByTag({
owner,
if (draftRelease) { repo,
// Publish the draft release using the release_id tag: tagName
});
release = response.data;
} catch (tagError) {
core.info(`Release not found by tag, searching all releases...`);
const releases = await github.rest.repos.listReleases({
owner,
repo,
per_page: 100
});
release = releases.data.find(r => r.draft && r.tag_name === tagName);
if (!release) {
throw new Error(`No release found with tag ${tagName}`);
}
}
await github.rest.repos.updateRelease({ await github.rest.repos.updateRelease({
owner: context.repo.owner, owner,
repo: context.repo.repo, repo,
release_id: draftRelease.id, // Use release_id release_id: release.id,
draft: false draft: false,
prerelease: release.prerelease
}); });
core.info(`Draft Release ${draftRelease.tag_name} published successfully.`); const status = release.draft ? "was draft" : "was already published";
} else { core.info(`Release ${tagName} ensured to be published (${status}).`);
core.info("No matching draft release found.");
} } catch (error) {
core.warning(`Could not find or update release for tag ${tagName}: ${error.message}`);
}
+1 -1
View File
@@ -43,7 +43,7 @@ COPY --from=builder $SERVER_DIR/start-config.yml $SERVER_DIR/
COPY --from=builder $SERVER_DIR/go.mod $SERVER_DIR/ COPY --from=builder $SERVER_DIR/go.mod $SERVER_DIR/
COPY --from=builder $SERVER_DIR/go.sum $SERVER_DIR/ COPY --from=builder $SERVER_DIR/go.sum $SERVER_DIR/
RUN go get github.com/openimsdk/gomake@v0.0.14-alpha.5 RUN go get github.com/openimsdk/gomake@v0.0.15-alpha.5
# Set the command to run when the container starts # Set the command to run when the container starts
ENTRYPOINT ["sh", "-c", "mage start && tail -f /dev/null"] ENTRYPOINT ["sh", "-c", "mage start && tail -f /dev/null"]
+1 -10
View File
@@ -5,13 +5,4 @@ etcd:
username: '' username: ''
password: '' password: ''
rpcService:
user: user-rpc-service
friend: friend-rpc-service
msg: msg-rpc-service
push: push-rpc-service
messageGateway: messagegateway-rpc-service
group: group-rpc-service
auth: auth-rpc-service
conversation: conversation-rpc-service
third: third-rpc-service
+19 -17
View File
@@ -1,10 +1,12 @@
groupCreated: groupCreated:
isSendMsg: true isSendMsg: true
# Deprecated. Fixed as 1. # Reliability level of the message sending.
# Set to 1 to send only when online, 2 for guaranteed delivery.
reliabilityLevel: 1 reliabilityLevel: 1
# Deprecated. Fixed as false. # This setting is effective only when 'isSendMsg' is true.
# It controls whether to count unread messages.
unreadCount: false unreadCount: false
# Configuration for offline push notifications. # Configuration for offline push notifications.
offlinePush: offlinePush:
# Enables or disables offline push notifications. # Enables or disables offline push notifications.
enable: false enable: false
@@ -31,7 +33,7 @@ joinGroupApplication:
reliabilityLevel: 1 reliabilityLevel: 1
unreadCount: false unreadCount: false
offlinePush: offlinePush:
enable: false enable: true
title: joinGroupApplication title title: joinGroupApplication title
desc: joinGroupApplication desc desc: joinGroupApplication desc
ext: joinGroupApplication ext ext: joinGroupApplication ext
@@ -51,7 +53,7 @@ groupApplicationAccepted:
reliabilityLevel: 1 reliabilityLevel: 1
unreadCount: false unreadCount: false
offlinePush: offlinePush:
enable: false enable: true
title: groupApplicationAccepted title title: groupApplicationAccepted title
desc: groupApplicationAccepted desc desc: groupApplicationAccepted desc
ext: groupApplicationAccepted ext ext: groupApplicationAccepted ext
@@ -61,7 +63,7 @@ groupApplicationRejected:
reliabilityLevel: 1 reliabilityLevel: 1
unreadCount: false unreadCount: false
offlinePush: offlinePush:
enable: false enable: true
title: groupApplicationRejected title title: groupApplicationRejected title
desc: groupApplicationRejected desc desc: groupApplicationRejected desc
ext: groupApplicationRejected ext ext: groupApplicationRejected ext
@@ -198,7 +200,7 @@ friendApplicationAdded:
reliabilityLevel: 1 reliabilityLevel: 1
unreadCount: false unreadCount: false
offlinePush: offlinePush:
enable: false enable: true
title: Somebody applies to add you as a friend title: Somebody applies to add you as a friend
desc: Somebody applies to add you as a friend desc: Somebody applies to add you as a friend
ext: Somebody applies to add you as a friend ext: Somebody applies to add you as a friend
@@ -228,7 +230,7 @@ friendAdded:
reliabilityLevel: 1 reliabilityLevel: 1
unreadCount: false unreadCount: false
offlinePush: offlinePush:
enable: true enable: false
title: We have become friends title: We have become friends
desc: We have become friends desc: We have become friends
ext: We have become friends ext: We have become friends
@@ -238,7 +240,7 @@ friendDeleted:
reliabilityLevel: 1 reliabilityLevel: 1
unreadCount: false unreadCount: false
offlinePush: offlinePush:
enable: true enable: false
title: deleted a friend title: deleted a friend
desc: deleted a friend desc: deleted a friend
ext: deleted a friend ext: deleted a friend
@@ -248,7 +250,7 @@ friendRemarkSet:
reliabilityLevel: 1 reliabilityLevel: 1
unreadCount: false unreadCount: false
offlinePush: offlinePush:
enable: true enable: false
title: Your friend's profile has been changed title: Your friend's profile has been changed
desc: Your friend's profile has been changed desc: Your friend's profile has been changed
ext: Your friend's profile has been changed ext: Your friend's profile has been changed
@@ -258,7 +260,7 @@ blackAdded:
reliabilityLevel: 1 reliabilityLevel: 1
unreadCount: false unreadCount: false
offlinePush: offlinePush:
enable: true enable: false
title: blocked a user title: blocked a user
desc: blocked a user desc: blocked a user
ext: blocked a user ext: blocked a user
@@ -268,7 +270,7 @@ blackDeleted:
reliabilityLevel: 1 reliabilityLevel: 1
unreadCount: false unreadCount: false
offlinePush: offlinePush:
enable: true enable: false
title: Remove a blocked user title: Remove a blocked user
desc: Remove a blocked user desc: Remove a blocked user
ext: Remove a blocked user ext: Remove a blocked user
@@ -278,7 +280,7 @@ friendInfoUpdated:
reliabilityLevel: 1 reliabilityLevel: 1
unreadCount: false unreadCount: false
offlinePush: offlinePush:
enable: true enable: false
title: friend info updated title: friend info updated
desc: friend info updated desc: friend info updated
ext: friend info updated ext: friend info updated
@@ -289,7 +291,7 @@ userInfoUpdated:
reliabilityLevel: 1 reliabilityLevel: 1
unreadCount: false unreadCount: false
offlinePush: offlinePush:
enable: true enable: false
title: userInfo updated title: userInfo updated
desc: userInfo updated desc: userInfo updated
ext: userInfo updated ext: userInfo updated
@@ -310,7 +312,7 @@ conversationChanged:
reliabilityLevel: 1 reliabilityLevel: 1
unreadCount: false unreadCount: false
offlinePush: offlinePush:
enable: true enable: false
title: conversation changed title: conversation changed
desc: conversation changed desc: conversation changed
ext: conversation changed ext: conversation changed
@@ -320,7 +322,7 @@ conversationSetPrivate:
reliabilityLevel: 1 reliabilityLevel: 1
unreadCount: false unreadCount: false
offlinePush: offlinePush:
enable: true enable: false
title: burn after reading title: burn after reading
desc: burn after reading desc: burn after reading
ext: burn after reading ext: burn after reading
+15 -2
View File
@@ -1,9 +1,22 @@
secret: openIM123 secret: openIM123
rpcRegisterName:
user: user
friend: friend
msg: msg
push: push
messageGateway: messageGateway
group: group
auth: auth
conversation: conversation
third: third
imAdminUserID: [ imAdmin ] imAdminUserID: [ imAdmin ]
# 1: For Android, iOS, Windows, Mac, and web platforms, only one instance can be online at a time # 1: For Android, iOS, Windows, Mac, and web platforms, only one instance can be online at a time
multiLogin: multiLogin:
policy: 1 policy: 1
# max num of tokens in one end maxNumOneEnd: 30
maxNumOneEnd: 30
rpcMaxBodySize:
requestMaxBodySize: 8388608
responseMaxBodySize: 8388608
+50 -3
View File
@@ -99,15 +99,62 @@ services:
environment: environment:
#KAFKA_HEAP_OPTS: "-Xms128m -Xmx256m" #KAFKA_HEAP_OPTS: "-Xms128m -Xmx256m"
TZ: Asia/Shanghai TZ: Asia/Shanghai
# Unique identifier for the Kafka node (required in controller mode)
KAFKA_CFG_NODE_ID: 0 KAFKA_CFG_NODE_ID: 0
# Defines the roles this Kafka node plays: broker, controller, or both
KAFKA_CFG_PROCESS_ROLES: controller,broker KAFKA_CFG_PROCESS_ROLES: controller,broker
# Specifies which nodes are controller nodes for quorum voting.
# The syntax follows the KRaft mode (no ZooKeeper): node.id@host:port
# The controller listener endpoint here is kafka:9093
KAFKA_CFG_CONTROLLER_QUORUM_VOTERS: 0@kafka:9093 KAFKA_CFG_CONTROLLER_QUORUM_VOTERS: 0@kafka:9093
KAFKA_CFG_LISTENERS: PLAINTEXT://:9092,CONTROLLER://:9093,EXTERNAL://:9094 # Specifies which listener is used for controller-to-controller communication
KAFKA_CFG_ADVERTISED_LISTENERS: PLAINTEXT://kafka:9092,EXTERNAL://localhost:19094
KAFKA_CFG_LISTENER_SECURITY_PROTOCOL_MAP: CONTROLLER:PLAINTEXT,EXTERNAL:PLAINTEXT,PLAINTEXT:PLAINTEXT
KAFKA_CFG_CONTROLLER_LISTENER_NAMES: CONTROLLER KAFKA_CFG_CONTROLLER_LISTENER_NAMES: CONTROLLER
# Default number of partitions for new topics
KAFKA_NUM_PARTITIONS: 8 KAFKA_NUM_PARTITIONS: 8
# Whether to enable automatic topic creation
KAFKA_CFG_AUTO_CREATE_TOPICS_ENABLE: "true" KAFKA_CFG_AUTO_CREATE_TOPICS_ENABLE: "true"
# Kafka internal listeners; Kafka supports multiple ports with different protocols
# Each port is used for a specific purpose: INTERNAL for internal broker communication,
# CONTROLLER for controller communication, EXTERNAL for external client connections.
# These logical listener names are mapped to actual protocols via KAFKA_CFG_LISTENER_SECURITY_PROTOCOL_MAP
# In short, Kafka is listening on three logical ports: 9092 for internal communication,
# 9093 for controller traffic, and 9094 for external access.
KAFKA_CFG_LISTENERS: "INTERNAL://:9092,CONTROLLER://:9093,EXTERNAL://:9094"
# Addresses advertised to clients. INTERNAL://kafka:9092 uses the internal Docker service name 'kafka',
# so other containers can access Kafka via kafka:9092.
# EXTERNAL://localhost:19094 is the address external clients (e.g., in the LAN) should use to connect.
# If Kafka is deployed on a different machine than IM, 'localhost' should be replaced with the LAN IP.
KAFKA_CFG_ADVERTISED_LISTENERS: "INTERNAL://kafka:9092,EXTERNAL://localhost:19094"
# Maps logical listener names to actual protocols.
# Supported protocols include: PLAINTEXT, SSL, SASL_PLAINTEXT, SASL_SSL
KAFKA_CFG_LISTENER_SECURITY_PROTOCOL_MAP: "CONTROLLER:PLAINTEXT,EXTERNAL:PLAINTEXT,INTERNAL:PLAINTEXT"
# Defines which listener is used for inter-broker communication within the Kafka cluster
KAFKA_CFG_INTER_BROKER_LISTENER_NAME: "INTERNAL"
# Authentication configuration variables - comment out to disable auth
# KAFKA_USERNAME: "openIM"
# KAFKA_PASSWORD: "openIM123"
command: >
/bin/sh -c '
if [ -n "$${KAFKA_USERNAME}" ] && [ -n "$${KAFKA_PASSWORD}" ]; then
echo "=== Kafka SASL Authentication ENABLED ==="
echo "Username: $${KAFKA_USERNAME}"
# Set environment variables for SASL authentication
export KAFKA_CFG_LISTENERS="SASL_PLAINTEXT://:9092,CONTROLLER://:9093,EXTERNAL://:9094"
export KAFKA_CFG_ADVERTISED_LISTENERS="SASL_PLAINTEXT://kafka:9092,EXTERNAL://localhost:19094"
export KAFKA_CFG_LISTENER_SECURITY_PROTOCOL_MAP="CONTROLLER:PLAINTEXT,EXTERNAL:SASL_PLAINTEXT,SASL_PLAINTEXT:SASL_PLAINTEXT"
export KAFKA_CFG_SASL_ENABLED_MECHANISMS="PLAIN"
export KAFKA_CFG_SASL_MECHANISM_INTER_BROKER_PROTOCOL="PLAIN"
export KAFKA_CFG_INTER_BROKER_LISTENER_NAME="SASL_PLAINTEXT"
export KAFKA_CLIENT_USERS="$${KAFKA_USERNAME}"
export KAFKA_CLIENT_PASSWORDS="$${KAFKA_PASSWORD}"
fi
# Start Kafka with the configured environment
exec /opt/bitnami/scripts/kafka/entrypoint.sh /opt/bitnami/scripts/kafka/run.sh
'
networks: networks:
- openim - openim
+6 -6
View File
@@ -12,8 +12,8 @@ require (
github.com/gorilla/websocket v1.5.1 github.com/gorilla/websocket v1.5.1
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0
github.com/mitchellh/mapstructure v1.5.0 github.com/mitchellh/mapstructure v1.5.0
github.com/openimsdk/protocol v0.0.72-alpha.71 github.com/openimsdk/protocol v0.0.73-alpha.12
github.com/openimsdk/tools v0.0.50-alpha.72 github.com/openimsdk/tools v0.0.50-alpha.84
github.com/pkg/errors v0.9.1 // indirect github.com/pkg/errors v0.9.1 // indirect
github.com/prometheus/client_golang v1.18.0 github.com/prometheus/client_golang v1.18.0
github.com/stretchr/testify v1.9.0 github.com/stretchr/testify v1.9.0
@@ -35,7 +35,7 @@ require (
github.com/hashicorp/golang-lru/v2 v2.0.7 github.com/hashicorp/golang-lru/v2 v2.0.7
github.com/kelindar/bitmap v1.5.2 github.com/kelindar/bitmap v1.5.2
github.com/likexian/gokit v0.25.13 github.com/likexian/gokit v0.25.13
github.com/openimsdk/gomake v0.0.14-alpha.5 github.com/openimsdk/gomake v0.0.15-alpha.5
github.com/redis/go-redis/v9 v9.4.0 github.com/redis/go-redis/v9 v9.4.0
github.com/robfig/cron/v3 v3.0.1 github.com/robfig/cron/v3 v3.0.1
github.com/shirou/gopsutil v3.21.11+incompatible github.com/shirou/gopsutil v3.21.11+incompatible
@@ -43,9 +43,6 @@ require (
go.etcd.io/etcd/client/v3 v3.5.13 go.etcd.io/etcd/client/v3 v3.5.13
go.uber.org/automaxprocs v1.5.3 go.uber.org/automaxprocs v1.5.3
golang.org/x/sync v0.8.0 golang.org/x/sync v0.8.0
k8s.io/api v0.31.2
k8s.io/apimachinery v0.31.2
k8s.io/client-go v0.31.2
) )
require ( require (
@@ -201,6 +198,9 @@ require (
gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect
gorm.io/gorm v1.25.8 // indirect gorm.io/gorm v1.25.8 // indirect
k8s.io/api v0.31.2 // indirect
k8s.io/apimachinery v0.31.2 // indirect
k8s.io/client-go v0.31.2 // indirect
k8s.io/klog/v2 v2.130.1 // indirect k8s.io/klog/v2 v2.130.1 // indirect
k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 // indirect k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 // indirect
k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 // indirect k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 // indirect
+6 -6
View File
@@ -345,12 +345,12 @@ github.com/onsi/ginkgo/v2 v2.19.0 h1:9Cnnf7UHo57Hy3k6/m5k3dRfGTMXGvxhHFvkDTCTpvA
github.com/onsi/ginkgo/v2 v2.19.0/go.mod h1:rlwLi9PilAFJ8jCg9UE1QP6VBpd6/xj3SRC0d6TU0To= github.com/onsi/ginkgo/v2 v2.19.0/go.mod h1:rlwLi9PilAFJ8jCg9UE1QP6VBpd6/xj3SRC0d6TU0To=
github.com/onsi/gomega v1.25.0 h1:Vw7br2PCDYijJHSfBOWhov+8cAnUf8MfMaIOV323l6Y= github.com/onsi/gomega v1.25.0 h1:Vw7br2PCDYijJHSfBOWhov+8cAnUf8MfMaIOV323l6Y=
github.com/onsi/gomega v1.25.0/go.mod h1:r+zV744Re+DiYCIPRlYOTxn0YkOLcAnW8k1xXdMPGhM= github.com/onsi/gomega v1.25.0/go.mod h1:r+zV744Re+DiYCIPRlYOTxn0YkOLcAnW8k1xXdMPGhM=
github.com/openimsdk/gomake v0.0.14-alpha.5 h1:VY9c5x515lTfmdhhPjMvR3BBRrRquAUCFsz7t7vbv7Y= github.com/openimsdk/gomake v0.0.15-alpha.5 h1:eEZCEHm+NsmcO3onXZPIUbGFCYPYbsX5beV3ZyOsGhY=
github.com/openimsdk/gomake v0.0.14-alpha.5/go.mod h1:PndCozNc2IsQIciyn9mvEblYWZwJmAI+06z94EY+csI= github.com/openimsdk/gomake v0.0.15-alpha.5/go.mod h1:PndCozNc2IsQIciyn9mvEblYWZwJmAI+06z94EY+csI=
github.com/openimsdk/protocol v0.0.72-alpha.71 h1:R3utzOlqepaJWTAmnfJi4ccUM/XIoFasSyjQMOipM70= github.com/openimsdk/protocol v0.0.73-alpha.12 h1:2NYawXeHChYUeSme6QJ9pOLh+Empce2WmwEtbP4JvKk=
github.com/openimsdk/protocol v0.0.72-alpha.71/go.mod h1:WF7EuE55vQvpyUAzDXcqg+B+446xQyEba0X35lTINmw= github.com/openimsdk/protocol v0.0.73-alpha.12/go.mod h1:WF7EuE55vQvpyUAzDXcqg+B+446xQyEba0X35lTINmw=
github.com/openimsdk/tools v0.0.50-alpha.72 h1:d/vaZjIfvrNp3EeRJEIiamBO7HiPx6CP4wiuq8NpfzY= github.com/openimsdk/tools v0.0.50-alpha.84 h1:jN60Ys/0edZjL/TDmm/5VSJFP4pGYRipkWqhILJbq/8=
github.com/openimsdk/tools v0.0.50-alpha.72/go.mod h1:B+oqV0zdewN7OiEHYJm+hW+8/Te7B8tHHgD8rK5ZLZk= github.com/openimsdk/tools v0.0.50-alpha.84/go.mod h1:n2poR3asX1e1XZce4O+MOWAp+X02QJRFvhcLCXZdzRo=
github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM= github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM=
github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs= github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs=
github.com/pierrec/lz4/v4 v4.1.21 h1:yOVMLb6qSIDP67pl/5F7RepeKYu/VmTyEXvuMI5d9mQ= github.com/pierrec/lz4/v4 v4.1.21 h1:yOVMLb6qSIDP67pl/5F7RepeKYu/VmTyEXvuMI5d9mQ=
+311 -310
View File
@@ -1,312 +1,313 @@
package api package api
import ( //
"encoding/json" //import (
"reflect" // "encoding/json"
"strconv" // "reflect"
"time" // "strconv"
// "time"
"github.com/gin-gonic/gin" //
"github.com/openimsdk/open-im-server/v3/pkg/apistruct" // "github.com/gin-gonic/gin"
"github.com/openimsdk/open-im-server/v3/pkg/authverify" // "github.com/openimsdk/open-im-server/v3/pkg/apistruct"
"github.com/openimsdk/open-im-server/v3/pkg/common/config" // "github.com/openimsdk/open-im-server/v3/pkg/authverify"
"github.com/openimsdk/open-im-server/v3/pkg/common/discovery/etcd" // "github.com/openimsdk/open-im-server/v3/pkg/common/config"
"github.com/openimsdk/open-im-server/v3/version" // "github.com/openimsdk/open-im-server/v3/pkg/common/discovery/etcd"
"github.com/openimsdk/tools/apiresp" // "github.com/openimsdk/open-im-server/v3/version"
"github.com/openimsdk/tools/errs" // "github.com/openimsdk/tools/apiresp"
"github.com/openimsdk/tools/log" // "github.com/openimsdk/tools/errs"
"github.com/openimsdk/tools/utils/runtimeenv" // "github.com/openimsdk/tools/log"
clientv3 "go.etcd.io/etcd/client/v3" // "github.com/openimsdk/tools/utils/runtimeenv"
) // clientv3 "go.etcd.io/etcd/client/v3"
//)
const ( //
// wait for Restart http call return //const (
waitHttp = time.Millisecond * 200 // // wait for Restart http call return
) // waitHttp = time.Millisecond * 200
//)
type ConfigManager struct { //
imAdminUserID []string //type ConfigManager struct {
config *config.AllConfig // imAdminUserID []string
client *clientv3.Client // config *config.AllConfig
// client *clientv3.Client
configPath string //
runtimeEnv string // configPath string
} // runtimeEnv string
//}
func NewConfigManager(IMAdminUserID []string, cfg *config.AllConfig, client *clientv3.Client, configPath string, runtimeEnv string) *ConfigManager { //
cm := &ConfigManager{ //func NewConfigManager(IMAdminUserID []string, cfg *config.AllConfig, client *clientv3.Client, configPath string, runtimeEnv string) *ConfigManager {
imAdminUserID: IMAdminUserID, // cm := &ConfigManager{
config: cfg, // imAdminUserID: IMAdminUserID,
client: client, // config: cfg,
configPath: configPath, // client: client,
runtimeEnv: runtimeEnv, // configPath: configPath,
} // runtimeEnv: runtimeEnv,
return cm // }
} // return cm
//}
func (cm *ConfigManager) CheckAdmin(c *gin.Context) { //
if err := authverify.CheckAdmin(c, cm.imAdminUserID); err != nil { //func (cm *ConfigManager) CheckAdmin(c *gin.Context) {
apiresp.GinError(c, err) // if err := authverify.CheckAdmin(c, cm.imAdminUserID); err != nil {
c.Abort() // apiresp.GinError(c, err)
} // c.Abort()
} // }
//}
func (cm *ConfigManager) GetConfig(c *gin.Context) { //
var req apistruct.GetConfigReq //func (cm *ConfigManager) GetConfig(c *gin.Context) {
if err := c.BindJSON(&req); err != nil { // var req apistruct.GetConfigReq
apiresp.GinError(c, errs.ErrArgs.WithDetail(err.Error()).Wrap()) // if err := c.BindJSON(&req); err != nil {
return // apiresp.GinError(c, errs.ErrArgs.WithDetail(err.Error()).Wrap())
} // return
conf := cm.config.Name2Config(req.ConfigName) // }
if conf == nil { // conf := cm.config.Name2Config(req.ConfigName)
apiresp.GinError(c, errs.ErrArgs.WithDetail("config name not found").Wrap()) // if conf == nil {
return // apiresp.GinError(c, errs.ErrArgs.WithDetail("config name not found").Wrap())
} // return
b, err := json.Marshal(conf) // }
if err != nil { // b, err := json.Marshal(conf)
apiresp.GinError(c, err) // if err != nil {
return // apiresp.GinError(c, err)
} // return
apiresp.GinSuccess(c, string(b)) // }
} // apiresp.GinSuccess(c, string(b))
//}
func (cm *ConfigManager) GetConfigList(c *gin.Context) { //
var resp apistruct.GetConfigListResp //func (cm *ConfigManager) GetConfigList(c *gin.Context) {
resp.ConfigNames = cm.config.GetConfigNames() // var resp apistruct.GetConfigListResp
resp.Environment = runtimeenv.PrintRuntimeEnvironment() // resp.ConfigNames = cm.config.GetConfigNames()
resp.Version = version.Version // resp.Environment = runtimeenv.PrintRuntimeEnvironment()
// resp.Version = version.Version
apiresp.GinSuccess(c, resp) //
} // apiresp.GinSuccess(c, resp)
//}
func (cm *ConfigManager) SetConfig(c *gin.Context) { //
if cm.config.Discovery.Enable != config.ETCD { //func (cm *ConfigManager) SetConfig(c *gin.Context) {
apiresp.GinError(c, errs.New("only etcd support set config").Wrap()) // if cm.config.Discovery.Enable != config.ETCD {
return // apiresp.GinError(c, errs.New("only etcd support set config").Wrap())
} // return
var req apistruct.SetConfigReq // }
if err := c.BindJSON(&req); err != nil { // var req apistruct.SetConfigReq
apiresp.GinError(c, errs.ErrArgs.WithDetail(err.Error()).Wrap()) // if err := c.BindJSON(&req); err != nil {
return // apiresp.GinError(c, errs.ErrArgs.WithDetail(err.Error()).Wrap())
} // return
var err error // }
switch req.ConfigName { // var err error
case cm.config.Discovery.GetConfigFileName(): // switch req.ConfigName {
err = compareAndSave[config.Discovery](c, cm.config.Name2Config(req.ConfigName), &req, cm) // case cm.config.Discovery.GetConfigFileName():
case cm.config.Kafka.GetConfigFileName(): // err = compareAndSave[config.Discovery](c, cm.config.Name2Config(req.ConfigName), &req, cm)
err = compareAndSave[config.Kafka](c, cm.config.Name2Config(req.ConfigName), &req, cm) // case cm.config.Kafka.GetConfigFileName():
case cm.config.LocalCache.GetConfigFileName(): // err = compareAndSave[config.Kafka](c, cm.config.Name2Config(req.ConfigName), &req, cm)
err = compareAndSave[config.LocalCache](c, cm.config.Name2Config(req.ConfigName), &req, cm) // case cm.config.LocalCache.GetConfigFileName():
case cm.config.Log.GetConfigFileName(): // err = compareAndSave[config.LocalCache](c, cm.config.Name2Config(req.ConfigName), &req, cm)
err = compareAndSave[config.Log](c, cm.config.Name2Config(req.ConfigName), &req, cm) // case cm.config.Log.GetConfigFileName():
case cm.config.Minio.GetConfigFileName(): // err = compareAndSave[config.Log](c, cm.config.Name2Config(req.ConfigName), &req, cm)
err = compareAndSave[config.Minio](c, cm.config.Name2Config(req.ConfigName), &req, cm) // case cm.config.Minio.GetConfigFileName():
case cm.config.Mongo.GetConfigFileName(): // err = compareAndSave[config.Minio](c, cm.config.Name2Config(req.ConfigName), &req, cm)
err = compareAndSave[config.Mongo](c, cm.config.Name2Config(req.ConfigName), &req, cm) // case cm.config.Mongo.GetConfigFileName():
case cm.config.Notification.GetConfigFileName(): // err = compareAndSave[config.Mongo](c, cm.config.Name2Config(req.ConfigName), &req, cm)
err = compareAndSave[config.Notification](c, cm.config.Name2Config(req.ConfigName), &req, cm) // case cm.config.Notification.GetConfigFileName():
case cm.config.API.GetConfigFileName(): // err = compareAndSave[config.Notification](c, cm.config.Name2Config(req.ConfigName), &req, cm)
err = compareAndSave[config.API](c, cm.config.Name2Config(req.ConfigName), &req, cm) // case cm.config.API.GetConfigFileName():
case cm.config.CronTask.GetConfigFileName(): // err = compareAndSave[config.API](c, cm.config.Name2Config(req.ConfigName), &req, cm)
err = compareAndSave[config.CronTask](c, cm.config.Name2Config(req.ConfigName), &req, cm) // case cm.config.CronTask.GetConfigFileName():
case cm.config.MsgGateway.GetConfigFileName(): // err = compareAndSave[config.CronTask](c, cm.config.Name2Config(req.ConfigName), &req, cm)
err = compareAndSave[config.MsgGateway](c, cm.config.Name2Config(req.ConfigName), &req, cm) // case cm.config.MsgGateway.GetConfigFileName():
case cm.config.MsgTransfer.GetConfigFileName(): // err = compareAndSave[config.MsgGateway](c, cm.config.Name2Config(req.ConfigName), &req, cm)
err = compareAndSave[config.MsgTransfer](c, cm.config.Name2Config(req.ConfigName), &req, cm) // case cm.config.MsgTransfer.GetConfigFileName():
case cm.config.Push.GetConfigFileName(): // err = compareAndSave[config.MsgTransfer](c, cm.config.Name2Config(req.ConfigName), &req, cm)
err = compareAndSave[config.Push](c, cm.config.Name2Config(req.ConfigName), &req, cm) // case cm.config.Push.GetConfigFileName():
case cm.config.Auth.GetConfigFileName(): // err = compareAndSave[config.Push](c, cm.config.Name2Config(req.ConfigName), &req, cm)
err = compareAndSave[config.Auth](c, cm.config.Name2Config(req.ConfigName), &req, cm) // case cm.config.Auth.GetConfigFileName():
case cm.config.Conversation.GetConfigFileName(): // err = compareAndSave[config.Auth](c, cm.config.Name2Config(req.ConfigName), &req, cm)
err = compareAndSave[config.Conversation](c, cm.config.Name2Config(req.ConfigName), &req, cm) // case cm.config.Conversation.GetConfigFileName():
case cm.config.Friend.GetConfigFileName(): // err = compareAndSave[config.Conversation](c, cm.config.Name2Config(req.ConfigName), &req, cm)
err = compareAndSave[config.Friend](c, cm.config.Name2Config(req.ConfigName), &req, cm) // case cm.config.Friend.GetConfigFileName():
case cm.config.Group.GetConfigFileName(): // err = compareAndSave[config.Friend](c, cm.config.Name2Config(req.ConfigName), &req, cm)
err = compareAndSave[config.Group](c, cm.config.Name2Config(req.ConfigName), &req, cm) // case cm.config.Group.GetConfigFileName():
case cm.config.Msg.GetConfigFileName(): // err = compareAndSave[config.Group](c, cm.config.Name2Config(req.ConfigName), &req, cm)
err = compareAndSave[config.Msg](c, cm.config.Name2Config(req.ConfigName), &req, cm) // case cm.config.Msg.GetConfigFileName():
case cm.config.Third.GetConfigFileName(): // err = compareAndSave[config.Msg](c, cm.config.Name2Config(req.ConfigName), &req, cm)
err = compareAndSave[config.Third](c, cm.config.Name2Config(req.ConfigName), &req, cm) // case cm.config.Third.GetConfigFileName():
case cm.config.User.GetConfigFileName(): // err = compareAndSave[config.Third](c, cm.config.Name2Config(req.ConfigName), &req, cm)
err = compareAndSave[config.User](c, cm.config.Name2Config(req.ConfigName), &req, cm) // case cm.config.User.GetConfigFileName():
case cm.config.Redis.GetConfigFileName(): // err = compareAndSave[config.User](c, cm.config.Name2Config(req.ConfigName), &req, cm)
err = compareAndSave[config.Redis](c, cm.config.Name2Config(req.ConfigName), &req, cm) // case cm.config.Redis.GetConfigFileName():
case cm.config.Share.GetConfigFileName(): // err = compareAndSave[config.Redis](c, cm.config.Name2Config(req.ConfigName), &req, cm)
err = compareAndSave[config.Share](c, cm.config.Name2Config(req.ConfigName), &req, cm) // case cm.config.Share.GetConfigFileName():
case cm.config.Webhooks.GetConfigFileName(): // err = compareAndSave[config.Share](c, cm.config.Name2Config(req.ConfigName), &req, cm)
err = compareAndSave[config.Webhooks](c, cm.config.Name2Config(req.ConfigName), &req, cm) // case cm.config.Webhooks.GetConfigFileName():
default: // err = compareAndSave[config.Webhooks](c, cm.config.Name2Config(req.ConfigName), &req, cm)
apiresp.GinError(c, errs.ErrArgs.Wrap()) // default:
return // apiresp.GinError(c, errs.ErrArgs.Wrap())
} // return
if err != nil { // }
apiresp.GinError(c, errs.ErrArgs.WithDetail(err.Error()).Wrap()) // if err != nil {
return // apiresp.GinError(c, errs.ErrArgs.WithDetail(err.Error()).Wrap())
} // return
apiresp.GinSuccess(c, nil) // }
} // apiresp.GinSuccess(c, nil)
//}
func compareAndSave[T any](c *gin.Context, old any, req *apistruct.SetConfigReq, cm *ConfigManager) error { //
conf := new(T) //func compareAndSave[T any](c *gin.Context, old any, req *apistruct.SetConfigReq, cm *ConfigManager) error {
err := json.Unmarshal([]byte(req.Data), &conf) // conf := new(T)
if err != nil { // err := json.Unmarshal([]byte(req.Data), &conf)
return errs.ErrArgs.WithDetail(err.Error()).Wrap() // if err != nil {
} // return errs.ErrArgs.WithDetail(err.Error()).Wrap()
eq := reflect.DeepEqual(old, conf) // }
if eq { // eq := reflect.DeepEqual(old, conf)
return nil // if eq {
} // return nil
data, err := json.Marshal(conf) // }
if err != nil { // data, err := json.Marshal(conf)
return errs.ErrArgs.WithDetail(err.Error()).Wrap() // if err != nil {
} // return errs.ErrArgs.WithDetail(err.Error()).Wrap()
_, err = cm.client.Put(c, etcd.BuildKey(req.ConfigName), string(data)) // }
if err != nil { // _, err = cm.client.Put(c, etcd.BuildKey(req.ConfigName), string(data))
return errs.WrapMsg(err, "save to etcd failed") // if err != nil {
} // return errs.WrapMsg(err, "save to etcd failed")
return nil // }
} // return nil
//}
func (cm *ConfigManager) ResetConfig(c *gin.Context) { //
go func() { //func (cm *ConfigManager) ResetConfig(c *gin.Context) {
if err := cm.resetConfig(c, true); err != nil { // go func() {
log.ZError(c, "reset config err", err) // if err := cm.resetConfig(c, true); err != nil {
} // log.ZError(c, "reset config err", err)
}() // }
apiresp.GinSuccess(c, nil) // }()
} // apiresp.GinSuccess(c, nil)
//}
func (cm *ConfigManager) resetConfig(c *gin.Context, checkChange bool, ops ...clientv3.Op) error { //
txn := cm.client.Txn(c) //func (cm *ConfigManager) resetConfig(c *gin.Context, checkChange bool, ops ...clientv3.Op) error {
type initConf struct { // txn := cm.client.Txn(c)
old any // type initConf struct {
new any // old any
} // new any
configMap := map[string]*initConf{ // }
cm.config.Discovery.GetConfigFileName(): {old: &cm.config.Discovery, new: new(config.Discovery)}, // configMap := map[string]*initConf{
cm.config.Kafka.GetConfigFileName(): {old: &cm.config.Kafka, new: new(config.Kafka)}, // cm.config.Discovery.GetConfigFileName(): {old: &cm.config.Discovery, new: new(config.Discovery)},
cm.config.LocalCache.GetConfigFileName(): {old: &cm.config.LocalCache, new: new(config.LocalCache)}, // cm.config.Kafka.GetConfigFileName(): {old: &cm.config.Kafka, new: new(config.Kafka)},
cm.config.Log.GetConfigFileName(): {old: &cm.config.Log, new: new(config.Log)}, // cm.config.LocalCache.GetConfigFileName(): {old: &cm.config.LocalCache, new: new(config.LocalCache)},
cm.config.Minio.GetConfigFileName(): {old: &cm.config.Minio, new: new(config.Minio)}, // cm.config.Log.GetConfigFileName(): {old: &cm.config.Log, new: new(config.Log)},
cm.config.Mongo.GetConfigFileName(): {old: &cm.config.Mongo, new: new(config.Mongo)}, // cm.config.Minio.GetConfigFileName(): {old: &cm.config.Minio, new: new(config.Minio)},
cm.config.Notification.GetConfigFileName(): {old: &cm.config.Notification, new: new(config.Notification)}, // cm.config.Mongo.GetConfigFileName(): {old: &cm.config.Mongo, new: new(config.Mongo)},
cm.config.API.GetConfigFileName(): {old: &cm.config.API, new: new(config.API)}, // cm.config.Notification.GetConfigFileName(): {old: &cm.config.Notification, new: new(config.Notification)},
cm.config.CronTask.GetConfigFileName(): {old: &cm.config.CronTask, new: new(config.CronTask)}, // cm.config.API.GetConfigFileName(): {old: &cm.config.API, new: new(config.API)},
cm.config.MsgGateway.GetConfigFileName(): {old: &cm.config.MsgGateway, new: new(config.MsgGateway)}, // cm.config.CronTask.GetConfigFileName(): {old: &cm.config.CronTask, new: new(config.CronTask)},
cm.config.MsgTransfer.GetConfigFileName(): {old: &cm.config.MsgTransfer, new: new(config.MsgTransfer)}, // cm.config.MsgGateway.GetConfigFileName(): {old: &cm.config.MsgGateway, new: new(config.MsgGateway)},
cm.config.Push.GetConfigFileName(): {old: &cm.config.Push, new: new(config.Push)}, // cm.config.MsgTransfer.GetConfigFileName(): {old: &cm.config.MsgTransfer, new: new(config.MsgTransfer)},
cm.config.Auth.GetConfigFileName(): {old: &cm.config.Auth, new: new(config.Auth)}, // cm.config.Push.GetConfigFileName(): {old: &cm.config.Push, new: new(config.Push)},
cm.config.Conversation.GetConfigFileName(): {old: &cm.config.Conversation, new: new(config.Conversation)}, // cm.config.Auth.GetConfigFileName(): {old: &cm.config.Auth, new: new(config.Auth)},
cm.config.Friend.GetConfigFileName(): {old: &cm.config.Friend, new: new(config.Friend)}, // cm.config.Conversation.GetConfigFileName(): {old: &cm.config.Conversation, new: new(config.Conversation)},
cm.config.Group.GetConfigFileName(): {old: &cm.config.Group, new: new(config.Group)}, // cm.config.Friend.GetConfigFileName(): {old: &cm.config.Friend, new: new(config.Friend)},
cm.config.Msg.GetConfigFileName(): {old: &cm.config.Msg, new: new(config.Msg)}, // cm.config.Group.GetConfigFileName(): {old: &cm.config.Group, new: new(config.Group)},
cm.config.Third.GetConfigFileName(): {old: &cm.config.Third, new: new(config.Third)}, // cm.config.Msg.GetConfigFileName(): {old: &cm.config.Msg, new: new(config.Msg)},
cm.config.User.GetConfigFileName(): {old: &cm.config.User, new: new(config.User)}, // cm.config.Third.GetConfigFileName(): {old: &cm.config.Third, new: new(config.Third)},
cm.config.Redis.GetConfigFileName(): {old: &cm.config.Redis, new: new(config.Redis)}, // cm.config.User.GetConfigFileName(): {old: &cm.config.User, new: new(config.User)},
cm.config.Share.GetConfigFileName(): {old: &cm.config.Share, new: new(config.Share)}, // cm.config.Redis.GetConfigFileName(): {old: &cm.config.Redis, new: new(config.Redis)},
cm.config.Webhooks.GetConfigFileName(): {old: &cm.config.Webhooks, new: new(config.Webhooks)}, // cm.config.Share.GetConfigFileName(): {old: &cm.config.Share, new: new(config.Share)},
} // cm.config.Webhooks.GetConfigFileName(): {old: &cm.config.Webhooks, new: new(config.Webhooks)},
// }
changedKeys := make([]string, 0, len(configMap)) //
for k, v := range configMap { // changedKeys := make([]string, 0, len(configMap))
err := config.Load( // for k, v := range configMap {
cm.configPath, // err := config.Load(
k, // cm.configPath,
config.EnvPrefixMap[k], // k,
cm.runtimeEnv, // config.EnvPrefixMap[k],
v.new, // cm.runtimeEnv,
) // v.new,
if err != nil { // )
log.ZError(c, "load config failed", err) // if err != nil {
continue // log.ZError(c, "load config failed", err)
} // continue
equal := reflect.DeepEqual(v.old, v.new) // }
if !checkChange || !equal { // equal := reflect.DeepEqual(v.old, v.new)
changedKeys = append(changedKeys, k) // if !checkChange || !equal {
} // changedKeys = append(changedKeys, k)
} // }
// }
for _, k := range changedKeys { //
data, err := json.Marshal(configMap[k].new) // for _, k := range changedKeys {
if err != nil { // data, err := json.Marshal(configMap[k].new)
log.ZError(c, "marshal config failed", err) // if err != nil {
continue // log.ZError(c, "marshal config failed", err)
} // continue
ops = append(ops, clientv3.OpPut(etcd.BuildKey(k), string(data))) // }
} // ops = append(ops, clientv3.OpPut(etcd.BuildKey(k), string(data)))
if len(ops) > 0 { // }
txn.Then(ops...) // if len(ops) > 0 {
_, err := txn.Commit() // txn.Then(ops...)
if err != nil { // _, err := txn.Commit()
return errs.WrapMsg(err, "commit etcd txn failed") // if err != nil {
} // return errs.WrapMsg(err, "commit etcd txn failed")
} // }
return nil // }
} // return nil
//}
func (cm *ConfigManager) Restart(c *gin.Context) { //
go cm.restart(c) //func (cm *ConfigManager) Restart(c *gin.Context) {
apiresp.GinSuccess(c, nil) // go cm.restart(c)
} // apiresp.GinSuccess(c, nil)
//}
func (cm *ConfigManager) restart(c *gin.Context) { //
time.Sleep(waitHttp) // wait for Restart http call return //func (cm *ConfigManager) restart(c *gin.Context) {
t := time.Now().Unix() // time.Sleep(waitHttp) // wait for Restart http call return
_, err := cm.client.Put(c, etcd.BuildKey(etcd.RestartKey), strconv.Itoa(int(t))) // t := time.Now().Unix()
if err != nil { // _, err := cm.client.Put(c, etcd.BuildKey(etcd.RestartKey), strconv.Itoa(int(t)))
log.ZError(c, "restart etcd put key failed", err) // if err != nil {
} // log.ZError(c, "restart etcd put key failed", err)
} // }
//}
func (cm *ConfigManager) SetEnableConfigManager(c *gin.Context) { //
if cm.config.Discovery.Enable != config.ETCD { //func (cm *ConfigManager) SetEnableConfigManager(c *gin.Context) {
apiresp.GinError(c, errs.New("only etcd support config manager").Wrap()) // if cm.config.Discovery.Enable != config.ETCD {
return // apiresp.GinError(c, errs.New("only etcd support config manager").Wrap())
} // return
var req apistruct.SetEnableConfigManagerReq // }
if err := c.BindJSON(&req); err != nil { // var req apistruct.SetEnableConfigManagerReq
apiresp.GinError(c, errs.ErrArgs.WithDetail(err.Error()).Wrap()) // if err := c.BindJSON(&req); err != nil {
return // apiresp.GinError(c, errs.ErrArgs.WithDetail(err.Error()).Wrap())
} // return
var enableStr string // }
if req.Enable { // var enableStr string
enableStr = etcd.Enable // if req.Enable {
} else { // enableStr = etcd.Enable
enableStr = etcd.Disable // } else {
} // enableStr = etcd.Disable
resp, err := cm.client.Get(c, etcd.BuildKey(etcd.EnableConfigCenterKey)) // }
if err != nil { // resp, err := cm.client.Get(c, etcd.BuildKey(etcd.EnableConfigCenterKey))
apiresp.GinError(c, errs.WrapMsg(err, "getEnableConfigManager failed")) // if err != nil {
return // apiresp.GinError(c, errs.WrapMsg(err, "getEnableConfigManager failed"))
} // return
if !(resp.Count > 0 && string(resp.Kvs[0].Value) == etcd.Enable) && req.Enable { // }
go func() { // if !(resp.Count > 0 && string(resp.Kvs[0].Value) == etcd.Enable) && req.Enable {
time.Sleep(waitHttp) // wait for Restart http call return // go func() {
err := cm.resetConfig(c, false, clientv3.OpPut(etcd.BuildKey(etcd.EnableConfigCenterKey), enableStr)) // time.Sleep(waitHttp) // wait for Restart http call return
if err != nil { // err := cm.resetConfig(c, false, clientv3.OpPut(etcd.BuildKey(etcd.EnableConfigCenterKey), enableStr))
log.ZError(c, "resetConfig failed", err) // if err != nil {
} // log.ZError(c, "resetConfig failed", err)
}() // }
} else { // }()
_, err = cm.client.Put(c, etcd.BuildKey(etcd.EnableConfigCenterKey), enableStr) // } else {
if err != nil { // _, err = cm.client.Put(c, etcd.BuildKey(etcd.EnableConfigCenterKey), enableStr)
apiresp.GinError(c, errs.WrapMsg(err, "setEnableConfigManager failed")) // if err != nil {
return // apiresp.GinError(c, errs.WrapMsg(err, "setEnableConfigManager failed"))
} // return
} // }
// }
apiresp.GinSuccess(c, nil) //
} // apiresp.GinSuccess(c, nil)
//}
func (cm *ConfigManager) GetEnableConfigManager(c *gin.Context) { //
resp, err := cm.client.Get(c, etcd.BuildKey(etcd.EnableConfigCenterKey)) //func (cm *ConfigManager) GetEnableConfigManager(c *gin.Context) {
if err != nil { // resp, err := cm.client.Get(c, etcd.BuildKey(etcd.EnableConfigCenterKey))
apiresp.GinError(c, errs.WrapMsg(err, "getEnableConfigManager failed")) // if err != nil {
return // apiresp.GinError(c, errs.WrapMsg(err, "getEnableConfigManager failed"))
} // return
var enable bool // }
if resp.Count > 0 && string(resp.Kvs[0].Value) == etcd.Enable { // var enable bool
enable = true // if resp.Count > 0 && string(resp.Kvs[0].Value) == etcd.Enable {
} // enable = true
apiresp.GinSuccess(c, &apistruct.GetEnableConfigManagerResp{Enable: enable}) // }
} // apiresp.GinSuccess(c, &apistruct.GetEnableConfigManagerResp{Enable: enable})
//}
+4
View File
@@ -114,3 +114,7 @@ func (o *FriendApi) GetIncrementalBlacks(c *gin.Context) {
func (o *FriendApi) GetFullFriendUserIDs(c *gin.Context) { func (o *FriendApi) GetFullFriendUserIDs(c *gin.Context) {
a2r.Call(c, relation.FriendClient.GetFullFriendUserIDs, o.Client) a2r.Call(c, relation.FriendClient.GetFullFriendUserIDs, o.Client)
} }
func (o *FriendApi) GetSelfUnhandledApplyCount(c *gin.Context) {
a2r.Call(c, relation.FriendClient.GetSelfUnhandledApplyCount, o.Client)
}
+4
View File
@@ -165,3 +165,7 @@ func (o *GroupApi) GetFullGroupMemberUserIDs(c *gin.Context) {
func (o *GroupApi) GetFullJoinGroupIDs(c *gin.Context) { func (o *GroupApi) GetFullJoinGroupIDs(c *gin.Context) {
a2r.Call(c, group.GroupClient.GetFullJoinGroupIDs, o.Client) a2r.Call(c, group.GroupClient.GetFullJoinGroupIDs, o.Client)
} }
func (o *GroupApi) GetGroupApplicationUnhandledCount(c *gin.Context) {
a2r.Call(c, group.GroupClient.GetGroupApplicationUnhandledCount, o.Client)
}
+36 -60
View File
@@ -1,17 +1,3 @@
// Copyright © 2023 OpenIM. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package api package api
import ( import (
@@ -26,45 +12,46 @@ import (
"syscall" "syscall"
"time" "time"
conf "github.com/openimsdk/open-im-server/v3/pkg/common/config" "github.com/openimsdk/open-im-server/v3/pkg/common/config"
kdisc "github.com/openimsdk/open-im-server/v3/pkg/common/discovery" "github.com/openimsdk/tools/mw"
disetcd "github.com/openimsdk/open-im-server/v3/pkg/common/discovery/etcd" "github.com/openimsdk/tools/utils/datautil"
"github.com/openimsdk/tools/utils/network"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
kdisc "github.com/openimsdk/open-im-server/v3/pkg/common/discoveryregister"
"github.com/openimsdk/open-im-server/v3/pkg/common/prommetrics" "github.com/openimsdk/open-im-server/v3/pkg/common/prommetrics"
"github.com/openimsdk/tools/discovery"
"github.com/openimsdk/tools/discovery/etcd" "github.com/openimsdk/tools/discovery/etcd"
"github.com/openimsdk/tools/errs" "github.com/openimsdk/tools/errs"
"github.com/openimsdk/tools/log" "github.com/openimsdk/tools/log"
"github.com/openimsdk/tools/mw"
"github.com/openimsdk/tools/system/program" "github.com/openimsdk/tools/system/program"
"github.com/openimsdk/tools/utils/datautil"
"github.com/openimsdk/tools/utils/jsonutil" "github.com/openimsdk/tools/utils/jsonutil"
"github.com/openimsdk/tools/utils/network"
"github.com/openimsdk/tools/utils/runtimeenv"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
) )
type Config struct { type Config struct {
*conf.AllConfig API config.API
Share config.Share
RuntimeEnv string Discovery config.Discovery
ConfigPath string
} }
func Start(ctx context.Context, index int, config *Config) error { func Start(ctx context.Context, index int, cfg *Config) error {
apiPort, err := datautil.GetElemByIndex(config.API.Api.Ports, index) apiPort, err := datautil.GetElemByIndex(cfg.API.Api.Ports, index)
if err != nil { if err != nil {
return err return err
} }
config.RuntimeEnv = runtimeenv.PrintRuntimeEnvironment() var client discovery.SvcDiscoveryRegistry
client, err := kdisc.NewDiscoveryRegister(&config.Discovery, config.RuntimeEnv, []string{ // Determine whether zk is passed according to whether it is a clustered deployment
config.Discovery.RpcService.MessageGateway, client, err = kdisc.NewDiscoveryRegister(&cfg.Discovery, &cfg.Share, []string{
cfg.Share.RpcRegisterName.MessageGateway,
}) })
if err != nil { if err != nil {
return errs.WrapMsg(err, "failed to register discovery service") return errs.WrapMsg(err, "failed to register discovery service")
} }
client.AddOption(mw.GrpcClient(), grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithDefaultServiceConfig(fmt.Sprintf(`{"LoadBalancingPolicy": "%s"}`, "round_robin"))) client.AddOption(mw.GrpcClient(), grpc.WithTransportCredentials(insecure.NewCredentials()),
grpc.WithDefaultServiceConfig(fmt.Sprintf(`{"LoadBalancingPolicy": "%s"}`, "round_robin")))
var ( var (
netDone = make(chan struct{}, 1) netDone = make(chan struct{}, 1)
@@ -72,6 +59,10 @@ func Start(ctx context.Context, index int, config *Config) error {
prometheusPort int prometheusPort int
) )
router, err := newGinRouter(ctx, client, cfg)
if err != nil {
return err
}
registerIP, err := network.GetRpcRegisterIP("") registerIP, err := network.GetRpcRegisterIP("")
if err != nil { if err != nil {
return err return err
@@ -88,20 +79,16 @@ func Start(ctx context.Context, index int, config *Config) error {
return listener, port, nil return listener, port, nil
} }
if config.API.Prometheus.AutoSetPorts && config.Discovery.Enable != conf.ETCD { if cfg.API.Prometheus.AutoSetPorts && cfg.Discovery.Enable != config.ETCD {
return errs.New("only etcd support autoSetPorts", "RegisterName", "api").Wrap() return errs.New("only etcd support autoSetPorts", "RegisterName", "api").Wrap()
} }
router, err := newGinRouter(ctx, client, config) if cfg.API.Prometheus.Enable {
if err != nil {
return err
}
if config.API.Prometheus.Enable {
var ( var (
listener net.Listener listener net.Listener
) )
if config.API.Prometheus.AutoSetPorts { if cfg.API.Prometheus.AutoSetPorts {
listener, prometheusPort, err = getAutoPort() listener, prometheusPort, err = getAutoPort()
if err != nil { if err != nil {
return err return err
@@ -114,7 +101,7 @@ func Start(ctx context.Context, index int, config *Config) error {
return errs.WrapMsg(err, "etcd put err") return errs.WrapMsg(err, "etcd put err")
} }
} else { } else {
prometheusPort, err = datautil.GetElemByIndex(config.API.Prometheus.Ports, index) prometheusPort, err = datautil.GetElemByIndex(cfg.API.Prometheus.Ports, index)
if err != nil { if err != nil {
return err return err
} }
@@ -132,41 +119,30 @@ func Start(ctx context.Context, index int, config *Config) error {
}() }()
} }
address := net.JoinHostPort(network.GetListenIP(config.API.Api.ListenIP), strconv.Itoa(apiPort)) address := net.JoinHostPort(network.GetListenIP(cfg.API.Api.ListenIP), strconv.Itoa(apiPort))
server := http.Server{Addr: address, Handler: router} server := http.Server{Addr: address, Handler: router}
log.CInfo(ctx, "API server is initializing", "runtimeEnv", config.RuntimeEnv, "address", address, "apiPort", apiPort, "prometheusPort", prometheusPort) log.CInfo(ctx, "API server is initializing", "address", address, "apiPort", apiPort, "prometheusPort", prometheusPort)
go func() { go func() {
err = server.ListenAndServe() err = server.ListenAndServe()
if err != nil && !errors.Is(err, http.ErrServerClosed) { if err != nil && !errors.Is(err, http.ErrServerClosed) {
netErr = errs.WrapMsg(err, fmt.Sprintf("api start err: %s", server.Addr)) netErr = errs.WrapMsg(err, fmt.Sprintf("api start err: %s", server.Addr))
netDone <- struct{}{} netDone <- struct{}{}
} }
}() }()
if config.Discovery.Enable == conf.ETCD {
cm := disetcd.NewConfigManager(client.(*etcd.SvcDiscoveryRegistryImpl).GetClient(), config.GetConfigNames())
cm.Watch(ctx)
}
sigs := make(chan os.Signal, 1) sigs := make(chan os.Signal, 1)
signal.Notify(sigs, syscall.SIGTERM) signal.Notify(sigs, syscall.SIGTERM)
shutdown := func() error { ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second) defer cancel()
defer cancel()
err := server.Shutdown(ctx)
if err != nil {
return errs.WrapMsg(err, "shutdown err")
}
return nil
}
disetcd.RegisterShutDown(shutdown)
select { select {
case <-sigs: case <-sigs:
program.SIGTERMExit() program.SIGTERMExit()
if err := shutdown(); err != nil { err := server.Shutdown(ctx)
return err if err != nil {
return errs.WrapMsg(err, "shutdown err")
} }
case <-netDone: case <-netDone:
close(netDone) close(netDone)
+61 -21
View File
@@ -2,10 +2,14 @@ package jssdk
import ( import (
"context" "context"
"github.com/openimsdk/open-im-server/v3/pkg/rpcli"
"sort" "sort"
"github.com/openimsdk/open-im-server/v3/pkg/rpcli"
"github.com/openimsdk/protocol/constant"
"github.com/openimsdk/tools/log"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/openimsdk/protocol/conversation" "github.com/openimsdk/protocol/conversation"
"github.com/openimsdk/protocol/jssdk" "github.com/openimsdk/protocol/jssdk"
"github.com/openimsdk/protocol/msg" "github.com/openimsdk/protocol/msg"
@@ -109,10 +113,7 @@ func (x *JSSdk) getActiveConversations(ctx context.Context, req *jssdk.GetActive
if len(conversationIDs) == 0 { if len(conversationIDs) == 0 {
return &jssdk.GetActiveConversationsResp{}, nil return &jssdk.GetActiveConversationsResp{}, nil
} }
readSeq, err := x.msgClient.GetHasReadSeqs(ctx, conversationIDs, req.OwnerUserID)
if err != nil {
return nil, err
}
activeConversation, err := x.msgClient.GetActiveConversation(ctx, conversationIDs) activeConversation, err := x.msgClient.GetActiveConversation(ctx, conversationIDs)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -120,6 +121,10 @@ func (x *JSSdk) getActiveConversations(ctx context.Context, req *jssdk.GetActive
if len(activeConversation) == 0 { if len(activeConversation) == 0 {
return &jssdk.GetActiveConversationsResp{}, nil return &jssdk.GetActiveConversationsResp{}, nil
} }
readSeq, err := x.msgClient.GetHasReadSeqs(ctx, conversationIDs, req.OwnerUserID)
if err != nil {
return nil, err
}
sortConversations := sortActiveConversations{ sortConversations := sortActiveConversations{
Conversation: activeConversation, Conversation: activeConversation,
} }
@@ -147,6 +152,7 @@ func (x *JSSdk) getActiveConversations(ctx context.Context, req *jssdk.GetActive
if err != nil { if err != nil {
return nil, err return nil, err
} }
x.checkMessagesAndGetLastMessage(ctx, req.OwnerUserID, msgs)
conversationMap := datautil.SliceToMap(conversations, func(c *conversation.Conversation) string { conversationMap := datautil.SliceToMap(conversations, func(c *conversation.Conversation) string {
return c.ConversationID return c.ConversationID
}) })
@@ -156,16 +162,15 @@ func (x *JSSdk) getActiveConversations(ctx context.Context, req *jssdk.GetActive
if !ok { if !ok {
continue continue
} }
var lastMsg *sdkws.MsgData
if msgList, ok := msgs[c.ConversationID]; ok && len(msgList.Msgs) > 0 { if msgList, ok := msgs[c.ConversationID]; ok && len(msgList.Msgs) > 0 {
lastMsg = msgList.Msgs[0] resp = append(resp, &jssdk.ConversationMsg{
Conversation: conv,
LastMsg: msgList.Msgs[0],
MaxSeq: c.MaxSeq,
ReadSeq: readSeq[c.ConversationID],
})
} }
resp = append(resp, &jssdk.ConversationMsg{
Conversation: conv,
LastMsg: lastMsg,
MaxSeq: c.MaxSeq,
ReadSeq: readSeq[c.ConversationID],
})
} }
if err := x.fillConversations(ctx, resp); err != nil { if err := x.fillConversations(ctx, resp); err != nil {
return nil, err return nil, err
@@ -219,18 +224,18 @@ func (x *JSSdk) getConversations(ctx context.Context, req *jssdk.GetConversation
return nil, err return nil, err
} }
} }
x.checkMessagesAndGetLastMessage(ctx, req.OwnerUserID, msgs)
resp := make([]*jssdk.ConversationMsg, 0, len(conversations)) resp := make([]*jssdk.ConversationMsg, 0, len(conversations))
for _, c := range conversations { for _, c := range conversations {
var lastMsg *sdkws.MsgData
if msgList, ok := msgs[c.ConversationID]; ok && len(msgList.Msgs) > 0 { if msgList, ok := msgs[c.ConversationID]; ok && len(msgList.Msgs) > 0 {
lastMsg = msgList.Msgs[0] resp = append(resp, &jssdk.ConversationMsg{
Conversation: c,
LastMsg: msgList.Msgs[0],
MaxSeq: maxSeqs[c.ConversationID],
ReadSeq: readSeqs[c.ConversationID],
})
} }
resp = append(resp, &jssdk.ConversationMsg{
Conversation: c,
LastMsg: lastMsg,
MaxSeq: maxSeqs[c.ConversationID],
ReadSeq: readSeqs[c.ConversationID],
})
} }
if err := x.fillConversations(ctx, resp); err != nil { if err := x.fillConversations(ctx, resp); err != nil {
return nil, err return nil, err
@@ -247,3 +252,38 @@ func (x *JSSdk) getConversations(ctx context.Context, req *jssdk.GetConversation
UnreadCount: unreadCount, UnreadCount: unreadCount,
}, nil }, nil
} }
// This function checks whether the latest MaxSeq message is valid.
// If not, it needs to fetch a valid message again.
func (x *JSSdk) checkMessagesAndGetLastMessage(ctx context.Context, userID string, messages map[string]*sdkws.PullMsgs) {
var conversationIDs []string
for conversationID, message := range messages {
allInValid := true
for _, data := range message.Msgs {
if data.Status < constant.MsgStatusHasDeleted {
allInValid = false
break
}
}
// when the conversation has been deleted by the user, the length of message.Msgs is empty
if allInValid && len(message.Msgs) > 0 {
conversationIDs = append(conversationIDs, conversationID)
}
}
if len(conversationIDs) > 0 {
resp, err := x.msgClient.GetLastMessage(ctx, &msg.GetLastMessageReq{
UserID: userID,
ConversationIDs: conversationIDs,
})
if err != nil {
log.ZError(ctx, "fetchLatestValidMessages", err, "conversationIDs", conversationIDs)
return
}
for conversationID, message := range resp.Msgs {
messages[conversationID] = &sdkws.PullMsgs{Msgs: []*sdkws.MsgData{message}}
}
}
}
+2 -3
View File
@@ -2,9 +2,6 @@ package jssdk
import ( import (
"context" "context"
"io"
"strings"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/openimsdk/tools/a2r" "github.com/openimsdk/tools/a2r"
"github.com/openimsdk/tools/apiresp" "github.com/openimsdk/tools/apiresp"
@@ -12,6 +9,8 @@ import (
"github.com/openimsdk/tools/errs" "github.com/openimsdk/tools/errs"
"google.golang.org/grpc" "google.golang.org/grpc"
"google.golang.org/protobuf/proto" "google.golang.org/protobuf/proto"
"io"
"strings"
) )
func field[A, B, C any](ctx context.Context, fn func(ctx context.Context, req *A, opts ...grpc.CallOption) (*B, error), req *A, get func(*B) C) (C, error) { func field[A, B, C any](ctx context.Context, fn func(ctx context.Context, req *A, opts ...grpc.CallOption) (*B, error), req *A, get func(*B) C) (C, error) {
+19 -31
View File
@@ -248,44 +248,24 @@ func (m *MessageApi) SendMessage(c *gin.Context) {
func (m *MessageApi) SendBusinessNotification(c *gin.Context) { func (m *MessageApi) SendBusinessNotification(c *gin.Context) {
req := struct { req := struct {
Key string `json:"key"` Key string `json:"key"`
Data string `json:"data"` Data string `json:"data"`
SendUserID string `json:"sendUserID" binding:"required"` SendUserID string `json:"sendUserID" binding:"required"`
RecvUserID string `json:"recvUserID"` RecvUserID string `json:"recvUserID" binding:"required"`
RecvGroupID string `json:"recvGroupID"`
SendMsg bool `json:"sendMsg"`
ReliabilityLevel *int `json:"reliabilityLevel"`
}{} }{}
if err := c.BindJSON(&req); err != nil { if err := c.BindJSON(&req); err != nil {
apiresp.GinError(c, errs.ErrArgs.WithDetail(err.Error()).Wrap()) apiresp.GinError(c, errs.ErrArgs.WithDetail(err.Error()).Wrap())
return return
} }
if req.RecvUserID == "" && req.RecvGroupID == "" {
apiresp.GinError(c, errs.ErrArgs.WrapMsg("recvUserID and recvGroupID cannot be empty at the same time"))
return
}
if req.RecvUserID != "" && req.RecvGroupID != "" {
apiresp.GinError(c, errs.ErrArgs.WrapMsg("recvUserID and recvGroupID cannot be set at the same time"))
return
}
var sessionType int32
if req.RecvUserID != "" {
sessionType = constant.SingleChatType
} else {
sessionType = constant.ReadGroupChatType
}
if req.ReliabilityLevel == nil {
req.ReliabilityLevel = datautil.ToPtr(1)
}
if !authverify.IsAppManagerUid(c, m.imAdminUserID) { if !authverify.IsAppManagerUid(c, m.imAdminUserID) {
apiresp.GinError(c, errs.ErrNoPermission.WrapMsg("only app manager can send message")) apiresp.GinError(c, errs.ErrNoPermission.WrapMsg("only app manager can send message"))
return return
} }
sendMsgReq := msg.SendMsgReq{ sendMsgReq := msg.SendMsgReq{
MsgData: &sdkws.MsgData{ MsgData: &sdkws.MsgData{
SendID: req.SendUserID, SendID: req.SendUserID,
RecvID: req.RecvUserID, RecvID: req.RecvUserID,
GroupID: req.RecvGroupID,
Content: []byte(jsonutil.StructToJsonString(&sdkws.NotificationElem{ Content: []byte(jsonutil.StructToJsonString(&sdkws.NotificationElem{
Detail: jsonutil.StructToJsonString(&struct { Detail: jsonutil.StructToJsonString(&struct {
Key string `json:"key"` Key string `json:"key"`
@@ -294,14 +274,14 @@ func (m *MessageApi) SendBusinessNotification(c *gin.Context) {
})), })),
MsgFrom: constant.SysMsgType, MsgFrom: constant.SysMsgType,
ContentType: constant.BusinessNotification, ContentType: constant.BusinessNotification,
SessionType: sessionType, SessionType: constant.SingleChatType,
CreateTime: timeutil.GetCurrentTimestampByMill(), CreateTime: timeutil.GetCurrentTimestampByMill(),
ClientMsgID: idutil.GetMsgIDByMD5(mcontext.GetOpUserID(c)), ClientMsgID: idutil.GetMsgIDByMD5(mcontext.GetOpUserID(c)),
Options: config.GetOptionsByNotification(config.NotificationConfig{ Options: config.GetOptionsByNotification(config.NotificationConfig{
IsSendMsg: req.SendMsg, IsSendMsg: false,
ReliabilityLevel: *req.ReliabilityLevel, ReliabilityLevel: 1,
UnreadCount: false, UnreadCount: false,
}), }, nil),
}, },
} }
respPb, err := m.Client.SendMsg(c, &sendMsgReq) respPb, err := m.Client.SendMsg(c, &sendMsgReq)
@@ -391,3 +371,11 @@ func (m *MessageApi) SearchMsg(c *gin.Context) {
func (m *MessageApi) GetServerTime(c *gin.Context) { func (m *MessageApi) GetServerTime(c *gin.Context) {
a2r.Call(c, msg.MsgClient.GetServerTime, m.Client) a2r.Call(c, msg.MsgClient.GetServerTime, m.Client)
} }
func (m *MessageApi) GetStreamMsg(c *gin.Context) {
a2r.Call(c, msg.MsgClient.GetServerTime, m.Client)
}
func (m *MessageApi) AppendStreamMsg(c *gin.Context) {
a2r.Call(c, msg.MsgClient.GetServerTime, m.Client)
}
+14 -14
View File
@@ -5,7 +5,7 @@ import (
"net/http" "net/http"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
conf "github.com/openimsdk/open-im-server/v3/pkg/common/config" "github.com/openimsdk/open-im-server/v3/pkg/common/config"
"github.com/openimsdk/open-im-server/v3/pkg/common/prommetrics" "github.com/openimsdk/open-im-server/v3/pkg/common/prommetrics"
"github.com/openimsdk/tools/apiresp" "github.com/openimsdk/tools/apiresp"
"github.com/openimsdk/tools/discovery" "github.com/openimsdk/tools/discovery"
@@ -20,18 +20,18 @@ type PrometheusDiscoveryApi struct {
client *clientv3.Client client *clientv3.Client
} }
func NewPrometheusDiscoveryApi(config *Config, client discovery.SvcDiscoveryRegistry) *PrometheusDiscoveryApi { func NewPrometheusDiscoveryApi(cfg *Config, client discovery.SvcDiscoveryRegistry) *PrometheusDiscoveryApi {
api := &PrometheusDiscoveryApi{ api := &PrometheusDiscoveryApi{
config: config, config: cfg,
} }
if config.Discovery.Enable == conf.ETCD { if cfg.Discovery.Enable == config.ETCD {
api.client = client.(*etcd.SvcDiscoveryRegistryImpl).GetClient() api.client = client.(*etcd.SvcDiscoveryRegistryImpl).GetClient()
} }
return api return api
} }
func (p *PrometheusDiscoveryApi) Enable(c *gin.Context) { func (p *PrometheusDiscoveryApi) Enable(c *gin.Context) {
if p.config.Discovery.Enable != conf.ETCD { if p.config.Discovery.Enable != config.ETCD {
c.JSON(http.StatusOK, []struct{}{}) c.JSON(http.StatusOK, []struct{}{})
c.Abort() c.Abort()
} }
@@ -74,39 +74,39 @@ func (p *PrometheusDiscoveryApi) Api(c *gin.Context) {
} }
func (p *PrometheusDiscoveryApi) User(c *gin.Context) { func (p *PrometheusDiscoveryApi) User(c *gin.Context) {
p.discovery(c, p.config.Discovery.RpcService.User) p.discovery(c, p.config.Share.RpcRegisterName.User)
} }
func (p *PrometheusDiscoveryApi) Group(c *gin.Context) { func (p *PrometheusDiscoveryApi) Group(c *gin.Context) {
p.discovery(c, p.config.Discovery.RpcService.Group) p.discovery(c, p.config.Share.RpcRegisterName.Group)
} }
func (p *PrometheusDiscoveryApi) Msg(c *gin.Context) { func (p *PrometheusDiscoveryApi) Msg(c *gin.Context) {
p.discovery(c, p.config.Discovery.RpcService.Msg) p.discovery(c, p.config.Share.RpcRegisterName.Msg)
} }
func (p *PrometheusDiscoveryApi) Friend(c *gin.Context) { func (p *PrometheusDiscoveryApi) Friend(c *gin.Context) {
p.discovery(c, p.config.Discovery.RpcService.Friend) p.discovery(c, p.config.Share.RpcRegisterName.Friend)
} }
func (p *PrometheusDiscoveryApi) Conversation(c *gin.Context) { func (p *PrometheusDiscoveryApi) Conversation(c *gin.Context) {
p.discovery(c, p.config.Discovery.RpcService.Conversation) p.discovery(c, p.config.Share.RpcRegisterName.Conversation)
} }
func (p *PrometheusDiscoveryApi) Third(c *gin.Context) { func (p *PrometheusDiscoveryApi) Third(c *gin.Context) {
p.discovery(c, p.config.Discovery.RpcService.Third) p.discovery(c, p.config.Share.RpcRegisterName.Third)
} }
func (p *PrometheusDiscoveryApi) Auth(c *gin.Context) { func (p *PrometheusDiscoveryApi) Auth(c *gin.Context) {
p.discovery(c, p.config.Discovery.RpcService.Auth) p.discovery(c, p.config.Share.RpcRegisterName.Auth)
} }
func (p *PrometheusDiscoveryApi) Push(c *gin.Context) { func (p *PrometheusDiscoveryApi) Push(c *gin.Context) {
p.discovery(c, p.config.Discovery.RpcService.Push) p.discovery(c, p.config.Share.RpcRegisterName.Push)
} }
func (p *PrometheusDiscoveryApi) MessageGateway(c *gin.Context) { func (p *PrometheusDiscoveryApi) MessageGateway(c *gin.Context) {
p.discovery(c, p.config.Discovery.RpcService.MessageGateway) p.discovery(c, p.config.Share.RpcRegisterName.MessageGateway)
} }
func (p *PrometheusDiscoveryApi) MessageTransfer(c *gin.Context) { func (p *PrometheusDiscoveryApi) MessageTransfer(c *gin.Context) {
+28 -47
View File
@@ -5,29 +5,29 @@ import (
"net/http" "net/http"
"strings" "strings"
"github.com/gin-contrib/gzip"
"github.com/gin-gonic/gin"
"github.com/gin-gonic/gin/binding"
"github.com/go-playground/validator/v10"
"github.com/openimsdk/open-im-server/v3/internal/api/jssdk"
"github.com/openimsdk/open-im-server/v3/pkg/common/config"
"github.com/openimsdk/open-im-server/v3/pkg/common/prommetrics"
"github.com/openimsdk/open-im-server/v3/pkg/common/servererrs"
"github.com/openimsdk/open-im-server/v3/pkg/rpcli" "github.com/openimsdk/open-im-server/v3/pkg/rpcli"
pbAuth "github.com/openimsdk/protocol/auth" pbAuth "github.com/openimsdk/protocol/auth"
"github.com/openimsdk/protocol/constant"
"github.com/openimsdk/protocol/conversation" "github.com/openimsdk/protocol/conversation"
"github.com/openimsdk/protocol/group" "github.com/openimsdk/protocol/group"
"github.com/openimsdk/protocol/msg" "github.com/openimsdk/protocol/msg"
"github.com/openimsdk/protocol/relation" "github.com/openimsdk/protocol/relation"
"github.com/openimsdk/protocol/third" "github.com/openimsdk/protocol/third"
"github.com/openimsdk/protocol/user" "github.com/openimsdk/protocol/user"
"github.com/openimsdk/open-im-server/v3/internal/api/jssdk"
"github.com/gin-contrib/gzip"
"github.com/gin-gonic/gin"
"github.com/gin-gonic/gin/binding"
"github.com/go-playground/validator/v10"
"github.com/openimsdk/open-im-server/v3/pkg/common/prommetrics"
"github.com/openimsdk/open-im-server/v3/pkg/common/servererrs"
"github.com/openimsdk/protocol/constant"
"github.com/openimsdk/tools/apiresp" "github.com/openimsdk/tools/apiresp"
"github.com/openimsdk/tools/discovery" "github.com/openimsdk/tools/discovery"
"github.com/openimsdk/tools/discovery/etcd"
"github.com/openimsdk/tools/log" "github.com/openimsdk/tools/log"
"github.com/openimsdk/tools/mw" "github.com/openimsdk/tools/mw"
clientv3 "go.etcd.io/etcd/client/v3"
) )
const ( const (
@@ -52,32 +52,32 @@ func prommetricsGin() gin.HandlerFunc {
} }
} }
func newGinRouter(ctx context.Context, client discovery.SvcDiscoveryRegistry, cfg *Config) (*gin.Engine, error) { func newGinRouter(ctx context.Context, client discovery.SvcDiscoveryRegistry, config *Config) (*gin.Engine, error) {
authConn, err := client.GetConn(ctx, cfg.Discovery.RpcService.Auth) authConn, err := client.GetConn(ctx, config.Share.RpcRegisterName.Auth)
if err != nil { if err != nil {
return nil, err return nil, err
} }
userConn, err := client.GetConn(ctx, cfg.Discovery.RpcService.User) userConn, err := client.GetConn(ctx, config.Share.RpcRegisterName.User)
if err != nil { if err != nil {
return nil, err return nil, err
} }
groupConn, err := client.GetConn(ctx, cfg.Discovery.RpcService.Group) groupConn, err := client.GetConn(ctx, config.Share.RpcRegisterName.Group)
if err != nil { if err != nil {
return nil, err return nil, err
} }
friendConn, err := client.GetConn(ctx, cfg.Discovery.RpcService.Friend) friendConn, err := client.GetConn(ctx, config.Share.RpcRegisterName.Friend)
if err != nil { if err != nil {
return nil, err return nil, err
} }
conversationConn, err := client.GetConn(ctx, cfg.Discovery.RpcService.Conversation) conversationConn, err := client.GetConn(ctx, config.Share.RpcRegisterName.Conversation)
if err != nil { if err != nil {
return nil, err return nil, err
} }
thirdConn, err := client.GetConn(ctx, cfg.Discovery.RpcService.Third) thirdConn, err := client.GetConn(ctx, config.Share.RpcRegisterName.Third)
if err != nil { if err != nil {
return nil, err return nil, err
} }
msgConn, err := client.GetConn(ctx, cfg.Discovery.RpcService.Msg) msgConn, err := client.GetConn(ctx, config.Share.RpcRegisterName.Msg)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -86,7 +86,7 @@ func newGinRouter(ctx context.Context, client discovery.SvcDiscoveryRegistry, cf
if v, ok := binding.Validator.Engine().(*validator.Validate); ok { if v, ok := binding.Validator.Engine().(*validator.Validate); ok {
_ = v.RegisterValidation("required_if", RequiredIf) _ = v.RegisterValidation("required_if", RequiredIf)
} }
switch cfg.API.Api.CompressionLevel { switch config.API.Api.CompressionLevel {
case NoCompression: case NoCompression:
case DefaultCompression: case DefaultCompression:
r.Use(gzip.Gzip(gzip.DefaultCompression)) r.Use(gzip.Gzip(gzip.DefaultCompression))
@@ -95,12 +95,11 @@ func newGinRouter(ctx context.Context, client discovery.SvcDiscoveryRegistry, cf
case BestSpeed: case BestSpeed:
r.Use(gzip.Gzip(gzip.BestSpeed)) r.Use(gzip.Gzip(gzip.BestSpeed))
} }
r.Use(prommetricsGin(), gin.RecoveryWithWriter(gin.DefaultErrorWriter, mw.GinPanicErr), mw.CorsHandler(), r.Use(prommetricsGin(), gin.RecoveryWithWriter(gin.DefaultErrorWriter, mw.GinPanicErr), mw.CorsHandler(), mw.GinParseOperationID(), GinParseToken(rpcli.NewAuthClient(authConn)))
mw.GinParseOperationID(), GinParseToken(rpcli.NewAuthClient(authConn))) u := NewUserApi(user.NewUserClient(userConn), client, config.Share.RpcRegisterName)
m := NewMessageApi(msg.NewMsgClient(msgConn), rpcli.NewUserClient(userConn), config.Share.IMAdminUserID)
u := NewUserApi(user.NewUserClient(userConn), client, cfg.Discovery.RpcService) userRouterGroup := r.Group("/user")
{ {
userRouterGroup := r.Group("/user")
userRouterGroup.POST("/user_register", u.UserRegister) userRouterGroup.POST("/user_register", u.UserRegister)
userRouterGroup.POST("/update_user_info", u.UpdateUserInfo) userRouterGroup.POST("/update_user_info", u.UpdateUserInfo)
userRouterGroup.POST("/update_user_info_ex", u.UpdateUserInfoEx) userRouterGroup.POST("/update_user_info_ex", u.UpdateUserInfoEx)
@@ -150,6 +149,7 @@ func newGinRouter(ctx context.Context, client discovery.SvcDiscoveryRegistry, cf
friendRouterGroup.POST("/update_friends", f.UpdateFriends) friendRouterGroup.POST("/update_friends", f.UpdateFriends)
friendRouterGroup.POST("/get_incremental_friends", f.GetIncrementalFriends) friendRouterGroup.POST("/get_incremental_friends", f.GetIncrementalFriends)
friendRouterGroup.POST("/get_full_friend_user_ids", f.GetFullFriendUserIDs) friendRouterGroup.POST("/get_full_friend_user_ids", f.GetFullFriendUserIDs)
friendRouterGroup.POST("/get_self_unhandled_apply_count", f.GetSelfUnhandledApplyCount)
} }
g := NewGroupApi(group.NewGroupClient(groupConn)) g := NewGroupApi(group.NewGroupClient(groupConn))
@@ -186,6 +186,7 @@ func newGinRouter(ctx context.Context, client discovery.SvcDiscoveryRegistry, cf
groupRouterGroup.POST("/get_incremental_group_members_batch", g.GetIncrementalGroupMemberBatch) groupRouterGroup.POST("/get_incremental_group_members_batch", g.GetIncrementalGroupMemberBatch)
groupRouterGroup.POST("/get_full_group_member_user_ids", g.GetFullGroupMemberUserIDs) groupRouterGroup.POST("/get_full_group_member_user_ids", g.GetFullGroupMemberUserIDs)
groupRouterGroup.POST("/get_full_join_group_ids", g.GetFullJoinGroupIDs) groupRouterGroup.POST("/get_full_join_group_ids", g.GetFullJoinGroupIDs)
groupRouterGroup.POST("/get_group_application_unhandled_count", g.GetGroupApplicationUnhandledCount)
} }
// certificate // certificate
{ {
@@ -199,7 +200,7 @@ func newGinRouter(ctx context.Context, client discovery.SvcDiscoveryRegistry, cf
} }
// Third service // Third service
{ {
t := NewThirdApi(third.NewThirdClient(thirdConn), cfg.API.Prometheus.GrafanaURL) t := NewThirdApi(third.NewThirdClient(thirdConn), config.API.Prometheus.GrafanaURL)
thirdGroup := r.Group("/third") thirdGroup := r.Group("/third")
thirdGroup.GET("/prometheus", t.GetPrometheus) thirdGroup.GET("/prometheus", t.GetPrometheus)
thirdGroup.POST("/fcm_update_token", t.FcmUpdateToken) thirdGroup.POST("/fcm_update_token", t.FcmUpdateToken)
@@ -223,7 +224,6 @@ func newGinRouter(ctx context.Context, client discovery.SvcDiscoveryRegistry, cf
objectGroup.GET("/*name", t.ObjectRedirect) objectGroup.GET("/*name", t.ObjectRedirect)
} }
// Message // Message
m := NewMessageApi(msg.NewMsgClient(msgConn), rpcli.NewUserClient(userConn), cfg.Share.IMAdminUserID)
{ {
msgGroup := r.Group("/msg") msgGroup := r.Group("/msg")
msgGroup.POST("/newest_seq", m.GetSeq) msgGroup.POST("/newest_seq", m.GetSeq)
@@ -280,7 +280,7 @@ func newGinRouter(ctx context.Context, client discovery.SvcDiscoveryRegistry, cf
jssdk.POST("/get_active_conversations", j.GetActiveConversations) jssdk.POST("/get_active_conversations", j.GetActiveConversations)
} }
{ {
pd := NewPrometheusDiscoveryApi(cfg, client) pd := NewPrometheusDiscoveryApi(config, client)
proDiscoveryGroup := r.Group("/prometheus_discovery", pd.Enable) proDiscoveryGroup := r.Group("/prometheus_discovery", pd.Enable)
proDiscoveryGroup.GET("/api", pd.Api) proDiscoveryGroup.GET("/api", pd.Api)
proDiscoveryGroup.GET("/user", pd.User) proDiscoveryGroup.GET("/user", pd.User)
@@ -294,25 +294,6 @@ func newGinRouter(ctx context.Context, client discovery.SvcDiscoveryRegistry, cf
proDiscoveryGroup.GET("/msg_gateway", pd.MessageGateway) proDiscoveryGroup.GET("/msg_gateway", pd.MessageGateway)
proDiscoveryGroup.GET("/msg_transfer", pd.MessageTransfer) proDiscoveryGroup.GET("/msg_transfer", pd.MessageTransfer)
} }
var etcdClient *clientv3.Client
if cfg.Discovery.Enable == config.ETCD {
etcdClient = client.(*etcd.SvcDiscoveryRegistryImpl).GetClient()
}
cm := NewConfigManager(cfg.Share.IMAdminUserID, cfg.AllConfig, etcdClient, cfg.ConfigPath, cfg.RuntimeEnv)
{
configGroup := r.Group("/config", cm.CheckAdmin)
configGroup.POST("/get_config_list", cm.GetConfigList)
configGroup.POST("/get_config", cm.GetConfig)
configGroup.POST("/set_config", cm.SetConfig)
configGroup.POST("/reset_config", cm.ResetConfig)
configGroup.POST("/set_enable_config_manager", cm.SetEnableConfigManager)
configGroup.POST("/get_enable_config_manager", cm.GetEnableConfigManager)
}
{
r.POST("/restart", cm.CheckAdmin, cm.Restart)
}
return r, nil return r, nil
} }
+2 -2
View File
@@ -30,10 +30,10 @@ import (
type UserApi struct { type UserApi struct {
Client user.UserClient Client user.UserClient
discov discovery.SvcDiscoveryRegistry discov discovery.SvcDiscoveryRegistry
config config.RpcService config config.RpcRegisterName
} }
func NewUserApi(client user.UserClient, discov discovery.SvcDiscoveryRegistry, config config.RpcService) UserApi { func NewUserApi(client user.UserClient, discov discovery.SvcDiscoveryRegistry, config config.RpcRegisterName) UserApi {
return UserApi{Client: client, discov: discov, config: config} return UserApi{Client: client, discov: discov, config: config}
} }
+7 -13
View File
@@ -36,7 +36,7 @@ import (
) )
func (s *Server) InitServer(ctx context.Context, config *Config, disCov discovery.SvcDiscoveryRegistry, server *grpc.Server) error { func (s *Server) InitServer(ctx context.Context, config *Config, disCov discovery.SvcDiscoveryRegistry, server *grpc.Server) error {
userConn, err := disCov.GetConn(ctx, config.Discovery.RpcService.User) userConn, err := disCov.GetConn(ctx, config.Share.RpcRegisterName.User)
if err != nil { if err != nil {
return err return err
} }
@@ -54,19 +54,13 @@ func (s *Server) InitServer(ctx context.Context, config *Config, disCov discover
func (s *Server) Start(ctx context.Context, index int, conf *Config) error { func (s *Server) Start(ctx context.Context, index int, conf *Config) error {
return startrpc.Start(ctx, &conf.Discovery, &conf.MsgGateway.Prometheus, conf.MsgGateway.ListenIP, return startrpc.Start(ctx, &conf.Discovery, &conf.MsgGateway.Prometheus, conf.MsgGateway.ListenIP,
conf.MsgGateway.RPC.RegisterIP, conf.MsgGateway.RPC.RegisterIP,
conf.MsgGateway.RPC.AutoSetPorts, conf.MsgGateway.RPC.Ports, index, conf.MsgGateway.RPC.AutoSetPorts,
conf.Discovery.RpcService.MessageGateway, conf.MsgGateway.RPC.Ports, index,
nil, conf.Share.RpcRegisterName.MessageGateway,
&conf.Share,
conf, conf,
[]string{ []string{
conf.Share.GetConfigFileName(), conf.Share.RpcRegisterName.MessageGateway,
conf.Discovery.GetConfigFileName(),
conf.MsgGateway.GetConfigFileName(),
conf.WebhooksConfig.GetConfigFileName(),
conf.RedisConfig.GetConfigFileName(),
},
[]string{
conf.Discovery.RpcService.MessageGateway,
}, },
s.InitServer, s.InitServer,
) )
@@ -74,7 +68,7 @@ func (s *Server) Start(ctx context.Context, index int, conf *Config) error {
type Server struct { type Server struct {
msggateway.UnimplementedMsgGatewayServer msggateway.UnimplementedMsgGatewayServer
rpcPort int
LongConnServer LongConnServer LongConnServer LongConnServer
config *Config config *Config
pushTerminal map[int]struct{} pushTerminal map[int]struct{}
+24 -30
View File
@@ -2,7 +2,6 @@ package msggateway
import ( import (
"context" "context"
"errors"
"fmt" "fmt"
"net/http" "net/http"
"sync" "sync"
@@ -11,20 +10,18 @@ import (
"github.com/openimsdk/open-im-server/v3/pkg/rpcli" "github.com/openimsdk/open-im-server/v3/pkg/rpcli"
"github.com/openimsdk/open-im-server/v3/pkg/common/discovery/etcd"
"github.com/openimsdk/open-im-server/v3/pkg/common/webhook"
"github.com/openimsdk/open-im-server/v3/pkg/rpccache"
pbAuth "github.com/openimsdk/protocol/auth"
"github.com/openimsdk/tools/mcontext"
"github.com/go-playground/validator/v10" "github.com/go-playground/validator/v10"
"github.com/openimsdk/open-im-server/v3/pkg/common/prommetrics" "github.com/openimsdk/open-im-server/v3/pkg/common/prommetrics"
"github.com/openimsdk/open-im-server/v3/pkg/common/servererrs" "github.com/openimsdk/open-im-server/v3/pkg/common/servererrs"
"github.com/openimsdk/open-im-server/v3/pkg/common/webhook"
"github.com/openimsdk/open-im-server/v3/pkg/rpccache"
pbAuth "github.com/openimsdk/protocol/auth"
"github.com/openimsdk/protocol/constant" "github.com/openimsdk/protocol/constant"
"github.com/openimsdk/protocol/msggateway" "github.com/openimsdk/protocol/msggateway"
"github.com/openimsdk/tools/discovery" "github.com/openimsdk/tools/discovery"
"github.com/openimsdk/tools/errs" "github.com/openimsdk/tools/errs"
"github.com/openimsdk/tools/log" "github.com/openimsdk/tools/log"
"github.com/openimsdk/tools/mcontext"
"github.com/openimsdk/tools/utils/stringutil" "github.com/openimsdk/tools/utils/stringutil"
"golang.org/x/sync/errgroup" "golang.org/x/sync/errgroup"
) )
@@ -76,19 +73,19 @@ type kickHandler struct {
} }
func (ws *WsServer) SetDiscoveryRegistry(ctx context.Context, disCov discovery.SvcDiscoveryRegistry, config *Config) error { func (ws *WsServer) SetDiscoveryRegistry(ctx context.Context, disCov discovery.SvcDiscoveryRegistry, config *Config) error {
userConn, err := disCov.GetConn(ctx, config.Discovery.RpcService.User) userConn, err := disCov.GetConn(ctx, config.Share.RpcRegisterName.User)
if err != nil { if err != nil {
return err return err
} }
pushConn, err := disCov.GetConn(ctx, config.Discovery.RpcService.Push) pushConn, err := disCov.GetConn(ctx, config.Share.RpcRegisterName.Push)
if err != nil { if err != nil {
return err return err
} }
authConn, err := disCov.GetConn(ctx, config.Discovery.RpcService.Auth) authConn, err := disCov.GetConn(ctx, config.Share.RpcRegisterName.Auth)
if err != nil { if err != nil {
return err return err
} }
msgConn, err := disCov.GetConn(ctx, config.Discovery.RpcService.Msg) msgConn, err := disCov.GetConn(ctx, config.Share.RpcRegisterName.Msg)
if err != nil { if err != nil {
return err return err
} }
@@ -133,7 +130,7 @@ func NewWsServer(msgGatewayConfig *Config, opts ...Option) *WsServer {
for _, o := range opts { for _, o := range opts {
o(&config) o(&config)
} }
//userRpcClient := rpcclient.NewUserRpcClient(client, config.Discovery.RpcService.User, config.Share.IMAdminUserID) //userRpcClient := rpcclient.NewUserRpcClient(client, config.Share.RpcRegisterName.User, config.Share.IMAdminUserID)
v := validator.New() v := validator.New()
return &WsServer{ return &WsServer{
@@ -185,28 +182,21 @@ func (ws *WsServer) Run(done chan error) error {
go func() { go func() {
http.HandleFunc("/", ws.wsHandler) http.HandleFunc("/", ws.wsHandler)
err := server.ListenAndServe() err := server.ListenAndServe()
if err != nil && !errors.Is(err, http.ErrServerClosed) { defer close(netDone)
if err != nil && err != http.ErrServerClosed {
netErr = errs.WrapMsg(err, "ws start err", server.Addr) netErr = errs.WrapMsg(err, "ws start err", server.Addr)
netDone <- struct{}{}
} }
}() }()
ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second) ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
shutDown := func() error { defer cancel()
var err error
select {
case err = <-done:
sErr := server.Shutdown(ctx) sErr := server.Shutdown(ctx)
if sErr != nil { if sErr != nil {
return errs.WrapMsg(sErr, "shutdown err") return errs.WrapMsg(sErr, "shutdown err")
} }
close(shutdownDone) close(shutdownDone)
return nil
}
etcd.RegisterShutDown(shutDown)
defer cancel()
var err error
select {
case err = <-done:
if err := shutDown(); err != nil {
return err
}
if err != nil { if err != nil {
return err return err
} }
@@ -219,19 +209,23 @@ func (ws *WsServer) Run(done chan error) error {
var concurrentRequest = 3 var concurrentRequest = 3
func (ws *WsServer) sendUserOnlineInfoToOtherNode(ctx context.Context, client *Client) error { func (ws *WsServer) sendUserOnlineInfoToOtherNode(ctx context.Context, client *Client) error {
conns, err := ws.disCov.GetConns(ctx, ws.msgGatewayConfig.Discovery.RpcService.MessageGateway) conns, err := ws.disCov.GetConns(ctx, ws.msgGatewayConfig.Share.RpcRegisterName.MessageGateway)
if err != nil { if err != nil {
return err return err
} }
if len(conns) == 0 || (len(conns) == 1 && ws.disCov.IsSelfNode(conns[0])) {
return nil
}
wg := errgroup.Group{} wg := errgroup.Group{}
wg.SetLimit(concurrentRequest) wg.SetLimit(concurrentRequest)
// Online push user online message to other node // Online push user online message to other node
for _, v := range conns { for _, v := range conns {
v := v v := v
log.ZDebug(ctx, " sendUserOnlineInfoToOtherNode conn ", "target", v.Target()) log.ZDebug(ctx, "sendUserOnlineInfoToOtherNode conn")
if v.Target() == ws.disCov.GetSelfConnTarget() { if ws.disCov.IsSelfNode(v) {
log.ZDebug(ctx, "Filter out this node", "node", v.Target()) log.ZDebug(ctx, "Filter out this node")
continue continue
} }
@@ -242,7 +236,7 @@ func (ws *WsServer) sendUserOnlineInfoToOtherNode(ctx context.Context, client *C
PlatformID: int32(client.PlatformID), Token: client.token, PlatformID: int32(client.PlatformID), Token: client.token,
}) })
if err != nil { if err != nil {
log.ZWarn(ctx, "MultiTerminalLoginCheck err", err, "node", v.Target()) log.ZWarn(ctx, "MultiTerminalLoginCheck err", err)
} }
return nil return nil
}) })
+15 -31
View File
@@ -25,8 +25,6 @@ import (
"strconv" "strconv"
"syscall" "syscall"
disetcd "github.com/openimsdk/open-im-server/v3/pkg/common/discovery/etcd"
"github.com/openimsdk/tools/discovery"
"github.com/openimsdk/tools/discovery/etcd" "github.com/openimsdk/tools/discovery/etcd"
"github.com/openimsdk/tools/utils/jsonutil" "github.com/openimsdk/tools/utils/jsonutil"
"github.com/openimsdk/tools/utils/network" "github.com/openimsdk/tools/utils/network"
@@ -37,10 +35,10 @@ import (
"github.com/openimsdk/tools/db/mongoutil" "github.com/openimsdk/tools/db/mongoutil"
"github.com/openimsdk/tools/db/redisutil" "github.com/openimsdk/tools/db/redisutil"
"github.com/openimsdk/tools/utils/datautil" "github.com/openimsdk/tools/utils/datautil"
"github.com/openimsdk/tools/utils/runtimeenv"
conf "github.com/openimsdk/open-im-server/v3/pkg/common/config" conf "github.com/openimsdk/open-im-server/v3/pkg/common/config"
discRegister "github.com/openimsdk/open-im-server/v3/pkg/common/discovery" discRegister "github.com/openimsdk/open-im-server/v3/pkg/common/discoveryregister"
kdisc "github.com/openimsdk/open-im-server/v3/pkg/common/discoveryregister"
"github.com/openimsdk/open-im-server/v3/pkg/common/storage/controller" "github.com/openimsdk/open-im-server/v3/pkg/common/storage/controller"
"github.com/openimsdk/tools/errs" "github.com/openimsdk/tools/errs"
"github.com/openimsdk/tools/log" "github.com/openimsdk/tools/log"
@@ -59,8 +57,6 @@ type MsgTransfer struct {
historyMongoCH *OnlineHistoryMongoConsumerHandler historyMongoCH *OnlineHistoryMongoConsumerHandler
ctx context.Context ctx context.Context
cancel context.CancelFunc cancel context.CancelFunc
runTimeEnv string
} }
type Config struct { type Config struct {
@@ -74,9 +70,8 @@ type Config struct {
} }
func Start(ctx context.Context, index int, config *Config) error { func Start(ctx context.Context, index int, config *Config) error {
runTimeEnv := runtimeenv.PrintRuntimeEnvironment()
log.CInfo(ctx, "MSG-TRANSFER server is initializing", "runTimeEnv", runTimeEnv, "prometheusPorts", log.CInfo(ctx, "MSG-TRANSFER server is initializing", "prometheusPorts",
config.MsgTransfer.Prometheus.Ports, "index", index) config.MsgTransfer.Prometheus.Ports, "index", index)
mgocli, err := mongoutil.NewMongoDB(ctx, config.MongodbConfig.Build()) mgocli, err := mongoutil.NewMongoDB(ctx, config.MongodbConfig.Build())
@@ -87,27 +82,13 @@ func Start(ctx context.Context, index int, config *Config) error {
if err != nil { if err != nil {
return err return err
} }
client, err := discRegister.NewDiscoveryRegister(&config.Discovery, runTimeEnv, nil) client, err := discRegister.NewDiscoveryRegister(&config.Discovery, &config.Share, nil)
if err != nil { if err != nil {
return err return err
} }
client.AddOption(mw.GrpcClient(), grpc.WithTransportCredentials(insecure.NewCredentials()), client.AddOption(mw.GrpcClient(), grpc.WithTransportCredentials(insecure.NewCredentials()),
grpc.WithDefaultServiceConfig(fmt.Sprintf(`{"LoadBalancingPolicy": "%s"}`, "round_robin"))) grpc.WithDefaultServiceConfig(fmt.Sprintf(`{"LoadBalancingPolicy": "%s"}`, "round_robin")))
if config.Discovery.Enable == conf.ETCD {
cm := disetcd.NewConfigManager(client.(*etcd.SvcDiscoveryRegistryImpl).GetClient(), []string{
config.MsgTransfer.GetConfigFileName(),
config.RedisConfig.GetConfigFileName(),
config.MongodbConfig.GetConfigFileName(),
config.KafkaConfig.GetConfigFileName(),
config.Share.GetConfigFileName(),
config.WebhooksConfig.GetConfigFileName(),
config.Discovery.GetConfigFileName(),
conf.LogConfigFileName,
})
cm.Watch(ctx)
}
msgDocModel, err := mgo.NewMsgMongo(mgocli.GetDB()) msgDocModel, err := mgo.NewMsgMongo(mgocli.GetDB())
if err != nil { if err != nil {
return err return err
@@ -139,13 +120,11 @@ func Start(ctx context.Context, index int, config *Config) error {
msgTransfer := &MsgTransfer{ msgTransfer := &MsgTransfer{
historyCH: historyCH, historyCH: historyCH,
historyMongoCH: historyMongoCH, historyMongoCH: historyMongoCH,
runTimeEnv: runTimeEnv,
} }
return msgTransfer.Start(index, config)
return msgTransfer.Start(index, config, client)
} }
func (m *MsgTransfer) Start(index int, config *Config, client discovery.SvcDiscoveryRegistry) error { func (m *MsgTransfer) Start(index int, cfg *Config) error {
m.ctx, m.cancel = context.WithCancel(context.Background()) m.ctx, m.cancel = context.WithCancel(context.Background())
var ( var (
netDone = make(chan struct{}, 1) netDone = make(chan struct{}, 1)
@@ -160,6 +139,11 @@ func (m *MsgTransfer) Start(index int, config *Config, client discovery.SvcDisco
return err return err
} }
client, err := kdisc.NewDiscoveryRegister(&cfg.Discovery, &cfg.Share, nil)
if err != nil {
return errs.WrapMsg(err, "failed to register discovery service")
}
registerIP, err := network.GetRpcRegisterIP("") registerIP, err := network.GetRpcRegisterIP("")
if err != nil { if err != nil {
return err return err
@@ -176,17 +160,17 @@ func (m *MsgTransfer) Start(index int, config *Config, client discovery.SvcDisco
return listener, port, nil return listener, port, nil
} }
if config.MsgTransfer.Prometheus.AutoSetPorts && config.Discovery.Enable != conf.ETCD { if cfg.MsgTransfer.Prometheus.AutoSetPorts && cfg.Discovery.Enable != conf.ETCD {
return errs.New("only etcd support autoSetPorts", "RegisterName", "api").Wrap() return errs.New("only etcd support autoSetPorts", "RegisterName", "api").Wrap()
} }
if config.MsgTransfer.Prometheus.Enable { if cfg.MsgTransfer.Prometheus.Enable {
var ( var (
listener net.Listener listener net.Listener
prometheusPort int prometheusPort int
) )
if config.MsgTransfer.Prometheus.AutoSetPorts { if cfg.MsgTransfer.Prometheus.AutoSetPorts {
listener, prometheusPort, err = getAutoPort() listener, prometheusPort, err = getAutoPort()
if err != nil { if err != nil {
return err return err
@@ -199,7 +183,7 @@ func (m *MsgTransfer) Start(index int, config *Config, client discovery.SvcDisco
return errs.WrapMsg(err, "etcd put err") return errs.WrapMsg(err, "etcd put err")
} }
} else { } else {
prometheusPort, err = datautil.GetElemByIndex(config.MsgTransfer.Prometheus.Ports, index) prometheusPort, err = datautil.GetElemByIndex(cfg.MsgTransfer.Prometheus.Ports, index)
if err != nil { if err != nil {
return err return err
} }
@@ -18,17 +18,22 @@ import (
"context" "context"
"encoding/json" "encoding/json"
"errors" "errors"
"github.com/openimsdk/open-im-server/v3/pkg/rpcli"
"github.com/openimsdk/tools/discovery"
"strconv" "strconv"
"strings" "strings"
"sync" "sync"
"time" "time"
"github.com/IBM/sarama" "github.com/openimsdk/open-im-server/v3/pkg/rpcli"
"github.com/openimsdk/tools/discovery"
"github.com/go-redis/redis" "github.com/go-redis/redis"
"google.golang.org/protobuf/proto"
"github.com/openimsdk/open-im-server/v3/pkg/common/prommetrics" "github.com/openimsdk/open-im-server/v3/pkg/common/prommetrics"
"github.com/IBM/sarama"
"github.com/openimsdk/open-im-server/v3/pkg/common/storage/controller" "github.com/openimsdk/open-im-server/v3/pkg/common/storage/controller"
"github.com/openimsdk/open-im-server/v3/pkg/common/storage/kafka"
"github.com/openimsdk/open-im-server/v3/pkg/msgprocessor" "github.com/openimsdk/open-im-server/v3/pkg/msgprocessor"
"github.com/openimsdk/open-im-server/v3/pkg/tools/batcher" "github.com/openimsdk/open-im-server/v3/pkg/tools/batcher"
"github.com/openimsdk/protocol/constant" "github.com/openimsdk/protocol/constant"
@@ -37,9 +42,7 @@ import (
"github.com/openimsdk/tools/errs" "github.com/openimsdk/tools/errs"
"github.com/openimsdk/tools/log" "github.com/openimsdk/tools/log"
"github.com/openimsdk/tools/mcontext" "github.com/openimsdk/tools/mcontext"
"github.com/openimsdk/tools/mq/kafka"
"github.com/openimsdk/tools/utils/stringutil" "github.com/openimsdk/tools/utils/stringutil"
"google.golang.org/protobuf/proto"
) )
const ( const (
@@ -82,11 +85,11 @@ func NewOnlineHistoryRedisConsumerHandler(ctx context.Context, client discovery.
if err != nil { if err != nil {
return nil, err return nil, err
} }
groupConn, err := client.GetConn(ctx, config.Discovery.RpcService.Group) groupConn, err := client.GetConn(ctx, config.Share.RpcRegisterName.Group)
if err != nil { if err != nil {
return nil, err return nil, err
} }
conversationConn, err := client.GetConn(ctx, config.Discovery.RpcService.Conversation) conversationConn, err := client.GetConn(ctx, config.Share.RpcRegisterName.Conversation)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -138,53 +141,48 @@ func (och *OnlineHistoryRedisConsumerHandler) do(ctx context.Context, channelID
func (och *OnlineHistoryRedisConsumerHandler) doSetReadSeq(ctx context.Context, msgs []*ContextMsg) { func (och *OnlineHistoryRedisConsumerHandler) doSetReadSeq(ctx context.Context, msgs []*ContextMsg) {
var conversationID string // Outer map: conversationID -> (userID -> maxHasReadSeq)
var userSeqMap map[string]int64 conversationUserSeq := make(map[string]map[string]int64)
for _, msg := range msgs { for _, msg := range msgs {
if msg.message.ContentType != constant.HasReadReceipt { if msg.message.ContentType != constant.HasReadReceipt {
continue continue
} }
var elem sdkws.NotificationElem var elem sdkws.NotificationElem
if err := json.Unmarshal(msg.message.Content, &elem); err != nil { if err := json.Unmarshal(msg.message.Content, &elem); err != nil {
log.ZWarn(ctx, "handlerConversationRead Unmarshal NotificationElem msg err", err, "msg", msg) log.ZWarn(ctx, "Unmarshal NotificationElem error", err, "msg", msg)
continue continue
} }
var tips sdkws.MarkAsReadTips var tips sdkws.MarkAsReadTips
if err := json.Unmarshal([]byte(elem.Detail), &tips); err != nil { if err := json.Unmarshal([]byte(elem.Detail), &tips); err != nil {
log.ZWarn(ctx, "handlerConversationRead Unmarshal MarkAsReadTips msg err", err, "msg", msg) log.ZWarn(ctx, "Unmarshal MarkAsReadTips error", err, "msg", msg)
continue continue
} }
//The conversation ID for each batch of messages processed by the batcher is the same. if len(tips.ConversationID) == 0 || tips.HasReadSeq < 0 {
conversationID = tips.ConversationID
if len(tips.Seqs) > 0 {
for _, seq := range tips.Seqs {
if tips.HasReadSeq < seq {
tips.HasReadSeq = seq
}
}
clear(tips.Seqs)
tips.Seqs = nil
}
if tips.HasReadSeq < 0 {
continue continue
} }
if userSeqMap == nil {
userSeqMap = make(map[string]int64)
}
if userSeqMap[tips.MarkAsReadUserID] > tips.HasReadSeq { // Calculate the max seq from tips.Seqs
continue for _, seq := range tips.Seqs {
if tips.HasReadSeq < seq {
tips.HasReadSeq = seq
}
}
if _, ok := conversationUserSeq[tips.ConversationID]; !ok {
conversationUserSeq[tips.ConversationID] = make(map[string]int64)
}
if conversationUserSeq[tips.ConversationID][tips.MarkAsReadUserID] < tips.HasReadSeq {
conversationUserSeq[tips.ConversationID][tips.MarkAsReadUserID] = tips.HasReadSeq
} }
userSeqMap[tips.MarkAsReadUserID] = tips.HasReadSeq
} }
if userSeqMap == nil { log.ZInfo(ctx, "doSetReadSeq", "conversationUserSeq", conversationUserSeq)
return
} // persist to db
if len(conversationID) == 0 { for convID, userSeqMap := range conversationUserSeq {
log.ZWarn(ctx, "conversation err", nil, "conversationID", conversationID) if err := och.msgTransferDatabase.SetHasReadSeqToDB(ctx, convID, userSeqMap); err != nil {
} log.ZWarn(ctx, "SetHasReadSeqToDB error", err, "conversationID", convID, "userSeqMap", userSeqMap)
if err := och.msgTransferDatabase.SetHasReadSeqToDB(ctx, conversationID, userSeqMap); err != nil { }
log.ZWarn(ctx, "set read seq to db error", err, "conversationID", conversationID, "userSeqMap", userSeqMap)
} }
} }
@@ -288,6 +286,7 @@ func (och *OnlineHistoryRedisConsumerHandler) handleMsg(ctx context.Context, key
} }
if isNewConversation { if isNewConversation {
ctx := storageList[0].ctx
switch msg.SessionType { switch msg.SessionType {
case constant.ReadGroupChatType: case constant.ReadGroupChatType:
log.ZDebug(ctx, "group chat first create conversation", "conversationID", log.ZDebug(ctx, "group chat first create conversation", "conversationID",
@@ -21,9 +21,9 @@ import (
"github.com/openimsdk/open-im-server/v3/pkg/common/config" "github.com/openimsdk/open-im-server/v3/pkg/common/config"
"github.com/openimsdk/open-im-server/v3/pkg/common/prommetrics" "github.com/openimsdk/open-im-server/v3/pkg/common/prommetrics"
"github.com/openimsdk/open-im-server/v3/pkg/common/storage/controller" "github.com/openimsdk/open-im-server/v3/pkg/common/storage/controller"
"github.com/openimsdk/open-im-server/v3/pkg/common/storage/kafka"
pbmsg "github.com/openimsdk/protocol/msg" pbmsg "github.com/openimsdk/protocol/msg"
"github.com/openimsdk/tools/log" "github.com/openimsdk/tools/log"
"github.com/openimsdk/tools/mq/kafka"
"google.golang.org/protobuf/proto" "google.golang.org/protobuf/proto"
) )
@@ -73,10 +73,14 @@ func (mc *OnlineHistoryMongoConsumerHandler) handleChatWs2Mongo(ctx context.Cont
} else { } else {
prommetrics.MsgInsertMongoSuccessCounter.Inc() prommetrics.MsgInsertMongoSuccessCounter.Inc()
} }
var seqs []int64 //var seqs []int64
for _, msg := range msgFromMQ.MsgData { //for _, msg := range msgFromMQ.MsgData {
seqs = append(seqs, msg.Seq) // seqs = append(seqs, msg.Seq)
} //}
//if err := mc.msgTransferDatabase.DeleteMessagesFromCache(ctx, msgFromMQ.ConversationID, seqs); err != nil {
// log.ZError(ctx, "remove cache msg from redis err", err, "msg",
// msgFromMQ.MsgData, "conversationID", msgFromMQ.ConversationID)
//}
} }
func (*OnlineHistoryMongoConsumerHandler) Setup(_ sarama.ConsumerGroupSession) error { return nil } func (*OnlineHistoryMongoConsumerHandler) Setup(_ sarama.ConsumerGroupSession) error { return nil }
+1 -1
View File
@@ -7,12 +7,12 @@ import (
"github.com/openimsdk/open-im-server/v3/internal/push/offlinepush" "github.com/openimsdk/open-im-server/v3/internal/push/offlinepush"
"github.com/openimsdk/open-im-server/v3/internal/push/offlinepush/options" "github.com/openimsdk/open-im-server/v3/internal/push/offlinepush/options"
"github.com/openimsdk/open-im-server/v3/pkg/common/prommetrics" "github.com/openimsdk/open-im-server/v3/pkg/common/prommetrics"
"github.com/openimsdk/open-im-server/v3/pkg/common/storage/kafka"
"github.com/openimsdk/protocol/constant" "github.com/openimsdk/protocol/constant"
pbpush "github.com/openimsdk/protocol/push" pbpush "github.com/openimsdk/protocol/push"
"github.com/openimsdk/protocol/sdkws" "github.com/openimsdk/protocol/sdkws"
"github.com/openimsdk/tools/errs" "github.com/openimsdk/tools/errs"
"github.com/openimsdk/tools/log" "github.com/openimsdk/tools/log"
"github.com/openimsdk/tools/mq/kafka"
"github.com/openimsdk/tools/utils/jsonutil" "github.com/openimsdk/tools/utils/jsonutil"
"google.golang.org/protobuf/proto" "google.golang.org/protobuf/proto"
) )
+8 -13
View File
@@ -2,19 +2,15 @@ package push
import ( import (
"context" "context"
"errors"
"sync" "sync"
"github.com/openimsdk/protocol/msggateway" "github.com/openimsdk/protocol/msggateway"
"github.com/openimsdk/protocol/sdkws" "github.com/openimsdk/protocol/sdkws"
"github.com/openimsdk/tools/discovery" "github.com/openimsdk/tools/discovery"
"github.com/openimsdk/tools/errs"
"github.com/openimsdk/tools/log" "github.com/openimsdk/tools/log"
"github.com/openimsdk/tools/utils/datautil" "github.com/openimsdk/tools/utils/datautil"
"golang.org/x/sync/errgroup" "golang.org/x/sync/errgroup"
"google.golang.org/grpc" "google.golang.org/grpc"
conf "github.com/openimsdk/open-im-server/v3/pkg/common/config"
) )
type OnlinePusher interface { type OnlinePusher interface {
@@ -42,16 +38,15 @@ func (u emptyOnlinePusher) GetOnlinePushFailedUserIDs(ctx context.Context, msg *
} }
func NewOnlinePusher(disCov discovery.SvcDiscoveryRegistry, config *Config) OnlinePusher { func NewOnlinePusher(disCov discovery.SvcDiscoveryRegistry, config *Config) OnlinePusher {
if config.runTimeEnv == conf.KUBERNETES {
return NewDefaultAllNode(disCov, config)
}
switch config.Discovery.Enable { switch config.Discovery.Enable {
case conf.ETCD: case "k8s":
return NewK8sStaticConsistentHash(disCov, config)
case "zookeeper":
return NewDefaultAllNode(disCov, config)
case "etcd":
return NewDefaultAllNode(disCov, config) return NewDefaultAllNode(disCov, config)
default: default:
log.ZError(context.Background(), "NewOnlinePusher is error", errs.Wrap(errors.New("unsupported discovery type")), "type", config.Discovery.Enable) return newEmptyOnlinePusher()
return nil
} }
} }
@@ -66,7 +61,7 @@ func NewDefaultAllNode(disCov discovery.SvcDiscoveryRegistry, config *Config) *D
func (d *DefaultAllNode) GetConnsAndOnlinePush(ctx context.Context, msg *sdkws.MsgData, func (d *DefaultAllNode) GetConnsAndOnlinePush(ctx context.Context, msg *sdkws.MsgData,
pushToUserIDs []string) (wsResults []*msggateway.SingleMsgToUserResults, err error) { pushToUserIDs []string) (wsResults []*msggateway.SingleMsgToUserResults, err error) {
conns, err := d.disCov.GetConns(ctx, d.config.Discovery.RpcService.MessageGateway) conns, err := d.disCov.GetConns(ctx, d.config.Share.RpcRegisterName.MessageGateway)
if len(conns) == 0 { if len(conns) == 0 {
log.ZWarn(ctx, "get gateway conn 0 ", nil) log.ZWarn(ctx, "get gateway conn 0 ", nil)
} else { } else {
@@ -166,7 +161,7 @@ func (k *K8sStaticConsistentHash) GetConnsAndOnlinePush(ctx context.Context, msg
} }
} }
log.ZDebug(ctx, "genUsers send hosts struct:", "usersHost", usersHost) log.ZDebug(ctx, "genUsers send hosts struct:", "usersHost", usersHost)
var usersConns = make(map[*grpc.ClientConn][]string) var usersConns = make(map[grpc.ClientConnInterface][]string)
for host, userIds := range usersHost { for host, userIds := range usersHost {
tconn, _ := k.disCov.GetConn(ctx, host) tconn, _ := k.disCov.GetConn(ctx, host)
usersConns[tconn] = userIds usersConns[tconn] = userIds
+10 -6
View File
@@ -14,6 +14,7 @@ import (
"github.com/openimsdk/open-im-server/v3/internal/push/offlinepush/options" "github.com/openimsdk/open-im-server/v3/internal/push/offlinepush/options"
"github.com/openimsdk/open-im-server/v3/pkg/common/prommetrics" "github.com/openimsdk/open-im-server/v3/pkg/common/prommetrics"
"github.com/openimsdk/open-im-server/v3/pkg/common/storage/controller" "github.com/openimsdk/open-im-server/v3/pkg/common/storage/controller"
"github.com/openimsdk/open-im-server/v3/pkg/common/storage/kafka"
"github.com/openimsdk/open-im-server/v3/pkg/common/webhook" "github.com/openimsdk/open-im-server/v3/pkg/common/webhook"
"github.com/openimsdk/open-im-server/v3/pkg/msgprocessor" "github.com/openimsdk/open-im-server/v3/pkg/msgprocessor"
"github.com/openimsdk/open-im-server/v3/pkg/rpccache" "github.com/openimsdk/open-im-server/v3/pkg/rpccache"
@@ -25,7 +26,6 @@ import (
"github.com/openimsdk/tools/discovery" "github.com/openimsdk/tools/discovery"
"github.com/openimsdk/tools/log" "github.com/openimsdk/tools/log"
"github.com/openimsdk/tools/mcontext" "github.com/openimsdk/tools/mcontext"
"github.com/openimsdk/tools/mq/kafka"
"github.com/openimsdk/tools/utils/datautil" "github.com/openimsdk/tools/utils/datautil"
"github.com/openimsdk/tools/utils/jsonutil" "github.com/openimsdk/tools/utils/jsonutil"
"github.com/openimsdk/tools/utils/timeutil" "github.com/openimsdk/tools/utils/timeutil"
@@ -58,19 +58,19 @@ func NewConsumerHandler(ctx context.Context, config *Config, database controller
if err != nil { if err != nil {
return nil, err return nil, err
} }
userConn, err := client.GetConn(ctx, config.Discovery.RpcService.User) userConn, err := client.GetConn(ctx, config.Share.RpcRegisterName.User)
if err != nil { if err != nil {
return nil, err return nil, err
} }
groupConn, err := client.GetConn(ctx, config.Discovery.RpcService.Group) groupConn, err := client.GetConn(ctx, config.Share.RpcRegisterName.Group)
if err != nil { if err != nil {
return nil, err return nil, err
} }
msgConn, err := client.GetConn(ctx, config.Discovery.RpcService.Msg) msgConn, err := client.GetConn(ctx, config.Share.RpcRegisterName.Msg)
if err != nil { if err != nil {
return nil, err return nil, err
} }
conversationConn, err := client.GetConn(ctx, config.Discovery.RpcService.Conversation) conversationConn, err := client.GetConn(ctx, config.Share.RpcRegisterName.Conversation)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -142,6 +142,7 @@ func (c *ConsumerHandler) ConsumeClaim(sess sarama.ConsumerGroupSession, claim s
for msg := range claim.Messages() { for msg := range claim.Messages() {
ctx := c.pushConsumerGroup.GetContextFromMsg(msg) ctx := c.pushConsumerGroup.GetContextFromMsg(msg)
ctx = mcontext.WithOpUserIDContext(ctx, c.config.Share.IMAdminUserID[0])
c.handleMs2PsChat(ctx, msg.Value) c.handleMs2PsChat(ctx, msg.Value)
sess.MarkMessage(msg, "") sess.MarkMessage(msg, "")
} }
@@ -208,7 +209,10 @@ func (c *ConsumerHandler) shouldPushOffline(_ context.Context, msg *sdkws.MsgDat
if !isOfflinePush { if !isOfflinePush {
return false return false
} }
if msg.ContentType == constant.SignalingNotification { switch msg.ContentType {
case constant.RoomParticipantsConnectedNotification:
return false
case constant.RoomParticipantsDisconnectedNotification:
return false return false
} }
return true return true
+3 -3
View File
@@ -60,7 +60,7 @@ func Start(ctx context.Context, config *Config, client discovery.SvcDiscoveryReg
if err != nil { if err != nil {
return err return err
} }
userConn, err := client.GetConn(ctx, config.Discovery.RpcService.User) userConn, err := client.GetConn(ctx, config.Share.RpcRegisterName.User)
if err != nil { if err != nil {
return err return err
} }
@@ -187,12 +187,12 @@ func (s *authServer) ForceLogout(ctx context.Context, req *pbauth.ForceLogoutReq
} }
func (s *authServer) forceKickOff(ctx context.Context, userID string, platformID int32) error { func (s *authServer) forceKickOff(ctx context.Context, userID string, platformID int32) error {
conns, err := s.RegisterCenter.GetConns(ctx, s.config.Discovery.RpcService.MessageGateway) conns, err := s.RegisterCenter.GetConns(ctx, s.config.Share.RpcRegisterName.MessageGateway)
if err != nil { if err != nil {
return err return err
} }
for _, v := range conns { for _, v := range conns {
log.ZDebug(ctx, "forceKickOff", "conn", v.Target()) log.ZDebug(ctx, "forceKickOff", "userID", userID, "platformID", platformID)
client := msggateway.NewMsgGatewayClient(v) client := msggateway.NewMsgGatewayClient(v)
kickReq := &msggateway.KickUserOfflineReq{KickUserIDList: []string{userID}, PlatformID: platformID} kickReq := &msggateway.KickUserOfflineReq{KickUserIDList: []string{userID}, PlatformID: platformID}
_, err := client.KickUserOffline(ctx, kickReq) _, err := client.KickUserOffline(ctx, kickReq)
+4 -6
View File
@@ -79,15 +79,15 @@ func Start(ctx context.Context, config *Config, client discovery.SvcDiscoveryReg
if err != nil { if err != nil {
return err return err
} }
userConn, err := client.GetConn(ctx, config.Discovery.RpcService.User) userConn, err := client.GetConn(ctx, config.Share.RpcRegisterName.User)
if err != nil { if err != nil {
return err return err
} }
groupConn, err := client.GetConn(ctx, config.Discovery.RpcService.Group) groupConn, err := client.GetConn(ctx, config.Share.RpcRegisterName.Group)
if err != nil { if err != nil {
return err return err
} }
msgConn, err := client.GetConn(ctx, config.Discovery.RpcService.Msg) msgConn, err := client.GetConn(ctx, config.Share.RpcRegisterName.Msg)
if err != nil { if err != nil {
return err return err
} }
@@ -238,14 +238,12 @@ func (c *conversationServer) SetConversations(ctx context.Context, req *pbconver
if req.Conversation == nil { if req.Conversation == nil {
return nil, errs.ErrArgs.WrapMsg("conversation must not be nil") return nil, errs.ErrArgs.WrapMsg("conversation must not be nil")
} }
if req.Conversation.ConversationType == constant.WriteGroupChatType { if req.Conversation.ConversationType == constant.WriteGroupChatType {
groupInfo, err := c.groupClient.GetGroupInfo(ctx, req.Conversation.GroupID) groupInfo, err := c.groupClient.GetGroupInfo(ctx, req.Conversation.GroupID)
if err != nil { if err != nil {
return nil, err return nil, err
} }
if groupInfo == nil {
return nil, servererrs.ErrGroupIDNotFound.WrapMsg(req.Conversation.GroupID)
}
if groupInfo.Status == constant.GroupStatusDismissed { if groupInfo.Status == constant.GroupStatusDismissed {
return nil, servererrs.ErrDismissedAlready.WrapMsg("group dismissed") return nil, servererrs.ErrDismissedAlready.WrapMsg("group dismissed")
} }
+4 -3
View File
@@ -16,21 +16,22 @@ package conversation
import ( import (
"context" "context"
"github.com/openimsdk/open-im-server/v3/pkg/notification"
"github.com/openimsdk/open-im-server/v3/pkg/rpcli" "github.com/openimsdk/open-im-server/v3/pkg/rpcli"
"github.com/openimsdk/protocol/msg" "github.com/openimsdk/protocol/msg"
"github.com/openimsdk/open-im-server/v3/pkg/common/config" "github.com/openimsdk/open-im-server/v3/pkg/common/config"
"github.com/openimsdk/open-im-server/v3/pkg/notification"
"github.com/openimsdk/protocol/constant" "github.com/openimsdk/protocol/constant"
"github.com/openimsdk/protocol/sdkws" "github.com/openimsdk/protocol/sdkws"
) )
type ConversationNotificationSender struct { type ConversationNotificationSender struct {
*rpcclient.NotificationSender *notification.NotificationSender
} }
func NewConversationNotificationSender(conf *config.Notification, msgClient *rpcli.MsgClient) *ConversationNotificationSender { func NewConversationNotificationSender(conf *config.Notification, msgClient *rpcli.MsgClient) *ConversationNotificationSender {
return &ConversationNotificationSender{rpcclient.NewNotificationSender(conf, rpcclient.WithRpcClient(func(ctx context.Context, req *msg.SendMsgReq) (*msg.SendMsgResp, error) { return &ConversationNotificationSender{notification.NewNotificationSender(conf, notification.WithRpcClient(func(ctx context.Context, req *msg.SendMsgReq) (*msg.SendMsgResp, error) {
return msgClient.SendMsg(ctx, req) return msgClient.SendMsg(ctx, req)
}))} }))}
} }
+3
View File
@@ -33,6 +33,9 @@ func (s *groupServer) GetGroupInfoCache(ctx context.Context, req *pbgroup.GetGro
} }
func (s *groupServer) GetGroupMemberCache(ctx context.Context, req *pbgroup.GetGroupMemberCacheReq) (*pbgroup.GetGroupMemberCacheResp, error) { func (s *groupServer) GetGroupMemberCache(ctx context.Context, req *pbgroup.GetGroupMemberCacheReq) (*pbgroup.GetGroupMemberCacheResp, error) {
if err := s.checkAdminOrInGroup(ctx, req.GroupID); err != nil {
return nil, err
}
members, err := s.db.TakeGroupMember(ctx, req.GroupID, req.GroupMemberID) members, err := s.db.TakeGroupMember(ctx, req.GroupID, req.GroupMemberID)
if err != nil { if err != nil {
return nil, err return nil, err
+18 -6
View File
@@ -16,6 +16,7 @@ package group
import ( import (
"context" "context"
"strings"
"time" "time"
pbgroup "github.com/openimsdk/protocol/group" pbgroup "github.com/openimsdk/protocol/group"
@@ -55,41 +56,52 @@ func UpdateGroupInfoMap(ctx context.Context, group *sdkws.GroupInfoForSet) map[s
return m return m
} }
func UpdateGroupInfoExMap(ctx context.Context, group *pbgroup.SetGroupInfoExReq) (map[string]any, error) { func UpdateGroupInfoExMap(ctx context.Context, group *pbgroup.SetGroupInfoExReq) (m map[string]any, normalFlag, groupNameFlag, notificationFlag bool, err error) {
m := make(map[string]any) m = make(map[string]any)
if group.GroupName != nil { if group.GroupName != nil {
if group.GroupName.Value != "" { if strings.TrimSpace(group.GroupName.Value) != "" {
m["group_name"] = group.GroupName.Value m["group_name"] = group.GroupName.Value
groupNameFlag = true
} else { } else {
return nil, errs.ErrArgs.WrapMsg("group name is empty") return nil, normalFlag, notificationFlag, groupNameFlag, errs.ErrArgs.WrapMsg("group name is empty")
} }
} }
if group.Notification != nil { if group.Notification != nil {
notificationFlag = true
group.Notification.Value = strings.TrimSpace(group.Notification.Value) // if Notification only contains spaces, set it to empty string
m["notification"] = group.Notification.Value m["notification"] = group.Notification.Value
m["notification_update_time"] = time.Now()
m["notification_user_id"] = mcontext.GetOpUserID(ctx) m["notification_user_id"] = mcontext.GetOpUserID(ctx)
m["notification_update_time"] = time.Now()
} }
if group.Introduction != nil { if group.Introduction != nil {
m["introduction"] = group.Introduction.Value m["introduction"] = group.Introduction.Value
normalFlag = true
} }
if group.FaceURL != nil { if group.FaceURL != nil {
m["face_url"] = group.FaceURL.Value m["face_url"] = group.FaceURL.Value
normalFlag = true
} }
if group.NeedVerification != nil { if group.NeedVerification != nil {
m["need_verification"] = group.NeedVerification.Value m["need_verification"] = group.NeedVerification.Value
normalFlag = true
} }
if group.LookMemberInfo != nil { if group.LookMemberInfo != nil {
m["look_member_info"] = group.LookMemberInfo.Value m["look_member_info"] = group.LookMemberInfo.Value
normalFlag = true
} }
if group.ApplyMemberFriend != nil { if group.ApplyMemberFriend != nil {
m["apply_member_friend"] = group.ApplyMemberFriend.Value m["apply_member_friend"] = group.ApplyMemberFriend.Value
normalFlag = true
} }
if group.Ex != nil { if group.Ex != nil {
m["ex"] = group.Ex.Value m["ex"] = group.Ex.Value
normalFlag = true
} }
return m, nil return m, normalFlag, groupNameFlag, notificationFlag, nil
} }
func UpdateGroupStatusMap(status int) map[string]any { func UpdateGroupStatusMap(status int) map[string]any {
+159 -78
View File
@@ -17,28 +17,30 @@ package group
import ( import (
"context" "context"
"fmt" "fmt"
"github.com/openimsdk/open-im-server/v3/pkg/rpcli"
"math/big" "math/big"
"math/rand" "math/rand"
"strconv" "strconv"
"strings" "strings"
"time" "time"
"github.com/openimsdk/open-im-server/v3/pkg/authverify" "github.com/openimsdk/open-im-server/v3/pkg/rpcli"
"github.com/openimsdk/open-im-server/v3/pkg/callbackstruct"
"github.com/openimsdk/open-im-server/v3/pkg/common/config" "github.com/openimsdk/open-im-server/v3/pkg/common/config"
"github.com/openimsdk/open-im-server/v3/pkg/common/convert"
"github.com/openimsdk/open-im-server/v3/pkg/common/servererrs"
"github.com/openimsdk/open-im-server/v3/pkg/common/storage/common" "github.com/openimsdk/open-im-server/v3/pkg/common/storage/common"
"github.com/openimsdk/open-im-server/v3/pkg/common/storage/controller"
"github.com/openimsdk/open-im-server/v3/pkg/common/storage/database/mgo" "github.com/openimsdk/open-im-server/v3/pkg/common/storage/database/mgo"
"github.com/openimsdk/open-im-server/v3/pkg/common/storage/model" "github.com/openimsdk/open-im-server/v3/pkg/common/storage/model"
"github.com/openimsdk/open-im-server/v3/pkg/common/webhook" "github.com/openimsdk/open-im-server/v3/pkg/common/webhook"
"github.com/openimsdk/open-im-server/v3/pkg/localcache" "github.com/openimsdk/open-im-server/v3/pkg/localcache"
"github.com/openimsdk/open-im-server/v3/pkg/authverify"
"github.com/openimsdk/open-im-server/v3/pkg/callbackstruct"
"github.com/openimsdk/open-im-server/v3/pkg/common/convert"
"github.com/openimsdk/open-im-server/v3/pkg/common/servererrs"
"github.com/openimsdk/open-im-server/v3/pkg/common/storage/controller"
"github.com/openimsdk/open-im-server/v3/pkg/msgprocessor" "github.com/openimsdk/open-im-server/v3/pkg/msgprocessor"
"github.com/openimsdk/open-im-server/v3/pkg/notification/grouphash" "github.com/openimsdk/open-im-server/v3/pkg/notification/grouphash"
"github.com/openimsdk/protocol/constant" "github.com/openimsdk/protocol/constant"
pbconv "github.com/openimsdk/protocol/conversation" pbconversation "github.com/openimsdk/protocol/conversation"
pbgroup "github.com/openimsdk/protocol/group" pbgroup "github.com/openimsdk/protocol/group"
"github.com/openimsdk/protocol/sdkws" "github.com/openimsdk/protocol/sdkws"
"github.com/openimsdk/protocol/wrapperspb" "github.com/openimsdk/protocol/wrapperspb"
@@ -102,15 +104,15 @@ func Start(ctx context.Context, config *Config, client discovery.SvcDiscoveryReg
//msgRpcClient := rpcclient.NewMessageRpcClient(client, config.Share.RpcRegisterName.Msg) //msgRpcClient := rpcclient.NewMessageRpcClient(client, config.Share.RpcRegisterName.Msg)
//conversationRpcClient := rpcclient.NewConversationRpcClient(client, config.Share.RpcRegisterName.Conversation) //conversationRpcClient := rpcclient.NewConversationRpcClient(client, config.Share.RpcRegisterName.Conversation)
userConn, err := client.GetConn(ctx, config.Discovery.RpcService.User) userConn, err := client.GetConn(ctx, config.Share.RpcRegisterName.User)
if err != nil { if err != nil {
return err return err
} }
msgConn, err := client.GetConn(ctx, config.Discovery.RpcService.Msg) msgConn, err := client.GetConn(ctx, config.Share.RpcRegisterName.Msg)
if err != nil { if err != nil {
return err return err
} }
conversationConn, err := client.GetConn(ctx, config.Discovery.RpcService.Conversation) conversationConn, err := client.GetConn(ctx, config.Share.RpcRegisterName.Conversation)
if err != nil { if err != nil {
return err return err
} }
@@ -288,13 +290,14 @@ func (g *groupServer) CreateGroup(ctx context.Context, req *pbgroup.CreateGroupR
break break
} }
} }
g.notification.GroupCreatedNotification(ctx, tips) g.notification.GroupCreatedNotification(ctx, tips, req.SendMessage)
if req.GroupInfo.Notification != "" { if req.GroupInfo.Notification != "" {
notificationFlag := true
g.notification.GroupInfoSetAnnouncementNotification(ctx, &sdkws.GroupInfoSetAnnouncementTips{ g.notification.GroupInfoSetAnnouncementNotification(ctx, &sdkws.GroupInfoSetAnnouncementTips{
Group: tips.Group, Group: tips.Group,
OpUser: tips.OpUser, OpUser: tips.OpUser,
}) }, &notificationFlag)
} }
reqCallBackAfter := &pbgroup.CreateGroupReq{ reqCallBackAfter := &pbgroup.CreateGroupReq{
@@ -392,6 +395,8 @@ func (g *groupServer) InviteUserToGroup(ctx context.Context, req *pbgroup.Invite
if err := g.PopulateGroupMember(ctx, groupMember); err != nil { if err := g.PopulateGroupMember(ctx, groupMember); err != nil {
return nil, err return nil, err
} }
} else {
opUserID = mcontext.GetOpUserID(ctx)
} }
if err := g.webhookBeforeInviteUserToGroup(ctx, &g.config.WebhooksConfig.BeforeInviteUserToGroup, req); err != nil && err != servererrs.ErrCallbackContinue { if err := g.webhookBeforeInviteUserToGroup(ctx, &g.config.WebhooksConfig.BeforeInviteUserToGroup, req); err != nil && err != servererrs.ErrCallbackContinue {
@@ -421,12 +426,13 @@ func (g *groupServer) InviteUserToGroup(ctx context.Context, req *pbgroup.Invite
ReqMessage: request.ReqMsg, ReqMessage: request.ReqMsg,
JoinSource: request.JoinSource, JoinSource: request.JoinSource,
InviterUserID: request.InviterUserID, InviterUserID: request.InviterUserID,
}) }, request)
} }
return &pbgroup.InviteUserToGroupResp{}, nil return &pbgroup.InviteUserToGroupResp{}, nil
} }
} }
} }
var groupMembers []*model.GroupMember var groupMembers []*model.GroupMember
for _, userID := range req.InvitedUserIDs { for _, userID := range req.InvitedUserIDs {
member := &model.GroupMember{ member := &model.GroupMember{
@@ -447,12 +453,25 @@ func (g *groupServer) InviteUserToGroup(ctx context.Context, req *pbgroup.Invite
return nil, err return nil, err
} }
if err := g.db.CreateGroup(ctx, nil, groupMembers); err != nil { const singleQuantity = 50
return nil, err for start := 0; start < len(groupMembers); start += singleQuantity {
} end := start + singleQuantity
if end > len(groupMembers) {
end = len(groupMembers)
}
currentMembers := groupMembers[start:end]
if err = g.notification.GroupApplicationAgreeMemberEnterNotification(ctx, req.GroupID, opUserID, req.InvitedUserIDs...); err != nil { if err := g.db.CreateGroup(ctx, nil, currentMembers); err != nil {
return nil, err return nil, err
}
userIDs := datautil.Slice(currentMembers, func(e *model.GroupMember) string {
return e.UserID
})
if err = g.notification.GroupApplicationAgreeMemberEnterNotification(ctx, req.GroupID, req.SendMessage, opUserID, userIDs...); err != nil {
return nil, err
}
} }
return &pbgroup.InviteUserToGroupResp{}, nil return &pbgroup.InviteUserToGroupResp{}, nil
} }
@@ -472,7 +491,25 @@ func (g *groupServer) GetGroupAllMember(ctx context.Context, req *pbgroup.GetGro
return &resp, nil return &resp, nil
} }
func (g *groupServer) checkAdminOrInGroup(ctx context.Context, groupID string) error {
if authverify.IsAppManagerUid(ctx, g.config.Share.IMAdminUserID) {
return nil
}
opUserID := mcontext.GetOpUserID(ctx)
members, err := g.db.FindGroupMembers(ctx, groupID, []string{opUserID})
if err != nil {
return err
}
if len(members) == 0 {
return errs.ErrNoPermission.WrapMsg("op user not in group")
}
return nil
}
func (g *groupServer) GetGroupMemberList(ctx context.Context, req *pbgroup.GetGroupMemberListReq) (*pbgroup.GetGroupMemberListResp, error) { func (g *groupServer) GetGroupMemberList(ctx context.Context, req *pbgroup.GetGroupMemberListReq) (*pbgroup.GetGroupMemberListResp, error) {
if err := g.checkAdminOrInGroup(ctx, req.GroupID); err != nil {
return nil, err
}
var ( var (
total int64 total int64
members []*model.GroupMember members []*model.GroupMember
@@ -481,7 +518,7 @@ func (g *groupServer) GetGroupMemberList(ctx context.Context, req *pbgroup.GetGr
if req.Keyword == "" { if req.Keyword == "" {
total, members, err = g.db.PageGetGroupMember(ctx, req.GroupID, req.Pagination) total, members, err = g.db.PageGetGroupMember(ctx, req.GroupID, req.Pagination)
} else { } else {
members, err = g.db.FindGroupMemberAll(ctx, req.GroupID) total, members, err = g.db.SearchGroupMember(ctx, req.GroupID, req.Keyword, req.Pagination)
} }
if err != nil { if err != nil {
return nil, err return nil, err
@@ -489,27 +526,6 @@ func (g *groupServer) GetGroupMemberList(ctx context.Context, req *pbgroup.GetGr
if err := g.PopulateGroupMember(ctx, members...); err != nil { if err := g.PopulateGroupMember(ctx, members...); err != nil {
return nil, err return nil, err
} }
if req.Keyword != "" {
groupMembers := make([]*model.GroupMember, 0)
for _, member := range members {
if member.UserID == req.Keyword {
groupMembers = append(groupMembers, member)
total++
continue
}
if member.Nickname == req.Keyword {
groupMembers = append(groupMembers, member)
total++
continue
}
}
members := datautil.Paginate(groupMembers, int(req.Pagination.GetPageNumber()), int(req.Pagination.GetShowNumber()))
return &pbgroup.GetGroupMemberListResp{
Total: uint32(total),
Members: datautil.Batch(convert.Db2PbGroupMember, members),
}, nil
}
return &pbgroup.GetGroupMemberListResp{ return &pbgroup.GetGroupMemberListResp{
Total: uint32(total), Total: uint32(total),
Members: datautil.Batch(convert.Db2PbGroupMember, members), Members: datautil.Batch(convert.Db2PbGroupMember, members),
@@ -617,7 +633,7 @@ func (g *groupServer) KickGroupMember(ctx context.Context, req *pbgroup.KickGrou
for _, userID := range req.KickedUserIDs { for _, userID := range req.KickedUserIDs {
tips.KickedUserList = append(tips.KickedUserList, convert.Db2PbGroupMember(memberMap[userID])) tips.KickedUserList = append(tips.KickedUserList, convert.Db2PbGroupMember(memberMap[userID]))
} }
g.notification.MemberKickedNotification(ctx, tips) g.notification.MemberKickedNotification(ctx, tips, req.SendMessage)
if err := g.deleteMemberAndSetConversationSeq(ctx, req.GroupID, req.KickedUserIDs); err != nil { if err := g.deleteMemberAndSetConversationSeq(ctx, req.GroupID, req.KickedUserIDs); err != nil {
return nil, err return nil, err
} }
@@ -633,6 +649,9 @@ func (g *groupServer) GetGroupMembersInfo(ctx context.Context, req *pbgroup.GetG
if req.GroupID == "" { if req.GroupID == "" {
return nil, errs.ErrArgs.WrapMsg("groupID empty") return nil, errs.ErrArgs.WrapMsg("groupID empty")
} }
if err := g.checkAdminOrInGroup(ctx, req.GroupID); err != nil {
return nil, err
}
members, err := g.getGroupMembersInfo(ctx, req.GroupID, req.UserIDs) members, err := g.getGroupMembersInfo(ctx, req.GroupID, req.UserIDs)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -660,15 +679,34 @@ func (g *groupServer) getGroupMembersInfo(ctx context.Context, groupID string, u
// GetGroupApplicationList handles functions that get a list of group requests. // GetGroupApplicationList handles functions that get a list of group requests.
func (g *groupServer) GetGroupApplicationList(ctx context.Context, req *pbgroup.GetGroupApplicationListReq) (*pbgroup.GetGroupApplicationListResp, error) { func (g *groupServer) GetGroupApplicationList(ctx context.Context, req *pbgroup.GetGroupApplicationListReq) (*pbgroup.GetGroupApplicationListResp, error) {
groupIDs, err := g.db.FindUserManagedGroupID(ctx, req.FromUserID) var (
if err != nil { groupIDs []string
return nil, err err error
)
if len(req.GroupIDs) == 0 {
groupIDs, err = g.db.FindUserManagedGroupID(ctx, req.FromUserID)
if err != nil {
return nil, err
}
} else {
req.GroupIDs = datautil.Distinct(req.GroupIDs)
if !authverify.IsAppManagerUid(ctx, g.config.Share.IMAdminUserID) {
for _, groupID := range req.GroupIDs {
if err := g.CheckGroupAdmin(ctx, groupID); err != nil {
return nil, err
}
}
}
groupIDs = req.GroupIDs
} }
resp := &pbgroup.GetGroupApplicationListResp{} resp := &pbgroup.GetGroupApplicationListResp{}
if len(groupIDs) == 0 { if len(groupIDs) == 0 {
return resp, nil return resp, nil
} }
total, groupRequests, err := g.db.PageGroupRequest(ctx, groupIDs, req.Pagination) handleResults := datautil.Slice(req.HandleResults, func(e int32) int {
return int(e)
})
total, groupRequests, err := g.db.PageGroupRequest(ctx, groupIDs, handleResults, req.Pagination)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -733,6 +771,23 @@ func (g *groupServer) GetGroupsInfo(ctx context.Context, req *pbgroup.GetGroupsI
}, nil }, nil
} }
func (g *groupServer) GetGroupApplicationUnhandledCount(ctx context.Context, req *pbgroup.GetGroupApplicationUnhandledCountReq) (*pbgroup.GetGroupApplicationUnhandledCountResp, error) {
if err := authverify.CheckAccessV3(ctx, req.UserID, g.config.Share.IMAdminUserID); err != nil {
return nil, err
}
groupIDs, err := g.db.FindUserManagedGroupID(ctx, req.UserID)
if err != nil {
return nil, err
}
count, err := g.db.GetGroupApplicationUnhandledCount(ctx, groupIDs, req.Time)
if err != nil {
return nil, err
}
return &pbgroup.GetGroupApplicationUnhandledCountResp{
Count: count,
}, nil
}
func (g *groupServer) getGroupsInfo(ctx context.Context, groupIDs []string) ([]*sdkws.GroupInfo, error) { func (g *groupServer) getGroupsInfo(ctx context.Context, groupIDs []string) ([]*sdkws.GroupInfo, error) {
if len(groupIDs) == 0 { if len(groupIDs) == 0 {
return nil, nil return nil, nil
@@ -826,8 +881,14 @@ func (g *groupServer) GroupApplicationResponse(ctx context.Context, req *pbgroup
if member == nil { if member == nil {
log.ZDebug(ctx, "GroupApplicationResponse", "member is nil") log.ZDebug(ctx, "GroupApplicationResponse", "member is nil")
} else { } else {
if err = g.notification.GroupApplicationAgreeMemberEnterNotification(ctx, req.GroupID, groupRequest.InviterUserID, req.FromUserID); err != nil { if groupRequest.InviterUserID == "" {
return nil, err if err = g.notification.MemberEnterNotification(ctx, req.GroupID, req.FromUserID); err != nil {
return nil, err
}
} else {
if err = g.notification.GroupApplicationAgreeMemberEnterNotification(ctx, req.GroupID, nil, groupRequest.InviterUserID, req.FromUserID); err != nil {
return nil, err
}
} }
} }
case constant.GroupResponseRefuse: case constant.GroupResponseRefuse:
@@ -908,7 +969,7 @@ func (g *groupServer) JoinGroup(ctx context.Context, req *pbgroup.JoinGroupReq)
if err = g.db.CreateGroupRequest(ctx, []*model.GroupRequest{&groupRequest}); err != nil { if err = g.db.CreateGroupRequest(ctx, []*model.GroupRequest{&groupRequest}); err != nil {
return nil, err return nil, err
} }
g.notification.JoinGroupApplicationNotification(ctx, req) g.notification.JoinGroupApplicationNotification(ctx, req, &groupRequest)
return &pbgroup.JoinGroupResp{}, nil return &pbgroup.JoinGroupResp{}, nil
} }
@@ -1014,7 +1075,7 @@ func (g *groupServer) SetGroupInfo(ctx context.Context, req *pbgroup.SetGroupInf
if req.GroupInfoForSet.Notification != "" { if req.GroupInfoForSet.Notification != "" {
num -= 3 num -= 3
func() { func() {
conversation := &pbconv.ConversationReq{ conversation := &pbconversation.ConversationReq{
ConversationID: msgprocessor.GetConversationIDBySessionType(constant.ReadGroupChatType, req.GroupInfoForSet.GroupID), ConversationID: msgprocessor.GetConversationIDBySessionType(constant.ReadGroupChatType, req.GroupInfoForSet.GroupID),
ConversationType: constant.ReadGroupChatType, ConversationType: constant.ReadGroupChatType,
GroupID: req.GroupInfoForSet.GroupID, GroupID: req.GroupInfoForSet.GroupID,
@@ -1029,7 +1090,8 @@ func (g *groupServer) SetGroupInfo(ctx context.Context, req *pbgroup.SetGroupInf
log.ZWarn(ctx, "SetConversations", err, "UserIDs", resp.UserIDs, "conversation", conversation) log.ZWarn(ctx, "SetConversations", err, "UserIDs", resp.UserIDs, "conversation", conversation)
} }
}() }()
g.notification.GroupInfoSetAnnouncementNotification(ctx, &sdkws.GroupInfoSetAnnouncementTips{Group: tips.Group, OpUser: tips.OpUser}) notficationFlag := true
g.notification.GroupInfoSetAnnouncementNotification(ctx, &sdkws.GroupInfoSetAnnouncementTips{Group: tips.Group, OpUser: tips.OpUser}, &notficationFlag)
} }
if req.GroupInfoForSet.GroupName != "" { if req.GroupInfoForSet.GroupName != "" {
num-- num--
@@ -1090,7 +1152,7 @@ func (g *groupServer) SetGroupInfoEx(ctx context.Context, req *pbgroup.SetGroupI
return nil, err return nil, err
} }
updatedData, err := UpdateGroupInfoExMap(ctx, req) updatedData, normalFlag, groupNameFlag, notificationFlag, err := UpdateGroupInfoExMap(ctx, req)
if len(updatedData) == 0 { if len(updatedData) == 0 {
return &pbgroup.SetGroupInfoExResp{}, nil return &pbgroup.SetGroupInfoExResp{}, nil
} }
@@ -1118,41 +1180,38 @@ func (g *groupServer) SetGroupInfoEx(ctx context.Context, req *pbgroup.SetGroupI
tips.OpUser = g.groupMemberDB2PB(opMember, 0) tips.OpUser = g.groupMemberDB2PB(opMember, 0)
} }
num := len(updatedData) if notificationFlag {
if req.Notification != nil {
num -= 3
if req.Notification.Value != "" { if req.Notification.Value != "" {
func() { conversation := &pbconversation.ConversationReq{
conversation := &pbconv.ConversationReq{ ConversationID: msgprocessor.GetConversationIDBySessionType(constant.ReadGroupChatType, req.GroupID),
ConversationID: msgprocessor.GetConversationIDBySessionType(constant.ReadGroupChatType, req.GroupID), ConversationType: constant.ReadGroupChatType,
ConversationType: constant.ReadGroupChatType, GroupID: req.GroupID,
GroupID: req.GroupID, }
}
resp, err := g.GetGroupMemberUserIDs(ctx, &pbgroup.GetGroupMemberUserIDsReq{GroupID: req.GroupID}) resp, err := g.GetGroupMemberUserIDs(ctx, &pbgroup.GetGroupMemberUserIDsReq{GroupID: req.GroupID})
if err != nil { if err != nil {
log.ZWarn(ctx, "GetGroupMemberIDs is failed.", err) log.ZWarn(ctx, "GetGroupMemberIDs is failed.", err)
return return nil, err
} }
conversation.GroupAtType = &wrapperspb.Int32Value{Value: constant.GroupNotification} conversation.GroupAtType = &wrapperspb.Int32Value{Value: constant.GroupNotification}
if err := g.conversationClient.SetConversations(ctx, resp.UserIDs, conversation); err != nil { if err := g.conversationClient.SetConversations(ctx, resp.UserIDs, conversation); err != nil {
log.ZWarn(ctx, "SetConversations", err, "UserIDs", resp.UserIDs, "conversation", conversation) log.ZWarn(ctx, "SetConversations", err, "UserIDs", resp.UserIDs, "conversation", conversation)
} }
}()
g.notification.GroupInfoSetAnnouncementNotification(ctx, &sdkws.GroupInfoSetAnnouncementTips{Group: tips.Group, OpUser: tips.OpUser}) g.notification.GroupInfoSetAnnouncementNotification(ctx, &sdkws.GroupInfoSetAnnouncementTips{Group: tips.Group, OpUser: tips.OpUser}, &notificationFlag)
} else {
notificationFlag = false
g.notification.GroupInfoSetAnnouncementNotification(ctx, &sdkws.GroupInfoSetAnnouncementTips{Group: tips.Group, OpUser: tips.OpUser}, &notificationFlag)
} }
} }
if req.GroupName != nil { if groupNameFlag {
num--
g.notification.GroupInfoSetNameNotification(ctx, &sdkws.GroupInfoSetNameTips{Group: tips.Group, OpUser: tips.OpUser}) g.notification.GroupInfoSetNameNotification(ctx, &sdkws.GroupInfoSetNameTips{Group: tips.Group, OpUser: tips.OpUser})
} }
if num > 0 { // if updatedData > 0, send the normal notification
if normalFlag {
g.notification.GroupInfoSetNotification(ctx, tips) g.notification.GroupInfoSetNotification(ctx, tips)
} }
@@ -1274,6 +1333,9 @@ func (g *groupServer) GetGroups(ctx context.Context, req *pbgroup.GetGroupsReq)
} }
func (g *groupServer) GetGroupMembersCMS(ctx context.Context, req *pbgroup.GetGroupMembersCMSReq) (*pbgroup.GetGroupMembersCMSResp, error) { func (g *groupServer) GetGroupMembersCMS(ctx context.Context, req *pbgroup.GetGroupMembersCMSReq) (*pbgroup.GetGroupMembersCMSResp, error) {
if err := g.checkAdminOrInGroup(ctx, req.GroupID); err != nil {
return nil, err
}
total, members, err := g.db.SearchGroupMember(ctx, req.UserName, req.GroupID, req.Pagination) total, members, err := g.db.SearchGroupMember(ctx, req.UserName, req.GroupID, req.Pagination)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -1294,7 +1356,10 @@ func (g *groupServer) GetUserReqApplicationList(ctx context.Context, req *pbgrou
if err != nil { if err != nil {
return nil, err return nil, err
} }
total, requests, err := g.db.PageGroupRequestUser(ctx, req.UserID, req.Pagination) handleResults := datautil.Slice(req.HandleResults, func(e int32) int {
return int(e)
})
total, requests, err := g.db.PageGroupRequestUser(ctx, req.UserID, req.GroupIDs, handleResults, req.Pagination)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -1372,7 +1437,7 @@ func (g *groupServer) DismissGroup(ctx context.Context, req *pbgroup.DismissGrou
if mcontext.GetOpUserID(ctx) == owner.UserID { if mcontext.GetOpUserID(ctx) == owner.UserID {
tips.OpUser = g.groupMemberDB2PB(owner, 0) tips.OpUser = g.groupMemberDB2PB(owner, 0)
} }
g.notification.GroupDismissedNotification(ctx, tips) g.notification.GroupDismissedNotification(ctx, tips, req.SendMessage)
} }
membersID, err := g.db.FindGroupMemberUserID(ctx, group.GroupID) membersID, err := g.db.FindGroupMemberUserID(ctx, group.GroupID)
if err != nil { if err != nil {
@@ -1649,6 +1714,11 @@ func (g *groupServer) GetGroupAbstractInfo(ctx context.Context, req *pbgroup.Get
if datautil.Duplicate(req.GroupIDs) { if datautil.Duplicate(req.GroupIDs) {
return nil, errs.ErrArgs.WrapMsg("groupIDs duplicate") return nil, errs.ErrArgs.WrapMsg("groupIDs duplicate")
} }
for _, groupID := range req.GroupIDs {
if err := g.checkAdminOrInGroup(ctx, groupID); err != nil {
return nil, err
}
}
groups, err := g.db.FindGroup(ctx, req.GroupIDs) groups, err := g.db.FindGroup(ctx, req.GroupIDs)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -1677,6 +1747,9 @@ func (g *groupServer) GetUserInGroupMembers(ctx context.Context, req *pbgroup.Ge
if len(req.GroupIDs) == 0 { if len(req.GroupIDs) == 0 {
return nil, errs.ErrArgs.WrapMsg("groupIDs empty") return nil, errs.ErrArgs.WrapMsg("groupIDs empty")
} }
if err := authverify.CheckAccessV3(ctx, req.UserID, g.config.Share.IMAdminUserID); err != nil {
return nil, err
}
members, err := g.db.FindGroupMemberUser(ctx, req.GroupIDs, req.UserID) members, err := g.db.FindGroupMemberUser(ctx, req.GroupIDs, req.UserID)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -1696,6 +1769,11 @@ func (g *groupServer) GetGroupMemberUserIDs(ctx context.Context, req *pbgroup.Ge
if err != nil { if err != nil {
return nil, err return nil, err
} }
if !authverify.IsAppManagerUid(ctx, g.config.Share.IMAdminUserID) {
if !datautil.Contain(mcontext.GetOpUserID(ctx), userIDs...) {
return nil, errs.ErrNoPermission.WrapMsg("opUser no permission")
}
}
return &pbgroup.GetGroupMemberUserIDsResp{ return &pbgroup.GetGroupMemberUserIDsResp{
UserIDs: userIDs, UserIDs: userIDs,
}, nil }, nil
@@ -1705,6 +1783,9 @@ func (g *groupServer) GetGroupMemberRoleLevel(ctx context.Context, req *pbgroup.
if len(req.RoleLevels) == 0 { if len(req.RoleLevels) == 0 {
return nil, errs.ErrArgs.WrapMsg("RoleLevels empty") return nil, errs.ErrArgs.WrapMsg("RoleLevels empty")
} }
if err := g.checkAdminOrInGroup(ctx, req.GroupID); err != nil {
return nil, err
}
members, err := g.db.FindGroupMemberRoleLevels(ctx, req.GroupID, req.RoleLevels) members, err := g.db.FindGroupMemberRoleLevels(ctx, req.GroupID, req.RoleLevels)
if err != nil { if err != nil {
return nil, err return nil, err
+105 -40
View File
@@ -20,6 +20,7 @@ import (
"fmt" "fmt"
"time" "time"
"github.com/google/uuid"
"github.com/openimsdk/open-im-server/v3/pkg/rpcli" "github.com/openimsdk/open-im-server/v3/pkg/rpcli"
"github.com/openimsdk/open-im-server/v3/pkg/authverify" "github.com/openimsdk/open-im-server/v3/pkg/authverify"
@@ -52,11 +53,11 @@ const (
func NewNotificationSender(db controller.GroupDatabase, config *Config, userClient *rpcli.UserClient, msgClient *rpcli.MsgClient, conversationClient *rpcli.ConversationClient) *NotificationSender { func NewNotificationSender(db controller.GroupDatabase, config *Config, userClient *rpcli.UserClient, msgClient *rpcli.MsgClient, conversationClient *rpcli.ConversationClient) *NotificationSender {
return &NotificationSender{ return &NotificationSender{
NotificationSender: rpcclient.NewNotificationSender(&config.NotificationConfig, NotificationSender: notification.NewNotificationSender(&config.NotificationConfig,
rpcclient.WithRpcClient(func(ctx context.Context, req *msg.SendMsgReq) (*msg.SendMsgResp, error) { notification.WithRpcClient(func(ctx context.Context, req *msg.SendMsgReq) (*msg.SendMsgResp, error) {
return msgClient.SendMsg(ctx, req) return msgClient.SendMsg(ctx, req)
}), }),
rpcclient.WithUserRpcClient(userClient.GetUserInfo), notification.WithUserRpcClient(userClient.GetUserInfo),
), ),
getUsersInfo: func(ctx context.Context, userIDs []string) ([]common_user.CommonUser, error) { getUsersInfo: func(ctx context.Context, userIDs []string) ([]common_user.CommonUser, error) {
users, err := userClient.GetUsersInfo(ctx, userIDs) users, err := userClient.GetUsersInfo(ctx, userIDs)
@@ -73,7 +74,7 @@ func NewNotificationSender(db controller.GroupDatabase, config *Config, userClie
} }
type NotificationSender struct { type NotificationSender struct {
*rpcclient.NotificationSender *notification.NotificationSender
getUsersInfo func(ctx context.Context, userIDs []string) ([]common_user.CommonUser, error) getUsersInfo func(ctx context.Context, userIDs []string) ([]common_user.CommonUser, error)
db controller.GroupDatabase db controller.GroupDatabase
config *Config config *Config
@@ -233,17 +234,17 @@ func (g *NotificationSender) groupMemberDB2PB(member *model.GroupMember, appMang
return result, nil return result, nil
} */ } */
func (g *NotificationSender) fillOpUser(ctx context.Context, opUser **sdkws.GroupMemberFullInfo, groupID string) (err error) { func (g *NotificationSender) fillOpUser(ctx context.Context, targetUser **sdkws.GroupMemberFullInfo, groupID string) (err error) {
return g.fillOpUserByUserID(ctx, mcontext.GetOpUserID(ctx), opUser, groupID) return g.fillUserByUserID(ctx, mcontext.GetOpUserID(ctx), targetUser, groupID)
} }
func (g *NotificationSender) fillOpUserByUserID(ctx context.Context, userID string, opUser **sdkws.GroupMemberFullInfo, groupID string) error { func (g *NotificationSender) fillUserByUserID(ctx context.Context, userID string, targetUser **sdkws.GroupMemberFullInfo, groupID string) error {
if opUser == nil { if targetUser == nil {
return errs.ErrInternalServer.WrapMsg("**sdkws.GroupMemberFullInfo is nil") return errs.ErrInternalServer.WrapMsg("**sdkws.GroupMemberFullInfo is nil")
} }
if groupID != "" { if groupID != "" {
if authverify.IsManagerUserID(userID, g.config.Share.IMAdminUserID) { if authverify.IsManagerUserID(userID, g.config.Share.IMAdminUserID) {
*opUser = &sdkws.GroupMemberFullInfo{ *targetUser = &sdkws.GroupMemberFullInfo{
GroupID: groupID, GroupID: groupID,
UserID: userID, UserID: userID,
RoleLevel: constant.GroupAdmin, RoleLevel: constant.GroupAdmin,
@@ -252,7 +253,7 @@ func (g *NotificationSender) fillOpUserByUserID(ctx context.Context, userID stri
} else { } else {
member, err := g.db.TakeGroupMember(ctx, groupID, userID) member, err := g.db.TakeGroupMember(ctx, groupID, userID)
if err == nil { if err == nil {
*opUser = g.groupMemberDB2PB(member, 0) *targetUser = g.groupMemberDB2PB(member, 0)
} else if !(errors.Is(err, mongo.ErrNoDocuments) || errs.ErrRecordNotFound.Is(err)) { } else if !(errors.Is(err, mongo.ErrNoDocuments) || errs.ErrRecordNotFound.Is(err)) {
return err return err
} }
@@ -262,8 +263,8 @@ func (g *NotificationSender) fillOpUserByUserID(ctx context.Context, userID stri
if err != nil { if err != nil {
return err return err
} }
if *opUser == nil { if *targetUser == nil {
*opUser = &sdkws.GroupMemberFullInfo{ *targetUser = &sdkws.GroupMemberFullInfo{
GroupID: groupID, GroupID: groupID,
UserID: userID, UserID: userID,
Nickname: user.Nickname, Nickname: user.Nickname,
@@ -271,11 +272,11 @@ func (g *NotificationSender) fillOpUserByUserID(ctx context.Context, userID stri
OperatorUserID: userID, OperatorUserID: userID,
} }
} else { } else {
if (*opUser).Nickname == "" { if (*targetUser).Nickname == "" {
(*opUser).Nickname = user.Nickname (*targetUser).Nickname = user.Nickname
} }
if (*opUser).FaceURL == "" { if (*targetUser).FaceURL == "" {
(*opUser).FaceURL = user.FaceURL (*targetUser).FaceURL = user.FaceURL
} }
} }
return nil return nil
@@ -283,7 +284,8 @@ func (g *NotificationSender) fillOpUserByUserID(ctx context.Context, userID stri
func (g *NotificationSender) setVersion(ctx context.Context, version *uint64, versionID *string, collName string, id string) { func (g *NotificationSender) setVersion(ctx context.Context, version *uint64, versionID *string, collName string, id string) {
versions := versionctx.GetVersionLog(ctx).Get() versions := versionctx.GetVersionLog(ctx).Get()
for _, coll := range versions { for i := len(versions) - 1; i >= 0; i-- {
coll := versions[i]
if coll.Name == collName && coll.Doc.DID == id { if coll.Name == collName && coll.Doc.DID == id {
*version = uint64(coll.Doc.Version) *version = uint64(coll.Doc.Version)
*versionID = coll.Doc.ID.Hex() *versionID = coll.Doc.ID.Hex()
@@ -307,7 +309,7 @@ func (g *NotificationSender) setSortVersion(ctx context.Context, version *uint64
} }
} }
func (g *NotificationSender) GroupCreatedNotification(ctx context.Context, tips *sdkws.GroupCreatedTips) { func (g *NotificationSender) GroupCreatedNotification(ctx context.Context, tips *sdkws.GroupCreatedTips, SendMessage *bool) {
var err error var err error
defer func() { defer func() {
if err != nil { if err != nil {
@@ -318,7 +320,7 @@ func (g *NotificationSender) GroupCreatedNotification(ctx context.Context, tips
return return
} }
g.setVersion(ctx, &tips.GroupMemberVersion, &tips.GroupMemberVersionID, database.GroupMemberVersionName, tips.Group.GroupID) g.setVersion(ctx, &tips.GroupMemberVersion, &tips.GroupMemberVersionID, database.GroupMemberVersionName, tips.Group.GroupID)
g.Notification(ctx, mcontext.GetOpUserID(ctx), tips.Group.GroupID, constant.GroupCreatedNotification, tips) g.Notification(ctx, mcontext.GetOpUserID(ctx), tips.Group.GroupID, constant.GroupCreatedNotification, tips, notification.WithSendMessage(SendMessage))
} }
func (g *NotificationSender) GroupInfoSetNotification(ctx context.Context, tips *sdkws.GroupInfoSetTips) { func (g *NotificationSender) GroupInfoSetNotification(ctx context.Context, tips *sdkws.GroupInfoSetTips) {
@@ -332,7 +334,7 @@ func (g *NotificationSender) GroupInfoSetNotification(ctx context.Context, tips
return return
} }
g.setVersion(ctx, &tips.GroupMemberVersion, &tips.GroupMemberVersionID, database.GroupMemberVersionName, tips.Group.GroupID) g.setVersion(ctx, &tips.GroupMemberVersion, &tips.GroupMemberVersionID, database.GroupMemberVersionName, tips.Group.GroupID)
g.Notification(ctx, mcontext.GetOpUserID(ctx), tips.Group.GroupID, constant.GroupInfoSetNotification, tips, rpcclient.WithRpcGetUserName()) g.Notification(ctx, mcontext.GetOpUserID(ctx), tips.Group.GroupID, constant.GroupInfoSetNotification, tips, notification.WithRpcGetUserName())
} }
func (g *NotificationSender) GroupInfoSetNameNotification(ctx context.Context, tips *sdkws.GroupInfoSetNameTips) { func (g *NotificationSender) GroupInfoSetNameNotification(ctx context.Context, tips *sdkws.GroupInfoSetNameTips) {
@@ -349,7 +351,7 @@ func (g *NotificationSender) GroupInfoSetNameNotification(ctx context.Context, t
g.Notification(ctx, mcontext.GetOpUserID(ctx), tips.Group.GroupID, constant.GroupInfoSetNameNotification, tips) g.Notification(ctx, mcontext.GetOpUserID(ctx), tips.Group.GroupID, constant.GroupInfoSetNameNotification, tips)
} }
func (g *NotificationSender) GroupInfoSetAnnouncementNotification(ctx context.Context, tips *sdkws.GroupInfoSetAnnouncementTips) { func (g *NotificationSender) GroupInfoSetAnnouncementNotification(ctx context.Context, tips *sdkws.GroupInfoSetAnnouncementTips, sendMessage *bool) {
var err error var err error
defer func() { defer func() {
if err != nil { if err != nil {
@@ -360,16 +362,49 @@ func (g *NotificationSender) GroupInfoSetAnnouncementNotification(ctx context.Co
return return
} }
g.setVersion(ctx, &tips.GroupMemberVersion, &tips.GroupMemberVersionID, database.GroupMemberVersionName, tips.Group.GroupID) g.setVersion(ctx, &tips.GroupMemberVersion, &tips.GroupMemberVersionID, database.GroupMemberVersionName, tips.Group.GroupID)
g.Notification(ctx, mcontext.GetOpUserID(ctx), tips.Group.GroupID, constant.GroupInfoSetAnnouncementNotification, tips, rpcclient.WithRpcGetUserName()) g.Notification(ctx, mcontext.GetOpUserID(ctx), tips.Group.GroupID, constant.GroupInfoSetAnnouncementNotification, tips, notification.WithRpcGetUserName(), notification.WithSendMessage(sendMessage))
} }
func (g *NotificationSender) JoinGroupApplicationNotification(ctx context.Context, req *pbgroup.JoinGroupReq) { func (g *NotificationSender) uuid() string {
return uuid.New().String()
}
func (g *NotificationSender) getGroupRequest(ctx context.Context, groupID string, userID string) (*sdkws.GroupRequest, error) {
request, err := g.db.TakeGroupRequest(ctx, groupID, userID)
if err != nil {
return nil, err
}
users, err := g.getUsersInfo(ctx, []string{userID})
if err != nil {
return nil, err
}
if len(users) == 0 {
return nil, servererrs.ErrUserIDNotFound.WrapMsg(fmt.Sprintf("user %s not found", userID))
}
info, ok := users[0].(*sdkws.UserInfo)
if !ok {
info = &sdkws.UserInfo{
UserID: users[0].GetUserID(),
Nickname: users[0].GetNickname(),
FaceURL: users[0].GetFaceURL(),
Ex: users[0].GetEx(),
}
}
return convert.Db2PbGroupRequest(request, info, nil), nil
}
func (g *NotificationSender) JoinGroupApplicationNotification(ctx context.Context, req *pbgroup.JoinGroupReq, dbReq *model.GroupRequest) {
var err error var err error
defer func() { defer func() {
if err != nil { if err != nil {
log.ZError(ctx, stringutil.GetFuncName(1)+" failed", err) log.ZError(ctx, stringutil.GetFuncName(1)+" failed", err)
} }
}() }()
request, err := g.getGroupRequest(ctx, dbReq.GroupID, dbReq.UserID)
if err != nil {
log.ZError(ctx, "JoinGroupApplicationNotification getGroupRequest", err, "dbReq", dbReq)
return
}
var group *sdkws.GroupInfo var group *sdkws.GroupInfo
group, err = g.getGroupInfo(ctx, req.GroupID) group, err = g.getGroupInfo(ctx, req.GroupID)
if err != nil { if err != nil {
@@ -385,7 +420,13 @@ func (g *NotificationSender) JoinGroupApplicationNotification(ctx context.Contex
return return
} }
userIDs = append(userIDs, req.InviterUserID, mcontext.GetOpUserID(ctx)) userIDs = append(userIDs, req.InviterUserID, mcontext.GetOpUserID(ctx))
tips := &sdkws.JoinGroupApplicationTips{Group: group, Applicant: user, ReqMsg: req.ReqMessage} tips := &sdkws.JoinGroupApplicationTips{
Group: group,
Applicant: user,
ReqMsg: req.ReqMessage,
Uuid: g.uuid(),
Request: request,
}
for _, userID := range datautil.Distinct(userIDs) { for _, userID := range datautil.Distinct(userIDs) {
g.Notification(ctx, mcontext.GetOpUserID(ctx), userID, constant.JoinGroupApplicationNotification, tips) g.Notification(ctx, mcontext.GetOpUserID(ctx), userID, constant.JoinGroupApplicationNotification, tips)
} }
@@ -415,6 +456,11 @@ func (g *NotificationSender) GroupApplicationAcceptedNotification(ctx context.Co
log.ZError(ctx, stringutil.GetFuncName(1)+" failed", err) log.ZError(ctx, stringutil.GetFuncName(1)+" failed", err)
} }
}() }()
request, err := g.getGroupRequest(ctx, req.GroupID, req.FromUserID)
if err != nil {
log.ZError(ctx, "GroupApplicationAcceptedNotification getGroupRequest", err, "req", req)
return
}
var group *sdkws.GroupInfo var group *sdkws.GroupInfo
group, err = g.getGroupInfo(ctx, req.GroupID) group, err = g.getGroupInfo(ctx, req.GroupID)
if err != nil { if err != nil {
@@ -430,8 +476,14 @@ func (g *NotificationSender) GroupApplicationAcceptedNotification(ctx context.Co
if err = g.fillOpUser(ctx, &opUser, group.GroupID); err != nil { if err = g.fillOpUser(ctx, &opUser, group.GroupID); err != nil {
return return
} }
tips := &sdkws.GroupApplicationAcceptedTips{
Group: group,
OpUser: opUser,
HandleMsg: req.HandledMsg,
Uuid: g.uuid(),
Request: request,
}
for _, userID := range append(userIDs, req.FromUserID) { for _, userID := range append(userIDs, req.FromUserID) {
tips := &sdkws.GroupApplicationAcceptedTips{Group: group, OpUser: opUser, HandleMsg: req.HandledMsg}
if userID == req.FromUserID { if userID == req.FromUserID {
tips.ReceiverAs = applicantReceiver tips.ReceiverAs = applicantReceiver
} else { } else {
@@ -448,6 +500,11 @@ func (g *NotificationSender) GroupApplicationRejectedNotification(ctx context.Co
log.ZError(ctx, stringutil.GetFuncName(1)+" failed", err) log.ZError(ctx, stringutil.GetFuncName(1)+" failed", err)
} }
}() }()
request, err := g.getGroupRequest(ctx, req.GroupID, req.FromUserID)
if err != nil {
log.ZError(ctx, "GroupApplicationAcceptedNotification getGroupRequest", err, "req", req)
return
}
var group *sdkws.GroupInfo var group *sdkws.GroupInfo
group, err = g.getGroupInfo(ctx, req.GroupID) group, err = g.getGroupInfo(ctx, req.GroupID)
if err != nil { if err != nil {
@@ -463,8 +520,14 @@ func (g *NotificationSender) GroupApplicationRejectedNotification(ctx context.Co
if err = g.fillOpUser(ctx, &opUser, group.GroupID); err != nil { if err = g.fillOpUser(ctx, &opUser, group.GroupID); err != nil {
return return
} }
tips := &sdkws.GroupApplicationRejectedTips{
Group: group,
OpUser: opUser,
HandleMsg: req.HandledMsg,
Uuid: g.uuid(),
Request: request,
}
for _, userID := range append(userIDs, req.FromUserID) { for _, userID := range append(userIDs, req.FromUserID) {
tips := &sdkws.GroupApplicationAcceptedTips{Group: group, OpUser: opUser, HandleMsg: req.HandledMsg}
if userID == req.FromUserID { if userID == req.FromUserID {
tips.ReceiverAs = applicantReceiver tips.ReceiverAs = applicantReceiver
} else { } else {
@@ -505,7 +568,7 @@ func (g *NotificationSender) GroupOwnerTransferredNotification(ctx context.Conte
g.Notification(ctx, mcontext.GetOpUserID(ctx), group.GroupID, constant.GroupOwnerTransferredNotification, tips) g.Notification(ctx, mcontext.GetOpUserID(ctx), group.GroupID, constant.GroupOwnerTransferredNotification, tips)
} }
func (g *NotificationSender) MemberKickedNotification(ctx context.Context, tips *sdkws.MemberKickedTips) { func (g *NotificationSender) MemberKickedNotification(ctx context.Context, tips *sdkws.MemberKickedTips, SendMessage *bool) {
var err error var err error
defer func() { defer func() {
if err != nil { if err != nil {
@@ -516,10 +579,14 @@ func (g *NotificationSender) MemberKickedNotification(ctx context.Context, tips
return return
} }
g.setVersion(ctx, &tips.GroupMemberVersion, &tips.GroupMemberVersionID, database.GroupMemberVersionName, tips.Group.GroupID) g.setVersion(ctx, &tips.GroupMemberVersion, &tips.GroupMemberVersionID, database.GroupMemberVersionName, tips.Group.GroupID)
g.Notification(ctx, mcontext.GetOpUserID(ctx), tips.Group.GroupID, constant.MemberKickedNotification, tips) g.Notification(ctx, mcontext.GetOpUserID(ctx), tips.Group.GroupID, constant.MemberKickedNotification, tips, notification.WithSendMessage(SendMessage))
} }
func (g *NotificationSender) GroupApplicationAgreeMemberEnterNotification(ctx context.Context, groupID string, invitedOpUserID string, entrantUserID ...string) error { func (g *NotificationSender) GroupApplicationAgreeMemberEnterNotification(ctx context.Context, groupID string, SendMessage *bool, invitedOpUserID string, entrantUserID ...string) error {
return g.groupApplicationAgreeMemberEnterNotification(ctx, groupID, SendMessage, invitedOpUserID, entrantUserID...)
}
func (g *NotificationSender) groupApplicationAgreeMemberEnterNotification(ctx context.Context, groupID string, SendMessage *bool, invitedOpUserID string, entrantUserID ...string) error {
var err error var err error
defer func() { defer func() {
if err != nil { if err != nil {
@@ -556,20 +623,18 @@ func (g *NotificationSender) GroupApplicationAgreeMemberEnterNotification(ctx co
InvitedUserList: users, InvitedUserList: users,
} }
opUserID := mcontext.GetOpUserID(ctx) opUserID := mcontext.GetOpUserID(ctx)
if err = g.fillOpUserByUserID(ctx, opUserID, &tips.OpUser, tips.Group.GroupID); err != nil { if err = g.fillUserByUserID(ctx, opUserID, &tips.OpUser, tips.Group.GroupID); err != nil {
return nil return nil
} }
switch { if invitedOpUserID == opUserID {
case invitedOpUserID == "":
case invitedOpUserID == opUserID:
tips.InviterUser = tips.OpUser tips.InviterUser = tips.OpUser
default: } else {
if err = g.fillOpUserByUserID(ctx, invitedOpUserID, &tips.InviterUser, tips.Group.GroupID); err != nil { if err = g.fillUserByUserID(ctx, invitedOpUserID, &tips.InviterUser, tips.Group.GroupID); err != nil {
return err return err
} }
} }
g.setVersion(ctx, &tips.GroupMemberVersion, &tips.GroupMemberVersionID, database.GroupMemberVersionName, tips.Group.GroupID) g.setVersion(ctx, &tips.GroupMemberVersion, &tips.GroupMemberVersionID, database.GroupMemberVersionName, tips.Group.GroupID)
g.Notification(ctx, mcontext.GetOpUserID(ctx), group.GroupID, constant.MemberInvitedNotification, tips) g.Notification(ctx, mcontext.GetOpUserID(ctx), group.GroupID, constant.MemberInvitedNotification, tips, notification.WithSendMessage(SendMessage))
return nil return nil
} }
@@ -614,7 +679,7 @@ func (g *NotificationSender) MemberEnterNotification(ctx context.Context, groupI
return nil return nil
} }
func (g *NotificationSender) GroupDismissedNotification(ctx context.Context, tips *sdkws.GroupDismissedTips) { func (g *NotificationSender) GroupDismissedNotification(ctx context.Context, tips *sdkws.GroupDismissedTips, SendMessage *bool) {
var err error var err error
defer func() { defer func() {
if err != nil { if err != nil {
@@ -624,7 +689,7 @@ func (g *NotificationSender) GroupDismissedNotification(ctx context.Context, tip
if err = g.fillOpUser(ctx, &tips.OpUser, tips.Group.GroupID); err != nil { if err = g.fillOpUser(ctx, &tips.OpUser, tips.Group.GroupID); err != nil {
return return
} }
g.Notification(ctx, mcontext.GetOpUserID(ctx), tips.Group.GroupID, constant.GroupDismissedNotification, tips) g.Notification(ctx, mcontext.GetOpUserID(ctx), tips.Group.GroupID, constant.GroupDismissedNotification, tips, notification.WithSendMessage(SendMessage))
} }
func (g *NotificationSender) GroupMemberMutedNotification(ctx context.Context, groupID, groupMemberUserID string, mutedSeconds uint32) { func (g *NotificationSender) GroupMemberMutedNotification(ctx context.Context, groupID, groupMemberUserID string, mutedSeconds uint32) {
@@ -781,7 +846,7 @@ func (g *NotificationSender) GroupMemberSetToAdminNotification(ctx context.Conte
if err = g.fillOpUser(ctx, &tips.OpUser, tips.Group.GroupID); err != nil { if err = g.fillOpUser(ctx, &tips.OpUser, tips.Group.GroupID); err != nil {
return return
} }
g.setVersion(ctx, &tips.GroupMemberVersion, &tips.GroupMemberVersionID, database.GroupMemberVersionName, tips.Group.GroupID) g.setSortVersion(ctx, &tips.GroupMemberVersion, &tips.GroupMemberVersionID, database.GroupMemberVersionName, tips.Group.GroupID, &tips.GroupSortVersion)
g.Notification(ctx, mcontext.GetOpUserID(ctx), group.GroupID, constant.GroupMemberSetToAdminNotification, tips) g.Notification(ctx, mcontext.GetOpUserID(ctx), group.GroupID, constant.GroupMemberSetToAdminNotification, tips)
} }
@@ -806,6 +871,6 @@ func (g *NotificationSender) GroupMemberSetToOrdinaryUserNotification(ctx contex
if err = g.fillOpUser(ctx, &tips.OpUser, tips.Group.GroupID); err != nil { if err = g.fillOpUser(ctx, &tips.OpUser, tips.Group.GroupID); err != nil {
return return
} }
g.setVersion(ctx, &tips.GroupMemberVersion, &tips.GroupMemberVersionID, database.GroupMemberVersionName, tips.Group.GroupID) g.setSortVersion(ctx, &tips.GroupMemberVersion, &tips.GroupMemberVersionID, database.GroupMemberVersionName, tips.Group.GroupID, &tips.GroupSortVersion)
g.Notification(ctx, mcontext.GetOpUserID(ctx), group.GroupID, constant.GroupMemberSetToOrdinaryUserNotification, tips) g.Notification(ctx, mcontext.GetOpUserID(ctx), group.GroupID, constant.GroupMemberSetToOrdinaryUserNotification, tips)
} }
+4
View File
@@ -18,6 +18,7 @@ import (
"context" "context"
"time" "time"
"github.com/openimsdk/open-im-server/v3/pkg/authverify"
"github.com/openimsdk/protocol/group" "github.com/openimsdk/protocol/group"
"github.com/openimsdk/tools/errs" "github.com/openimsdk/tools/errs"
) )
@@ -26,6 +27,9 @@ func (s *groupServer) GroupCreateCount(ctx context.Context, req *group.GroupCrea
if req.Start > req.End { if req.Start > req.End {
return nil, errs.ErrArgs.WrapMsg("start > end: %d > %d", req.Start, req.End) return nil, errs.ErrArgs.WrapMsg("start > end: %d > %d", req.Start, req.End)
} }
if err := authverify.CheckAdmin(ctx, s.config.Share.IMAdminUserID); err != nil {
return nil, err
}
total, err := s.db.CountTotal(ctx, nil) total, err := s.db.CountTotal(ctx, nil)
if err != nil { if err != nil {
return nil, err return nil, err
+43 -153
View File
@@ -12,15 +12,23 @@ import (
pbgroup "github.com/openimsdk/protocol/group" pbgroup "github.com/openimsdk/protocol/group"
"github.com/openimsdk/protocol/sdkws" "github.com/openimsdk/protocol/sdkws"
"github.com/openimsdk/tools/errs" "github.com/openimsdk/tools/errs"
"github.com/openimsdk/tools/log" "github.com/openimsdk/tools/mcontext"
"github.com/openimsdk/tools/utils/datautil"
) )
func (s *groupServer) GetFullGroupMemberUserIDs(ctx context.Context, req *pbgroup.GetFullGroupMemberUserIDsReq) (*pbgroup.GetFullGroupMemberUserIDsResp, error) { const versionSyncLimit = 500
vl, err := s.db.FindMaxGroupMemberVersionCache(ctx, req.GroupID)
func (g *groupServer) GetFullGroupMemberUserIDs(ctx context.Context, req *pbgroup.GetFullGroupMemberUserIDsReq) (*pbgroup.GetFullGroupMemberUserIDsResp, error) {
userIDs, err := g.db.FindGroupMemberUserID(ctx, req.GroupID)
if err != nil { if err != nil {
return nil, err return nil, err
} }
userIDs, err := s.db.FindGroupMemberUserID(ctx, req.GroupID) if !authverify.IsAppManagerUid(ctx, g.config.Share.IMAdminUserID) {
if !datautil.Contain(mcontext.GetOpUserID(ctx), userIDs...) {
return nil, errs.ErrNoPermission.WrapMsg("op user not in group")
}
}
vl, err := g.db.FindMaxGroupMemberVersionCache(ctx, req.GroupID)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -37,6 +45,9 @@ func (s *groupServer) GetFullGroupMemberUserIDs(ctx context.Context, req *pbgrou
} }
func (s *groupServer) GetFullJoinGroupIDs(ctx context.Context, req *pbgroup.GetFullJoinGroupIDsReq) (*pbgroup.GetFullJoinGroupIDsResp, error) { func (s *groupServer) GetFullJoinGroupIDs(ctx context.Context, req *pbgroup.GetFullJoinGroupIDsReq) (*pbgroup.GetFullJoinGroupIDsResp, error) {
if err := authverify.CheckAccessV3(ctx, req.UserID, s.config.Share.IMAdminUserID); err != nil {
return nil, err
}
vl, err := s.db.FindMaxJoinGroupVersionCache(ctx, req.UserID) vl, err := s.db.FindMaxJoinGroupVersionCache(ctx, req.UserID)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -58,6 +69,9 @@ func (s *groupServer) GetFullJoinGroupIDs(ctx context.Context, req *pbgroup.GetF
} }
func (s *groupServer) GetIncrementalGroupMember(ctx context.Context, req *pbgroup.GetIncrementalGroupMemberReq) (*pbgroup.GetIncrementalGroupMemberResp, error) { func (s *groupServer) GetIncrementalGroupMember(ctx context.Context, req *pbgroup.GetIncrementalGroupMemberReq) (*pbgroup.GetIncrementalGroupMemberResp, error) {
if err := s.checkAdminOrInGroup(ctx, req.GroupID); err != nil {
return nil, err
}
group, err := s.db.TakeGroup(ctx, req.GroupID) group, err := s.db.TakeGroup(ctx, req.GroupID)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -132,152 +146,8 @@ func (s *groupServer) GetIncrementalGroupMember(ctx context.Context, req *pbgrou
return resp, nil return resp, nil
} }
func (s *groupServer) BatchGetIncrementalGroupMember(ctx context.Context, req *pbgroup.BatchGetIncrementalGroupMemberReq) (resp *pbgroup.BatchGetIncrementalGroupMemberResp, err error) { func (g *groupServer) GetIncrementalJoinGroup(ctx context.Context, req *pbgroup.GetIncrementalJoinGroupReq) (*pbgroup.GetIncrementalJoinGroupResp, error) {
type VersionInfo struct { if err := authverify.CheckAccessV3(ctx, req.UserID, g.config.Share.IMAdminUserID); err != nil {
GroupID string
VersionID string
VersionNumber uint64
}
var groupIDs []string
groupsVersionMap := make(map[string]*VersionInfo)
groupsMap := make(map[string]*model.Group)
hasGroupUpdateMap := make(map[string]bool)
sortVersionMap := make(map[string]uint64)
var targetKeys, versionIDs []string
var versionNumbers []uint64
var requestBodyLen int
for _, group := range req.ReqList {
groupsVersionMap[group.GroupID] = &VersionInfo{
GroupID: group.GroupID,
VersionID: group.VersionID,
VersionNumber: group.Version,
}
groupIDs = append(groupIDs, group.GroupID)
}
groups, err := s.db.FindGroup(ctx, groupIDs)
if err != nil {
return nil, errs.Wrap(err)
}
for _, group := range groups {
if group.Status == constant.GroupStatusDismissed {
err = servererrs.ErrDismissedAlready.Wrap()
log.ZError(ctx, "This group is Dismissed Already", err, "group is", group.GroupID)
delete(groupsVersionMap, group.GroupID)
} else {
groupsMap[group.GroupID] = group
}
}
for groupID, vInfo := range groupsVersionMap {
targetKeys = append(targetKeys, groupID)
versionIDs = append(versionIDs, vInfo.VersionID)
versionNumbers = append(versionNumbers, vInfo.VersionNumber)
}
opt := incrversion.BatchOption[[]*sdkws.GroupMemberFullInfo, pbgroup.BatchGetIncrementalGroupMemberResp]{
Ctx: ctx,
TargetKeys: targetKeys,
VersionIDs: versionIDs,
VersionNumbers: versionNumbers,
Versions: func(ctx context.Context, groupIDs []string, versions []uint64, limits []int) (map[string]*model.VersionLog, error) {
vLogs, err := s.db.BatchFindMemberIncrVersion(ctx, groupIDs, versions, limits)
if err != nil {
return nil, errs.Wrap(err)
}
for groupID, vlog := range vLogs {
vlogElems := make([]model.VersionLogElem, 0, len(vlog.Logs))
for i, log := range vlog.Logs {
switch log.EID {
case model.VersionGroupChangeID:
vlog.LogLen--
hasGroupUpdateMap[groupID] = true
case model.VersionSortChangeID:
vlog.LogLen--
sortVersionMap[groupID] = uint64(log.Version)
default:
vlogElems = append(vlogElems, vlog.Logs[i])
}
}
vlog.Logs = vlogElems
if vlog.LogLen > 0 {
hasGroupUpdateMap[groupID] = true
}
}
return vLogs, nil
},
CacheMaxVersions: s.db.BatchFindMaxGroupMemberVersionCache,
Find: func(ctx context.Context, groupID string, ids []string) ([]*sdkws.GroupMemberFullInfo, error) {
memberInfo, err := s.getGroupMembersInfo(ctx, groupID, ids)
if err != nil {
return nil, err
}
return memberInfo, err
},
Resp: func(versions map[string]*model.VersionLog, deleteIdsMap map[string][]string, insertListMap, updateListMap map[string][]*sdkws.GroupMemberFullInfo, fullMap map[string]bool) *pbgroup.BatchGetIncrementalGroupMemberResp {
resList := make(map[string]*pbgroup.GetIncrementalGroupMemberResp)
for groupID, versionLog := range versions {
resList[groupID] = &pbgroup.GetIncrementalGroupMemberResp{
VersionID: versionLog.ID.Hex(),
Version: uint64(versionLog.Version),
Full: fullMap[groupID],
Delete: deleteIdsMap[groupID],
Insert: insertListMap[groupID],
Update: updateListMap[groupID],
SortVersion: sortVersionMap[groupID],
}
requestBodyLen += len(insertListMap[groupID]) + len(updateListMap[groupID]) + len(deleteIdsMap[groupID])
if requestBodyLen > 200 {
break
}
}
return &pbgroup.BatchGetIncrementalGroupMemberResp{
RespList: resList,
}
},
}
resp, err = opt.Build()
if err != nil {
return nil, errs.Wrap(err)
}
for groupID, val := range resp.RespList {
if val.Full || hasGroupUpdateMap[groupID] {
count, err := s.db.FindGroupMemberNum(ctx, groupID)
if err != nil {
return nil, err
}
owner, err := s.db.TakeGroupOwner(ctx, groupID)
if err != nil {
return nil, err
}
resp.RespList[groupID].Group = s.groupDB2PB(groupsMap[groupID], owner.UserID, count)
}
}
return resp, nil
}
func (s *groupServer) GetIncrementalJoinGroup(ctx context.Context, req *pbgroup.GetIncrementalJoinGroupReq) (*pbgroup.GetIncrementalJoinGroupResp, error) {
if err := authverify.CheckAccessV3(ctx, req.UserID, s.config.Share.IMAdminUserID); err != nil {
return nil, err return nil, err
} }
opt := incrversion.Option[*sdkws.GroupInfo, pbgroup.GetIncrementalJoinGroupResp]{ opt := incrversion.Option[*sdkws.GroupInfo, pbgroup.GetIncrementalJoinGroupResp]{
@@ -285,9 +155,9 @@ func (s *groupServer) GetIncrementalJoinGroup(ctx context.Context, req *pbgroup.
VersionKey: req.UserID, VersionKey: req.UserID,
VersionID: req.VersionID, VersionID: req.VersionID,
VersionNumber: req.Version, VersionNumber: req.Version,
Version: s.db.FindJoinIncrVersion, Version: g.db.FindJoinIncrVersion,
CacheMaxVersion: s.db.FindMaxJoinGroupVersionCache, CacheMaxVersion: g.db.FindMaxJoinGroupVersionCache,
Find: s.getGroupsInfo, Find: g.getGroupsInfo,
Resp: func(version *model.VersionLog, delIDs []string, insertList, updateList []*sdkws.GroupInfo, full bool) *pbgroup.GetIncrementalJoinGroupResp { Resp: func(version *model.VersionLog, delIDs []string, insertList, updateList []*sdkws.GroupInfo, full bool) *pbgroup.GetIncrementalJoinGroupResp {
return &pbgroup.GetIncrementalJoinGroupResp{ return &pbgroup.GetIncrementalJoinGroupResp{
VersionID: version.ID.Hex(), VersionID: version.ID.Hex(),
@@ -301,3 +171,23 @@ func (s *groupServer) GetIncrementalJoinGroup(ctx context.Context, req *pbgroup.
} }
return opt.Build() return opt.Build()
} }
func (g *groupServer) BatchGetIncrementalGroupMember(ctx context.Context, req *pbgroup.BatchGetIncrementalGroupMemberReq) (*pbgroup.BatchGetIncrementalGroupMemberResp, error) {
var num int
resp := make(map[string]*pbgroup.GetIncrementalGroupMemberResp)
for _, memberReq := range req.ReqList {
if _, ok := resp[memberReq.GroupID]; ok {
continue
}
memberResp, err := g.GetIncrementalGroupMember(ctx, memberReq)
if err != nil {
return nil, err
}
resp[memberReq.GroupID] = memberResp
num += len(memberResp.Insert) + len(memberResp.Update) + len(memberResp.Delete)
if num >= versionSyncLimit {
break
}
}
return &pbgroup.BatchGetIncrementalGroupMemberResp{RespList: resp}, nil
}
+3 -3
View File
@@ -23,11 +23,11 @@ import (
) )
type MsgNotificationSender struct { type MsgNotificationSender struct {
*rpcclient.NotificationSender *notification.NotificationSender
} }
func NewMsgNotificationSender(config *Config, opts ...rpcclient.NotificationSenderOptions) *MsgNotificationSender { func NewMsgNotificationSender(config *Config, opts ...notification.NotificationSenderOptions) *MsgNotificationSender {
return &MsgNotificationSender{rpcclient.NewNotificationSender(&config.NotificationConfig, opts...)} return &MsgNotificationSender{notification.NewNotificationSender(&config.NotificationConfig, opts...)}
} }
func (m *MsgNotificationSender) UserDeleteMsgsNotification(ctx context.Context, userID, conversationID string, seqs []int64) { func (m *MsgNotificationSender) UserDeleteMsgsNotification(ctx context.Context, userID, conversationID string, seqs []int64) {
+8 -8
View File
@@ -17,6 +17,7 @@ package msg
import ( import (
"context" "context"
"github.com/openimsdk/open-im-server/v3/pkg/notification"
"github.com/openimsdk/open-im-server/v3/pkg/rpcli" "github.com/openimsdk/open-im-server/v3/pkg/rpcli"
"github.com/openimsdk/open-im-server/v3/pkg/common/config" "github.com/openimsdk/open-im-server/v3/pkg/common/config"
@@ -28,7 +29,6 @@ import (
"github.com/openimsdk/tools/db/redisutil" "github.com/openimsdk/tools/db/redisutil"
"github.com/openimsdk/open-im-server/v3/pkg/common/storage/controller" "github.com/openimsdk/open-im-server/v3/pkg/common/storage/controller"
"github.com/openimsdk/open-im-server/v3/pkg/notification"
"github.com/openimsdk/open-im-server/v3/pkg/rpccache" "github.com/openimsdk/open-im-server/v3/pkg/rpccache"
"github.com/openimsdk/protocol/constant" "github.com/openimsdk/protocol/constant"
"github.com/openimsdk/protocol/conversation" "github.com/openimsdk/protocol/conversation"
@@ -64,7 +64,7 @@ type msgServer struct {
GroupLocalCache *rpccache.GroupLocalCache // Local cache for group data. GroupLocalCache *rpccache.GroupLocalCache // Local cache for group data.
ConversationLocalCache *rpccache.ConversationLocalCache // Local cache for conversation data. ConversationLocalCache *rpccache.ConversationLocalCache // Local cache for conversation data.
Handlers MessageInterceptorChain // Chain of handlers for processing messages. Handlers MessageInterceptorChain // Chain of handlers for processing messages.
notificationSender *rpcclient.NotificationSender // RPC client for sending notifications. notificationSender *notification.NotificationSender // RPC client for sending notifications.
msgNotificationSender *MsgNotificationSender // RPC client for sending msg notifications. msgNotificationSender *MsgNotificationSender // RPC client for sending msg notifications.
config *Config // Global configuration settings. config *Config // Global configuration settings.
webhookClient *webhook.Client webhookClient *webhook.Client
@@ -104,19 +104,19 @@ func Start(ctx context.Context, config *Config, client discovery.SvcDiscoveryReg
if err != nil { if err != nil {
return err return err
} }
userConn, err := client.GetConn(ctx, config.Discovery.RpcService.User) userConn, err := client.GetConn(ctx, config.Share.RpcRegisterName.User)
if err != nil { if err != nil {
return err return err
} }
groupConn, err := client.GetConn(ctx, config.Discovery.RpcService.Group) groupConn, err := client.GetConn(ctx, config.Share.RpcRegisterName.Group)
if err != nil { if err != nil {
return err return err
} }
friendConn, err := client.GetConn(ctx, config.Discovery.RpcService.Friend) friendConn, err := client.GetConn(ctx, config.Share.RpcRegisterName.Friend)
if err != nil { if err != nil {
return err return err
} }
conversationConn, err := client.GetConn(ctx, config.Discovery.RpcService.Conversation) conversationConn, err := client.GetConn(ctx, config.Share.RpcRegisterName.Conversation)
if err != nil { if err != nil {
return err return err
} }
@@ -133,8 +133,8 @@ func Start(ctx context.Context, config *Config, client discovery.SvcDiscoveryReg
conversationClient: conversationClient, conversationClient: conversationClient,
} }
s.notificationSender = rpcclient.NewNotificationSender(&config.NotificationConfig, rpcclient.WithLocalSendMsg(s.SendMsg)) s.notificationSender = notification.NewNotificationSender(&config.NotificationConfig, notification.WithLocalSendMsg(s.SendMsg))
s.msgNotificationSender = NewMsgNotificationSender(config, rpcclient.WithLocalSendMsg(s.SendMsg)) s.msgNotificationSender = NewMsgNotificationSender(config, notification.WithLocalSendMsg(s.SendMsg))
msg.RegisterMsgServer(server, s) msg.RegisterMsgServer(server, s)
+63 -15
View File
@@ -17,6 +17,7 @@ package relation
import ( import (
"context" "context"
"github.com/openimsdk/open-im-server/v3/pkg/notification/common_user"
"github.com/openimsdk/open-im-server/v3/pkg/rpcli" "github.com/openimsdk/open-im-server/v3/pkg/rpcli"
"github.com/openimsdk/tools/mq/memamq" "github.com/openimsdk/tools/mq/memamq"
@@ -92,32 +93,34 @@ func Start(ctx context.Context, config *Config, client discovery.SvcDiscoveryReg
return err return err
} }
userConn, err := client.GetConn(ctx, config.Discovery.RpcService.User) userConn, err := client.GetConn(ctx, config.Share.RpcRegisterName.User)
if err != nil { if err != nil {
return err return err
} }
msgConn, err := client.GetConn(ctx, config.Discovery.RpcService.Msg) msgConn, err := client.GetConn(ctx, config.Share.RpcRegisterName.Msg)
if err != nil { if err != nil {
return err return err
} }
userClient := rpcli.NewUserClient(userConn) userClient := rpcli.NewUserClient(userConn)
database := controller.NewFriendDatabase(
friendMongoDB,
friendRequestMongoDB,
redis.NewFriendCacheRedis(rdb, &config.LocalCacheConfig, friendMongoDB, redis.GetRocksCacheOptions()),
mgocli.GetTx(),
)
// Initialize notification sender // Initialize notification sender
notificationSender := NewFriendNotificationSender( notificationSender := NewFriendNotificationSender(
&config.NotificationConfig, &config.NotificationConfig,
rpcli.NewMsgClient(msgConn), rpcli.NewMsgClient(msgConn),
WithRpcFunc(userClient.GetUsersInfo), WithRpcFunc(userClient.GetUsersInfo),
WithFriendDB(database),
) )
localcache.InitLocalCache(&config.LocalCacheConfig) localcache.InitLocalCache(&config.LocalCacheConfig)
// Register Friend server with refactored MongoDB and Redis integrations // Register Friend server with refactored MongoDB and Redis integrations
relation.RegisterFriendServer(server, &friendServer{ relation.RegisterFriendServer(server, &friendServer{
db: controller.NewFriendDatabase( db: database,
friendMongoDB,
friendRequestMongoDB,
redis.NewFriendCacheRedis(rdb, &config.LocalCacheConfig, friendMongoDB, redis.GetRocksCacheOptions()),
mgocli.GetTx(),
),
blackDatabase: controller.NewBlackDatabase( blackDatabase: controller.NewBlackDatabase(
blackMongoDB, blackMongoDB,
redis.NewBlackCacheRedis(rdb, &config.LocalCacheConfig, blackMongoDB, redis.GetRocksCacheOptions()), redis.NewBlackCacheRedis(rdb, &config.LocalCacheConfig, blackMongoDB, redis.GetRocksCacheOptions()),
@@ -191,7 +194,7 @@ func (s *friendServer) ImportFriends(ctx context.Context, req *relation.ImportFr
FromUserID: req.OwnerUserID, FromUserID: req.OwnerUserID,
ToUserID: userID, ToUserID: userID,
HandleResult: constant.FriendResponseAgree, HandleResult: constant.FriendResponseAgree,
}) }, false)
} }
s.webhookAfterImportFriends(ctx, &s.config.WebhooksConfig.AfterImportFriends, req) s.webhookAfterImportFriends(ctx, &s.config.WebhooksConfig.AfterImportFriends, req)
@@ -220,7 +223,7 @@ func (s *friendServer) RespondFriendApply(ctx context.Context, req *relation.Res
return nil, err return nil, err
} }
s.webhookAfterAddFriendAgree(ctx, &s.config.WebhooksConfig.AfterAddFriendAgree, req) s.webhookAfterAddFriendAgree(ctx, &s.config.WebhooksConfig.AfterAddFriendAgree, req)
s.notificationSender.FriendApplicationAgreedNotification(ctx, req) s.notificationSender.FriendApplicationAgreedNotification(ctx, req, true)
return resp, nil return resp, nil
} }
if req.HandleResult == constant.FriendResponseRefuse { if req.HandleResult == constant.FriendResponseRefuse {
@@ -281,6 +284,9 @@ func (s *friendServer) SetFriendRemark(ctx context.Context, req *relation.SetFri
} }
func (s *friendServer) GetFriendInfo(ctx context.Context, req *relation.GetFriendInfoReq) (*relation.GetFriendInfoResp, error) { func (s *friendServer) GetFriendInfo(ctx context.Context, req *relation.GetFriendInfoReq) (*relation.GetFriendInfoResp, error) {
if err := authverify.CheckAccessV3(ctx, req.OwnerUserID, s.config.Share.IMAdminUserID); err != nil {
return nil, err
}
friends, err := s.db.FindFriendsWithError(ctx, req.OwnerUserID, req.FriendUserIDs) friends, err := s.db.FindFriendsWithError(ctx, req.OwnerUserID, req.FriendUserIDs)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -293,6 +299,9 @@ func (s *friendServer) GetDesignatedFriends(ctx context.Context, req *relation.G
if datautil.Duplicate(req.FriendUserIDs) { if datautil.Duplicate(req.FriendUserIDs) {
return nil, errs.ErrArgs.WrapMsg("friend userID repeated") return nil, errs.ErrArgs.WrapMsg("friend userID repeated")
} }
if err := authverify.CheckAccessV3(ctx, req.OwnerUserID, s.config.Share.IMAdminUserID); err != nil {
return nil, err
}
friends, err := s.getFriend(ctx, req.OwnerUserID, req.FriendUserIDs) friends, err := s.getFriend(ctx, req.OwnerUserID, req.FriendUserIDs)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -322,7 +331,7 @@ func (s *friendServer) GetDesignatedFriendsApply(ctx context.Context,
return nil, err return nil, err
} }
resp = &relation.GetDesignatedFriendsApplyResp{} resp = &relation.GetDesignatedFriendsApplyResp{}
resp.FriendRequests, err = convert.FriendRequestDB2Pb(ctx, friendRequests, s.userClient.GetUsersInfoMap) resp.FriendRequests, err = convert.FriendRequestDB2Pb(ctx, friendRequests, s.getCommonUserMap)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -335,13 +344,16 @@ func (s *friendServer) GetPaginationFriendsApplyTo(ctx context.Context, req *rel
return nil, err return nil, err
} }
total, friendRequests, err := s.db.PageFriendRequestToMe(ctx, req.UserID, req.Pagination) handleResults := datautil.Slice(req.HandleResults, func(e int32) int {
return int(e)
})
total, friendRequests, err := s.db.PageFriendRequestToMe(ctx, req.UserID, handleResults, req.Pagination)
if err != nil { if err != nil {
return nil, err return nil, err
} }
resp = &relation.GetPaginationFriendsApplyToResp{} resp = &relation.GetPaginationFriendsApplyToResp{}
resp.FriendRequests, err = convert.FriendRequestDB2Pb(ctx, friendRequests, s.userClient.GetUsersInfoMap) resp.FriendRequests, err = convert.FriendRequestDB2Pb(ctx, friendRequests, s.getCommonUserMap)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -358,12 +370,15 @@ func (s *friendServer) GetPaginationFriendsApplyFrom(ctx context.Context, req *r
return nil, err return nil, err
} }
total, friendRequests, err := s.db.PageFriendRequestFromMe(ctx, req.UserID, req.Pagination) handleResults := datautil.Slice(req.HandleResults, func(e int32) int {
return int(e)
})
total, friendRequests, err := s.db.PageFriendRequestFromMe(ctx, req.UserID, handleResults, req.Pagination)
if err != nil { if err != nil {
return nil, err return nil, err
} }
resp.FriendRequests, err = convert.FriendRequestDB2Pb(ctx, friendRequests, s.userClient.GetUsersInfoMap) resp.FriendRequests, err = convert.FriendRequestDB2Pb(ctx, friendRequests, s.getCommonUserMap)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -427,6 +442,10 @@ func (s *friendServer) GetSpecifiedFriendsInfo(ctx context.Context, req *relatio
return nil, errs.ErrArgs.WrapMsg("userIDList repeated") return nil, errs.ErrArgs.WrapMsg("userIDList repeated")
} }
if err := authverify.CheckAccessV3(ctx, req.OwnerUserID, s.config.Share.IMAdminUserID); err != nil {
return nil, err
}
userMap, err := s.userClient.GetUsersInfoMap(ctx, req.UserIDList) userMap, err := s.userClient.GetUsersInfoMap(ctx, req.UserIDList)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -506,6 +525,10 @@ func (s *friendServer) UpdateFriends(
return nil, errs.ErrArgs.WrapMsg("friendIDList repeated") return nil, errs.ErrArgs.WrapMsg("friendIDList repeated")
} }
if err := authverify.CheckAccessV3(ctx, req.OwnerUserID, s.config.Share.IMAdminUserID); err != nil {
return nil, err
}
_, err := s.db.FindFriendsWithError(ctx, req.OwnerUserID, req.FriendUserIDs) _, err := s.db.FindFriendsWithError(ctx, req.OwnerUserID, req.FriendUserIDs)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -531,3 +554,28 @@ func (s *friendServer) UpdateFriends(
s.notificationSender.FriendsInfoUpdateNotification(ctx, req.OwnerUserID, req.FriendUserIDs) s.notificationSender.FriendsInfoUpdateNotification(ctx, req.OwnerUserID, req.FriendUserIDs)
return resp, nil return resp, nil
} }
func (s *friendServer) GetSelfUnhandledApplyCount(ctx context.Context, req *relation.GetSelfUnhandledApplyCountReq) (*relation.GetSelfUnhandledApplyCountResp, error) {
if err := authverify.CheckAccessV3(ctx, req.UserID, s.config.Share.IMAdminUserID); err != nil {
return nil, err
}
count, err := s.db.GetUnhandledCount(ctx, req.UserID, req.Time)
if err != nil {
return nil, err
}
return &relation.GetSelfUnhandledApplyCountResp{
Count: count,
}, nil
}
func (s *friendServer) getCommonUserMap(ctx context.Context, userIDs []string) (map[string]common_user.CommonUser, error) {
users, err := s.userClient.GetUsersInfo(ctx, userIDs)
if err != nil {
return nil, err
}
return datautil.SliceToMapAny(users, func(e *sdkws.UserInfo) (string, common_user.CommonUser) {
return e.UserID, e
}), nil
}
+107 -60
View File
@@ -16,8 +16,12 @@ package relation
import ( import (
"context" "context"
"github.com/openimsdk/open-im-server/v3/pkg/rpcli" "github.com/openimsdk/open-im-server/v3/pkg/rpcli"
"github.com/openimsdk/protocol/msg" "github.com/openimsdk/protocol/msg"
"github.com/openimsdk/tools/errs"
"github.com/openimsdk/tools/log"
"github.com/openimsdk/tools/utils/datautil"
"github.com/openimsdk/open-im-server/v3/pkg/common/storage/database" "github.com/openimsdk/open-im-server/v3/pkg/common/storage/database"
"github.com/openimsdk/open-im-server/v3/pkg/common/storage/versionctx" "github.com/openimsdk/open-im-server/v3/pkg/common/storage/versionctx"
@@ -36,7 +40,7 @@ import (
) )
type FriendNotificationSender struct { type FriendNotificationSender struct {
*rpcclient.NotificationSender *notification.NotificationSender
// Target not found err // Target not found err
getUsersInfo func(ctx context.Context, userIDs []string) ([]common_user.CommonUser, error) getUsersInfo func(ctx context.Context, userIDs []string) ([]common_user.CommonUser, error)
// db controller // db controller
@@ -51,9 +55,7 @@ func WithFriendDB(db controller.FriendDatabase) friendNotificationSenderOptions
} }
} }
func WithDBFunc( func WithDBFunc(fn func(ctx context.Context, userIDs []string) (users []*relationtb.User, err error)) friendNotificationSenderOptions {
fn func(ctx context.Context, userIDs []string) (users []*relationtb.User, err error),
) friendNotificationSenderOptions {
return func(s *FriendNotificationSender) { return func(s *FriendNotificationSender) {
f := func(ctx context.Context, userIDs []string) (result []common_user.CommonUser, err error) { f := func(ctx context.Context, userIDs []string) (result []common_user.CommonUser, err error) {
users, err := fn(ctx, userIDs) users, err := fn(ctx, userIDs)
@@ -69,9 +71,7 @@ func WithDBFunc(
} }
} }
func WithRpcFunc( func WithRpcFunc(fn func(ctx context.Context, userIDs []string) ([]*sdkws.UserInfo, error)) friendNotificationSenderOptions {
fn func(ctx context.Context, userIDs []string) ([]*sdkws.UserInfo, error),
) friendNotificationSenderOptions {
return func(s *FriendNotificationSender) { return func(s *FriendNotificationSender) {
f := func(ctx context.Context, userIDs []string) (result []common_user.CommonUser, err error) { f := func(ctx context.Context, userIDs []string) (result []common_user.CommonUser, err error) {
users, err := fn(ctx, userIDs) users, err := fn(ctx, userIDs)
@@ -89,7 +89,7 @@ func WithRpcFunc(
func NewFriendNotificationSender(conf *config.Notification, msgClient *rpcli.MsgClient, opts ...friendNotificationSenderOptions) *FriendNotificationSender { func NewFriendNotificationSender(conf *config.Notification, msgClient *rpcli.MsgClient, opts ...friendNotificationSenderOptions) *FriendNotificationSender {
f := &FriendNotificationSender{ f := &FriendNotificationSender{
NotificationSender: rpcclient.NewNotificationSender(conf, rpcclient.WithRpcClient(func(ctx context.Context, req *msg.SendMsgReq) (*msg.SendMsgResp, error) { NotificationSender: notification.NewNotificationSender(conf, notification.WithRpcClient(func(ctx context.Context, req *msg.SendMsgReq) (*msg.SendMsgResp, error) {
return msgClient.SendMsg(ctx, req) return msgClient.SendMsg(ctx, req)
})), })),
} }
@@ -99,10 +99,7 @@ func NewFriendNotificationSender(conf *config.Notification, msgClient *rpcli.Msg
return f return f
} }
func (f *FriendNotificationSender) getUsersInfoMap( func (f *FriendNotificationSender) getUsersInfoMap(ctx context.Context, userIDs []string) (map[string]*sdkws.UserInfo, error) {
ctx context.Context,
userIDs []string,
) (map[string]*sdkws.UserInfo, error) {
users, err := f.getUsersInfo(ctx, userIDs) users, err := f.getUsersInfo(ctx, userIDs)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -115,10 +112,7 @@ func (f *FriendNotificationSender) getUsersInfoMap(
} }
//nolint:unused //nolint:unused
func (f *FriendNotificationSender) getFromToUserNickname( func (f *FriendNotificationSender) getFromToUserNickname(ctx context.Context, fromUserID, toUserID string) (string, string, error) {
ctx context.Context,
fromUserID, toUserID string,
) (string, string, error) {
users, err := f.getUsersInfoMap(ctx, []string{fromUserID, toUserID}) users, err := f.getUsersInfoMap(ctx, []string{fromUserID, toUserID})
if err != nil { if err != nil {
return "", "", nil return "", "", nil
@@ -131,60 +125,113 @@ func (f *FriendNotificationSender) UserInfoUpdatedNotification(ctx context.Conte
f.Notification(ctx, mcontext.GetOpUserID(ctx), changedUserID, constant.UserInfoUpdatedNotification, &tips) f.Notification(ctx, mcontext.GetOpUserID(ctx), changedUserID, constant.UserInfoUpdatedNotification, &tips)
} }
func (f *FriendNotificationSender) getCommonUserMap(ctx context.Context, userIDs []string) (map[string]common_user.CommonUser, error) {
users, err := f.getUsersInfo(ctx, userIDs)
if err != nil {
return nil, err
}
return datautil.SliceToMap(users, func(e common_user.CommonUser) string {
return e.GetUserID()
}), nil
}
func (f *FriendNotificationSender) getFriendRequests(ctx context.Context, fromUserID, toUserID string) (*sdkws.FriendRequest, error) {
if f.db == nil {
return nil, errs.ErrInternalServer.WithDetail("db is nil")
}
friendRequests, err := f.db.FindBothFriendRequests(ctx, fromUserID, toUserID)
if err != nil {
return nil, err
}
requests, err := convert.FriendRequestDB2Pb(ctx, friendRequests, f.getCommonUserMap)
if err != nil {
return nil, err
}
for _, request := range requests {
if request.FromUserID == fromUserID && request.ToUserID == toUserID {
return request, nil
}
}
return nil, errs.ErrRecordNotFound.WrapMsg("friend request not found", "fromUserID", fromUserID, "toUserID", toUserID)
}
func (f *FriendNotificationSender) FriendApplicationAddNotification(ctx context.Context, req *relation.ApplyToAddFriendReq) { func (f *FriendNotificationSender) FriendApplicationAddNotification(ctx context.Context, req *relation.ApplyToAddFriendReq) {
tips := sdkws.FriendApplicationTips{FromToUserID: &sdkws.FromToUserID{ request, err := f.getFriendRequests(ctx, req.FromUserID, req.ToUserID)
FromUserID: req.FromUserID, if err != nil {
ToUserID: req.ToUserID, log.ZError(ctx, "FriendApplicationAddNotification get friend request", err, "fromUserID", req.FromUserID, "toUserID", req.ToUserID)
}} return
}
tips := sdkws.FriendApplicationTips{
FromToUserID: &sdkws.FromToUserID{
FromUserID: req.FromUserID,
ToUserID: req.ToUserID,
},
Request: request,
}
f.Notification(ctx, req.FromUserID, req.ToUserID, constant.FriendApplicationNotification, &tips) f.Notification(ctx, req.FromUserID, req.ToUserID, constant.FriendApplicationNotification, &tips)
} }
func (f *FriendNotificationSender) FriendApplicationAgreedNotification( func (f *FriendNotificationSender) FriendApplicationAgreedNotification(ctx context.Context, req *relation.RespondFriendApplyReq, checkReq bool) {
ctx context.Context, var (
req *relation.RespondFriendApplyReq, request *sdkws.FriendRequest
) { err error
tips := sdkws.FriendApplicationApprovedTips{FromToUserID: &sdkws.FromToUserID{ )
FromUserID: req.FromUserID, if checkReq {
ToUserID: req.ToUserID, request, err = f.getFriendRequests(ctx, req.FromUserID, req.ToUserID)
}, HandleMsg: req.HandleMsg} if err != nil {
log.ZError(ctx, "FriendApplicationAgreedNotification get friend request", err, "fromUserID", req.FromUserID, "toUserID", req.ToUserID)
return
}
}
tips := sdkws.FriendApplicationApprovedTips{
FromToUserID: &sdkws.FromToUserID{
FromUserID: req.FromUserID,
ToUserID: req.ToUserID,
},
HandleMsg: req.HandleMsg,
Request: request,
}
f.Notification(ctx, req.ToUserID, req.FromUserID, constant.FriendApplicationApprovedNotification, &tips) f.Notification(ctx, req.ToUserID, req.FromUserID, constant.FriendApplicationApprovedNotification, &tips)
} }
func (f *FriendNotificationSender) FriendApplicationRefusedNotification( func (f *FriendNotificationSender) FriendApplicationRefusedNotification(ctx context.Context, req *relation.RespondFriendApplyReq) {
ctx context.Context, request, err := f.getFriendRequests(ctx, req.FromUserID, req.ToUserID)
req *relation.RespondFriendApplyReq, if err != nil {
) { log.ZError(ctx, "FriendApplicationRefusedNotification get friend request", err, "fromUserID", req.FromUserID, "toUserID", req.ToUserID)
tips := sdkws.FriendApplicationApprovedTips{FromToUserID: &sdkws.FromToUserID{ return
FromUserID: req.FromUserID, }
ToUserID: req.ToUserID, tips := sdkws.FriendApplicationRejectedTips{
}, HandleMsg: req.HandleMsg} FromToUserID: &sdkws.FromToUserID{
FromUserID: req.FromUserID,
ToUserID: req.ToUserID,
},
HandleMsg: req.HandleMsg,
Request: request,
}
f.Notification(ctx, req.ToUserID, req.FromUserID, constant.FriendApplicationRejectedNotification, &tips) f.Notification(ctx, req.ToUserID, req.FromUserID, constant.FriendApplicationRejectedNotification, &tips)
} }
func (f *FriendNotificationSender) FriendAddedNotification( //func (f *FriendNotificationSender) FriendAddedNotification(ctx context.Context, operationID, opUserID, fromUserID, toUserID string) error {
ctx context.Context, // tips := sdkws.FriendAddedTips{Friend: &sdkws.FriendInfo{}, OpUser: &sdkws.PublicUserInfo{}}
operationID, opUserID, fromUserID, toUserID string, // user, err := f.getUsersInfo(ctx, []string{opUserID})
) error { // if err != nil {
tips := sdkws.FriendAddedTips{Friend: &sdkws.FriendInfo{}, OpUser: &sdkws.PublicUserInfo{}} // return err
user, err := f.getUsersInfo(ctx, []string{opUserID}) // }
if err != nil { // tips.OpUser.UserID = user[0].GetUserID()
return err // tips.OpUser.Ex = user[0].GetEx()
} // tips.OpUser.Nickname = user[0].GetNickname()
tips.OpUser.UserID = user[0].GetUserID() // tips.OpUser.FaceURL = user[0].GetFaceURL()
tips.OpUser.Ex = user[0].GetEx() // friends, err := f.db.FindFriendsWithError(ctx, fromUserID, []string{toUserID})
tips.OpUser.Nickname = user[0].GetNickname() // if err != nil {
tips.OpUser.FaceURL = user[0].GetFaceURL() // return err
friends, err := f.db.FindFriendsWithError(ctx, fromUserID, []string{toUserID}) // }
if err != nil { // tips.Friend, err = convert.FriendDB2Pb(ctx, friends[0], f.getUsersInfoMap)
return err // if err != nil {
} // return err
tips.Friend, err = convert.FriendDB2Pb(ctx, friends[0], f.getUsersInfoMap) // }
if err != nil { // f.Notification(ctx, fromUserID, toUserID, constant.FriendAddedNotification, &tips)
return err // return nil
} //}
f.Notification(ctx, fromUserID, toUserID, constant.FriendAddedNotification, &tips)
return nil
}
func (f *FriendNotificationSender) FriendDeletedNotification(ctx context.Context, req *relation.DeleteFriendReq) { func (f *FriendNotificationSender) FriendDeletedNotification(ctx context.Context, req *relation.DeleteFriendReq) {
tips := sdkws.FriendDeletedTips{FromToUserID: &sdkws.FromToUserID{ tips := sdkws.FriendDeletedTips{FromToUserID: &sdkws.FromToUserID{
+5 -1
View File
@@ -2,10 +2,11 @@ package relation
import ( import (
"context" "context"
"slices"
"github.com/openimsdk/open-im-server/v3/pkg/util/hashutil" "github.com/openimsdk/open-im-server/v3/pkg/util/hashutil"
"github.com/openimsdk/protocol/sdkws" "github.com/openimsdk/protocol/sdkws"
"github.com/openimsdk/tools/log" "github.com/openimsdk/tools/log"
"slices"
"github.com/openimsdk/open-im-server/v3/internal/rpc/incrversion" "github.com/openimsdk/open-im-server/v3/internal/rpc/incrversion"
"github.com/openimsdk/open-im-server/v3/pkg/authverify" "github.com/openimsdk/open-im-server/v3/pkg/authverify"
@@ -39,6 +40,9 @@ func (s *friendServer) NotificationUserInfoUpdate(ctx context.Context, req *rela
} }
func (s *friendServer) GetFullFriendUserIDs(ctx context.Context, req *relation.GetFullFriendUserIDsReq) (*relation.GetFullFriendUserIDsResp, error) { func (s *friendServer) GetFullFriendUserIDs(ctx context.Context, req *relation.GetFullFriendUserIDsReq) (*relation.GetFullFriendUserIDsResp, error) {
if err := authverify.CheckAccessV3(ctx, req.UserID, s.config.Share.IMAdminUserID); err != nil {
return nil, err
}
vl, err := s.db.FindMaxFriendVersionCache(ctx, req.UserID) vl, err := s.db.FindMaxFriendVersionCache(ctx, req.UserID)
if err != nil { if err != nil {
return nil, err return nil, err
+6 -2
View File
@@ -19,11 +19,12 @@ import (
"encoding/base64" "encoding/base64"
"encoding/hex" "encoding/hex"
"encoding/json" "encoding/json"
"github.com/openimsdk/open-im-server/v3/pkg/authverify"
"path" "path"
"strconv" "strconv"
"time" "time"
"github.com/openimsdk/open-im-server/v3/pkg/authverify"
"github.com/google/uuid" "github.com/google/uuid"
"github.com/openimsdk/open-im-server/v3/pkg/common/servererrs" "github.com/openimsdk/open-im-server/v3/pkg/common/servererrs"
"github.com/openimsdk/open-im-server/v3/pkg/common/storage/model" "github.com/openimsdk/open-im-server/v3/pkg/common/storage/model"
@@ -37,7 +38,10 @@ import (
) )
func (t *thirdServer) PartLimit(ctx context.Context, req *third.PartLimitReq) (*third.PartLimitResp, error) { func (t *thirdServer) PartLimit(ctx context.Context, req *third.PartLimitReq) (*third.PartLimitResp, error) {
limit := t.s3dataBase.PartLimit() limit, err := t.s3dataBase.PartLimit()
if err != nil {
return nil, err
}
return &third.PartLimitResp{ return &third.PartLimitResp{
MinPartSize: limit.MinPartSize, MinPartSize: limit.MinPartSize,
MaxPartSize: limit.MaxPartSize, MaxPartSize: limit.MaxPartSize,
+11 -7
View File
@@ -17,26 +17,28 @@ package third
import ( import (
"context" "context"
"fmt" "fmt"
"github.com/openimsdk/open-im-server/v3/pkg/rpcli"
"time" "time"
"github.com/openimsdk/open-im-server/v3/pkg/rpcli"
"github.com/openimsdk/tools/s3/aws"
"github.com/openimsdk/tools/s3/disable"
"google.golang.org/grpc"
"github.com/openimsdk/open-im-server/v3/pkg/common/config" "github.com/openimsdk/open-im-server/v3/pkg/common/config"
"github.com/openimsdk/open-im-server/v3/pkg/common/storage/cache/redis" "github.com/openimsdk/open-im-server/v3/pkg/common/storage/cache/redis"
"github.com/openimsdk/open-im-server/v3/pkg/common/storage/controller"
"github.com/openimsdk/open-im-server/v3/pkg/common/storage/database/mgo" "github.com/openimsdk/open-im-server/v3/pkg/common/storage/database/mgo"
"github.com/openimsdk/open-im-server/v3/pkg/localcache" "github.com/openimsdk/open-im-server/v3/pkg/localcache"
"github.com/openimsdk/tools/s3/aws"
"github.com/openimsdk/tools/s3/kodo"
"github.com/openimsdk/open-im-server/v3/pkg/common/storage/controller"
"github.com/openimsdk/protocol/third" "github.com/openimsdk/protocol/third"
"github.com/openimsdk/tools/db/mongoutil" "github.com/openimsdk/tools/db/mongoutil"
"github.com/openimsdk/tools/db/redisutil" "github.com/openimsdk/tools/db/redisutil"
"github.com/openimsdk/tools/discovery" "github.com/openimsdk/tools/discovery"
"github.com/openimsdk/tools/s3" "github.com/openimsdk/tools/s3"
"github.com/openimsdk/tools/s3/cos" "github.com/openimsdk/tools/s3/cos"
"github.com/openimsdk/tools/s3/kodo"
"github.com/openimsdk/tools/s3/minio" "github.com/openimsdk/tools/s3/minio"
"github.com/openimsdk/tools/s3/oss" "github.com/openimsdk/tools/s3/oss"
"google.golang.org/grpc"
) )
type thirdServer struct { type thirdServer struct {
@@ -94,13 +96,15 @@ func Start(ctx context.Context, config *Config, client discovery.SvcDiscoveryReg
o, err = kodo.NewKodo(*config.RpcConfig.Object.Kodo.Build()) o, err = kodo.NewKodo(*config.RpcConfig.Object.Kodo.Build())
case "aws": case "aws":
o, err = aws.NewAws(*config.RpcConfig.Object.Aws.Build()) o, err = aws.NewAws(*config.RpcConfig.Object.Aws.Build())
case "":
o = disable.NewDisable()
default: default:
err = fmt.Errorf("invalid object enable: %s", enable) err = fmt.Errorf("invalid object enable: %s", enable)
} }
if err != nil { if err != nil {
return err return err
} }
userConn, err := client.GetConn(ctx, config.Discovery.RpcService.User) userConn, err := client.GetConn(ctx, config.Share.RpcRegisterName.User)
if err != nil { if err != nil {
return err return err
} }
+3 -2
View File
@@ -16,6 +16,7 @@ package user
import ( import (
"context" "context"
"github.com/openimsdk/open-im-server/v3/pkg/rpcli" "github.com/openimsdk/open-im-server/v3/pkg/rpcli"
"github.com/openimsdk/protocol/msg" "github.com/openimsdk/protocol/msg"
@@ -29,7 +30,7 @@ import (
) )
type UserNotificationSender struct { type UserNotificationSender struct {
*rpcclient.NotificationSender *notification.NotificationSender
getUsersInfo func(ctx context.Context, userIDs []string) ([]common_user.CommonUser, error) getUsersInfo func(ctx context.Context, userIDs []string) ([]common_user.CommonUser, error)
// db controller // db controller
db controller.UserDatabase db controller.UserDatabase
@@ -63,7 +64,7 @@ func WithUserFunc(
func NewUserNotificationSender(config *Config, msgClient *rpcli.MsgClient, opts ...userNotificationSenderOptions) *UserNotificationSender { func NewUserNotificationSender(config *Config, msgClient *rpcli.MsgClient, opts ...userNotificationSenderOptions) *UserNotificationSender {
f := &UserNotificationSender{ f := &UserNotificationSender{
NotificationSender: rpcclient.NewNotificationSender(&config.NotificationConfig, rpcclient.WithRpcClient(func(ctx context.Context, req *msg.SendMsgReq) (*msg.SendMsgResp, error) { NotificationSender: notification.NewNotificationSender(&config.NotificationConfig, notification.WithRpcClient(func(ctx context.Context, req *msg.SendMsgReq) (*msg.SendMsgResp, error) {
return msgClient.SendMsg(ctx, req) return msgClient.SendMsg(ctx, req)
})), })),
} }
+3 -3
View File
@@ -94,15 +94,15 @@ func Start(ctx context.Context, config *Config, client registry.SvcDiscoveryRegi
if err != nil { if err != nil {
return err return err
} }
msgConn, err := client.GetConn(ctx, config.Discovery.RpcService.Msg) msgConn, err := client.GetConn(ctx, config.Share.RpcRegisterName.Msg)
if err != nil { if err != nil {
return err return err
} }
groupConn, err := client.GetConn(ctx, config.Discovery.RpcService.Group) groupConn, err := client.GetConn(ctx, config.Share.RpcRegisterName.Group)
if err != nil { if err != nil {
return err return err
} }
friendConn, err := client.GetConn(ctx, config.Discovery.RpcService.Friend) friendConn, err := client.GetConn(ctx, config.Share.RpcRegisterName.Friend)
if err != nil { if err != nil {
return err return err
} }
+25 -27
View File
@@ -1,19 +1,30 @@
// Copyright © 2023 OpenIM. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package tools package tools
import ( import (
"context" "context"
"github.com/openimsdk/open-im-server/v3/pkg/common/config" "github.com/openimsdk/open-im-server/v3/pkg/common/config"
kdisc "github.com/openimsdk/open-im-server/v3/pkg/common/discovery" kdisc "github.com/openimsdk/open-im-server/v3/pkg/common/discoveryregister"
disetcd "github.com/openimsdk/open-im-server/v3/pkg/common/discovery/etcd"
pbconversation "github.com/openimsdk/protocol/conversation" pbconversation "github.com/openimsdk/protocol/conversation"
"github.com/openimsdk/protocol/msg" "github.com/openimsdk/protocol/msg"
"github.com/openimsdk/protocol/third" "github.com/openimsdk/protocol/third"
"github.com/openimsdk/tools/discovery/etcd"
"github.com/openimsdk/tools/mcontext" "github.com/openimsdk/tools/mcontext"
"github.com/openimsdk/tools/mw" "github.com/openimsdk/tools/mw"
"github.com/openimsdk/tools/utils/runtimeenv"
"google.golang.org/grpc" "google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure" "google.golang.org/grpc/credentials/insecure"
@@ -26,51 +37,38 @@ type CronTaskConfig struct {
CronTask config.CronTask CronTask config.CronTask
Share config.Share Share config.Share
Discovery config.Discovery Discovery config.Discovery
runTimeEnv string
} }
func Start(ctx context.Context, conf *CronTaskConfig) error { func Start(ctx context.Context, config *CronTaskConfig) error {
conf.runTimeEnv = runtimeenv.PrintRuntimeEnvironment() log.CInfo(ctx, "CRON-TASK server is initializing", "chatRecordsClearTime", config.CronTask.CronExecuteTime, "msgDestructTime", config.CronTask.RetainChatRecords)
if config.CronTask.RetainChatRecords < 1 {
log.CInfo(ctx, "CRON-TASK server is initializing", "runTimeEnv", conf.runTimeEnv, "chatRecordsClearTime", conf.CronTask.CronExecuteTime, "msgDestructTime", conf.CronTask.RetainChatRecords)
if conf.CronTask.RetainChatRecords < 1 {
return errs.New("msg destruct time must be greater than 1").Wrap() return errs.New("msg destruct time must be greater than 1").Wrap()
} }
client, err := kdisc.NewDiscoveryRegister(&conf.Discovery, conf.runTimeEnv, nil) client, err := kdisc.NewDiscoveryRegister(&config.Discovery, &config.Share, nil)
if err != nil { if err != nil {
return errs.WrapMsg(err, "failed to register discovery service") return errs.WrapMsg(err, "failed to register discovery service")
} }
client.AddOption(mw.GrpcClient(), grpc.WithTransportCredentials(insecure.NewCredentials())) client.AddOption(mw.GrpcClient(), grpc.WithTransportCredentials(insecure.NewCredentials()))
ctx = mcontext.SetOpUserID(ctx, conf.Share.IMAdminUserID[0]) ctx = mcontext.SetOpUserID(ctx, config.Share.IMAdminUserID[0])
msgConn, err := client.GetConn(ctx, conf.Discovery.RpcService.Msg) msgConn, err := client.GetConn(ctx, config.Share.RpcRegisterName.Msg)
if err != nil { if err != nil {
return err return err
} }
thirdConn, err := client.GetConn(ctx, conf.Discovery.RpcService.Third) thirdConn, err := client.GetConn(ctx, config.Share.RpcRegisterName.Third)
if err != nil { if err != nil {
return err return err
} }
conversationConn, err := client.GetConn(ctx, conf.Discovery.RpcService.Conversation) conversationConn, err := client.GetConn(ctx, config.Share.RpcRegisterName.Conversation)
if err != nil { if err != nil {
return err return err
} }
if conf.Discovery.Enable == config.ETCD {
cm := disetcd.NewConfigManager(client.(*etcd.SvcDiscoveryRegistryImpl).GetClient(), []string{
conf.CronTask.GetConfigFileName(),
conf.Share.GetConfigFileName(),
conf.Discovery.GetConfigFileName(),
})
cm.Watch(ctx)
}
srv := &cronServer{ srv := &cronServer{
ctx: ctx, ctx: ctx,
config: conf, config: config,
cron: cron.New(), cron: cron.New(),
msgClient: msg.NewMsgClient(msgConn), msgClient: msg.NewMsgClient(msgConn),
conversationClient: pbconversation.NewConversationClient(conversationConn), conversationClient: pbconversation.NewConversationClient(conversationConn),
@@ -86,7 +84,7 @@ func Start(ctx context.Context, conf *CronTaskConfig) error {
if err := srv.registerClearUserMsg(); err != nil { if err := srv.registerClearUserMsg(); err != nil {
return err return err
} }
log.ZDebug(ctx, "start cron task", "CronExecuteTime", conf.CronTask.CronExecuteTime) log.ZDebug(ctx, "start cron task", "CronExecuteTime", config.CronTask.CronExecuteTime)
srv.cron.Start() srv.cron.Start()
<-ctx.Done() <-ctx.Done()
return nil return nil
+63
View File
@@ -0,0 +1,63 @@
package tools
import (
"context"
"github.com/openimsdk/open-im-server/v3/pkg/common/config"
kdisc "github.com/openimsdk/open-im-server/v3/pkg/common/discoveryregister"
pbconversation "github.com/openimsdk/protocol/conversation"
"github.com/openimsdk/protocol/msg"
"github.com/openimsdk/protocol/third"
"github.com/openimsdk/tools/mcontext"
"github.com/openimsdk/tools/mw"
"github.com/robfig/cron/v3"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
"testing"
)
func TestName(t *testing.T) {
conf := &config.Discovery{
Enable: config.ETCD,
Etcd: config.Etcd{
RootDirectory: "openim",
Address: []string{"localhost:12379"},
},
}
client, err := kdisc.NewDiscoveryRegister(conf, "source")
if err != nil {
panic(err)
}
client.AddOption(mw.GrpcClient(), grpc.WithTransportCredentials(insecure.NewCredentials()))
ctx := mcontext.SetOpUserID(context.Background(), "imAdmin")
msgConn, err := client.GetConn(ctx, "msg-rpc-service")
if err != nil {
panic(err)
}
thirdConn, err := client.GetConn(ctx, "third-rpc-service")
if err != nil {
panic(err)
}
conversationConn, err := client.GetConn(ctx, "conversation-rpc-service")
if err != nil {
panic(err)
}
srv := &cronServer{
ctx: ctx,
config: &CronTaskConfig{
CronTask: config.CronTask{
RetainChatRecords: 1,
FileExpireTime: 1,
DeleteObjectType: []string{"msg-picture", "msg-file", "msg-voice", "msg-video", "msg-video-snapshot", "sdklog", ""},
},
},
cron: cron.New(),
msgClient: msg.NewMsgClient(msgConn),
conversationClient: pbconversation.NewConversationClient(conversationConn),
thirdClient: third.NewThirdClient(thirdConn),
}
srv.deleteMsg()
//srv.clearS3()
//srv.clearUserMsg()
}
+11 -2
View File
@@ -4,14 +4,23 @@
package main package main
import ( import (
"github.com/openimsdk/gomake/mageutil" "flag"
"os" "os"
"github.com/openimsdk/gomake/mageutil"
) )
var Default = Build var Default = Build
func Build() { func Build() {
mageutil.Build() flag.Parse()
bin := flag.Args()
if len(bin) != 0 {
bin = bin[1:]
}
mageutil.Build(bin)
} }
func Start() { func Start() {
-24
View File
@@ -1,24 +0,0 @@
package apistruct
type GetConfigReq struct {
ConfigName string `json:"configName"`
}
type GetConfigListResp struct {
Environment string `json:"environment"`
Version string `json:"version"`
ConfigNames []string `json:"configNames"`
}
type SetConfigReq struct {
ConfigName string `json:"configName"`
Data string `json:"data"`
}
type SetEnableConfigManagerReq struct {
Enable bool `json:"enable"`
}
type GetEnableConfigManagerResp struct {
Enable bool `json:"enable"`
}
+4 -25
View File
@@ -18,7 +18,6 @@ import (
"context" "context"
"github.com/openimsdk/open-im-server/v3/internal/api" "github.com/openimsdk/open-im-server/v3/internal/api"
"github.com/openimsdk/open-im-server/v3/pkg/common/config"
"github.com/openimsdk/open-im-server/v3/version" "github.com/openimsdk/open-im-server/v3/version"
"github.com/openimsdk/tools/system/program" "github.com/openimsdk/tools/system/program"
"github.com/spf13/cobra" "github.com/spf13/cobra"
@@ -32,36 +31,16 @@ type ApiCmd struct {
} }
func NewApiCmd() *ApiCmd { func NewApiCmd() *ApiCmd {
apiConfig := api.Config{AllConfig: &config.AllConfig{}} var apiConfig api.Config
ret := &ApiCmd{apiConfig: &apiConfig} ret := &ApiCmd{apiConfig: &apiConfig}
ret.configMap = map[string]any{ ret.configMap = map[string]any{
config.DiscoveryConfigFilename: &apiConfig.Discovery, OpenIMAPICfgFileName: &apiConfig.API,
config.KafkaConfigFileName: &apiConfig.Kafka, ShareFileName: &apiConfig.Share,
config.LocalCacheConfigFileName: &apiConfig.LocalCache, DiscoveryConfigFilename: &apiConfig.Discovery,
config.LogConfigFileName: &apiConfig.Log,
config.MinioConfigFileName: &apiConfig.Minio,
config.MongodbConfigFileName: &apiConfig.Mongo,
config.NotificationFileName: &apiConfig.Notification,
config.OpenIMAPICfgFileName: &apiConfig.API,
config.OpenIMCronTaskCfgFileName: &apiConfig.CronTask,
config.OpenIMMsgGatewayCfgFileName: &apiConfig.MsgGateway,
config.OpenIMMsgTransferCfgFileName: &apiConfig.MsgTransfer,
config.OpenIMPushCfgFileName: &apiConfig.Push,
config.OpenIMRPCAuthCfgFileName: &apiConfig.Auth,
config.OpenIMRPCConversationCfgFileName: &apiConfig.Conversation,
config.OpenIMRPCFriendCfgFileName: &apiConfig.Friend,
config.OpenIMRPCGroupCfgFileName: &apiConfig.Group,
config.OpenIMRPCMsgCfgFileName: &apiConfig.Msg,
config.OpenIMRPCThirdCfgFileName: &apiConfig.Third,
config.OpenIMRPCUserCfgFileName: &apiConfig.User,
config.RedisConfigFileName: &apiConfig.Redis,
config.ShareFileName: &apiConfig.Share,
config.WebhooksConfigFileName: &apiConfig.Webhooks,
} }
ret.RootCmd = NewRootCmd(program.GetProcessName(), WithConfigMap(ret.configMap)) ret.RootCmd = NewRootCmd(program.GetProcessName(), WithConfigMap(ret.configMap))
ret.ctx = context.WithValue(context.Background(), "version", version.Version) ret.ctx = context.WithValue(context.Background(), "version", version.Version)
ret.Command.RunE = func(cmd *cobra.Command, args []string) error { ret.Command.RunE = func(cmd *cobra.Command, args []string) error {
apiConfig.ConfigPath = ret.configPath
return ret.runE() return ret.runE()
} }
return ret return ret
+6 -13
View File
@@ -18,7 +18,6 @@ import (
"context" "context"
"github.com/openimsdk/open-im-server/v3/internal/rpc/auth" "github.com/openimsdk/open-im-server/v3/internal/rpc/auth"
"github.com/openimsdk/open-im-server/v3/pkg/common/config"
"github.com/openimsdk/open-im-server/v3/pkg/common/startrpc" "github.com/openimsdk/open-im-server/v3/pkg/common/startrpc"
"github.com/openimsdk/open-im-server/v3/version" "github.com/openimsdk/open-im-server/v3/version"
"github.com/openimsdk/tools/system/program" "github.com/openimsdk/tools/system/program"
@@ -36,10 +35,10 @@ func NewAuthRpcCmd() *AuthRpcCmd {
var authConfig auth.Config var authConfig auth.Config
ret := &AuthRpcCmd{authConfig: &authConfig} ret := &AuthRpcCmd{authConfig: &authConfig}
ret.configMap = map[string]any{ ret.configMap = map[string]any{
config.OpenIMRPCAuthCfgFileName: &authConfig.RpcConfig, OpenIMRPCAuthCfgFileName: &authConfig.RpcConfig,
config.RedisConfigFileName: &authConfig.RedisConfig, RedisConfigFileName: &authConfig.RedisConfig,
config.ShareFileName: &authConfig.Share, ShareFileName: &authConfig.Share,
config.DiscoveryConfigFilename: &authConfig.Discovery, DiscoveryConfigFilename: &authConfig.Discovery,
} }
ret.RootCmd = NewRootCmd(program.GetProcessName(), WithConfigMap(ret.configMap)) ret.RootCmd = NewRootCmd(program.GetProcessName(), WithConfigMap(ret.configMap))
ret.ctx = context.WithValue(context.Background(), "version", version.Version) ret.ctx = context.WithValue(context.Background(), "version", version.Version)
@@ -57,15 +56,9 @@ func (a *AuthRpcCmd) Exec() error {
func (a *AuthRpcCmd) runE() error { func (a *AuthRpcCmd) runE() error {
return startrpc.Start(a.ctx, &a.authConfig.Discovery, &a.authConfig.RpcConfig.Prometheus, a.authConfig.RpcConfig.RPC.ListenIP, return startrpc.Start(a.ctx, &a.authConfig.Discovery, &a.authConfig.RpcConfig.Prometheus, a.authConfig.RpcConfig.RPC.ListenIP,
a.authConfig.RpcConfig.RPC.RegisterIP, a.authConfig.RpcConfig.RPC.AutoSetPorts, a.authConfig.RpcConfig.RPC.Ports, a.authConfig.RpcConfig.RPC.RegisterIP, a.authConfig.RpcConfig.RPC.AutoSetPorts, a.authConfig.RpcConfig.RPC.Ports,
a.Index(), a.authConfig.Discovery.RpcService.Auth, nil, a.authConfig, a.Index(), a.authConfig.Share.RpcRegisterName.Auth, &a.authConfig.Share, a.authConfig,
[]string{ []string{
a.authConfig.RpcConfig.GetConfigFileName(), a.authConfig.Share.RpcRegisterName.MessageGateway,
a.authConfig.Share.GetConfigFileName(),
a.authConfig.RedisConfig.GetConfigFileName(),
a.authConfig.Discovery.GetConfigFileName(),
},
[]string{
a.authConfig.Discovery.RpcService.MessageGateway,
}, },
auth.Start) auth.Start)
} }
+9 -18
View File
@@ -18,7 +18,6 @@ import (
"context" "context"
"github.com/openimsdk/open-im-server/v3/internal/rpc/conversation" "github.com/openimsdk/open-im-server/v3/internal/rpc/conversation"
"github.com/openimsdk/open-im-server/v3/pkg/common/config"
"github.com/openimsdk/open-im-server/v3/pkg/common/startrpc" "github.com/openimsdk/open-im-server/v3/pkg/common/startrpc"
"github.com/openimsdk/open-im-server/v3/version" "github.com/openimsdk/open-im-server/v3/version"
"github.com/openimsdk/tools/system/program" "github.com/openimsdk/tools/system/program"
@@ -36,13 +35,13 @@ func NewConversationRpcCmd() *ConversationRpcCmd {
var conversationConfig conversation.Config var conversationConfig conversation.Config
ret := &ConversationRpcCmd{conversationConfig: &conversationConfig} ret := &ConversationRpcCmd{conversationConfig: &conversationConfig}
ret.configMap = map[string]any{ ret.configMap = map[string]any{
config.OpenIMRPCConversationCfgFileName: &conversationConfig.RpcConfig, OpenIMRPCConversationCfgFileName: &conversationConfig.RpcConfig,
config.RedisConfigFileName: &conversationConfig.RedisConfig, RedisConfigFileName: &conversationConfig.RedisConfig,
config.MongodbConfigFileName: &conversationConfig.MongodbConfig, MongodbConfigFileName: &conversationConfig.MongodbConfig,
config.ShareFileName: &conversationConfig.Share, ShareFileName: &conversationConfig.Share,
config.NotificationFileName: &conversationConfig.NotificationConfig, NotificationFileName: &conversationConfig.NotificationConfig,
config.LocalCacheConfigFileName: &conversationConfig.LocalCacheConfig, LocalCacheConfigFileName: &conversationConfig.LocalCacheConfig,
config.DiscoveryConfigFilename: &conversationConfig.Discovery, DiscoveryConfigFilename: &conversationConfig.Discovery,
} }
ret.RootCmd = NewRootCmd(program.GetProcessName(), WithConfigMap(ret.configMap)) ret.RootCmd = NewRootCmd(program.GetProcessName(), WithConfigMap(ret.configMap))
ret.ctx = context.WithValue(context.Background(), "version", version.Version) ret.ctx = context.WithValue(context.Background(), "version", version.Version)
@@ -59,15 +58,7 @@ func (a *ConversationRpcCmd) Exec() error {
func (a *ConversationRpcCmd) runE() error { func (a *ConversationRpcCmd) runE() error {
return startrpc.Start(a.ctx, &a.conversationConfig.Discovery, &a.conversationConfig.RpcConfig.Prometheus, a.conversationConfig.RpcConfig.RPC.ListenIP, return startrpc.Start(a.ctx, &a.conversationConfig.Discovery, &a.conversationConfig.RpcConfig.Prometheus, a.conversationConfig.RpcConfig.RPC.ListenIP,
a.conversationConfig.RpcConfig.RPC.RegisterIP, a.conversationConfig.RpcConfig.RPC.AutoSetPorts, a.conversationConfig.RpcConfig.RPC.Ports, a.conversationConfig.RpcConfig.RPC.RegisterIP, a.conversationConfig.RpcConfig.RPC.AutoSetPorts, a.conversationConfig.RpcConfig.RPC.Ports,
a.Index(), a.conversationConfig.Discovery.RpcService.Conversation, &a.conversationConfig.NotificationConfig, a.conversationConfig, a.Index(), a.conversationConfig.Share.RpcRegisterName.Conversation, &a.conversationConfig.Share, a.conversationConfig,
[]string{ nil,
a.conversationConfig.RpcConfig.GetConfigFileName(),
a.conversationConfig.RedisConfig.GetConfigFileName(),
a.conversationConfig.MongodbConfig.GetConfigFileName(),
a.conversationConfig.NotificationConfig.GetConfigFileName(),
a.conversationConfig.Share.GetConfigFileName(),
a.conversationConfig.LocalCacheConfig.GetConfigFileName(),
a.conversationConfig.Discovery.GetConfigFileName(),
}, nil,
conversation.Start) conversation.Start)
} }
+3 -4
View File
@@ -18,7 +18,6 @@ import (
"context" "context"
"github.com/openimsdk/open-im-server/v3/internal/tools" "github.com/openimsdk/open-im-server/v3/internal/tools"
"github.com/openimsdk/open-im-server/v3/pkg/common/config"
"github.com/openimsdk/open-im-server/v3/version" "github.com/openimsdk/open-im-server/v3/version"
"github.com/openimsdk/tools/system/program" "github.com/openimsdk/tools/system/program"
"github.com/spf13/cobra" "github.com/spf13/cobra"
@@ -35,9 +34,9 @@ func NewCronTaskCmd() *CronTaskCmd {
var cronTaskConfig tools.CronTaskConfig var cronTaskConfig tools.CronTaskConfig
ret := &CronTaskCmd{cronTaskConfig: &cronTaskConfig} ret := &CronTaskCmd{cronTaskConfig: &cronTaskConfig}
ret.configMap = map[string]any{ ret.configMap = map[string]any{
config.OpenIMCronTaskCfgFileName: &cronTaskConfig.CronTask, OpenIMCronTaskCfgFileName: &cronTaskConfig.CronTask,
config.ShareFileName: &cronTaskConfig.Share, ShareFileName: &cronTaskConfig.Share,
config.DiscoveryConfigFilename: &cronTaskConfig.Discovery, DiscoveryConfigFilename: &cronTaskConfig.Discovery,
} }
ret.RootCmd = NewRootCmd(program.GetProcessName(), WithConfigMap(ret.configMap)) ret.RootCmd = NewRootCmd(program.GetProcessName(), WithConfigMap(ret.configMap))
ret.ctx = context.WithValue(context.Background(), "version", version.Version) ret.ctx = context.WithValue(context.Background(), "version", version.Version)
+10 -20
View File
@@ -18,7 +18,6 @@ import (
"context" "context"
"github.com/openimsdk/open-im-server/v3/internal/rpc/relation" "github.com/openimsdk/open-im-server/v3/internal/rpc/relation"
"github.com/openimsdk/open-im-server/v3/pkg/common/config"
"github.com/openimsdk/open-im-server/v3/pkg/common/startrpc" "github.com/openimsdk/open-im-server/v3/pkg/common/startrpc"
"github.com/openimsdk/open-im-server/v3/version" "github.com/openimsdk/open-im-server/v3/version"
"github.com/openimsdk/tools/system/program" "github.com/openimsdk/tools/system/program"
@@ -36,14 +35,14 @@ func NewFriendRpcCmd() *FriendRpcCmd {
var relationConfig relation.Config var relationConfig relation.Config
ret := &FriendRpcCmd{relationConfig: &relationConfig} ret := &FriendRpcCmd{relationConfig: &relationConfig}
ret.configMap = map[string]any{ ret.configMap = map[string]any{
config.OpenIMRPCFriendCfgFileName: &relationConfig.RpcConfig, OpenIMRPCFriendCfgFileName: &relationConfig.RpcConfig,
config.RedisConfigFileName: &relationConfig.RedisConfig, RedisConfigFileName: &relationConfig.RedisConfig,
config.MongodbConfigFileName: &relationConfig.MongodbConfig, MongodbConfigFileName: &relationConfig.MongodbConfig,
config.ShareFileName: &relationConfig.Share, ShareFileName: &relationConfig.Share,
config.NotificationFileName: &relationConfig.NotificationConfig, NotificationFileName: &relationConfig.NotificationConfig,
config.WebhooksConfigFileName: &relationConfig.WebhooksConfig, WebhooksConfigFileName: &relationConfig.WebhooksConfig,
config.LocalCacheConfigFileName: &relationConfig.LocalCacheConfig, LocalCacheConfigFileName: &relationConfig.LocalCacheConfig,
config.DiscoveryConfigFilename: &relationConfig.Discovery, DiscoveryConfigFilename: &relationConfig.Discovery,
} }
ret.RootCmd = NewRootCmd(program.GetProcessName(), WithConfigMap(ret.configMap)) ret.RootCmd = NewRootCmd(program.GetProcessName(), WithConfigMap(ret.configMap))
ret.ctx = context.WithValue(context.Background(), "version", version.Version) ret.ctx = context.WithValue(context.Background(), "version", version.Version)
@@ -60,16 +59,7 @@ func (a *FriendRpcCmd) Exec() error {
func (a *FriendRpcCmd) runE() error { func (a *FriendRpcCmd) runE() error {
return startrpc.Start(a.ctx, &a.relationConfig.Discovery, &a.relationConfig.RpcConfig.Prometheus, a.relationConfig.RpcConfig.RPC.ListenIP, return startrpc.Start(a.ctx, &a.relationConfig.Discovery, &a.relationConfig.RpcConfig.Prometheus, a.relationConfig.RpcConfig.RPC.ListenIP,
a.relationConfig.RpcConfig.RPC.RegisterIP, a.relationConfig.RpcConfig.RPC.AutoSetPorts, a.relationConfig.RpcConfig.RPC.Ports, a.relationConfig.RpcConfig.RPC.RegisterIP, a.relationConfig.RpcConfig.RPC.AutoSetPorts, a.relationConfig.RpcConfig.RPC.Ports,
a.Index(), a.relationConfig.Discovery.RpcService.Friend, &a.relationConfig.NotificationConfig, a.relationConfig, a.Index(), a.relationConfig.Share.RpcRegisterName.Friend, &a.relationConfig.Share, a.relationConfig,
[]string{ nil,
a.relationConfig.RpcConfig.GetConfigFileName(),
a.relationConfig.RedisConfig.GetConfigFileName(),
a.relationConfig.MongodbConfig.GetConfigFileName(),
a.relationConfig.NotificationConfig.GetConfigFileName(),
a.relationConfig.Share.GetConfigFileName(),
a.relationConfig.WebhooksConfig.GetConfigFileName(),
a.relationConfig.LocalCacheConfig.GetConfigFileName(),
a.relationConfig.Discovery.GetConfigFileName(),
}, nil,
relation.Start) relation.Start)
} }
+10 -20
View File
@@ -18,7 +18,6 @@ import (
"context" "context"
"github.com/openimsdk/open-im-server/v3/internal/rpc/group" "github.com/openimsdk/open-im-server/v3/internal/rpc/group"
"github.com/openimsdk/open-im-server/v3/pkg/common/config"
"github.com/openimsdk/open-im-server/v3/pkg/common/startrpc" "github.com/openimsdk/open-im-server/v3/pkg/common/startrpc"
"github.com/openimsdk/open-im-server/v3/pkg/common/storage/versionctx" "github.com/openimsdk/open-im-server/v3/pkg/common/storage/versionctx"
"github.com/openimsdk/open-im-server/v3/version" "github.com/openimsdk/open-im-server/v3/version"
@@ -37,14 +36,14 @@ func NewGroupRpcCmd() *GroupRpcCmd {
var groupConfig group.Config var groupConfig group.Config
ret := &GroupRpcCmd{groupConfig: &groupConfig} ret := &GroupRpcCmd{groupConfig: &groupConfig}
ret.configMap = map[string]any{ ret.configMap = map[string]any{
config.OpenIMRPCGroupCfgFileName: &groupConfig.RpcConfig, OpenIMRPCGroupCfgFileName: &groupConfig.RpcConfig,
config.RedisConfigFileName: &groupConfig.RedisConfig, RedisConfigFileName: &groupConfig.RedisConfig,
config.MongodbConfigFileName: &groupConfig.MongodbConfig, MongodbConfigFileName: &groupConfig.MongodbConfig,
config.ShareFileName: &groupConfig.Share, ShareFileName: &groupConfig.Share,
config.NotificationFileName: &groupConfig.NotificationConfig, NotificationFileName: &groupConfig.NotificationConfig,
config.WebhooksConfigFileName: &groupConfig.WebhooksConfig, WebhooksConfigFileName: &groupConfig.WebhooksConfig,
config.LocalCacheConfigFileName: &groupConfig.LocalCacheConfig, LocalCacheConfigFileName: &groupConfig.LocalCacheConfig,
config.DiscoveryConfigFilename: &groupConfig.Discovery, DiscoveryConfigFilename: &groupConfig.Discovery,
} }
ret.RootCmd = NewRootCmd(program.GetProcessName(), WithConfigMap(ret.configMap)) ret.RootCmd = NewRootCmd(program.GetProcessName(), WithConfigMap(ret.configMap))
ret.ctx = context.WithValue(context.Background(), "version", version.Version) ret.ctx = context.WithValue(context.Background(), "version", version.Version)
@@ -61,16 +60,7 @@ func (a *GroupRpcCmd) Exec() error {
func (a *GroupRpcCmd) runE() error { func (a *GroupRpcCmd) runE() error {
return startrpc.Start(a.ctx, &a.groupConfig.Discovery, &a.groupConfig.RpcConfig.Prometheus, a.groupConfig.RpcConfig.RPC.ListenIP, return startrpc.Start(a.ctx, &a.groupConfig.Discovery, &a.groupConfig.RpcConfig.Prometheus, a.groupConfig.RpcConfig.RPC.ListenIP,
a.groupConfig.RpcConfig.RPC.RegisterIP, a.groupConfig.RpcConfig.RPC.AutoSetPorts, a.groupConfig.RpcConfig.RPC.Ports, a.groupConfig.RpcConfig.RPC.RegisterIP, a.groupConfig.RpcConfig.RPC.AutoSetPorts, a.groupConfig.RpcConfig.RPC.Ports,
a.Index(), a.groupConfig.Discovery.RpcService.Group, &a.groupConfig.NotificationConfig, a.groupConfig, a.Index(), a.groupConfig.Share.RpcRegisterName.Group, &a.groupConfig.Share, a.groupConfig,
[]string{ nil,
a.groupConfig.RpcConfig.GetConfigFileName(),
a.groupConfig.RedisConfig.GetConfigFileName(),
a.groupConfig.MongodbConfig.GetConfigFileName(),
a.groupConfig.NotificationConfig.GetConfigFileName(),
a.groupConfig.Share.GetConfigFileName(),
a.groupConfig.WebhooksConfig.GetConfigFileName(),
a.groupConfig.LocalCacheConfig.GetConfigFileName(),
a.groupConfig.Discovery.GetConfigFileName(),
}, nil,
group.Start, versionctx.EnableVersionCtx()) group.Start, versionctx.EnableVersionCtx())
} }
+11 -22
View File
@@ -18,7 +18,6 @@ import (
"context" "context"
"github.com/openimsdk/open-im-server/v3/internal/rpc/msg" "github.com/openimsdk/open-im-server/v3/internal/rpc/msg"
"github.com/openimsdk/open-im-server/v3/pkg/common/config"
"github.com/openimsdk/open-im-server/v3/pkg/common/startrpc" "github.com/openimsdk/open-im-server/v3/pkg/common/startrpc"
"github.com/openimsdk/open-im-server/v3/version" "github.com/openimsdk/open-im-server/v3/version"
"github.com/openimsdk/tools/system/program" "github.com/openimsdk/tools/system/program"
@@ -36,15 +35,15 @@ func NewMsgRpcCmd() *MsgRpcCmd {
var msgConfig msg.Config var msgConfig msg.Config
ret := &MsgRpcCmd{msgConfig: &msgConfig} ret := &MsgRpcCmd{msgConfig: &msgConfig}
ret.configMap = map[string]any{ ret.configMap = map[string]any{
config.OpenIMRPCMsgCfgFileName: &msgConfig.RpcConfig, OpenIMRPCMsgCfgFileName: &msgConfig.RpcConfig,
config.RedisConfigFileName: &msgConfig.RedisConfig, RedisConfigFileName: &msgConfig.RedisConfig,
config.MongodbConfigFileName: &msgConfig.MongodbConfig, MongodbConfigFileName: &msgConfig.MongodbConfig,
config.KafkaConfigFileName: &msgConfig.KafkaConfig, KafkaConfigFileName: &msgConfig.KafkaConfig,
config.ShareFileName: &msgConfig.Share, ShareFileName: &msgConfig.Share,
config.NotificationFileName: &msgConfig.NotificationConfig, NotificationFileName: &msgConfig.NotificationConfig,
config.WebhooksConfigFileName: &msgConfig.WebhooksConfig, WebhooksConfigFileName: &msgConfig.WebhooksConfig,
config.LocalCacheConfigFileName: &msgConfig.LocalCacheConfig, LocalCacheConfigFileName: &msgConfig.LocalCacheConfig,
config.DiscoveryConfigFilename: &msgConfig.Discovery, DiscoveryConfigFilename: &msgConfig.Discovery,
} }
ret.RootCmd = NewRootCmd(program.GetProcessName(), WithConfigMap(ret.configMap)) ret.RootCmd = NewRootCmd(program.GetProcessName(), WithConfigMap(ret.configMap))
ret.ctx = context.WithValue(context.Background(), "version", version.Version) ret.ctx = context.WithValue(context.Background(), "version", version.Version)
@@ -61,17 +60,7 @@ func (a *MsgRpcCmd) Exec() error {
func (a *MsgRpcCmd) runE() error { func (a *MsgRpcCmd) runE() error {
return startrpc.Start(a.ctx, &a.msgConfig.Discovery, &a.msgConfig.RpcConfig.Prometheus, a.msgConfig.RpcConfig.RPC.ListenIP, return startrpc.Start(a.ctx, &a.msgConfig.Discovery, &a.msgConfig.RpcConfig.Prometheus, a.msgConfig.RpcConfig.RPC.ListenIP,
a.msgConfig.RpcConfig.RPC.RegisterIP, a.msgConfig.RpcConfig.RPC.AutoSetPorts, a.msgConfig.RpcConfig.RPC.Ports, a.msgConfig.RpcConfig.RPC.RegisterIP, a.msgConfig.RpcConfig.RPC.AutoSetPorts, a.msgConfig.RpcConfig.RPC.Ports,
a.Index(), a.msgConfig.Discovery.RpcService.Msg, &a.msgConfig.NotificationConfig, a.msgConfig, a.Index(), a.msgConfig.Share.RpcRegisterName.Msg, &a.msgConfig.Share, a.msgConfig,
[]string{ nil,
a.msgConfig.RpcConfig.GetConfigFileName(),
a.msgConfig.RedisConfig.GetConfigFileName(),
a.msgConfig.MongodbConfig.GetConfigFileName(),
a.msgConfig.KafkaConfig.GetConfigFileName(),
a.msgConfig.NotificationConfig.GetConfigFileName(),
a.msgConfig.Share.GetConfigFileName(),
a.msgConfig.WebhooksConfig.GetConfigFileName(),
a.msgConfig.LocalCacheConfig.GetConfigFileName(),
a.msgConfig.Discovery.GetConfigFileName(),
}, nil,
msg.Start) msg.Start)
} }
+5 -6
View File
@@ -18,7 +18,6 @@ import (
"context" "context"
"github.com/openimsdk/open-im-server/v3/internal/msggateway" "github.com/openimsdk/open-im-server/v3/internal/msggateway"
"github.com/openimsdk/open-im-server/v3/pkg/common/config"
"github.com/openimsdk/open-im-server/v3/version" "github.com/openimsdk/open-im-server/v3/version"
"github.com/openimsdk/tools/system/program" "github.com/openimsdk/tools/system/program"
@@ -36,11 +35,11 @@ func NewMsgGatewayCmd() *MsgGatewayCmd {
var msgGatewayConfig msggateway.Config var msgGatewayConfig msggateway.Config
ret := &MsgGatewayCmd{msgGatewayConfig: &msgGatewayConfig} ret := &MsgGatewayCmd{msgGatewayConfig: &msgGatewayConfig}
ret.configMap = map[string]any{ ret.configMap = map[string]any{
config.OpenIMMsgGatewayCfgFileName: &msgGatewayConfig.MsgGateway, OpenIMMsgGatewayCfgFileName: &msgGatewayConfig.MsgGateway,
config.ShareFileName: &msgGatewayConfig.Share, ShareFileName: &msgGatewayConfig.Share,
config.RedisConfigFileName: &msgGatewayConfig.RedisConfig, RedisConfigFileName: &msgGatewayConfig.RedisConfig,
config.WebhooksConfigFileName: &msgGatewayConfig.WebhooksConfig, WebhooksConfigFileName: &msgGatewayConfig.WebhooksConfig,
config.DiscoveryConfigFilename: &msgGatewayConfig.Discovery, DiscoveryConfigFilename: &msgGatewayConfig.Discovery,
} }
ret.RootCmd = NewRootCmd(program.GetProcessName(), WithConfigMap(ret.configMap)) ret.RootCmd = NewRootCmd(program.GetProcessName(), WithConfigMap(ret.configMap))
ret.ctx = context.WithValue(context.Background(), "version", version.Version) ret.ctx = context.WithValue(context.Background(), "version", version.Version)
+7 -8
View File
@@ -18,7 +18,6 @@ import (
"context" "context"
"github.com/openimsdk/open-im-server/v3/internal/msgtransfer" "github.com/openimsdk/open-im-server/v3/internal/msgtransfer"
"github.com/openimsdk/open-im-server/v3/pkg/common/config"
"github.com/openimsdk/open-im-server/v3/version" "github.com/openimsdk/open-im-server/v3/version"
"github.com/openimsdk/tools/system/program" "github.com/openimsdk/tools/system/program"
"github.com/spf13/cobra" "github.com/spf13/cobra"
@@ -35,13 +34,13 @@ func NewMsgTransferCmd() *MsgTransferCmd {
var msgTransferConfig msgtransfer.Config var msgTransferConfig msgtransfer.Config
ret := &MsgTransferCmd{msgTransferConfig: &msgTransferConfig} ret := &MsgTransferCmd{msgTransferConfig: &msgTransferConfig}
ret.configMap = map[string]any{ ret.configMap = map[string]any{
config.OpenIMMsgTransferCfgFileName: &msgTransferConfig.MsgTransfer, OpenIMMsgTransferCfgFileName: &msgTransferConfig.MsgTransfer,
config.RedisConfigFileName: &msgTransferConfig.RedisConfig, RedisConfigFileName: &msgTransferConfig.RedisConfig,
config.MongodbConfigFileName: &msgTransferConfig.MongodbConfig, MongodbConfigFileName: &msgTransferConfig.MongodbConfig,
config.KafkaConfigFileName: &msgTransferConfig.KafkaConfig, KafkaConfigFileName: &msgTransferConfig.KafkaConfig,
config.ShareFileName: &msgTransferConfig.Share, ShareFileName: &msgTransferConfig.Share,
config.WebhooksConfigFileName: &msgTransferConfig.WebhooksConfig, WebhooksConfigFileName: &msgTransferConfig.WebhooksConfig,
config.DiscoveryConfigFilename: &msgTransferConfig.Discovery, DiscoveryConfigFilename: &msgTransferConfig.Discovery,
} }
ret.RootCmd = NewRootCmd(program.GetProcessName(), WithConfigMap(ret.configMap)) ret.RootCmd = NewRootCmd(program.GetProcessName(), WithConfigMap(ret.configMap))
ret.ctx = context.WithValue(context.Background(), "version", version.Version) ret.ctx = context.WithValue(context.Background(), "version", version.Version)
+2 -3
View File
@@ -15,7 +15,6 @@
package cmd package cmd
import ( import (
"github.com/openimsdk/open-im-server/v3/pkg/common/config"
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
@@ -27,11 +26,11 @@ func (m *MsgUtilsCmd) AddUserIDFlag() {
m.Command.PersistentFlags().StringP("userID", "u", "", "openIM userID") m.Command.PersistentFlags().StringP("userID", "u", "", "openIM userID")
} }
func (m *MsgUtilsCmd) AddIndexFlag() { func (m *MsgUtilsCmd) AddIndexFlag() {
m.Command.PersistentFlags().IntP(config.FlagTransferIndex, "i", 0, "process startup sequence number") m.Command.PersistentFlags().IntP(FlagTransferIndex, "i", 0, "process startup sequence number")
} }
func (m *MsgUtilsCmd) AddConfigDirFlag() { func (m *MsgUtilsCmd) AddConfigDirFlag() {
m.Command.PersistentFlags().StringP(config.FlagConf, "c", "", "path of config directory") m.Command.PersistentFlags().StringP(FlagConf, "c", "", "path of config directory")
} }
+10 -21
View File
@@ -18,7 +18,6 @@ import (
"context" "context"
"github.com/openimsdk/open-im-server/v3/internal/push" "github.com/openimsdk/open-im-server/v3/internal/push"
"github.com/openimsdk/open-im-server/v3/pkg/common/config"
"github.com/openimsdk/open-im-server/v3/pkg/common/startrpc" "github.com/openimsdk/open-im-server/v3/pkg/common/startrpc"
"github.com/openimsdk/open-im-server/v3/version" "github.com/openimsdk/open-im-server/v3/version"
"github.com/openimsdk/tools/system/program" "github.com/openimsdk/tools/system/program"
@@ -36,14 +35,14 @@ func NewPushRpcCmd() *PushRpcCmd {
var pushConfig push.Config var pushConfig push.Config
ret := &PushRpcCmd{pushConfig: &pushConfig} ret := &PushRpcCmd{pushConfig: &pushConfig}
ret.configMap = map[string]any{ ret.configMap = map[string]any{
config.OpenIMPushCfgFileName: &pushConfig.RpcConfig, OpenIMPushCfgFileName: &pushConfig.RpcConfig,
config.RedisConfigFileName: &pushConfig.RedisConfig, RedisConfigFileName: &pushConfig.RedisConfig,
config.KafkaConfigFileName: &pushConfig.KafkaConfig, KafkaConfigFileName: &pushConfig.KafkaConfig,
config.ShareFileName: &pushConfig.Share, ShareFileName: &pushConfig.Share,
config.NotificationFileName: &pushConfig.NotificationConfig, NotificationFileName: &pushConfig.NotificationConfig,
config.WebhooksConfigFileName: &pushConfig.WebhooksConfig, WebhooksConfigFileName: &pushConfig.WebhooksConfig,
config.LocalCacheConfigFileName: &pushConfig.LocalCacheConfig, LocalCacheConfigFileName: &pushConfig.LocalCacheConfig,
config.DiscoveryConfigFilename: &pushConfig.Discovery, DiscoveryConfigFilename: &pushConfig.Discovery,
} }
ret.RootCmd = NewRootCmd(program.GetProcessName(), WithConfigMap(ret.configMap)) ret.RootCmd = NewRootCmd(program.GetProcessName(), WithConfigMap(ret.configMap))
ret.ctx = context.WithValue(context.Background(), "version", version.Version) ret.ctx = context.WithValue(context.Background(), "version", version.Version)
@@ -61,19 +60,9 @@ func (a *PushRpcCmd) Exec() error {
func (a *PushRpcCmd) runE() error { func (a *PushRpcCmd) runE() error {
return startrpc.Start(a.ctx, &a.pushConfig.Discovery, &a.pushConfig.RpcConfig.Prometheus, a.pushConfig.RpcConfig.RPC.ListenIP, return startrpc.Start(a.ctx, &a.pushConfig.Discovery, &a.pushConfig.RpcConfig.Prometheus, a.pushConfig.RpcConfig.RPC.ListenIP,
a.pushConfig.RpcConfig.RPC.RegisterIP, a.pushConfig.RpcConfig.RPC.AutoSetPorts, a.pushConfig.RpcConfig.RPC.Ports, a.pushConfig.RpcConfig.RPC.RegisterIP, a.pushConfig.RpcConfig.RPC.AutoSetPorts, a.pushConfig.RpcConfig.RPC.Ports,
a.Index(), a.pushConfig.Discovery.RpcService.Push, &a.pushConfig.NotificationConfig, a.pushConfig, a.Index(), a.pushConfig.Share.RpcRegisterName.Push, &a.pushConfig.Share, a.pushConfig,
[]string{ []string{
a.pushConfig.RpcConfig.GetConfigFileName(), a.pushConfig.Share.RpcRegisterName.MessageGateway,
a.pushConfig.RedisConfig.GetConfigFileName(),
a.pushConfig.KafkaConfig.GetConfigFileName(),
a.pushConfig.NotificationConfig.GetConfigFileName(),
a.pushConfig.Share.GetConfigFileName(),
a.pushConfig.WebhooksConfig.GetConfigFileName(),
a.pushConfig.LocalCacheConfig.GetConfigFileName(),
a.pushConfig.Discovery.GetConfigFileName(),
},
[]string{
a.pushConfig.Discovery.RpcService.MessageGateway,
}, },
push.Start) push.Start)
} }
+25 -103
View File
@@ -1,20 +1,28 @@
// Copyright © 2023 OpenIM. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package cmd package cmd
import ( import (
"context"
"encoding/json"
"fmt" "fmt"
"path/filepath"
"github.com/openimsdk/open-im-server/v3/pkg/common/config" "github.com/openimsdk/open-im-server/v3/pkg/common/config"
kdisc "github.com/openimsdk/open-im-server/v3/pkg/common/discovery"
disetcd "github.com/openimsdk/open-im-server/v3/pkg/common/discovery/etcd"
"github.com/openimsdk/open-im-server/v3/version" "github.com/openimsdk/open-im-server/v3/version"
"github.com/openimsdk/tools/discovery/etcd"
"github.com/openimsdk/tools/errs" "github.com/openimsdk/tools/errs"
"github.com/openimsdk/tools/log" "github.com/openimsdk/tools/log"
"github.com/openimsdk/tools/utils/runtimeenv"
"github.com/spf13/cobra" "github.com/spf13/cobra"
clientv3 "go.etcd.io/etcd/client/v3"
) )
type RootCmd struct { type RootCmd struct {
@@ -25,7 +33,6 @@ type RootCmd struct {
log config.Log log config.Log
index int index int
configPath string configPath string
etcdClient *clientv3.Client
} }
func (r *RootCmd) ConfigPath() string { func (r *RootCmd) ConfigPath() string {
@@ -73,49 +80,23 @@ func NewRootCmd(processName string, opts ...func(*CmdOpts)) *RootCmd {
SilenceUsage: true, SilenceUsage: true,
SilenceErrors: false, SilenceErrors: false,
} }
cmd.Flags().StringP(config.FlagConf, "c", "", "path of config directory") cmd.Flags().StringP(FlagConf, "c", "", "path of config directory")
cmd.Flags().IntP(config.FlagTransferIndex, "i", 0, "process startup sequence number") cmd.Flags().IntP(FlagTransferIndex, "i", 0, "process startup sequence number")
rootCmd.Command = cmd rootCmd.Command = cmd
return rootCmd return rootCmd
} }
func (r *RootCmd) initEtcd() error {
configDirectory, _, err := r.getFlag(&r.Command)
if err != nil {
return err
}
disConfig := config.Discovery{}
env := runtimeenv.PrintRuntimeEnvironment()
err = config.Load(configDirectory, config.DiscoveryConfigFilename, config.EnvPrefixMap[config.DiscoveryConfigFilename],
env, &disConfig)
if err != nil {
return err
}
if disConfig.Enable == config.ETCD {
discov, _ := kdisc.NewDiscoveryRegister(&disConfig, env, nil)
r.etcdClient = discov.(*etcd.SvcDiscoveryRegistryImpl).GetClient()
}
return nil
}
func (r *RootCmd) persistentPreRun(cmd *cobra.Command, opts ...func(*CmdOpts)) error { func (r *RootCmd) persistentPreRun(cmd *cobra.Command, opts ...func(*CmdOpts)) error {
if err := r.initEtcd(); err != nil {
return err
}
cmdOpts := r.applyOptions(opts...) cmdOpts := r.applyOptions(opts...)
if err := r.initializeConfiguration(cmd, cmdOpts); err != nil { if err := r.initializeConfiguration(cmd, cmdOpts); err != nil {
return err return err
} }
if err := r.updateConfigFromEtcd(cmdOpts); err != nil {
return err
}
if err := r.initializeLogger(cmdOpts); err != nil { if err := r.initializeLogger(cmdOpts); err != nil {
return errs.WrapMsg(err, "failed to initialize logger") return errs.WrapMsg(err, "failed to initialize logger")
} }
if err := r.etcdClient.Close(); err != nil {
return errs.WrapMsg(err, "failed to close etcd client")
}
return nil return nil
} }
@@ -124,77 +105,18 @@ func (r *RootCmd) initializeConfiguration(cmd *cobra.Command, opts *CmdOpts) err
if err != nil { if err != nil {
return err return err
} }
runtimeEnv := runtimeenv.PrintRuntimeEnvironment()
// Load common configuration file // Load common configuration file
//opts.configMap[ShareFileName] = StructEnvPrefix{EnvPrefix: shareEnvPrefix, ConfigStruct: &r.share} //opts.configMap[ShareFileName] = StructEnvPrefix{EnvPrefix: shareEnvPrefix, ConfigStruct: &r.share}
for configFileName, configStruct := range opts.configMap { for configFileName, configStruct := range opts.configMap {
err := config.Load(configDirectory, configFileName, config.EnvPrefixMap[configFileName], runtimeEnv, configStruct) err := config.LoadConfig(filepath.Join(configDirectory, configFileName),
ConfigEnvPrefixMap[configFileName], configStruct)
if err != nil { if err != nil {
return err return err
} }
} }
// Load common log configuration file // Load common log configuration file
return config.Load(configDirectory, config.LogConfigFileName, config.EnvPrefixMap[config.LogConfigFileName], runtimeEnv, &r.log) return config.LoadConfig(filepath.Join(configDirectory, LogConfigFileName),
} ConfigEnvPrefixMap[LogConfigFileName], &r.log)
func (r *RootCmd) updateConfigFromEtcd(opts *CmdOpts) error {
if r.etcdClient == nil {
return nil
}
ctx := context.TODO()
res, err := r.etcdClient.Get(ctx, disetcd.BuildKey(disetcd.EnableConfigCenterKey))
if err != nil {
log.ZWarn(ctx, "root cmd updateConfigFromEtcd, etcd Get EnableConfigCenterKey err: %v", errs.Wrap(err))
return nil
}
if res.Count == 0 {
return nil
} else {
if string(res.Kvs[0].Value) == disetcd.Disable {
return nil
} else if string(res.Kvs[0].Value) != disetcd.Enable {
return errs.New("unknown EnableConfigCenter value").Wrap()
}
}
update := func(configFileName string, configStruct any) error {
key := disetcd.BuildKey(configFileName)
etcdRes, err := r.etcdClient.Get(ctx, key)
if err != nil {
log.ZWarn(ctx, "root cmd updateConfigFromEtcd, etcd Get err: %v", errs.Wrap(err))
return nil
}
if etcdRes.Count == 0 {
data, err := json.Marshal(configStruct)
if err != nil {
return errs.ErrArgs.WithDetail(err.Error()).Wrap()
}
_, err = r.etcdClient.Put(ctx, disetcd.BuildKey(configFileName), string(data))
if err != nil {
log.ZWarn(ctx, "root cmd updateConfigFromEtcd, etcd Put err: %v", errs.Wrap(err))
}
return nil
}
err = json.Unmarshal(etcdRes.Kvs[0].Value, configStruct)
if err != nil {
return errs.WrapMsg(err, "failed to unmarshal config from etcd")
}
return nil
}
for configFileName, configStruct := range opts.configMap {
if err := update(configFileName, configStruct); err != nil {
return err
}
}
if err := update(config.LogConfigFileName, &r.log); err != nil {
return err
}
// Load common log configuration file
return nil
} }
func (r *RootCmd) applyOptions(opts ...func(*CmdOpts)) *CmdOpts { func (r *RootCmd) applyOptions(opts ...func(*CmdOpts)) *CmdOpts {
@@ -235,12 +157,12 @@ func defaultCmdOpts() *CmdOpts {
} }
func (r *RootCmd) getFlag(cmd *cobra.Command) (string, int, error) { func (r *RootCmd) getFlag(cmd *cobra.Command) (string, int, error) {
configDirectory, err := cmd.Flags().GetString(config.FlagConf) configDirectory, err := cmd.Flags().GetString(FlagConf)
if err != nil { if err != nil {
return "", 0, errs.Wrap(err) return "", 0, errs.Wrap(err)
} }
r.configPath = configDirectory r.configPath = configDirectory
index, err := cmd.Flags().GetInt(config.FlagTransferIndex) index, err := cmd.Flags().GetInt(FlagTransferIndex)
if err != nil { if err != nil {
return "", 0, errs.Wrap(err) return "", 0, errs.Wrap(err)
} }
+10 -20
View File
@@ -18,7 +18,6 @@ import (
"context" "context"
"github.com/openimsdk/open-im-server/v3/internal/rpc/third" "github.com/openimsdk/open-im-server/v3/internal/rpc/third"
"github.com/openimsdk/open-im-server/v3/pkg/common/config"
"github.com/openimsdk/open-im-server/v3/pkg/common/startrpc" "github.com/openimsdk/open-im-server/v3/pkg/common/startrpc"
"github.com/openimsdk/open-im-server/v3/version" "github.com/openimsdk/open-im-server/v3/version"
"github.com/openimsdk/tools/system/program" "github.com/openimsdk/tools/system/program"
@@ -36,14 +35,14 @@ func NewThirdRpcCmd() *ThirdRpcCmd {
var thirdConfig third.Config var thirdConfig third.Config
ret := &ThirdRpcCmd{thirdConfig: &thirdConfig} ret := &ThirdRpcCmd{thirdConfig: &thirdConfig}
ret.configMap = map[string]any{ ret.configMap = map[string]any{
config.OpenIMRPCThirdCfgFileName: &thirdConfig.RpcConfig, OpenIMRPCThirdCfgFileName: &thirdConfig.RpcConfig,
config.RedisConfigFileName: &thirdConfig.RedisConfig, RedisConfigFileName: &thirdConfig.RedisConfig,
config.MongodbConfigFileName: &thirdConfig.MongodbConfig, MongodbConfigFileName: &thirdConfig.MongodbConfig,
config.ShareFileName: &thirdConfig.Share, ShareFileName: &thirdConfig.Share,
config.NotificationFileName: &thirdConfig.NotificationConfig, NotificationFileName: &thirdConfig.NotificationConfig,
config.MinioConfigFileName: &thirdConfig.MinioConfig, MinioConfigFileName: &thirdConfig.MinioConfig,
config.LocalCacheConfigFileName: &thirdConfig.LocalCacheConfig, LocalCacheConfigFileName: &thirdConfig.LocalCacheConfig,
config.DiscoveryConfigFilename: &thirdConfig.Discovery, DiscoveryConfigFilename: &thirdConfig.Discovery,
} }
ret.RootCmd = NewRootCmd(program.GetProcessName(), WithConfigMap(ret.configMap)) ret.RootCmd = NewRootCmd(program.GetProcessName(), WithConfigMap(ret.configMap))
ret.ctx = context.WithValue(context.Background(), "version", version.Version) ret.ctx = context.WithValue(context.Background(), "version", version.Version)
@@ -60,16 +59,7 @@ func (a *ThirdRpcCmd) Exec() error {
func (a *ThirdRpcCmd) runE() error { func (a *ThirdRpcCmd) runE() error {
return startrpc.Start(a.ctx, &a.thirdConfig.Discovery, &a.thirdConfig.RpcConfig.Prometheus, a.thirdConfig.RpcConfig.RPC.ListenIP, return startrpc.Start(a.ctx, &a.thirdConfig.Discovery, &a.thirdConfig.RpcConfig.Prometheus, a.thirdConfig.RpcConfig.RPC.ListenIP,
a.thirdConfig.RpcConfig.RPC.RegisterIP, a.thirdConfig.RpcConfig.RPC.AutoSetPorts, a.thirdConfig.RpcConfig.RPC.Ports, a.thirdConfig.RpcConfig.RPC.RegisterIP, a.thirdConfig.RpcConfig.RPC.AutoSetPorts, a.thirdConfig.RpcConfig.RPC.Ports,
a.Index(), a.thirdConfig.Discovery.RpcService.Third, &a.thirdConfig.NotificationConfig, a.thirdConfig, a.Index(), a.thirdConfig.Share.RpcRegisterName.Third, &a.thirdConfig.Share, a.thirdConfig,
[]string{ nil,
a.thirdConfig.RpcConfig.GetConfigFileName(),
a.thirdConfig.RedisConfig.GetConfigFileName(),
a.thirdConfig.MongodbConfig.GetConfigFileName(),
a.thirdConfig.NotificationConfig.GetConfigFileName(),
a.thirdConfig.Share.GetConfigFileName(),
a.thirdConfig.MinioConfig.GetConfigFileName(),
a.thirdConfig.LocalCacheConfig.GetConfigFileName(),
a.thirdConfig.Discovery.GetConfigFileName(),
}, nil,
third.Start) third.Start)
} }
+11 -22
View File
@@ -18,7 +18,6 @@ import (
"context" "context"
"github.com/openimsdk/open-im-server/v3/internal/rpc/user" "github.com/openimsdk/open-im-server/v3/internal/rpc/user"
"github.com/openimsdk/open-im-server/v3/pkg/common/config"
"github.com/openimsdk/open-im-server/v3/pkg/common/startrpc" "github.com/openimsdk/open-im-server/v3/pkg/common/startrpc"
"github.com/openimsdk/open-im-server/v3/version" "github.com/openimsdk/open-im-server/v3/version"
"github.com/openimsdk/tools/system/program" "github.com/openimsdk/tools/system/program"
@@ -36,15 +35,15 @@ func NewUserRpcCmd() *UserRpcCmd {
var userConfig user.Config var userConfig user.Config
ret := &UserRpcCmd{userConfig: &userConfig} ret := &UserRpcCmd{userConfig: &userConfig}
ret.configMap = map[string]any{ ret.configMap = map[string]any{
config.OpenIMRPCUserCfgFileName: &userConfig.RpcConfig, OpenIMRPCUserCfgFileName: &userConfig.RpcConfig,
config.RedisConfigFileName: &userConfig.RedisConfig, RedisConfigFileName: &userConfig.RedisConfig,
config.MongodbConfigFileName: &userConfig.MongodbConfig, MongodbConfigFileName: &userConfig.MongodbConfig,
config.KafkaConfigFileName: &userConfig.KafkaConfig, KafkaConfigFileName: &userConfig.KafkaConfig,
config.ShareFileName: &userConfig.Share, ShareFileName: &userConfig.Share,
config.NotificationFileName: &userConfig.NotificationConfig, NotificationFileName: &userConfig.NotificationConfig,
config.WebhooksConfigFileName: &userConfig.WebhooksConfig, WebhooksConfigFileName: &userConfig.WebhooksConfig,
config.LocalCacheConfigFileName: &userConfig.LocalCacheConfig, LocalCacheConfigFileName: &userConfig.LocalCacheConfig,
config.DiscoveryConfigFilename: &userConfig.Discovery, DiscoveryConfigFilename: &userConfig.Discovery,
} }
ret.RootCmd = NewRootCmd(program.GetProcessName(), WithConfigMap(ret.configMap)) ret.RootCmd = NewRootCmd(program.GetProcessName(), WithConfigMap(ret.configMap))
ret.ctx = context.WithValue(context.Background(), "version", version.Version) ret.ctx = context.WithValue(context.Background(), "version", version.Version)
@@ -61,17 +60,7 @@ func (a *UserRpcCmd) Exec() error {
func (a *UserRpcCmd) runE() error { func (a *UserRpcCmd) runE() error {
return startrpc.Start(a.ctx, &a.userConfig.Discovery, &a.userConfig.RpcConfig.Prometheus, a.userConfig.RpcConfig.RPC.ListenIP, return startrpc.Start(a.ctx, &a.userConfig.Discovery, &a.userConfig.RpcConfig.Prometheus, a.userConfig.RpcConfig.RPC.ListenIP,
a.userConfig.RpcConfig.RPC.RegisterIP, a.userConfig.RpcConfig.RPC.AutoSetPorts, a.userConfig.RpcConfig.RPC.Ports, a.userConfig.RpcConfig.RPC.RegisterIP, a.userConfig.RpcConfig.RPC.AutoSetPorts, a.userConfig.RpcConfig.RPC.Ports,
a.Index(), a.userConfig.Discovery.RpcService.User, &a.userConfig.NotificationConfig, a.userConfig, a.Index(), a.userConfig.Share.RpcRegisterName.User, &a.userConfig.Share, a.userConfig,
[]string{ nil,
a.userConfig.RpcConfig.GetConfigFileName(),
a.userConfig.RedisConfig.GetConfigFileName(),
a.userConfig.MongodbConfig.GetConfigFileName(),
a.userConfig.KafkaConfig.GetConfigFileName(),
a.userConfig.NotificationConfig.GetConfigFileName(),
a.userConfig.Share.GetConfigFileName(),
a.userConfig.WebhooksConfig.GetConfigFileName(),
a.userConfig.LocalCacheConfig.GetConfigFileName(),
a.userConfig.Discovery.GetConfigFileName(),
}, nil,
user.Start) user.Start)
} }
+16 -212
View File
@@ -18,9 +18,9 @@ import (
"strings" "strings"
"time" "time"
"github.com/openimsdk/open-im-server/v3/pkg/common/storage/kafka"
"github.com/openimsdk/tools/db/mongoutil" "github.com/openimsdk/tools/db/mongoutil"
"github.com/openimsdk/tools/db/redisutil" "github.com/openimsdk/tools/db/redisutil"
"github.com/openimsdk/tools/mq/kafka"
"github.com/openimsdk/tools/s3/aws" "github.com/openimsdk/tools/s3/aws"
"github.com/openimsdk/tools/s3/cos" "github.com/openimsdk/tools/s3/cos"
"github.com/openimsdk/tools/s3/kodo" "github.com/openimsdk/tools/s3/kodo"
@@ -358,7 +358,7 @@ type Redis struct {
Username string `mapstructure:"username"` Username string `mapstructure:"username"`
Password string `mapstructure:"password"` Password string `mapstructure:"password"`
ClusterMode bool `mapstructure:"clusterMode"` ClusterMode bool `mapstructure:"clusterMode"`
DB int `mapstructure:"storage"` DB int `mapstructure:"db"`
MaxRetry int `mapstructure:"maxRetry"` MaxRetry int `mapstructure:"maxRetry"`
PoolSize int `mapstructure:"poolSize"` PoolSize int `mapstructure:"poolSize"`
} }
@@ -380,9 +380,16 @@ type AfterConfig struct {
} }
type Share struct { type Share struct {
Secret string `mapstructure:"secret"` Secret string `mapstructure:"secret"`
IMAdminUserID []string `mapstructure:"imAdminUserID"` RpcRegisterName RpcRegisterName `mapstructure:"rpcRegisterName"`
MultiLogin MultiLogin `mapstructure:"multiLogin"` IMAdminUserID []string `mapstructure:"imAdminUserID"`
MultiLogin MultiLogin `mapstructure:"multiLogin"`
RPCMaxBodySize MaxRequestBody `mapstructure:"rpcMaxBodySize"`
}
type MaxRequestBody struct {
RequestMaxBodySize int `mapstructure:"requestMaxBodySize"`
ResponseMaxBodySize int `mapstructure:"responseMaxBodySize"`
} }
type MultiLogin struct { type MultiLogin struct {
@@ -477,41 +484,11 @@ type ZooKeeper struct {
} }
type Discovery struct { type Discovery struct {
Enable string `mapstructure:"enable"` Enable string `mapstructure:"enable"`
Etcd Etcd `mapstructure:"etcd"` Etcd Etcd `mapstructure:"etcd"`
Kubernetes Kubernetes `mapstructure:"kubernetes"` ZooKeeper ZooKeeper `mapstructure:"zooKeeper"`
RpcService RpcService `mapstructure:"rpcService"`
} }
type RpcService struct {
User string `mapstructure:"user"`
Friend string `mapstructure:"friend"`
Msg string `mapstructure:"msg"`
Push string `mapstructure:"push"`
MessageGateway string `mapstructure:"messageGateway"`
Group string `mapstructure:"group"`
Auth string `mapstructure:"auth"`
Conversation string `mapstructure:"conversation"`
Third string `mapstructure:"third"`
}
func (r *RpcService) GetServiceNames() []string {
return []string{
r.User,
r.Friend,
r.Msg,
r.Push,
r.MessageGateway,
r.Group,
r.Auth,
r.Conversation,
r.Third,
}
}
type Kubernetes struct {
Namespace string `yaml:"namespace"`
}
type Etcd struct { type Etcd struct {
RootDirectory string `mapstructure:"rootDirectory"` RootDirectory string `mapstructure:"rootDirectory"`
Address []string `mapstructure:"address"` Address []string `mapstructure:"address"`
@@ -635,7 +612,7 @@ func (l *CacheConfig) Enable() bool {
return l.Topic != "" && l.SlotNum > 0 && l.SlotSize > 0 return l.Topic != "" && l.SlotNum > 0 && l.SlotSize > 0
} }
const ( var (
DiscoveryConfigFilename = "discovery.yml" DiscoveryConfigFilename = "discovery.yml"
KafkaConfigFileName = "kafka.yml" KafkaConfigFileName = "kafka.yml"
LocalCacheConfigFileName = "local-cache.yml" LocalCacheConfigFileName = "local-cache.yml"
@@ -657,7 +634,6 @@ const (
RedisConfigFileName = "redis.yml" RedisConfigFileName = "redis.yml"
ShareFileName = "share.yml" ShareFileName = "share.yml"
WebhooksConfigFileName = "webhooks.yml" WebhooksConfigFileName = "webhooks.yml"
NotificationFileName = "notification.yml"
) )
func (d *Discovery) GetConfigFileName() string { func (d *Discovery) GetConfigFileName() string {
@@ -747,175 +723,3 @@ func (s *Share) GetConfigFileName() string {
func (w *Webhooks) GetConfigFileName() string { func (w *Webhooks) GetConfigFileName() string {
return WebhooksConfigFileName return WebhooksConfigFileName
} }
func InitNotification(notification *Notification) {
notification.GroupCreated.UnreadCount = false
notification.GroupCreated.ReliabilityLevel = 1
notification.GroupInfoSet.UnreadCount = false
notification.GroupInfoSet.ReliabilityLevel = 1
notification.JoinGroupApplication.UnreadCount = false
notification.JoinGroupApplication.ReliabilityLevel = 1
notification.MemberQuit.UnreadCount = false
notification.MemberQuit.ReliabilityLevel = 1
notification.GroupApplicationAccepted.UnreadCount = false
notification.GroupApplicationAccepted.ReliabilityLevel = 1
notification.GroupApplicationRejected.UnreadCount = false
notification.GroupApplicationRejected.ReliabilityLevel = 1
notification.GroupOwnerTransferred.UnreadCount = false
notification.GroupOwnerTransferred.ReliabilityLevel = 1
notification.MemberKicked.UnreadCount = false
notification.MemberKicked.ReliabilityLevel = 1
notification.MemberInvited.UnreadCount = false
notification.MemberInvited.ReliabilityLevel = 1
notification.MemberEnter.UnreadCount = false
notification.MemberEnter.ReliabilityLevel = 1
notification.GroupDismissed.UnreadCount = false
notification.GroupDismissed.ReliabilityLevel = 1
notification.GroupMuted.UnreadCount = false
notification.GroupMuted.ReliabilityLevel = 1
notification.GroupCancelMuted.UnreadCount = false
notification.GroupCancelMuted.ReliabilityLevel = 1
notification.GroupMemberMuted.UnreadCount = false
notification.GroupMemberMuted.ReliabilityLevel = 1
notification.GroupMemberCancelMuted.UnreadCount = false
notification.GroupMemberCancelMuted.ReliabilityLevel = 1
notification.GroupMemberInfoSet.UnreadCount = false
notification.GroupMemberInfoSet.ReliabilityLevel = 1
notification.GroupMemberSetToAdmin.UnreadCount = false
notification.GroupMemberSetToAdmin.ReliabilityLevel = 1
notification.GroupMemberSetToOrdinary.UnreadCount = false
notification.GroupMemberSetToOrdinary.ReliabilityLevel = 1
notification.GroupInfoSetAnnouncement.UnreadCount = false
notification.GroupInfoSetAnnouncement.ReliabilityLevel = 1
notification.GroupInfoSetName.UnreadCount = false
notification.GroupInfoSetName.ReliabilityLevel = 1
notification.FriendApplicationAdded.UnreadCount = false
notification.FriendApplicationAdded.ReliabilityLevel = 1
notification.FriendApplicationApproved.UnreadCount = false
notification.FriendApplicationApproved.ReliabilityLevel = 1
notification.FriendApplicationRejected.UnreadCount = false
notification.FriendApplicationRejected.ReliabilityLevel = 1
notification.FriendAdded.UnreadCount = false
notification.FriendAdded.ReliabilityLevel = 1
notification.FriendDeleted.UnreadCount = false
notification.FriendDeleted.ReliabilityLevel = 1
notification.FriendRemarkSet.UnreadCount = false
notification.FriendRemarkSet.ReliabilityLevel = 1
notification.BlackAdded.UnreadCount = false
notification.BlackAdded.ReliabilityLevel = 1
notification.BlackDeleted.UnreadCount = false
notification.BlackDeleted.ReliabilityLevel = 1
notification.FriendInfoUpdated.UnreadCount = false
notification.FriendInfoUpdated.ReliabilityLevel = 1
notification.UserInfoUpdated.UnreadCount = false
notification.UserInfoUpdated.ReliabilityLevel = 1
notification.UserStatusChanged.UnreadCount = false
notification.UserStatusChanged.ReliabilityLevel = 1
notification.ConversationChanged.UnreadCount = false
notification.ConversationChanged.ReliabilityLevel = 1
notification.ConversationSetPrivate.UnreadCount = false
notification.ConversationSetPrivate.ReliabilityLevel = 1
}
type AllConfig struct {
Discovery Discovery
Kafka Kafka
LocalCache LocalCache
Log Log
Minio Minio
Mongo Mongo
Notification Notification
API API
CronTask CronTask
MsgGateway MsgGateway
MsgTransfer MsgTransfer
Push Push
Auth Auth
Conversation Conversation
Friend Friend
Group Group
Msg Msg
Third Third
User User
Redis Redis
Share Share
Webhooks Webhooks
}
func (a *AllConfig) Name2Config(name string) any {
switch name {
case a.Discovery.GetConfigFileName():
return a.Discovery
case a.Kafka.GetConfigFileName():
return a.Kafka
case a.LocalCache.GetConfigFileName():
return a.LocalCache
case a.Log.GetConfigFileName():
return a.Log
case a.Minio.GetConfigFileName():
return a.Minio
case a.Mongo.GetConfigFileName():
return a.Mongo
case a.Notification.GetConfigFileName():
return a.Notification
case a.API.GetConfigFileName():
return a.API
case a.CronTask.GetConfigFileName():
return a.CronTask
case a.MsgGateway.GetConfigFileName():
return a.MsgGateway
case a.MsgTransfer.GetConfigFileName():
return a.MsgTransfer
case a.Push.GetConfigFileName():
return a.Push
case a.Auth.GetConfigFileName():
return a.Auth
case a.Conversation.GetConfigFileName():
return a.Conversation
case a.Friend.GetConfigFileName():
return a.Friend
case a.Group.GetConfigFileName():
return a.Group
case a.Msg.GetConfigFileName():
return a.Msg
case a.Third.GetConfigFileName():
return a.Third
case a.User.GetConfigFileName():
return a.User
case a.Redis.GetConfigFileName():
return a.Redis
case a.Share.GetConfigFileName():
return a.Share
case a.Webhooks.GetConfigFileName():
return a.Webhooks
default:
return nil
}
}
func (a *AllConfig) GetConfigNames() []string {
return []string{
a.Discovery.GetConfigFileName(),
a.Kafka.GetConfigFileName(),
a.LocalCache.GetConfigFileName(),
a.Log.GetConfigFileName(),
a.Minio.GetConfigFileName(),
a.Mongo.GetConfigFileName(),
a.Notification.GetConfigFileName(),
a.API.GetConfigFileName(),
a.CronTask.GetConfigFileName(),
a.MsgGateway.GetConfigFileName(),
a.MsgTransfer.GetConfigFileName(),
a.Push.GetConfigFileName(),
a.Auth.GetConfigFileName(),
a.Conversation.GetConfigFileName(),
a.Friend.GetConfigFileName(),
a.Group.GetConfigFileName(),
a.Msg.GetConfigFileName(),
a.Third.GetConfigFileName(),
a.User.GetConfigFileName(),
a.Redis.GetConfigFileName(),
a.Share.GetConfigFileName(),
a.Webhooks.GetConfigFileName(),
}
}
-2
View File
@@ -14,8 +14,6 @@
package config package config
const ConfKey = "conf"
const ( const (
MountConfigFilePath = "CONFIG_PATH" MountConfigFilePath = "CONFIG_PATH"
DeploymentType = "DEPLOYMENT_TYPE" DeploymentType = "DEPLOYMENT_TYPE"
+2 -18
View File
@@ -1,29 +1,13 @@
package config package config
import ( import (
"os"
"path/filepath"
"strings"
"github.com/mitchellh/mapstructure" "github.com/mitchellh/mapstructure"
"github.com/openimsdk/tools/errs" "github.com/openimsdk/tools/errs"
"github.com/spf13/viper" "github.com/spf13/viper"
"strings"
) )
func Load(configDirectory string, configFileName string, envPrefix string, runtimeEnv string, config any) error { func LoadConfig(path string, envPrefix string, config any) error {
if runtimeEnv == KUBERNETES {
mountPath := os.Getenv(MountConfigFilePath)
if mountPath == "" {
return errs.ErrArgs.WrapMsg(MountConfigFilePath + " env is empty")
}
return loadConfig(filepath.Join(mountPath, configFileName), envPrefix, config)
}
return loadConfig(filepath.Join(configDirectory, configFileName), envPrefix, config)
}
func loadConfig(path string, envPrefix string, config any) error {
v := viper.New() v := viper.New()
v.SetConfigFile(path) v.SetConfigFile(path)
v.SetEnvPrefix(envPrefix) v.SetEnvPrefix(envPrefix)
+8 -3
View File
@@ -26,7 +26,9 @@ import (
) )
const ( const (
FileName = "config.yaml" FileName = "config.yaml"
NotificationFileName = "notification.yaml"
DefaultFolderPath = "../config/"
) )
// return absolude path join ../config/, this is k8s container config path. // return absolude path join ../config/, this is k8s container config path.
@@ -56,10 +58,13 @@ func GetProjectRoot() (string, error) {
return projectRoot, nil return projectRoot, nil
} }
func GetOptionsByNotification(cfg NotificationConfig) msgprocessor.Options { func GetOptionsByNotification(cfg NotificationConfig, sendMessage *bool) msgprocessor.Options {
opts := msgprocessor.NewOptions() opts := msgprocessor.NewOptions()
if cfg.UnreadCount { if sendMessage != nil {
cfg.IsSendMsg = *sendMessage
}
if cfg.IsSendMsg {
opts = msgprocessor.WithOptions(opts, msgprocessor.WithUnreadCount(true)) opts = msgprocessor.WithOptions(opts, msgprocessor.WithUnreadCount(true))
} }
if cfg.OfflinePush.Enable { if cfg.OfflinePush.Enable {
+7 -5
View File
@@ -17,7 +17,9 @@ package convert
import ( import (
"context" "context"
"fmt" "fmt"
"github.com/openimsdk/open-im-server/v3/pkg/common/storage/model" "github.com/openimsdk/open-im-server/v3/pkg/common/storage/model"
"github.com/openimsdk/open-im-server/v3/pkg/notification/common_user"
"github.com/openimsdk/protocol/relation" "github.com/openimsdk/protocol/relation"
"github.com/openimsdk/protocol/sdkws" "github.com/openimsdk/protocol/sdkws"
@@ -98,7 +100,7 @@ func FriendOnlyDB2PbOnly(friendsDB []*model.Friend) []*relation.FriendInfoOnly {
}) })
} }
func FriendRequestDB2Pb(ctx context.Context, friendRequests []*model.FriendRequest, getUsers func(ctx context.Context, userIDs []string) (map[string]*sdkws.UserInfo, error)) ([]*sdkws.FriendRequest, error) { func FriendRequestDB2Pb(ctx context.Context, friendRequests []*model.FriendRequest, getUsers func(ctx context.Context, userIDs []string) (map[string]common_user.CommonUser, error)) ([]*sdkws.FriendRequest, error) {
if len(friendRequests) == 0 { if len(friendRequests) == 0 {
return nil, nil return nil, nil
} }
@@ -117,11 +119,11 @@ func FriendRequestDB2Pb(ctx context.Context, friendRequests []*model.FriendReque
fromUser := users[friendRequest.FromUserID] fromUser := users[friendRequest.FromUserID]
res = append(res, &sdkws.FriendRequest{ res = append(res, &sdkws.FriendRequest{
FromUserID: friendRequest.FromUserID, FromUserID: friendRequest.FromUserID,
FromNickname: fromUser.Nickname, FromNickname: fromUser.GetNickname(),
FromFaceURL: fromUser.FaceURL, FromFaceURL: fromUser.GetFaceURL(),
ToUserID: friendRequest.ToUserID, ToUserID: friendRequest.ToUserID,
ToNickname: toUser.Nickname, ToNickname: toUser.GetNickname(),
ToFaceURL: toUser.FaceURL, ToFaceURL: toUser.GetFaceURL(),
HandleResult: friendRequest.HandleResult, HandleResult: friendRequest.HandleResult,
ReqMsg: friendRequest.ReqMsg, ReqMsg: friendRequest.ReqMsg,
CreateTime: friendRequest.CreateTime.UnixMilli(), CreateTime: friendRequest.CreateTime.UnixMilli(),
-106
View File
@@ -1,106 +0,0 @@
package etcd
import (
"context"
"os"
"os/exec"
"runtime"
"sync"
"syscall"
"github.com/openimsdk/tools/errs"
"github.com/openimsdk/tools/log"
"github.com/openimsdk/tools/utils/datautil"
clientv3 "go.etcd.io/etcd/client/v3"
)
var (
ShutDowns []func() error
)
func RegisterShutDown(shutDown ...func() error) {
ShutDowns = append(ShutDowns, shutDown...)
}
type ConfigManager struct {
client *clientv3.Client
watchConfigNames []string
lock sync.Mutex
}
func BuildKey(s string) string {
return ConfigKeyPrefix + s
}
func NewConfigManager(client *clientv3.Client, configNames []string) *ConfigManager {
return &ConfigManager{
client: client,
watchConfigNames: datautil.Batch(func(s string) string { return BuildKey(s) }, append(configNames, RestartKey))}
}
func (c *ConfigManager) Watch(ctx context.Context) {
chans := make([]clientv3.WatchChan, 0, len(c.watchConfigNames))
for _, name := range c.watchConfigNames {
chans = append(chans, c.client.Watch(ctx, name, clientv3.WithPrefix()))
}
doWatch := func(watchChan clientv3.WatchChan) {
for watchResp := range watchChan {
if watchResp.Err() != nil {
log.ZError(ctx, "watch err", errs.Wrap(watchResp.Err()))
continue
}
for _, event := range watchResp.Events {
if event.IsModify() {
if datautil.Contain(string(event.Kv.Key), c.watchConfigNames...) {
c.lock.Lock()
err := restartServer(ctx)
if err != nil {
log.ZError(ctx, "restart server err", err)
}
c.lock.Unlock()
}
}
}
}
}
for _, ch := range chans {
go doWatch(ch)
}
}
func restartServer(ctx context.Context) error {
exePath, err := os.Executable()
if err != nil {
return errs.New("get executable path fail").Wrap()
}
args := os.Args
env := os.Environ()
cmd := exec.Command(exePath, args[1:]...)
cmd.Env = env
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
cmd.Stdin = os.Stdin
if runtime.GOOS != "windows" {
cmd.SysProcAttr = &syscall.SysProcAttr{}
}
log.ZInfo(ctx, "shutdown server")
for _, f := range ShutDowns {
if err = f(); err != nil {
log.ZError(ctx, "shutdown fail", err)
}
}
log.ZInfo(ctx, "restart server")
err = cmd.Start()
if err != nil {
return errs.New("restart server fail").Wrap()
}
log.ZInfo(ctx, "cmd start over")
os.Exit(0)
return nil
}
-9
View File
@@ -1,9 +0,0 @@
package etcd
const (
ConfigKeyPrefix = "/open-im/config/"
RestartKey = "restart"
EnableConfigCenterKey = "enable-config-center"
Enable = "enable"
Disable = "disable"
)
@@ -1,270 +0,0 @@
package kubernetes
import (
"context"
"fmt"
"log"
"os"
"sync"
"time"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/informers"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"
"k8s.io/client-go/tools/cache"
)
type KubernetesConnManager struct {
clientset *kubernetes.Clientset
namespace string
dialOptions []grpc.DialOption
rpcTargets map[string]string
selfTarget string
mu sync.RWMutex
connMap map[string][]*grpc.ClientConn
}
// NewKubernetesConnManager creates a new connection manager that uses Kubernetes services for service discovery.
func NewKubernetesConnManager(namespace string, options ...grpc.DialOption) (*KubernetesConnManager, error) {
config, err := rest.InClusterConfig()
if err != nil {
return nil, fmt.Errorf("failed to create in-cluster config: %v", err)
}
clientset, err := kubernetes.NewForConfig(config)
if err != nil {
return nil, fmt.Errorf("failed to create clientset: %v", err)
}
k := &KubernetesConnManager{
clientset: clientset,
namespace: namespace,
dialOptions: options,
connMap: make(map[string][]*grpc.ClientConn),
}
go k.watchEndpoints()
return k, nil
}
func (k *KubernetesConnManager) initializeConns(serviceName string) error {
port, err := k.getServicePort(serviceName)
if err != nil {
return err
}
endpoints, err := k.clientset.CoreV1().Endpoints(k.namespace).Get(context.Background(), serviceName, metav1.GetOptions{})
if err != nil {
return fmt.Errorf("failed to get endpoints for service %s: %v", serviceName, err)
}
// fmt.Println("Endpoints:", endpoints, "endpoints.Subsets:", endpoints.Subsets)
var conns []*grpc.ClientConn
for _, subset := range endpoints.Subsets {
for _, address := range subset.Addresses {
target := fmt.Sprintf("%s:%d", address.IP, port)
// fmt.Println("IP target:", target)
conn, err := grpc.Dial(target, append(k.dialOptions, grpc.WithTransportCredentials(insecure.NewCredentials()))...)
if err != nil {
return fmt.Errorf("failed to dial endpoint %s: %v", target, err)
}
conns = append(conns, conn)
}
}
k.mu.Lock()
k.connMap[serviceName] = conns
k.mu.Unlock()
return nil
}
// GetConns returns gRPC client connections for a given Kubernetes service name.
func (k *KubernetesConnManager) GetConns(ctx context.Context, serviceName string, opts ...grpc.DialOption) ([]*grpc.ClientConn, error) {
k.mu.RLock()
conns, exists := k.connMap[serviceName]
k.mu.RUnlock()
if exists {
return conns, nil
}
k.mu.Lock()
// Check if another goroutine has already initialized the connections when we released the read lock
conns, exists = k.connMap[serviceName]
if exists {
return conns, nil
}
k.mu.Unlock()
if err := k.initializeConns(serviceName); err != nil {
fmt.Println("Failed to initialize connections:", err)
return nil, fmt.Errorf("failed to initialize connections for service %s: %v", serviceName, err)
}
return k.connMap[serviceName], nil
}
// GetConn returns a single gRPC client connection for a given Kubernetes service name.
func (k *KubernetesConnManager) GetConn(ctx context.Context, serviceName string, opts ...grpc.DialOption) (*grpc.ClientConn, error) {
var target string
if k.rpcTargets[serviceName] == "" {
var err error
svcPort, err := k.getServicePort(serviceName)
if err != nil {
return nil, err
}
target = fmt.Sprintf("%s.%s.svc.cluster.local:%d", serviceName, k.namespace, svcPort)
// fmt.Println("SVC target:", target)
} else {
target = k.rpcTargets[serviceName]
}
return grpc.DialContext(
ctx,
target,
append([]grpc.DialOption{
grpc.WithTransportCredentials(insecure.NewCredentials()),
grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(1024*1024*10), grpc.MaxCallSendMsgSize(1024*1024*20)),
}, k.dialOptions...)...,
)
}
// GetSelfConnTarget returns the connection target for the current service.
func (k *KubernetesConnManager) GetSelfConnTarget() string {
if k.selfTarget == "" {
hostName := os.Getenv("HOSTNAME")
pod, err := k.clientset.CoreV1().Pods(k.namespace).Get(context.Background(), hostName, metav1.GetOptions{})
if err != nil {
log.Printf("failed to get pod %s: %v \n", hostName, err)
}
for pod.Status.PodIP == "" {
pod, err = k.clientset.CoreV1().Pods(k.namespace).Get(context.TODO(), hostName, metav1.GetOptions{})
if err != nil {
log.Printf("Error getting pod: %v \n", err)
}
time.Sleep(3 * time.Second)
}
var selfPort int32
for _, port := range pod.Spec.Containers[0].Ports {
if port.ContainerPort != 10001 {
selfPort = port.ContainerPort
break
}
}
k.selfTarget = fmt.Sprintf("%s:%d", pod.Status.PodIP, selfPort)
}
return k.selfTarget
}
// AddOption appends gRPC dial options to the existing options.
func (k *KubernetesConnManager) AddOption(opts ...grpc.DialOption) {
k.mu.Lock()
defer k.mu.Unlock()
k.dialOptions = append(k.dialOptions, opts...)
}
// CloseConn closes a given gRPC client connection.
func (k *KubernetesConnManager) CloseConn(conn *grpc.ClientConn) {
conn.Close()
}
// Close closes all gRPC connections managed by KubernetesConnManager.
func (k *KubernetesConnManager) Close() {
k.mu.Lock()
defer k.mu.Unlock()
for _, conns := range k.connMap {
for _, conn := range conns {
_ = conn.Close()
}
}
k.connMap = make(map[string][]*grpc.ClientConn)
}
func (k *KubernetesConnManager) Register(serviceName, host string, port int, opts ...grpc.DialOption) error {
return nil
}
func (k *KubernetesConnManager) UnRegister() error {
return nil
}
func (k *KubernetesConnManager) GetUserIdHashGatewayHost(ctx context.Context, userId string) (string, error) {
return "", nil
}
func (k *KubernetesConnManager) getServicePort(serviceName string) (int32, error) {
var svcPort int32
svc, err := k.clientset.CoreV1().Services(k.namespace).Get(context.Background(), serviceName, metav1.GetOptions{})
if err != nil {
fmt.Print("namespace:", k.namespace)
return 0, fmt.Errorf("failed to get service %s: %v", serviceName, err)
}
if len(svc.Spec.Ports) == 0 {
return 0, fmt.Errorf("service %s has no ports defined", serviceName)
}
for _, port := range svc.Spec.Ports {
// fmt.Println(serviceName, " Now Get Port:", port.Port)
if port.Port != 10001 {
svcPort = port.Port
break
}
}
return svcPort, nil
}
// watchEndpoints listens for changes in Pod resources.
func (k *KubernetesConnManager) watchEndpoints() {
informerFactory := informers.NewSharedInformerFactory(k.clientset, time.Minute*10)
informer := informerFactory.Core().V1().Pods().Informer()
// Watch for Pod changes (add, update, delete)
informer.AddEventHandler(cache.ResourceEventHandlerFuncs{
AddFunc: func(obj interface{}) {
k.handleEndpointChange(obj)
},
UpdateFunc: func(oldObj, newObj interface{}) {
k.handleEndpointChange(newObj)
},
DeleteFunc: func(obj interface{}) {
k.handleEndpointChange(obj)
},
})
informerFactory.Start(context.Background().Done())
<-context.Background().Done() // Block forever
}
func (k *KubernetesConnManager) handleEndpointChange(obj interface{}) {
endpoint, ok := obj.(*v1.Endpoints)
if !ok {
return
}
serviceName := endpoint.Name
if err := k.initializeConns(serviceName); err != nil {
fmt.Printf("Error initializing connections for %s: %v\n", serviceName, err)
}
}
@@ -12,4 +12,4 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
package direct // import "github.com/openimsdk/open-im-server/v3/pkg/common/discovery/direct" package direct // import "github.com/openimsdk/open-im-server/v3/pkg/common/discoveryregister/direct"
@@ -12,33 +12,29 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
package discovery package discoveryregister
import ( import (
"time" "time"
"github.com/openimsdk/open-im-server/v3/pkg/common/config" "github.com/openimsdk/open-im-server/v3/pkg/common/config"
"github.com/openimsdk/tools/discovery" "github.com/openimsdk/tools/discovery"
"google.golang.org/grpc"
"github.com/openimsdk/tools/discovery/kubernetes"
"github.com/openimsdk/tools/discovery/etcd" "github.com/openimsdk/tools/discovery/etcd"
"github.com/openimsdk/tools/discovery/kubernetes"
"github.com/openimsdk/tools/errs" "github.com/openimsdk/tools/errs"
"google.golang.org/grpc"
) )
// NewDiscoveryRegister creates a new service discovery and registry client based on the provided environment type. // NewDiscoveryRegister creates a new service discovery and registry client based on the provided environment type.
func NewDiscoveryRegister(discovery *config.Discovery, runtimeEnv string, watchNames []string) (discovery.SvcDiscoveryRegistry, error) { func NewDiscoveryRegister(discovery *config.Discovery, share *config.Share, watchNames []string) (discovery.SvcDiscoveryRegistry, error) {
if runtimeEnv == config.KUBERNETES { switch discovery.Enable {
return kubernetes.NewKubernetesConnManager(discovery.Kubernetes.Namespace, case "k8s":
return kubernetes.NewKubernetesConnManager("default",
grpc.WithDefaultCallOptions( grpc.WithDefaultCallOptions(
grpc.MaxCallSendMsgSize(1024*1024*20), grpc.MaxCallSendMsgSize(1024*1024*20),
), ),
) )
} case "etcd":
switch discovery.Enable {
case config.ETCD:
return etcd.NewSvcDiscoveryRegistry( return etcd.NewSvcDiscoveryRegistry(
discovery.Etcd.RootDirectory, discovery.Etcd.RootDirectory,
discovery.Etcd.Address, discovery.Etcd.Address,
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
package discovery package discoveryregister
import ( import (
"os" "os"
@@ -12,4 +12,4 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
package discovery // import "github.com/openimsdk/open-im-server/v3/pkg/common/discovery" package discoveryregister // import "github.com/openimsdk/open-im-server/v3/pkg/common/discoveryregister"
@@ -12,4 +12,4 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
package kubernetes // import "github.com/openimsdk/open-im-server/v3/pkg/common/discovery/kubernetes" package kubernetes // import "github.com/openimsdk/open-im-server/v3/pkg/common/discoveryregister/etcd"
@@ -0,0 +1,15 @@
// Copyright © 2024 OpenIM. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package zookeeper // import "github.com/openimsdk/open-im-server/v3/pkg/common/discoveryregister/zookeeper"
+6 -6
View File
@@ -43,25 +43,25 @@ func GetGrpcServerMetrics() *gp.ServerMetrics {
return grpcMetrics return grpcMetrics
} }
func GetGrpcCusMetrics(registerName string, discovery *config.Discovery) []prometheus.Collector { func GetGrpcCusMetrics(registerName string, share *config.Share) []prometheus.Collector {
switch registerName { switch registerName {
case discovery.RpcService.MessageGateway: case share.RpcRegisterName.MessageGateway:
return []prometheus.Collector{OnlineUserGauge} return []prometheus.Collector{OnlineUserGauge}
case discovery.RpcService.Msg: case share.RpcRegisterName.Msg:
return []prometheus.Collector{ return []prometheus.Collector{
SingleChatMsgProcessSuccessCounter, SingleChatMsgProcessSuccessCounter,
SingleChatMsgProcessFailedCounter, SingleChatMsgProcessFailedCounter,
GroupChatMsgProcessSuccessCounter, GroupChatMsgProcessSuccessCounter,
GroupChatMsgProcessFailedCounter, GroupChatMsgProcessFailedCounter,
} }
case discovery.RpcService.Push: case share.RpcRegisterName.Push:
return []prometheus.Collector{ return []prometheus.Collector{
MsgOfflinePushFailedCounter, MsgOfflinePushFailedCounter,
MsgLoneTimePushCounter, MsgLoneTimePushCounter,
} }
case discovery.RpcService.Auth: case share.RpcRegisterName.Auth:
return []prometheus.Collector{UserLoginCounter} return []prometheus.Collector{UserLoginCounter}
case discovery.RpcService.User: case share.RpcRegisterName.User:
return []prometheus.Collector{UserRegisterCounter} return []prometheus.Collector{UserRegisterCounter}
default: default:
return nil return nil
+54 -20
View File
@@ -22,20 +22,18 @@ import (
"net/http" "net/http"
"os" "os"
"os/signal" "os/signal"
"reflect"
"strconv" "strconv"
"syscall" "syscall"
"time" "time"
conf "github.com/openimsdk/open-im-server/v3/pkg/common/config" conf "github.com/openimsdk/open-im-server/v3/pkg/common/config"
disetcd "github.com/openimsdk/open-im-server/v3/pkg/common/discovery/etcd"
"github.com/openimsdk/tools/discovery/etcd" "github.com/openimsdk/tools/discovery/etcd"
"github.com/openimsdk/tools/utils/datautil" "github.com/openimsdk/tools/utils/datautil"
"github.com/openimsdk/tools/utils/jsonutil" "github.com/openimsdk/tools/utils/jsonutil"
"google.golang.org/grpc/status" "google.golang.org/grpc/status"
"github.com/openimsdk/tools/utils/runtimeenv" kdisc "github.com/openimsdk/open-im-server/v3/pkg/common/discoveryregister"
kdisc "github.com/openimsdk/open-im-server/v3/pkg/common/discovery"
"github.com/openimsdk/open-im-server/v3/pkg/common/prommetrics" "github.com/openimsdk/open-im-server/v3/pkg/common/prommetrics"
"github.com/openimsdk/tools/discovery" "github.com/openimsdk/tools/discovery"
"github.com/openimsdk/tools/errs" "github.com/openimsdk/tools/errs"
@@ -46,14 +44,56 @@ import (
"google.golang.org/grpc/credentials/insecure" "google.golang.org/grpc/credentials/insecure"
) )
func getConfigRpcMaxRequestBody(value reflect.Value) *conf.MaxRequestBody {
for value.Kind() == reflect.Pointer {
value = value.Elem()
}
if value.Kind() == reflect.Struct {
num := value.NumField()
for i := 0; i < num; i++ {
field := value.Field(i)
if !field.CanInterface() {
continue
}
for field.Kind() == reflect.Pointer {
field = field.Elem()
}
switch elem := field.Interface().(type) {
case conf.Share:
return &elem.RPCMaxBodySize
case conf.MaxRequestBody:
return &elem
}
if field.Kind() == reflect.Struct {
if elem := getConfigRpcMaxRequestBody(field); elem != nil {
return elem
}
}
}
}
return nil
}
// Start rpc server. // Start rpc server.
func Start[T any](ctx context.Context, discovery *conf.Discovery, prometheusConfig *conf.Prometheus, listenIP, func Start[T any](ctx context.Context, discovery *conf.Discovery, prometheusConfig *conf.Prometheus, listenIP,
registerIP string, autoSetPorts bool, rpcPorts []int, index int, rpcRegisterName string, notification *conf.Notification, config T, registerIP string, autoSetPorts bool, rpcPorts []int, index int, rpcRegisterName string, share *conf.Share, config T,
watchConfigNames []string, watchServiceNames []string, watchServiceNames []string,
rpcFn func(ctx context.Context, config T, client discovery.SvcDiscoveryRegistry, server *grpc.Server) error, rpcFn func(ctx context.Context, config T, client discovery.SvcDiscoveryRegistry, server *grpc.Server) error,
options ...grpc.ServerOption) error { options ...grpc.ServerOption) error {
watchConfigNames = append(watchConfigNames, conf.LogConfigFileName) maxRequestBody := &share.RPCMaxBodySize
var clientOptions []grpc.DialOption
if maxRequestBody != nil {
if maxRequestBody.RequestMaxBodySize > 0 {
options = append(options, grpc.MaxRecvMsgSize(maxRequestBody.RequestMaxBodySize))
clientOptions = append(clientOptions, grpc.WithDefaultCallOptions(grpc.MaxCallSendMsgSize(maxRequestBody.RequestMaxBodySize)))
}
if maxRequestBody.ResponseMaxBodySize > 0 {
options = append(options, grpc.MaxSendMsgSize(maxRequestBody.ResponseMaxBodySize))
clientOptions = append(clientOptions, grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(maxRequestBody.ResponseMaxBodySize)))
}
}
var ( var (
rpcTcpAddr string rpcTcpAddr string
netDone = make(chan struct{}, 2) netDone = make(chan struct{}, 2)
@@ -61,17 +101,11 @@ func Start[T any](ctx context.Context, discovery *conf.Discovery, prometheusConf
prometheusPort int prometheusPort int
) )
if notification != nil {
conf.InitNotification(notification)
}
registerIP, err := network.GetRpcRegisterIP(registerIP) registerIP, err := network.GetRpcRegisterIP(registerIP)
if err != nil { if err != nil {
return err return err
} }
runTimeEnv := runtimeenv.PrintRuntimeEnvironment()
if !autoSetPorts { if !autoSetPorts {
rpcPort, err := datautil.GetElemByIndex(rpcPorts, index) rpcPort, err := datautil.GetElemByIndex(rpcPorts, index)
if err != nil { if err != nil {
@@ -95,7 +129,7 @@ func Start[T any](ctx context.Context, discovery *conf.Discovery, prometheusConf
if autoSetPorts && discovery.Enable != conf.ETCD { if autoSetPorts && discovery.Enable != conf.ETCD {
return errs.New("only etcd support autoSetPorts", "rpcRegisterName", rpcRegisterName).Wrap() return errs.New("only etcd support autoSetPorts", "rpcRegisterName", rpcRegisterName).Wrap()
} }
client, err := kdisc.NewDiscoveryRegister(discovery, runTimeEnv, watchServiceNames) client, err := kdisc.NewDiscoveryRegister(discovery, share, watchServiceNames)
if err != nil { if err != nil {
return err return err
} }
@@ -103,6 +137,10 @@ func Start[T any](ctx context.Context, discovery *conf.Discovery, prometheusConf
defer client.Close() defer client.Close()
client.AddOption(mw.GrpcClient(), grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithDefaultServiceConfig(fmt.Sprintf(`{"LoadBalancingPolicy": "%s"}`, "round_robin"))) client.AddOption(mw.GrpcClient(), grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithDefaultServiceConfig(fmt.Sprintf(`{"LoadBalancingPolicy": "%s"}`, "round_robin")))
if len(clientOptions) > 0 {
client.AddOption(clientOptions...)
}
// var reg *prometheus.Registry // var reg *prometheus.Registry
// var metric *grpcprometheus.ServerMetrics // var metric *grpcprometheus.ServerMetrics
if prometheusConfig.Enable { if prometheusConfig.Enable {
@@ -142,7 +180,7 @@ func Start[T any](ctx context.Context, discovery *conf.Discovery, prometheusConf
return errs.WrapMsg(err, "listen err", "rpcTcpAddr", rpcTcpAddr) return errs.WrapMsg(err, "listen err", "rpcTcpAddr", rpcTcpAddr)
} }
} }
cs := prommetrics.GetGrpcCusMetrics(rpcRegisterName, discovery) cs := prommetrics.GetGrpcCusMetrics(rpcRegisterName, share)
go func() { go func() {
if err := prommetrics.RpcInit(cs, listener); err != nil && !errors.Is(err, http.ErrServerClosed) { if err := prommetrics.RpcInit(cs, listener); err != nil && !errors.Is(err, http.ErrServerClosed) {
netErr = errs.WrapMsg(err, fmt.Sprintf("rpc %s prometheus start err: %d", rpcRegisterName, prometheusPort)) netErr = errs.WrapMsg(err, fmt.Sprintf("rpc %s prometheus start err: %d", rpcRegisterName, prometheusPort))
@@ -177,6 +215,7 @@ func Start[T any](ctx context.Context, discovery *conf.Discovery, prometheusConf
} }
err = client.Register( err = client.Register(
ctx,
rpcRegisterName, rpcRegisterName,
registerIP, registerIP,
port, port,
@@ -194,11 +233,6 @@ func Start[T any](ctx context.Context, discovery *conf.Discovery, prometheusConf
} }
}() }()
if discovery.Enable == conf.ETCD {
cm := disetcd.NewConfigManager(client.(*etcd.SvcDiscoveryRegistryImpl).GetClient(), watchConfigNames)
cm.Watch(ctx)
}
sigs := make(chan os.Signal, 1) sigs := make(chan os.Signal, 1)
signal.Notify(sigs, syscall.SIGTERM) signal.Notify(sigs, syscall.SIGTERM)
select { select {
+14 -7
View File
@@ -17,10 +17,11 @@ package controller
import ( import (
"context" "context"
"fmt" "fmt"
"time"
"github.com/openimsdk/open-im-server/v3/pkg/common/storage/database" "github.com/openimsdk/open-im-server/v3/pkg/common/storage/database"
"github.com/openimsdk/open-im-server/v3/pkg/common/storage/database/mgo" "github.com/openimsdk/open-im-server/v3/pkg/common/storage/database/mgo"
"github.com/openimsdk/open-im-server/v3/pkg/common/storage/model" "github.com/openimsdk/open-im-server/v3/pkg/common/storage/model"
"time"
"github.com/openimsdk/open-im-server/v3/pkg/common/storage/cache" "github.com/openimsdk/open-im-server/v3/pkg/common/storage/cache"
"github.com/openimsdk/protocol/constant" "github.com/openimsdk/protocol/constant"
@@ -61,10 +62,10 @@ type FriendDatabase interface {
PageInWhoseFriends(ctx context.Context, friendUserID string, pagination pagination.Pagination) (total int64, friends []*model.Friend, err error) PageInWhoseFriends(ctx context.Context, friendUserID string, pagination pagination.Pagination) (total int64, friends []*model.Friend, err error)
// PageFriendRequestFromMe retrieves the friend requests sent by the user with pagination // PageFriendRequestFromMe retrieves the friend requests sent by the user with pagination
PageFriendRequestFromMe(ctx context.Context, userID string, pagination pagination.Pagination) (total int64, friends []*model.FriendRequest, err error) PageFriendRequestFromMe(ctx context.Context, userID string, handleResults []int, pagination pagination.Pagination) (total int64, friends []*model.FriendRequest, err error)
// PageFriendRequestToMe retrieves the friend requests received by the user with pagination // PageFriendRequestToMe retrieves the friend requests received by the user with pagination
PageFriendRequestToMe(ctx context.Context, userID string, pagination pagination.Pagination) (total int64, friends []*model.FriendRequest, err error) PageFriendRequestToMe(ctx context.Context, userID string, handleResults []int, pagination pagination.Pagination) (total int64, friends []*model.FriendRequest, err error)
// FindFriendsWithError fetches specified friends of a user and returns an error if any do not exist // FindFriendsWithError fetches specified friends of a user and returns an error if any do not exist
FindFriendsWithError(ctx context.Context, ownerUserID string, friendUserIDs []string) (friends []*model.Friend, err error) FindFriendsWithError(ctx context.Context, ownerUserID string, friendUserIDs []string) (friends []*model.Friend, err error)
@@ -87,6 +88,8 @@ type FriendDatabase interface {
FindFriendUserID(ctx context.Context, friendUserID string) ([]string, error) FindFriendUserID(ctx context.Context, friendUserID string) ([]string, error)
OwnerIncrVersion(ctx context.Context, ownerUserID string, friendUserIDs []string, state int32) error OwnerIncrVersion(ctx context.Context, ownerUserID string, friendUserIDs []string, state int32) error
GetUnhandledCount(ctx context.Context, userID string, ts int64) (int64, error)
} }
type friendDatabase struct { type friendDatabase struct {
@@ -334,13 +337,13 @@ func (f *friendDatabase) PageInWhoseFriends(ctx context.Context, friendUserID st
} }
// PageFriendRequestFromMe retrieves friend requests sent by me. It does not return an error if the result is empty. // PageFriendRequestFromMe retrieves friend requests sent by me. It does not return an error if the result is empty.
func (f *friendDatabase) PageFriendRequestFromMe(ctx context.Context, userID string, pagination pagination.Pagination) (total int64, friends []*model.FriendRequest, err error) { func (f *friendDatabase) PageFriendRequestFromMe(ctx context.Context, userID string, handleResults []int, pagination pagination.Pagination) (total int64, friends []*model.FriendRequest, err error) {
return f.friendRequest.FindFromUserID(ctx, userID, pagination) return f.friendRequest.FindFromUserID(ctx, userID, handleResults, pagination)
} }
// PageFriendRequestToMe retrieves friend requests received by me. It does not return an error if the result is empty. // PageFriendRequestToMe retrieves friend requests received by me. It does not return an error if the result is empty.
func (f *friendDatabase) PageFriendRequestToMe(ctx context.Context, userID string, pagination pagination.Pagination) (total int64, friends []*model.FriendRequest, err error) { func (f *friendDatabase) PageFriendRequestToMe(ctx context.Context, userID string, handleResults []int, pagination pagination.Pagination) (total int64, friends []*model.FriendRequest, err error) {
return f.friendRequest.FindToUserID(ctx, userID, pagination) return f.friendRequest.FindToUserID(ctx, userID, handleResults, pagination)
} }
// FindFriendsWithError retrieves specified friends' information for ownerUserID. Returns an error if any friend does not exist. // FindFriendsWithError retrieves specified friends' information for ownerUserID. Returns an error if any friend does not exist.
@@ -397,3 +400,7 @@ func (f *friendDatabase) OwnerIncrVersion(ctx context.Context, ownerUserID strin
} }
return f.cache.DelMaxFriendVersion(ownerUserID).ChainExecDel(ctx) return f.cache.DelMaxFriendVersion(ownerUserID).ChainExecDel(ctx)
} }
func (f *friendDatabase) GetUnhandledCount(ctx context.Context, userID string, ts int64) (int64, error) {
return f.friendRequest.GetUnhandledCount(ctx, userID, ts)
}
+13 -11
View File
@@ -68,7 +68,7 @@ type GroupDatabase interface {
// FindUserManagedGroupID retrieves group IDs managed by a user. // FindUserManagedGroupID retrieves group IDs managed by a user.
FindUserManagedGroupID(ctx context.Context, userID string) (groupIDs []string, err error) FindUserManagedGroupID(ctx context.Context, userID string) (groupIDs []string, err error)
// PageGroupRequest paginates through group requests for specified groups. // PageGroupRequest paginates through group requests for specified groups.
PageGroupRequest(ctx context.Context, groupIDs []string, pagination pagination.Pagination) (int64, []*model.GroupRequest, error) PageGroupRequest(ctx context.Context, groupIDs []string, handleResults []int, pagination pagination.Pagination) (int64, []*model.GroupRequest, error)
// GetGroupRoleLevelMemberIDs retrieves user IDs of group members with a specific role level. // GetGroupRoleLevelMemberIDs retrieves user IDs of group members with a specific role level.
GetGroupRoleLevelMemberIDs(ctx context.Context, groupID string, roleLevel int32) ([]string, error) GetGroupRoleLevelMemberIDs(ctx context.Context, groupID string, roleLevel int32) ([]string, error)
@@ -100,7 +100,7 @@ type GroupDatabase interface {
// FindGroupRequests retrieves multiple group join requests. // FindGroupRequests retrieves multiple group join requests.
FindGroupRequests(ctx context.Context, groupID string, userIDs []string) ([]*model.GroupRequest, error) FindGroupRequests(ctx context.Context, groupID string, userIDs []string) ([]*model.GroupRequest, error)
// PageGroupRequestUser paginates through group join requests made by a user. // PageGroupRequestUser paginates through group join requests made by a user.
PageGroupRequestUser(ctx context.Context, userID string, pagination pagination.Pagination) (int64, []*model.GroupRequest, error) PageGroupRequestUser(ctx context.Context, userID string, groupIDs []string, handleResults []int, pagination pagination.Pagination) (int64, []*model.GroupRequest, error)
// CountTotal counts the total number of groups as of a certain date. // CountTotal counts the total number of groups as of a certain date.
CountTotal(ctx context.Context, before *time.Time) (count int64, err error) CountTotal(ctx context.Context, before *time.Time) (count int64, err error)
@@ -124,6 +124,8 @@ type GroupDatabase interface {
SearchJoinGroup(ctx context.Context, userID string, keyword string, pagination pagination.Pagination) (int64, []*model.Group, error) SearchJoinGroup(ctx context.Context, userID string, keyword string, pagination pagination.Pagination) (int64, []*model.Group, error)
FindJoinGroupID(ctx context.Context, userID string) ([]string, error) FindJoinGroupID(ctx context.Context, userID string) ([]string, error)
GetGroupApplicationUnhandledCount(ctx context.Context, groupIDs []string, ts int64) (int64, error)
} }
func NewGroupDatabase( func NewGroupDatabase(
@@ -304,8 +306,8 @@ func (g *groupDatabase) FindUserManagedGroupID(ctx context.Context, userID strin
return g.groupMemberDB.FindUserManagedGroupID(ctx, userID) return g.groupMemberDB.FindUserManagedGroupID(ctx, userID)
} }
func (g *groupDatabase) PageGroupRequest(ctx context.Context, groupIDs []string, pagination pagination.Pagination) (int64, []*model.GroupRequest, error) { func (g *groupDatabase) PageGroupRequest(ctx context.Context, groupIDs []string, handleResults []int, pagination pagination.Pagination) (int64, []*model.GroupRequest, error) {
return g.groupRequestDB.PageGroup(ctx, groupIDs, pagination) return g.groupRequestDB.PageGroup(ctx, groupIDs, handleResults, pagination)
} }
func (g *groupDatabase) PageGetJoinGroup(ctx context.Context, userID string, pagination pagination.Pagination) (total int64, totalGroupMembers []*model.GroupMember, err error) { func (g *groupDatabase) PageGetJoinGroup(ctx context.Context, userID string, pagination pagination.Pagination) (total int64, totalGroupMembers []*model.GroupMember, err error) {
@@ -463,16 +465,12 @@ func (g *groupDatabase) CreateGroupRequest(ctx context.Context, requests []*mode
}) })
} }
func (g *groupDatabase) TakeGroupRequest( func (g *groupDatabase) TakeGroupRequest(ctx context.Context, groupID string, userID string) (*model.GroupRequest, error) {
ctx context.Context,
groupID string,
userID string,
) (*model.GroupRequest, error) {
return g.groupRequestDB.Take(ctx, groupID, userID) return g.groupRequestDB.Take(ctx, groupID, userID)
} }
func (g *groupDatabase) PageGroupRequestUser(ctx context.Context, userID string, pagination pagination.Pagination) (int64, []*model.GroupRequest, error) { func (g *groupDatabase) PageGroupRequestUser(ctx context.Context, userID string, groupIDs []string, handleResults []int, pagination pagination.Pagination) (int64, []*model.GroupRequest, error) {
return g.groupRequestDB.Page(ctx, userID, pagination) return g.groupRequestDB.Page(ctx, userID, groupIDs, handleResults, pagination)
} }
func (g *groupDatabase) CountTotal(ctx context.Context, before *time.Time) (count int64, err error) { func (g *groupDatabase) CountTotal(ctx context.Context, before *time.Time) (count int64, err error) {
@@ -565,3 +563,7 @@ func (g *groupDatabase) MemberGroupIncrVersion(ctx context.Context, groupID stri
} }
return g.cache.DelMaxGroupMemberVersion(groupID).ChainExecDel(ctx) return g.cache.DelMaxGroupMemberVersion(groupID).ChainExecDel(ctx)
} }
func (g *groupDatabase) GetGroupApplicationUnhandledCount(ctx context.Context, groupIDs []string, ts int64) (int64, error) {
return g.groupRequestDB.GetUnhandledCount(ctx, groupIDs, ts)
}
+2 -2
View File
@@ -22,6 +22,7 @@ import (
"strings" "strings"
"time" "time"
"github.com/openimsdk/open-im-server/v3/pkg/common/storage/kafka"
"github.com/openimsdk/tools/utils/jsonutil" "github.com/openimsdk/tools/utils/jsonutil"
"github.com/openimsdk/open-im-server/v3/pkg/common/storage/database" "github.com/openimsdk/open-im-server/v3/pkg/common/storage/database"
@@ -38,7 +39,6 @@ import (
"github.com/openimsdk/protocol/sdkws" "github.com/openimsdk/protocol/sdkws"
"github.com/openimsdk/tools/errs" "github.com/openimsdk/tools/errs"
"github.com/openimsdk/tools/log" "github.com/openimsdk/tools/log"
"github.com/openimsdk/tools/mq/kafka"
"github.com/openimsdk/tools/utils/datautil" "github.com/openimsdk/tools/utils/datautil"
) )
@@ -310,7 +310,7 @@ func (db *commonMsgDatabase) handlerDBMsg(ctx context.Context, cache map[int64][
log.ZError(ctx, "json.Unmarshal", err) log.ZError(ctx, "json.Unmarshal", err)
return return
} }
if quoteMsg.QuoteMessage == nil || quoteMsg.QuoteMessage.Content == "" { if quoteMsg.QuoteMessage == nil {
return return
} }
if quoteMsg.QuoteMessage.Content == "e30=" { if quoteMsg.QuoteMessage.Content == "e30=" {
@@ -2,7 +2,9 @@ package controller
import ( import (
"context" "context"
"github.com/openimsdk/open-im-server/v3/pkg/common/convert" "github.com/openimsdk/open-im-server/v3/pkg/common/convert"
"github.com/openimsdk/open-im-server/v3/pkg/common/storage/kafka"
"github.com/openimsdk/protocol/constant" "github.com/openimsdk/protocol/constant"
"github.com/openimsdk/tools/utils/datautil" "github.com/openimsdk/tools/utils/datautil"
@@ -14,7 +16,6 @@ import (
"github.com/openimsdk/protocol/sdkws" "github.com/openimsdk/protocol/sdkws"
"github.com/openimsdk/tools/errs" "github.com/openimsdk/tools/errs"
"github.com/openimsdk/tools/log" "github.com/openimsdk/tools/log"
"github.com/openimsdk/tools/mq/kafka"
"go.mongodb.org/mongo-driver/mongo" "go.mongodb.org/mongo-driver/mongo"
) )
+1 -1
View File
@@ -19,10 +19,10 @@ import (
"github.com/openimsdk/open-im-server/v3/pkg/common/config" "github.com/openimsdk/open-im-server/v3/pkg/common/config"
"github.com/openimsdk/open-im-server/v3/pkg/common/storage/cache" "github.com/openimsdk/open-im-server/v3/pkg/common/storage/cache"
"github.com/openimsdk/open-im-server/v3/pkg/common/storage/kafka"
"github.com/openimsdk/protocol/push" "github.com/openimsdk/protocol/push"
"github.com/openimsdk/protocol/sdkws" "github.com/openimsdk/protocol/sdkws"
"github.com/openimsdk/tools/log" "github.com/openimsdk/tools/log"
"github.com/openimsdk/tools/mq/kafka"
) )
type PushDatabase interface { type PushDatabase interface {
+2 -2
View File
@@ -30,7 +30,7 @@ import (
) )
type S3Database interface { type S3Database interface {
PartLimit() *s3.PartLimit PartLimit() (*s3.PartLimit, error)
PartSize(ctx context.Context, size int64) (int64, error) PartSize(ctx context.Context, size int64) (int64, error)
AuthSign(ctx context.Context, uploadID string, partNumbers []int) (*s3.AuthSignResult, error) AuthSign(ctx context.Context, uploadID string, partNumbers []int) (*s3.AuthSignResult, error)
InitiateMultipartUpload(ctx context.Context, hash string, size int64, expire time.Duration, maxParts int) (*cont.InitiateUploadResult, error) InitiateMultipartUpload(ctx context.Context, hash string, size int64, expire time.Duration, maxParts int) (*cont.InitiateUploadResult, error)
@@ -65,7 +65,7 @@ func (s *s3Database) PartSize(ctx context.Context, size int64) (int64, error) {
return s.s3.PartSize(ctx, size) return s.s3.PartSize(ctx, size)
} }
func (s *s3Database) PartLimit() *s3.PartLimit { func (s *s3Database) PartLimit() (*s3.PartLimit, error) {
return s.s3.PartLimit() return s.s3.PartLimit()
} }
@@ -16,6 +16,7 @@ package database
import ( import (
"context" "context"
"github.com/openimsdk/open-im-server/v3/pkg/common/storage/model" "github.com/openimsdk/open-im-server/v3/pkg/common/storage/model"
"github.com/openimsdk/tools/db/pagination" "github.com/openimsdk/tools/db/pagination"
) )
@@ -33,8 +34,9 @@ type FriendRequest interface {
Find(ctx context.Context, fromUserID, toUserID string) (friendRequest *model.FriendRequest, err error) Find(ctx context.Context, fromUserID, toUserID string) (friendRequest *model.FriendRequest, err error)
Take(ctx context.Context, fromUserID, toUserID string) (friendRequest *model.FriendRequest, err error) Take(ctx context.Context, fromUserID, toUserID string) (friendRequest *model.FriendRequest, err error)
// Get list of friend requests received by toUserID // Get list of friend requests received by toUserID
FindToUserID(ctx context.Context, toUserID string, pagination pagination.Pagination) (total int64, friendRequests []*model.FriendRequest, err error) FindToUserID(ctx context.Context, toUserID string, handleResults []int, pagination pagination.Pagination) (total int64, friendRequests []*model.FriendRequest, err error)
// Get list of friend requests sent by fromUserID // Get list of friend requests sent by fromUserID
FindFromUserID(ctx context.Context, fromUserID string, pagination pagination.Pagination) (total int64, friendRequests []*model.FriendRequest, err error) FindFromUserID(ctx context.Context, fromUserID string, handleResults []int, pagination pagination.Pagination) (total int64, friendRequests []*model.FriendRequest, err error)
FindBothFriendRequests(ctx context.Context, fromUserID, toUserID string) (friends []*model.FriendRequest, err error) FindBothFriendRequests(ctx context.Context, fromUserID, toUserID string) (friends []*model.FriendRequest, err error)
GetUnhandledCount(ctx context.Context, userID string, ts int64) (int64, error)
} }
+4 -2
View File
@@ -16,6 +16,7 @@ package database
import ( import (
"context" "context"
"github.com/openimsdk/open-im-server/v3/pkg/common/storage/model" "github.com/openimsdk/open-im-server/v3/pkg/common/storage/model"
"github.com/openimsdk/tools/db/pagination" "github.com/openimsdk/tools/db/pagination"
) )
@@ -26,6 +27,7 @@ type GroupRequest interface {
UpdateHandler(ctx context.Context, groupID string, userID string, handledMsg string, handleResult int32) (err error) UpdateHandler(ctx context.Context, groupID string, userID string, handledMsg string, handleResult int32) (err error)
Take(ctx context.Context, groupID string, userID string) (groupRequest *model.GroupRequest, err error) Take(ctx context.Context, groupID string, userID string) (groupRequest *model.GroupRequest, err error)
FindGroupRequests(ctx context.Context, groupID string, userIDs []string) ([]*model.GroupRequest, error) FindGroupRequests(ctx context.Context, groupID string, userIDs []string) ([]*model.GroupRequest, error)
Page(ctx context.Context, userID string, pagination pagination.Pagination) (total int64, groups []*model.GroupRequest, err error) Page(ctx context.Context, userID string, groupIDs []string, handleResults []int, pagination pagination.Pagination) (total int64, groups []*model.GroupRequest, err error)
PageGroup(ctx context.Context, groupIDs []string, pagination pagination.Pagination) (total int64, groups []*model.GroupRequest, err error) PageGroup(ctx context.Context, groupIDs []string, handleResults []int, pagination pagination.Pagination) (total int64, groups []*model.GroupRequest, err error)
GetUnhandledCount(ctx context.Context, groupIDs []string, ts int64) (int64, error)
} }
@@ -16,24 +16,35 @@ package mgo
import ( import (
"context" "context"
"time"
"go.mongodb.org/mongo-driver/mongo/options"
"github.com/openimsdk/open-im-server/v3/pkg/common/storage/database" "github.com/openimsdk/open-im-server/v3/pkg/common/storage/database"
"github.com/openimsdk/open-im-server/v3/pkg/common/storage/model" "github.com/openimsdk/open-im-server/v3/pkg/common/storage/model"
"github.com/openimsdk/tools/db/mongoutil"
"github.com/openimsdk/tools/db/pagination"
"go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/mongo" "go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
"github.com/openimsdk/tools/db/mongoutil"
"github.com/openimsdk/tools/db/pagination"
) )
func NewFriendRequestMongo(db *mongo.Database) (database.FriendRequest, error) { func NewFriendRequestMongo(db *mongo.Database) (database.FriendRequest, error) {
coll := db.Collection(database.FriendRequestName) coll := db.Collection(database.FriendRequestName)
_, err := coll.Indexes().CreateOne(context.Background(), mongo.IndexModel{ _, err := coll.Indexes().CreateMany(context.Background(), []mongo.IndexModel{
Keys: bson.D{ {
{Key: "from_user_id", Value: 1}, Keys: bson.D{
{Key: "to_user_id", Value: 1}, {Key: "from_user_id", Value: 1},
{Key: "to_user_id", Value: 1},
},
Options: options.Index().SetUnique(true),
},
{
Keys: bson.D{
{Key: "create_time", Value: -1},
},
}, },
Options: options.Index().SetUnique(true),
}) })
if err != nil { if err != nil {
return nil, err return nil, err
@@ -45,12 +56,24 @@ type FriendRequestMgo struct {
coll *mongo.Collection coll *mongo.Collection
} }
func (f *FriendRequestMgo) FindToUserID(ctx context.Context, toUserID string, pagination pagination.Pagination) (total int64, friendRequests []*model.FriendRequest, err error) { func (f *FriendRequestMgo) sort() any {
return mongoutil.FindPage[*model.FriendRequest](ctx, f.coll, bson.M{"to_user_id": toUserID}, pagination) return bson.D{{Key: "create_time", Value: -1}}
} }
func (f *FriendRequestMgo) FindFromUserID(ctx context.Context, fromUserID string, pagination pagination.Pagination) (total int64, friendRequests []*model.FriendRequest, err error) { func (f *FriendRequestMgo) FindToUserID(ctx context.Context, toUserID string, handleResults []int, pagination pagination.Pagination) (total int64, friendRequests []*model.FriendRequest, err error) {
return mongoutil.FindPage[*model.FriendRequest](ctx, f.coll, bson.M{"from_user_id": fromUserID}, pagination) filter := bson.M{"to_user_id": toUserID}
if len(handleResults) > 0 {
filter["handle_result"] = bson.M{"$in": handleResults}
}
return mongoutil.FindPage[*model.FriendRequest](ctx, f.coll, filter, pagination, options.Find().SetSort(f.sort()))
}
func (f *FriendRequestMgo) FindFromUserID(ctx context.Context, fromUserID string, handleResults []int, pagination pagination.Pagination) (total int64, friendRequests []*model.FriendRequest, err error) {
filter := bson.M{"from_user_id": fromUserID}
if len(handleResults) > 0 {
filter["handle_result"] = bson.M{"$in": handleResults}
}
return mongoutil.FindPage[*model.FriendRequest](ctx, f.coll, filter, pagination, options.Find().SetSort(f.sort()))
} }
func (f *FriendRequestMgo) FindBothFriendRequests(ctx context.Context, fromUserID, toUserID string) (friends []*model.FriendRequest, err error) { func (f *FriendRequestMgo) FindBothFriendRequests(ctx context.Context, fromUserID, toUserID string) (friends []*model.FriendRequest, err error) {
@@ -110,3 +133,11 @@ func (f *FriendRequestMgo) Find(ctx context.Context, fromUserID, toUserID string
func (f *FriendRequestMgo) Take(ctx context.Context, fromUserID, toUserID string) (friendRequest *model.FriendRequest, err error) { func (f *FriendRequestMgo) Take(ctx context.Context, fromUserID, toUserID string) (friendRequest *model.FriendRequest, err error) {
return f.Find(ctx, fromUserID, toUserID) return f.Find(ctx, fromUserID, toUserID)
} }
func (f *FriendRequestMgo) GetUnhandledCount(ctx context.Context, userID string, ts int64) (int64, error) {
filter := bson.M{"to_user_id": userID, "handle_result": 0}
if ts != 0 {
filter["create_time"] = bson.M{"$gt": time.Unix(ts, 0)}
}
return mongoutil.Count(ctx, f.coll, filter)
}
@@ -16,25 +16,36 @@ package mgo
import ( import (
"context" "context"
"time"
"github.com/openimsdk/open-im-server/v3/pkg/common/storage/database" "github.com/openimsdk/open-im-server/v3/pkg/common/storage/database"
"github.com/openimsdk/open-im-server/v3/pkg/common/storage/model" "github.com/openimsdk/open-im-server/v3/pkg/common/storage/model"
"github.com/openimsdk/tools/utils/datautil"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
"github.com/openimsdk/tools/db/mongoutil" "github.com/openimsdk/tools/db/mongoutil"
"github.com/openimsdk/tools/db/pagination" "github.com/openimsdk/tools/db/pagination"
"github.com/openimsdk/tools/errs" "github.com/openimsdk/tools/errs"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
) )
func NewGroupRequestMgo(db *mongo.Database) (database.GroupRequest, error) { func NewGroupRequestMgo(db *mongo.Database) (database.GroupRequest, error) {
coll := db.Collection(database.GroupRequestName) coll := db.Collection(database.GroupRequestName)
_, err := coll.Indexes().CreateOne(context.Background(), mongo.IndexModel{ _, err := coll.Indexes().CreateMany(context.Background(), []mongo.IndexModel{
Keys: bson.D{ {
{Key: "group_id", Value: 1}, Keys: bson.D{
{Key: "user_id", Value: 1}, {Key: "group_id", Value: 1},
{Key: "user_id", Value: 1},
},
Options: options.Index().SetUnique(true),
},
{
Keys: bson.D{
{Key: "req_time", Value: -1},
},
}, },
Options: options.Index().SetUnique(true),
}) })
if err != nil { if err != nil {
return nil, errs.Wrap(err) return nil, errs.Wrap(err)
@@ -66,10 +77,39 @@ func (g *GroupRequestMgo) FindGroupRequests(ctx context.Context, groupID string,
return mongoutil.Find[*model.GroupRequest](ctx, g.coll, bson.M{"group_id": groupID, "user_id": bson.M{"$in": userIDs}}) return mongoutil.Find[*model.GroupRequest](ctx, g.coll, bson.M{"group_id": groupID, "user_id": bson.M{"$in": userIDs}})
} }
func (g *GroupRequestMgo) Page(ctx context.Context, userID string, pagination pagination.Pagination) (total int64, groups []*model.GroupRequest, err error) { func (g *GroupRequestMgo) sort() any {
return mongoutil.FindPage[*model.GroupRequest](ctx, g.coll, bson.M{"user_id": userID}, pagination) return bson.D{{Key: "req_time", Value: -1}}
} }
func (g *GroupRequestMgo) PageGroup(ctx context.Context, groupIDs []string, pagination pagination.Pagination) (total int64, groups []*model.GroupRequest, err error) { func (g *GroupRequestMgo) Page(ctx context.Context, userID string, groupIDs []string, handleResults []int, pagination pagination.Pagination) (total int64, groups []*model.GroupRequest, err error) {
return mongoutil.FindPage[*model.GroupRequest](ctx, g.coll, bson.M{"group_id": bson.M{"$in": groupIDs}}, pagination) filter := bson.M{"user_id": userID}
if len(groupIDs) > 0 {
filter["group_id"] = bson.M{"$in": datautil.Distinct(groupIDs)}
}
if len(handleResults) > 0 {
filter["handle_result"] = bson.M{"$in": handleResults}
}
return mongoutil.FindPage[*model.GroupRequest](ctx, g.coll, filter, pagination, options.Find().SetSort(g.sort()))
}
func (g *GroupRequestMgo) PageGroup(ctx context.Context, groupIDs []string, handleResults []int, pagination pagination.Pagination) (total int64, groups []*model.GroupRequest, err error) {
if len(groupIDs) == 0 {
return 0, nil, nil
}
filter := bson.M{"group_id": bson.M{"$in": groupIDs}}
if len(handleResults) > 0 {
filter["handle_result"] = bson.M{"$in": handleResults}
}
return mongoutil.FindPage[*model.GroupRequest](ctx, g.coll, filter, pagination, options.Find().SetSort(g.sort()))
}
func (g *GroupRequestMgo) GetUnhandledCount(ctx context.Context, groupIDs []string, ts int64) (int64, error) {
if len(groupIDs) == 0 {
return 0, nil
}
filter := bson.M{"group_id": bson.M{"$in": groupIDs}, "handle_result": 0}
if ts != 0 {
filter["req_time"] = bson.M{"$gt": time.Unix(ts, 0)}
}
return mongoutil.Count(ctx, g.coll, filter)
} }
+33
View File
@@ -0,0 +1,33 @@
// Copyright © 2024 OpenIM open source community. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package kafka
type TLSConfig struct {
EnableTLS bool `yaml:"enableTLS"`
CACrt string `yaml:"caCrt"`
ClientCrt string `yaml:"clientCrt"`
ClientKey string `yaml:"clientKey"`
ClientKeyPwd string `yaml:"clientKeyPwd"`
InsecureSkipVerify bool `yaml:"insecureSkipVerify"`
}
type Config struct {
Username string `yaml:"username"`
Password string `yaml:"password"`
ProducerAck string `yaml:"producerAck"`
CompressType string `yaml:"compressType"`
Addr []string `yaml:"addr"`
TLS TLSConfig `yaml:"tls"`
}
@@ -0,0 +1,68 @@
// Copyright © 2023 OpenIM. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package kafka
import (
"context"
"errors"
"github.com/IBM/sarama"
"github.com/openimsdk/tools/log"
)
type MConsumerGroup struct {
sarama.ConsumerGroup
groupID string
topics []string
}
func NewMConsumerGroup(conf *Config, groupID string, topics []string, autoCommitEnable bool) (*MConsumerGroup, error) {
config, err := BuildConsumerGroupConfig(conf, sarama.OffsetNewest, autoCommitEnable)
if err != nil {
return nil, err
}
group, err := NewConsumerGroup(config, conf.Addr, groupID)
if err != nil {
return nil, err
}
return &MConsumerGroup{
ConsumerGroup: group,
groupID: groupID,
topics: topics,
}, nil
}
func (mc *MConsumerGroup) GetContextFromMsg(cMsg *sarama.ConsumerMessage) context.Context {
return GetContextWithMQHeader(cMsg.Headers)
}
func (mc *MConsumerGroup) RegisterHandleAndConsumer(ctx context.Context, handler sarama.ConsumerGroupHandler) {
for {
err := mc.ConsumerGroup.Consume(ctx, mc.topics, handler)
if errors.Is(err, sarama.ErrClosedConsumerGroup) {
return
}
if errors.Is(err, context.Canceled) {
return
}
if err != nil {
log.ZWarn(ctx, "consume err", err, "topic", mc.topics, "groupID", mc.groupID)
}
}
}
func (mc *MConsumerGroup) Close() error {
return mc.ConsumerGroup.Close()
}
+82
View File
@@ -0,0 +1,82 @@
// Copyright © 2023 OpenIM. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package kafka
import (
"context"
"github.com/IBM/sarama"
"github.com/openimsdk/tools/errs"
"google.golang.org/protobuf/proto"
)
// Producer represents a Kafka producer.
type Producer struct {
addr []string
topic string
config *sarama.Config
producer sarama.SyncProducer
}
func NewKafkaProducer(config *sarama.Config, addr []string, topic string) (*Producer, error) {
producer, err := NewProducer(config, addr)
if err != nil {
return nil, err
}
return &Producer{
addr: addr,
topic: topic,
config: config,
producer: producer,
}, nil
}
// SendMessage sends a message to the Kafka topic configured in the Producer.
func (p *Producer) SendMessage(ctx context.Context, key string, msg proto.Message) (int32, int64, error) {
// Marshal the protobuf message
bMsg, err := proto.Marshal(msg)
if err != nil {
return 0, 0, errs.WrapMsg(err, "kafka proto Marshal err")
}
if len(bMsg) == 0 {
return 0, 0, errs.WrapMsg(errEmptyMsg, "kafka proto Marshal err")
}
// Prepare Kafka message
kMsg := &sarama.ProducerMessage{
Topic: p.topic,
Key: sarama.StringEncoder(key),
Value: sarama.ByteEncoder(bMsg),
}
// Validate message key and value
if kMsg.Key.Length() == 0 || kMsg.Value.Length() == 0 {
return 0, 0, errs.Wrap(errEmptyMsg)
}
// Attach context metadata as headers
header, err := GetMQHeaderWithContext(ctx)
if err != nil {
return 0, 0, err
}
kMsg.Headers = header
// Send the message
partition, offset, err := p.producer.SendMessage(kMsg)
if err != nil {
return 0, 0, errs.WrapMsg(err, "p.producer.SendMessage error")
}
return partition, offset, nil
}
+85
View File
@@ -0,0 +1,85 @@
package kafka
import (
"bytes"
"strings"
"github.com/IBM/sarama"
"github.com/openimsdk/tools/errs"
)
func BuildConsumerGroupConfig(conf *Config, initial int64, autoCommitEnable bool) (*sarama.Config, error) {
kfk := sarama.NewConfig()
kfk.Version = sarama.V2_0_0_0
kfk.Consumer.Offsets.Initial = initial
kfk.Consumer.Offsets.AutoCommit.Enable = autoCommitEnable
kfk.Consumer.Return.Errors = false
if conf.Username != "" || conf.Password != "" {
kfk.Net.SASL.Enable = true
kfk.Net.SASL.User = conf.Username
kfk.Net.SASL.Password = conf.Password
}
if conf.TLS.EnableTLS {
tls, err := newTLSConfig(conf.TLS.ClientCrt, conf.TLS.ClientKey, conf.TLS.CACrt, []byte(conf.TLS.ClientKeyPwd), conf.TLS.InsecureSkipVerify)
if err != nil {
return nil, err
}
kfk.Net.TLS.Config = tls
kfk.Net.TLS.Enable = true
}
return kfk, nil
}
func NewConsumerGroup(conf *sarama.Config, addr []string, groupID string) (sarama.ConsumerGroup, error) {
cg, err := sarama.NewConsumerGroup(addr, groupID, conf)
if err != nil {
return nil, errs.WrapMsg(err, "NewConsumerGroup failed", "addr", addr, "groupID", groupID, "conf", *conf)
}
return cg, nil
}
func BuildProducerConfig(conf Config) (*sarama.Config, error) {
kfk := sarama.NewConfig()
kfk.Producer.Return.Successes = true
kfk.Producer.Return.Errors = true
kfk.Producer.Partitioner = sarama.NewHashPartitioner
if conf.Username != "" || conf.Password != "" {
kfk.Net.SASL.Enable = true
kfk.Net.SASL.User = conf.Username
kfk.Net.SASL.Password = conf.Password
}
switch strings.ToLower(conf.ProducerAck) {
case "no_response":
kfk.Producer.RequiredAcks = sarama.NoResponse
case "wait_for_local":
kfk.Producer.RequiredAcks = sarama.WaitForLocal
case "wait_for_all":
kfk.Producer.RequiredAcks = sarama.WaitForAll
default:
kfk.Producer.RequiredAcks = sarama.WaitForAll
}
if conf.CompressType == "" {
kfk.Producer.Compression = sarama.CompressionNone
} else {
if err := kfk.Producer.Compression.UnmarshalText(bytes.ToLower([]byte(conf.CompressType))); err != nil {
return nil, errs.WrapMsg(err, "UnmarshalText failed", "compressType", conf.CompressType)
}
}
if conf.TLS.EnableTLS {
tls, err := newTLSConfig(conf.TLS.ClientCrt, conf.TLS.ClientKey, conf.TLS.CACrt, []byte(conf.TLS.ClientKeyPwd), conf.TLS.InsecureSkipVerify)
if err != nil {
return nil, err
}
kfk.Net.TLS.Config = tls
kfk.Net.TLS.Enable = true
}
return kfk, nil
}
func NewProducer(conf *sarama.Config, addr []string) (sarama.SyncProducer, error) {
producer, err := sarama.NewSyncProducer(addr, conf)
if err != nil {
return nil, errs.WrapMsg(err, "NewSyncProducer failed", "addr", addr, "conf", *conf)
}
return producer, nil
}
+83
View File
@@ -0,0 +1,83 @@
// Copyright © 2024 OpenIM open source community. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package kafka
import (
"crypto/tls"
"crypto/x509"
"encoding/pem"
"os"
"github.com/openimsdk/tools/errs"
)
// decryptPEM decrypts a PEM block using a password.
func decryptPEM(data []byte, passphrase []byte) ([]byte, error) {
if len(passphrase) == 0 {
return data, nil
}
b, _ := pem.Decode(data)
d, err := x509.DecryptPEMBlock(b, passphrase)
if err != nil {
return nil, errs.WrapMsg(err, "DecryptPEMBlock failed")
}
return pem.EncodeToMemory(&pem.Block{
Type: b.Type,
Bytes: d,
}), nil
}
func readEncryptablePEMBlock(path string, pwd []byte) ([]byte, error) {
data, err := os.ReadFile(path)
if err != nil {
return nil, errs.WrapMsg(err, "ReadFile failed", "path", path)
}
return decryptPEM(data, pwd)
}
// newTLSConfig setup the TLS config from general config file.
func newTLSConfig(clientCertFile, clientKeyFile, caCertFile string, keyPwd []byte, insecureSkipVerify bool) (*tls.Config, error) {
var tlsConfig tls.Config
if clientCertFile != "" && clientKeyFile != "" {
certPEMBlock, err := os.ReadFile(clientCertFile)
if err != nil {
return nil, errs.WrapMsg(err, "ReadFile failed", "clientCertFile", clientCertFile)
}
keyPEMBlock, err := readEncryptablePEMBlock(clientKeyFile, keyPwd)
if err != nil {
return nil, err
}
cert, err := tls.X509KeyPair(certPEMBlock, keyPEMBlock)
if err != nil {
return nil, errs.WrapMsg(err, "X509KeyPair failed")
}
tlsConfig.Certificates = []tls.Certificate{cert}
}
if caCertFile != "" {
caCert, err := os.ReadFile(caCertFile)
if err != nil {
return nil, errs.WrapMsg(err, "ReadFile failed", "caCertFile", caCertFile)
}
caCertPool := x509.NewCertPool()
if ok := caCertPool.AppendCertsFromPEM(caCert); !ok {
return nil, errs.New("AppendCertsFromPEM failed")
}
tlsConfig.RootCAs = caCertPool
}
tlsConfig.InsecureSkipVerify = insecureSkipVerify
return &tlsConfig, nil
}
+34
View File
@@ -0,0 +1,34 @@
package kafka
import (
"context"
"errors"
"github.com/IBM/sarama"
"github.com/openimsdk/protocol/constant"
"github.com/openimsdk/tools/mcontext"
)
var errEmptyMsg = errors.New("kafka binary msg is empty")
// GetMQHeaderWithContext extracts message queue headers from the context.
func GetMQHeaderWithContext(ctx context.Context) ([]sarama.RecordHeader, error) {
operationID, opUserID, platform, connID, err := mcontext.GetCtxInfos(ctx)
if err != nil {
return nil, err
}
return []sarama.RecordHeader{
{Key: []byte(constant.OperationID), Value: []byte(operationID)},
{Key: []byte(constant.OpUserID), Value: []byte(opUserID)},
{Key: []byte(constant.OpUserPlatform), Value: []byte(platform)},
{Key: []byte(constant.ConnID), Value: []byte(connID)},
}, nil
}
// GetContextWithMQHeader creates a context from message queue headers.
func GetContextWithMQHeader(header []*sarama.RecordHeader) context.Context {
var values []string
for _, recordHeader := range header {
values = append(values, string(recordHeader.Value))
}
return mcontext.WithMustInfoCtx(values) // Attach extracted values to context
}

Some files were not shown because too many files have changed in this diff Show More