Compare commits

...

96 Commits

Author SHA1 Message Date
Xinwei Xiong 093b3f640c Merge pull request #1820 from openimsdk/feat/openim-auto-releasse
feat(release-v3.5): Provide more automation, as well as build capabilities
2024-01-26 21:19:35 +08:00
chao 90428ee76b fix: conversation aggregate find error (#1823) 2024-01-26 20:54:14 +08:00
Xinwei Xiong(cubxxw) 60368e429c feat: set openim lint
Signed-off-by: Xinwei Xiong(cubxxw) <3293172751nss@gmail.com>
2024-01-26 19:02:14 +08:00
Xinwei Xiong(cubxxw) 8161eab91b feat: set openim lint
Signed-off-by: Xinwei Xiong(cubxxw) <3293172751nss@gmail.com>
2024-01-26 19:02:11 +08:00
Xinwei Xiong(cubxxw) 2efa041664 feat: set openim lint
Signed-off-by: Xinwei Xiong(cubxxw) <3293172751nss@gmail.com>
2024-01-26 17:47:25 +08:00
Xinwei Xiong a2da40ceab Merge pull request #1806 from openimsdk/cubxxw-patch-1
Fix(release-v3.5) Enhance Component Monitoring with Dual Logging to Screen and File
2024-01-24 15:05:40 +08:00
Xinwei Xiong add05a5f26 fix: Update openim-tools.sh fix tools check 2024-01-24 15:02:32 +08:00
Brabem 776081b35d fix: add message typing field and fix the CallbackSingleMsgRead callback (#1784) 2024-01-19 18:28:30 +08:00
Xinwei Xiong 77f0b2460b Merge pull request #1774 from openimsdk/feat/set-docker-compose-pull-request
fix(release-v3.5): Update docker-compose.yml
2024-01-16 20:14:46 +08:00
Xinwei Xiong 8e5e792a60 Update release.sh 2024-01-16 10:57:45 +08:00
Xinwei Xiong 4c593d2436 Update release.sh 2024-01-16 10:57:30 +08:00
Xinwei Xiong 14838b0ac8 Update util.sh 2024-01-16 10:57:10 +08:00
Xinwei Xiong 2fb6674fc9 Update docker-compose.yml 2024-01-16 10:09:06 +08:00
Xinwei Xiong 3025c61a7a Merge pull request #1750 from AndrewZuo01/release-v3.5local
fix(realease-v3.5): add more cases for get_users api, so it accept users in older version
2024-01-16 10:04:05 +08:00
chao 077df0486e Merge pull request #1771 from withchao/release-v3.5
feat: send msg at text (#1705)
2024-01-15 17:49:24 +08:00
chao 820bf83102 feat: send msg at text (#1705)
(cherry picked from commit ceb950a57f)
2024-01-15 17:43:50 +08:00
Xinwei Xiong 6027d2dfa3 Merge pull request #1763 from openimsdk/cubxxw-patch-2
Stability Enhancement: Pin Docker Compose Images for minio and openim-web
2024-01-13 09:37:44 +08:00
Xinwei Xiong e6371f804d Update docker-compose.yml 2024-01-12 22:30:43 +08:00
AndrewZuo01 de54be961f Merge remote-tracking branch 'origin/release-v3.5local' into release-v3.5local
# Conflicts:
#	pkg/common/db/mgo/user.go
2024-01-12 21:33:37 +08:00
AndrewZuo01 4cc3e0b194 fix find user 2024-01-12 21:27:34 +08:00
Xinwei Xiong c7a884511b Update docker-compose.yml (#1760) 2024-01-12 21:16:56 +08:00
OpenIM Robot e5083d5809 Merge pull request #1758 from openimsdk/cubxxw-patch-1
Update openim-tools.sh  tools scripts images
2024-01-12 20:22:13 +08:00
Xinwei Xiong 266237861b Update openim-tools.sh 2024-01-12 20:21:36 +08:00
Xinwei Xiong 0aae970298 update release-v3.5 (#1755)
* update release-v3.5

* Update check-all.sh

* Update util.sh
2024-01-12 18:32:32 +08:00
AndrewZuo01 3be16ce3e8 fix pageFindUser 2024-01-12 15:19:40 +08:00
Xinwei Xiong c4a140043c Merge pull request #1748 from openimsdk/feat/add-openim-docs
docs: update oepnim docs openim-tools images
2024-01-12 13:49:04 +08:00
Xinwei Xiong 66bf8496b8 Update openim-tools.sh 2024-01-12 13:43:09 +08:00
Xinwei Xiong ce32c8d9c1 Update openim-tools.sh 2024-01-12 13:42:38 +08:00
Xinwei Xiong 760a119015 docs: update oepnim docs openim-tools images 2024-01-12 13:40:43 +08:00
Xinwei Xiong d236f4070c Merge pull request #1747 from withchao/release-v3.5
fix: NotificationUserInfoUpdate not notified
2024-01-12 11:31:44 +08:00
withchao a412ab33a6 fix: NotificationUserInfoUpdate
(cherry picked from commit 48ff03f854)
2024-01-12 11:20:53 +08:00
Xinwei Xiong ef19cf4f8d Merge pull request #1744 from AndrewZuo01/3.5
fix update friends
2024-01-11 20:56:40 +08:00
AndrewZuo01 07dfde965c fix update friends 2024-01-11 20:41:24 +08:00
Xinwei Xiong afac910ecf Merge pull request #1741 from withchao/release-v3.5
fix: GroupApplicationRejectedNotification not notification apply for userID
2024-01-11 18:28:20 +08:00
withchao c2b8279ddc fix: GroupApplicationAcceptedNotification 2024-01-11 16:31:24 +08:00
withchao 0c745782b0 fix: GroupApplicationAcceptedNotification
(cherry picked from commit 4c3c4555a3)
2024-01-11 14:51:42 +08:00
Xinwei Xiong 2ba45691c4 Merge pull request #1737 from luhaoling/realea3.5
fix: fix the mongo search error
2024-01-10 20:55:12 +08:00
luhaoling 8d5d54af33 fix: fix the mongo search error 2024-01-10 18:24:24 +08:00
Xinwei Xiong 571e239992 Merge pull request #1729 from luhaoling/realea3.5
fix: fix the getSortedConversationList api
2024-01-10 10:14:20 +08:00
luhaoling 159822e207 fix: fix the getSortedConversation api 2024-01-09 18:58:17 +08:00
luhaoling 89b59eb82e fix: fix the valiable name 2024-01-09 18:48:58 +08:00
Xinwei Xiong 76b35210aa Merge pull request #1719 from FGadvancer/release-v3.5
fix: group messages sync failed.
2024-01-09 10:57:38 +08:00
Gordon 093f9a3b34 fix: group messages sync failed. 2024-01-09 10:17:39 +08:00
Xinwei Xiong 7ad8a7241d Merge pull request #1712 from luhaoling/realea3.5
fix: fix some bug
2024-01-08 21:37:56 +08:00
luhaoling c71bcefcbe fix: fix some bug 2024-01-08 18:27:50 +08:00
Xinwei Xiong c4a8602428 Merge pull request #1690 from openimsdk/feat/optimizing-mongo-env
fix openim config mongo passwd env
2024-01-05 10:10:35 +08:00
Xinwei Xiong (cubxxw) b17b364b02 fix openim config mongo passwd env
Signed-off-by: Xinwei Xiong (cubxxw) <3293172751nss@gmail.com>
2024-01-05 10:02:37 +08:00
Xinwei Xiong 7e2c535f37 Merge pull request #1687 from openimsdk/main
rebase openim main to release-v3.5
2024-01-04 20:57:56 +08:00
Xinwei Xiong e0244d9ca7 Update docker-start-all.sh fix MSG_TRANSFER_PROM_PORT (#1679)
* fix: fix the bug

* fix: fix the imAdmin permission and searchNoficitaion resp

* 2023 Annual Summary Reflections and Aspirations

Signed-off-by: Xinwei Xiong (cubxxw) <3293172751nss@gmail.com>

* fix: dissmissGroup and lack of keyword bug (#1678)

* Update docker-start-all.sh

* Update env-template.yaml

* Update docker-start-all.sh

---------

Signed-off-by: Xinwei Xiong (cubxxw) <3293172751nss@gmail.com>
Co-authored-by: luhaoling <2198702716@qq.com>
Co-authored-by: Brabem <69128477+luhaoling@users.noreply.github.com>
Co-authored-by: OpenIM Bot <124379614+kubbot@users.noreply.github.com>
2024-01-04 12:51:21 +00:00
skiffer-git 6764fa5e70 MongoDB supports non-root users (#1684)
* MongoDB supports non-root users

Signed-off-by: skiffer-git <44203734@qq.com>

* Update component.go

* Update env-template.yaml

* Update docker-compose.yml

* Update environment.sh

* Update openim.yaml

* Update mongo-init.sh

---------

Signed-off-by: skiffer-git <44203734@qq.com>
Co-authored-by: Xinwei Xiong <3293172751@qq.com>
2024-01-04 12:50:29 +00:00
OpenIM Robot a3acba5eea Merge pull request #1683 from openimsdk/cubxxw-patch-2
Update docker-start-all.sh
2024-01-04 18:25:20 +08:00
Xinwei Xiong baf0d1888c Update docker-start-all.sh 2024-01-04 18:24:54 +08:00
Xinwei Xiong 5b99eb21e7 Merge pull request #1680 from openimsdk/cubxxw-patch-1
Update docker-start-all.sh fix MSG_TRANSFER_PROM_PORT
2024-01-04 17:43:26 +08:00
OpenIM Bot 58ab340d1e Update env-template.yaml 2024-01-04 17:42:57 +08:00
Xinwei Xiong d8eec588de Merge branch 'release-v3.5' into cubxxw-patch-1 2024-01-04 17:40:40 +08:00
Xinwei Xiong b19d1f1726 Update docker-start-all.sh 2024-01-04 17:39:32 +08:00
Brabem d446bdb943 fix: dissmissGroup and lack of keyword bug (#1678) 2024-01-04 17:24:51 +08:00
Brabem d3047d73b6 fix: dissmissGroup and lack of keyword bug (#1672)
* feat: add GetConversationsUnreadSeqAndMaxSeq func

* feat: add GetConversationsUnreadSeqAndMaxSeq func

* feat: add GetConversationsUnreadSeqAndMaxSeq func

* fix: fix the conflect

* feat: add GetConversationList Api

* fix: fix the error

* fix: move the GetConversationList to conversation folder

* fix: fix the go.mod

* fix: add InitiateFormData and CompleteFormData

* fix: fix the error

* fix: find error

* fix: test

* feat: add notification API

* fix: find error

* fix: fix the error

* fix: fix the PinFriend error

* fix: fix the Ex error

* fix: find the error

* fix: fix the rpc error

* fix: fix the error

* fix: fix the log error

* fix: fix the error1

* fix: fix the error

* fix: fix the script

* fix: fix the error

* fix: fix the error

* fix: fix the error of tag

* fix: fix the protocol

* fix: fix the error

* fix: fix the error

* fix: fix the err not wrap

* fix: fix the error

* fix: fix GetGroupMembers by nickname

* fix: support search userInfo by nickname or userID

* fix: fix the search by nickname error

* fix: fix the mod
2024-01-04 08:04:15 +00:00
Xinwei Xiong 09c3229d9d feat(main): fix openim docker start openim server internal port lock (#1673)
* fix: fix the bug

* fix: fix the imAdmin permission and searchNoficitaion resp

* 2023 Annual Summary Reflections and Aspirations

Signed-off-by: Xinwei Xiong (cubxxw) <3293172751nss@gmail.com>

---------

Signed-off-by: Xinwei Xiong (cubxxw) <3293172751nss@gmail.com>
Co-authored-by: luhaoling <2198702716@qq.com>
2024-01-04 08:01:32 +00:00
Xinwei Xiong 4abddd8247 Merge pull request #1674 from openimsdk/feat/add-docker-start
feat(release-v3.5): fix openim docker start openim server internal port lock
2024-01-04 15:59:55 +08:00
Xinwei Xiong c70ee5cbfb Merge branch 'release-v3.5' into feat/add-docker-start 2024-01-04 15:47:31 +08:00
Xinwei Xiong (cubxxw) 7bb44b8b88 2023 Annual Summary Reflections and Aspirations
Signed-off-by: Xinwei Xiong (cubxxw) <3293172751nss@gmail.com>
2024-01-04 15:40:14 +08:00
Xinwei Xiong e1c2e94ec9 Update env-template.yaml (#1670)
* Update env-template.yaml

* Update env-template.yaml

* Update env-template.yaml
2024-01-04 06:15:14 +00:00
Xinwei Xiong 019ef60616 Merge pull request #1666 from luhaoling/releases
fix: fix the imAdmin permission and searchNoficitaion resp
2024-01-03 18:27:16 +08:00
luhaoling 16bc053d09 fix: fix the imAdmin permission and searchNoficitaion resp 2024-01-03 18:11:02 +08:00
Brabem 9e2a256817 fix: fix the imAdmin permission (#1664)
* fix: fix the error

* fix: fix the SearchNotificationAccount resp

* fix: fix the god

* Update install-im-server.sh

* Update install-im-server.sh

* fix: fix the searchNotificationAccounts

* fix: fix the imAdmin competence

* fix: fix the error

* fix: fix the checkAdminV3

---------

Co-authored-by: Xinwei Xiong <3293172751@qq.com>
2024-01-03 17:24:02 +08:00
Brabem 587533df4d fix: update Notification update resp (#1663)
* fix: fix the error

* fix: fix the SearchNotificationAccount resp

* fix: fix the god

* Update install-im-server.sh

* Update install-im-server.sh

---------

Co-authored-by: Xinwei Xiong <3293172751@qq.com>
2024-01-03 07:46:38 +00:00
luhaoling 61f83212b2 fix: fix the bug 2024-01-03 15:39:31 +08:00
Gordon 5d1cf8c061 fix: Adjust the logic in multiTerminalLoginChecker to prevent onlineUserNum from decreasing below zero, thereby avoiding negative values. (#1658)
* fix: add notifications for some notifications.

* fix: modify dismissed group's status.

* fix: Adjust the logic in multiTerminalLoginChecker to prevent onlineUserNum from decreasing below zero, thereby avoiding negative values.
2024-01-03 07:32:42 +00:00
Brabem f1ba5c2bff fix: fix the error (#1653) 2024-01-03 02:07:01 +00:00
Xinwei Xiong 11108e1ddf fix: release openim version not auto build (#1660) 2024-01-02 12:59:26 +00:00
Gordon c19bafc49d fix: modify dismissed group's status. (#1655)
* fix: add notifications for some notifications.

* fix: modify dismissed group's status.
2024-01-02 07:37:05 +00:00
Xinwei Xiong d594d6f517 fix: install-im-server (#1648)
Signed-off-by: Xinwei Xiong (cubxxw) <3293172751nss@gmail.com>
2024-01-02 04:42:46 +00:00
chao 3fffc2f212 feat: support websocket first message method response code (#1651)
* upgrade package and rtc convert

* upgrade package and rtc convert

* upgrade package and rtc convert

* upgrade package and rtc convert

* friend user

* s3 form data

* s3 form data

* s3 form data

* s3 form data

* s3 form data

* s3 form data

* s3 form data

* s3 form data

* s3 form data

* ws

* ws

* ws

* ws

* ws
2024-01-02 03:51:09 +00:00
Xinwei Xiong 49f4e3f0de fix: fix Security vulnerability (#1646)
Signed-off-by: Xinwei Xiong (cubxxw) <3293172751nss@gmail.com>
2024-01-01 13:11:57 +00:00
Xinwei Xiong bed112d037 Update check-all.sh (#1643) 2023-12-30 03:12:19 +00:00
xuexihuang a7138cb3b7 feature:grafana web inside (#1636)
* feature:grafana web inside

* fix: using protocal v0.0.42

* fix: review values(but not use)
2023-12-30 02:40:42 +00:00
Xinwei Xiong 959c5a7ae1 feat: add openim scripts fix and optimize config about openim source code deployment for this PR? openim ci and scripts (#1641)
* fix: fix openim zk env set

Signed-off-by: Xinwei Xiong (cubxxw) <3293172751nss@gmail.com>

* fix: fix openim zk env set

Signed-off-by: Xinwei Xiong (cubxxw) <3293172751nss@gmail.com>

* feat: add openim docker prom

Signed-off-by: Xinwei Xiong (cubxxw) <3293172751nss@gmail.com>

* feat: add openim docker prom

Signed-off-by: Xinwei Xiong (cubxxw) <3293172751nss@gmail.com>

---------

Signed-off-by: Xinwei Xiong (cubxxw) <3293172751nss@gmail.com>
2023-12-29 22:29:52 +08:00
Xinwei Xiong 1e52376d7b fix: fix openim scripts and ci add openim check (#1632)
* fix: fix openim zk env set

Signed-off-by: Xinwei Xiong (cubxxw) <3293172751nss@gmail.com>

* fix: fix openim zk env set

Signed-off-by: Xinwei Xiong (cubxxw) <3293172751nss@gmail.com>

* feat: add openim docker prom

Signed-off-by: Xinwei Xiong (cubxxw) <3293172751nss@gmail.com>

---------

Signed-off-by: Xinwei Xiong (cubxxw) <3293172751nss@gmail.com>
2023-12-29 12:43:58 +00:00
AndrewZuo01 c68a61d8c2 fix setUserInfoEx (#1635)
* update set pin friends

* update set pin friends

* update set pin friends

* update set pin friends

* update set pin friends

* update set pin friends

* fix bugs

* fix bugs

* debug

* debug

* debug

* debug

* debug

* debug

* debug

* debug

* debug

* debug

* debug

* debug

* Update go.mod

* Update friend.go

* debug

* debug

* debug

* add pin friend test

* add pin friend test

* add pin friend test

* add pin friend test

* add pin friend test

* add pin friend test

* add pin friend test

* add pin friend test

* add pin friend test

* I cannot solve todo in test.sh

* update user command

* update user command

* update user command

* update user command

* update user command

* update user command

* update user command

* update user command

* update user command

* update user command

* update user command

* update user command

* update user command

* update user command

* Update go.mod

* fix group notification

* fix group notification

* update openimsdk tools

* update openim server remove duplicate code

* update openim server remove duplicate code

* update user command get

* update user command get

* update response of callback response error

* update black ex

* update join group ex

* update user pb2map

* update go sum

* update go sum

* update updateUserInfoEx

* update updateUserInfoEx

* update updateUserInfoEx add callback functions

* fix dismiss group function

* fix dismiss group function

* fix dismiss group function

* fix dismiss group function

* update pin friend to update friend

* fix go mod

* fix err golangci-lint

* fix UserPb2DBMap

* update comments, update go.mod check for register username

* update comments, update go.mod check for register username

* update comments, update go.mod check for register username

* update comments, update go.mod check for register username

* fix callback

* fix go.mod

* fix debug

* fix bugs

* update notification

* update notification

* update notification

* update notification

* update notification

* update notification

* update notification

* update notification

* fix updateUserInfoEx

* fix updateUserInfoEx

* modify go.mod

* fix updateUserInfoEx

* fix updateUserInfoEx

---------

Co-authored-by: Xinwei Xiong <3293172751@qq.com>
2023-12-29 20:38:43 +08:00
Brabem 7ddb84f7fc fix: add the GetconversationAPI 1542 (#1604)
* feat: add GetConversationsUnreadSeqAndMaxSeq func

* feat: add GetConversationsUnreadSeqAndMaxSeq func

* feat: add GetConversationsUnreadSeqAndMaxSeq func

* fix: fix the conflect

* feat: add GetConversationList Api

* fix: fix the error

* fix: move the GetConversationList to conversation folder

* fix: fix the go.mod

* fix: add InitiateFormData and CompleteFormData

* fix: fix the error

* fix: find error

* fix: test

* feat: add notification API

* fix: find error

* fix: fix the error

* fix: fix the PinFriend error

* fix: fix the Ex error

* fix: find the error

* fix: fix the rpc error

* fix: fix the error

* fix: fix the log error

* fix: fix the error1

* fix: fix the error

* fix: fix the script

* fix: fix the error

* fix: fix the error

* fix: fix the error of tag

* fix: fix the protocol

* fix: fix the error

* fix: fix the error

* fix: fix the err not wrap

* fix: fix the error

* fix: fix GetGroupMembers by nickname

* fix: fix the error

* fix: fix the FindOneByDocIDs

* fix: fix the protocol version

* fix: fit the protocol version
2023-12-29 18:14:15 +08:00
a3d21 cfde7bb0ff fix: mongo uri connect (#1611) 2023-12-29 02:25:03 +00:00
AndrewZuo01 998d4a3504 Add updates friend, set user ex (#1592)
* update set pin friends

* update set pin friends

* update set pin friends

* update set pin friends

* update set pin friends

* update set pin friends

* fix bugs

* fix bugs

* debug

* debug

* debug

* debug

* debug

* debug

* debug

* debug

* debug

* debug

* debug

* debug

* Update go.mod

* Update friend.go

* debug

* debug

* debug

* add pin friend test

* add pin friend test

* add pin friend test

* add pin friend test

* add pin friend test

* add pin friend test

* add pin friend test

* add pin friend test

* add pin friend test

* I cannot solve todo in test.sh

* update user command

* update user command

* update user command

* update user command

* update user command

* update user command

* update user command

* update user command

* update user command

* update user command

* update user command

* update user command

* update user command

* update user command

* Update go.mod

* fix group notification

* fix group notification

* update openimsdk tools

* update openim server remove duplicate code

* update openim server remove duplicate code

* update user command get

* update user command get

* update response of callback response error

* update black ex

* update join group ex

* update user pb2map

* update go sum

* update go sum

* update updateUserInfoEx

* update updateUserInfoEx

* update updateUserInfoEx add callback functions

* fix dismiss group function

* fix dismiss group function

* fix dismiss group function

* fix dismiss group function

* update pin friend to update friend

* fix go mod

* fix err golangci-lint

* fix UserPb2DBMap

* update comments, update go.mod check for register username

* update comments, update go.mod check for register username

* update comments, update go.mod check for register username

* update comments, update go.mod check for register username

* fix callback

* fix go.mod

* fix debug

* fix bugs

* update notification

* update notification

* update notification

* update notification

* update notification

* update notification

* update notification

* update notification

---------

Co-authored-by: Xinwei Xiong <3293172751@qq.com>
2023-12-28 06:45:27 +00:00
Xinwei Xiong b90b8a1a55 fix: fix openim zk env set (#1623)
* fix: fix openim zk env set

Signed-off-by: Xinwei Xiong (cubxxw) <3293172751nss@gmail.com>

* fix: fix openim zk env set

Signed-off-by: Xinwei Xiong (cubxxw) <3293172751nss@gmail.com>

---------

Signed-off-by: Xinwei Xiong (cubxxw) <3293172751nss@gmail.com>
2023-12-28 06:17:27 +00:00
Gordon 53a3f475f3 fix: add notifications for some notifications. (#1609) 2023-12-27 16:41:51 +00:00
Brabem cff90a3099 fix: fix the searchNotificationAccout by userID or nickname (#1617)
* feat: add notification API

* fix: fix the script

* fix: fix the error

* fix: fix the searchNotificationAccount

* fix: fix the protocol version

* fix: fix the proto version
2023-12-27 16:29:44 +00:00
Xinwei Xiong 2220645429 Update README.md (#1615) 2023-12-26 10:03:07 +00:00
chao cd1235fb32 feat: s3 FormData upload (#1614)
* upgrade package and rtc convert

* upgrade package and rtc convert

* upgrade package and rtc convert

* upgrade package and rtc convert

* friend user

* s3 form data

* s3 form data

* s3 form data

* s3 form data

* s3 form data

* s3 form data

* s3 form data

* s3 form data

* s3 form data
2023-12-26 09:29:42 +00:00
Xinwei Xiong 47dd6b17f6 Update openimci.yml (#1610)
* Update openimci.yml

* Update Makefile
2023-12-26 04:16:55 +00:00
Brabem 7389639f17 feat: add the notificationAccount (#1602)
* feat: add notification API

* fix: fix the script

* fix: fix the error
2023-12-26 02:15:15 +00:00
Gordon de451d4cea fix: online notifications do not push to herself. (#1534)
* fix: online notifications do not push to herself.

* fix: online notifications do not push to herself.

* fix: online notifications do not push to herself.
2023-12-25 08:30:18 +00:00
Xinwei Xiong 11a147792d Update prometheus.yml (#1586) 2023-12-25 08:28:42 +00:00
Xinwei Xiong f10528010b Update check-all.sh (#1591) 2023-12-25 08:26:59 +00:00
xuexihuang 34ed032af1 K8s environment supports multiple msggateway by consistent hash (#1600)
* feature:support multi msggateway

* feature:support multi msggateway

* feature:support multi msggateway by hash method

* fix:fix log

* change to consistent hash

* change go.mod

* fix:fix go routine values error

* fix:fix push filter logic

* fix:fix push filter logic

---------

Co-authored-by: lin.huang <lin.huang@apulis.com>
2023-12-25 15:32:07 +08:00
Brabem ed5f012c0d fix: fix the output format (#1585)
* fix: update the component output format

* fix: fix the successful tiops

* fix: update the error format
2023-12-20 02:22:55 +00:00
AndrewZuo01 87610568ae add crud for general function user process, add pinFriend (#1532)
* update set pin friends

* update set pin friends

* update set pin friends

* update set pin friends

* update set pin friends

* update set pin friends

* fix bugs

* fix bugs

* debug

* debug

* debug

* debug

* debug

* debug

* debug

* debug

* debug

* debug

* debug

* debug

* Update go.mod

* Update friend.go

* debug

* debug

* debug

* add pin friend test

* add pin friend test

* add pin friend test

* add pin friend test

* add pin friend test

* add pin friend test

* add pin friend test

* add pin friend test

* add pin friend test

* I cannot solve todo in test.sh

* update user command

* update user command

* update user command

* update user command

* update user command

* update user command

* update user command

* update user command

* update user command

* update user command

* update user command

* update user command

* update user command

* update user command

* Update go.mod

* fix group notification

* fix group notification

* update openimsdk tools

* update openim server remove duplicate code

* update openim server remove duplicate code

* update user command get

* update user command get

* update response of callback response error

* update black ex

* update join group ex

* update user pb2map

* update go sum

* update go sum

---------

Co-authored-by: Xinwei Xiong <3293172751@qq.com>
2023-12-18 10:39:58 +00:00
165 changed files with 7555 additions and 4251 deletions
+1 -1
View File
@@ -33,7 +33,7 @@ env:
OPEN_IM_SERVER_CLA_DOCUMENT: https://github.com/openim-sigs/cla/blob/main/README.md
OPEN_IM_SERVER_SIGNATURES_PATH: signatures/${{ github.event.repository.name }}/cla.json
OPEN_IM_SERVER_ALLOWLIST: kubbot,bot*,bot-*,bot/*,bot-/*,bot,*[bot]
OPEN_IM_SERVER_ALLOWLIST: kubbot,openimbot,bot*,dependabot,sweep-ai,*bot,bot-*,bot/*,bot-/*,bot,*[bot]
jobs:
CLAAssistant:
+130
View File
@@ -106,6 +106,16 @@ jobs:
ghcr.io/openimsdk/openim-api
openim/openim-api
registry.cn-hangzhou.aliyuncs.com/openimsdk/openim-api
tags: |
type=ref,event=tag
type=schedule
type=ref,event=branch
type=ref,event=pr
type=semver,pattern={{version}}
type=semver,pattern=v{{version}}
type=semver,pattern={{major}}.{{minor}}
type=semver,pattern={{major}}
type=sha
- name: Build and push Docker image for openim-api
uses: docker/build-push-action@v5
@@ -127,6 +137,16 @@ jobs:
ghcr.io/openimsdk/openim-cmdutils
openim/openim-cmdutils
registry.cn-hangzhou.aliyuncs.com/openimsdk/openim-cmdutils
tags: |
type=ref,event=tag
type=schedule
type=ref,event=branch
type=ref,event=pr
type=semver,pattern={{version}}
type=semver,pattern=v{{version}}
type=semver,pattern={{major}}.{{minor}}
type=semver,pattern={{major}}
type=sha
- name: Build and push Docker image for openim-cmdutils
uses: docker/build-push-action@v5
@@ -148,6 +168,16 @@ jobs:
ghcr.io/openimsdk/openim-crontask
openim/openim-crontask
registry.cn-hangzhou.aliyuncs.com/openimsdk/openim-crontask
tags: |
type=ref,event=tag
type=schedule
type=ref,event=branch
type=ref,event=pr
type=semver,pattern={{version}}
type=semver,pattern=v{{version}}
type=semver,pattern={{major}}.{{minor}}
type=semver,pattern={{major}}
type=sha
- name: Build and push Docker image for openim-crontask
uses: docker/build-push-action@v5
@@ -169,6 +199,16 @@ jobs:
ghcr.io/openimsdk/openim-msggateway
openim/openim-msggateway
registry.cn-hangzhou.aliyuncs.com/openimsdk/openim-msggateway
tags: |
type=ref,event=tag
type=schedule
type=ref,event=branch
type=ref,event=pr
type=semver,pattern={{version}}
type=semver,pattern=v{{version}}
type=semver,pattern={{major}}.{{minor}}
type=semver,pattern={{major}}
type=sha
- name: Build and push Docker image for openim-msggateway
uses: docker/build-push-action@v5
@@ -190,6 +230,16 @@ jobs:
ghcr.io/openimsdk/openim-msgtransfer
openim/openim-msgtransfer
registry.cn-hangzhou.aliyuncs.com/openimsdk/openim-msgtransfer
tags: |
type=ref,event=tag
type=schedule
type=ref,event=branch
type=ref,event=pr
type=semver,pattern={{version}}
type=semver,pattern=v{{version}}
type=semver,pattern={{major}}.{{minor}}
type=semver,pattern={{major}}
type=sha
- name: Build and push Docker image for openim-msgtransfer
uses: docker/build-push-action@v5
@@ -211,6 +261,16 @@ jobs:
ghcr.io/openimsdk/openim-push
openim/openim-push
registry.cn-hangzhou.aliyuncs.com/openimsdk/openim-push
tags: |
type=ref,event=tag
type=schedule
type=ref,event=branch
type=ref,event=pr
type=semver,pattern={{version}}
type=semver,pattern=v{{version}}
type=semver,pattern={{major}}.{{minor}}
type=semver,pattern={{major}}
type=sha
- name: Build and push Docker image for openim-push
uses: docker/build-push-action@v5
@@ -232,6 +292,16 @@ jobs:
ghcr.io/openimsdk/openim-rpc-auth
openim/openim-rpc-auth
registry.cn-hangzhou.aliyuncs.com/openimsdk/openim-rpc-auth
tags: |
type=ref,event=tag
type=schedule
type=ref,event=branch
type=ref,event=pr
type=semver,pattern={{version}}
type=semver,pattern=v{{version}}
type=semver,pattern={{major}}.{{minor}}
type=semver,pattern={{major}}
type=sha
- name: Build and push Docker image for openim-rpc-auth
uses: docker/build-push-action@v5
@@ -253,6 +323,16 @@ jobs:
ghcr.io/openimsdk/openim-rpc-conversation
openim/openim-rpc-conversation
registry.cn-hangzhou.aliyuncs.com/openimsdk/openim-rpc-conversation
tags: |
type=ref,event=tag
type=schedule
type=ref,event=branch
type=ref,event=pr
type=semver,pattern={{version}}
type=semver,pattern=v{{version}}
type=semver,pattern={{major}}.{{minor}}
type=semver,pattern={{major}}
type=sha
- name: Build and push Docker image for openim-rpc-conversation
uses: docker/build-push-action@v5
@@ -274,6 +354,16 @@ jobs:
ghcr.io/openimsdk/openim-rpc-friend
openim/openim-rpc-friend
registry.cn-hangzhou.aliyuncs.com/openimsdk/openim-rpc-friend
tags: |
type=ref,event=tag
type=schedule
type=ref,event=branch
type=ref,event=pr
type=semver,pattern={{version}}
type=semver,pattern=v{{version}}
type=semver,pattern={{major}}.{{minor}}
type=semver,pattern={{major}}
type=sha
- name: Build and push Docker image for openim-rpc-friend
uses: docker/build-push-action@v5
@@ -295,6 +385,16 @@ jobs:
ghcr.io/openimsdk/openim-rpc-group
openim/openim-rpc-group
registry.cn-hangzhou.aliyuncs.com/openimsdk/openim-rpc-group
tags: |
type=ref,event=tag
type=schedule
type=ref,event=branch
type=ref,event=pr
type=semver,pattern={{version}}
type=semver,pattern=v{{version}}
type=semver,pattern={{major}}.{{minor}}
type=semver,pattern={{major}}
type=sha
- name: Build and push Docker image for openim-rpc-group
uses: docker/build-push-action@v5
@@ -316,6 +416,16 @@ jobs:
ghcr.io/openimsdk/openim-rpc-msg
openim/openim-rpc-msg
registry.cn-hangzhou.aliyuncs.com/openimsdk/openim-rpc-msg
tags: |
type=ref,event=tag
type=schedule
type=ref,event=branch
type=ref,event=pr
type=semver,pattern={{version}}
type=semver,pattern=v{{version}}
type=semver,pattern={{major}}.{{minor}}
type=semver,pattern={{major}}
type=sha
- name: Build and push Docker image for openim-rpc-msg
uses: docker/build-push-action@v5
@@ -337,6 +447,16 @@ jobs:
ghcr.io/openimsdk/openim-rpc-third
openim/openim-rpc-third
registry.cn-hangzhou.aliyuncs.com/openimsdk/openim-rpc-third
tags: |
type=ref,event=tag
type=schedule
type=ref,event=branch
type=ref,event=pr
type=semver,pattern={{version}}
type=semver,pattern=v{{version}}
type=semver,pattern={{major}}.{{minor}}
type=semver,pattern={{major}}
type=sha
- name: Build and push Docker image for openim-rpc-third
uses: docker/build-push-action@v5
@@ -358,6 +478,16 @@ jobs:
ghcr.io/openimsdk/openim-rpc-user
openim/openim-rpc-user
registry.cn-hangzhou.aliyuncs.com/openimsdk/openim-rpc-user
tags: |
type=ref,event=tag
type=schedule
type=ref,event=branch
type=ref,event=pr
type=semver,pattern={{version}}
type=semver,pattern=v{{version}}
type=semver,pattern={{major}}.{{minor}}
type=semver,pattern={{major}}
type=sha
- name: Build and push Docker image for openim-rpc-user
uses: docker/build-push-action@v5
+34 -4
View File
@@ -92,11 +92,41 @@ jobs:
- name: Exec OpenIM API test
run: |
sudo make test-api
- name: Exec OpenIM E2E test
mkdir -p ./tmp
touch ./tmp/test.md
echo "# OpenIM Test" >> ./tmp/test.md
echo "## OpenIM API Test" >> ./tmp/test.md
echo "<details><summary>Command Output for OpenIM API Test</summary>" >> ./tmp/test.md
echo "<pre><code>" >> ./tmp/test.md
sudo make test-api | tee -a ./tmp/test.md
echo "</code></pre>" >> ./tmp/test.md
echo "</details>" >> ./tmp/test.md
- name: Exec OpenIM E2E Test
run: |
sudo make test-e2e
echo "" >> ./tmp/test.md
echo "## OpenIM E2E Test" >> ./tmp/test.md
echo "<details><summary>Command Output for OpenIM E2E Test</summary>" >> ./tmp/test.md
echo "<pre><code>" >> ./tmp/test.md
sudo make test-e2e | tee -a ./tmp/test.md
echo "</code></pre>" >> ./tmp/test.md
echo "</details>" >> ./tmp/test.md
- name: Comment PR with file
uses: thollander/actions-comment-pull-request@v2
with:
filePath: ./tmp/test.md
comment_tag: nrt_file
reactions: eyes, rocket
mode: recreate
GITHUB_TOKEN: ${{ secrets.BOT_GITHUB_TOKEN }}
continue-on-error: true
- name: Check outputs
run: |
echo "id : ${{ steps.nrt_message.outputs.id }}"
echo "body : ${{ steps.nrt_message.outputs.body }}"
echo "html_url : ${{ steps.nrt_message.outputs.html_url }}"
- name: Exec OpenIM System uninstall
run: |
+1
View File
@@ -17,6 +17,7 @@ on:
issues:
types:
- labeled
jobs:
add-comment:
if: github.event.label.name == 'help wanted' || github.event.label.name == 'good first issue'
+90 -30
View File
@@ -18,39 +18,42 @@ on:
push:
branches:
- main
- release-*
paths-ignore:
- "docs/**"
- "README.md"
- "README_zh-CN.md"
- "**.md"
- "docs/**"
- "CONTRIBUTING.md"
pull_request:
branches:
- main
- release-*
paths-ignore:
- "README.md"
- "README_zh-CN.md"
- "CONTRIBUTING.md"
- "CONTRIBUTING/**"
- "**.md"
- "docs/**"
env:
GO_VERSION: "1.19"
GOLANGCI_VERSION: "v1.50.1"
jobs:
openim:
name: Test with go ${{ matrix.go_version }} on ${{ matrix.os }}
runs-on: ${{ matrix.os }}
permissions:
contents: write
pull-requests: write
environment:
name: openim
strategy:
matrix:
go_version: ["1.19","1.20","1.21"]
os: [ubuntu-latest]
steps:
- name: Setup
uses: actions/checkout@v4
@@ -67,6 +70,9 @@ jobs:
version: '3.x' # If available, use the latest major version that's compatible
repo-token: ${{ secrets.GITHUB_TOKEN }}
- name: OpenIM Scripts Verification(make verify)
run: sudo make verify
- name: Module Operations
run: |
sudo make tidy
@@ -83,16 +89,14 @@ jobs:
- name: Build Source
run: sudo make build
- name: Build multiarch PLATFORMS
if: startsWith(github.ref, 'refs/heads/release-')
run: |
sudo make multiarch
- name: Cleanup Build
run: sudo make clean
- name: Push Changes to Main
uses: stefanzweifel/git-auto-commit-action@v5
with:
commit_message: "cicd: robot automated Change"
branch: main
continue-on-error: true
- name: Set Current Directory
id: set_directory
run: echo "::set-output name=directory::$(pwd)"
@@ -109,8 +113,11 @@ jobs:
continue-on-error: true
openim-start:
name: Test OpenIM install/start on ${{ matrix.os }}
name: Test OpenIM install/start on ${{ matrix.os }}-${{ matrix.arch }}
runs-on: ${{ matrix.os }}
permissions:
contents: write
pull-requests: write
environment:
name: openim
strategy:
@@ -129,15 +136,59 @@ jobs:
run: |
sudo make install
# - name: Check the OpenIM environment and status
# run: |
# sudo docker images
# sudo docker ps
- name: Check the OpenIM environment and status
if: runner.os == 'Linux' && matrix.arch == 'amd64'
id: docker_info
run: |
sleep 30
echo "images<<EOF" >> $GITHUB_ENV
sudo docker images >> $GITHUB_ENV
echo "EOF" >> $GITHUB_ENV
echo "containers<<EOF" >> $GITHUB_ENV
sudo docker ps >> $GITHUB_ENV
echo "EOF" >> $GITHUB_ENV
- name: Comment PR
uses: thollander/actions-comment-pull-request@v2
if: runner.os == 'Linux' && matrix.arch == 'amd64'
with:
message: |
> [!TIP]
> Run make install to check the status
### Docker Images:
<details><summary>Click to expand docker images</summary>
```bash
${{ env.images }}
```
</details>
### Docker Processes:
<details><summary>Click to expand docker ps</summary>
```bash
${{ env.containers }}
```
</details>
GITHUB_TOKEN: ${{ secrets.BOT_GITHUB_TOKEN }}
execute-scripts:
name: Execute OpenIM Script On ${{ matrix.os }}
name: Execute OpenIM Script On ${{ matrix.os }}-${{ matrix.arch }}
runs-on: ${{ matrix.os }}
permissions:
contents: write
pull-requests: write
environment:
name: openim
strategy:
matrix:
go_version: ["1.20"]
go_version: ["1.21"]
os: ["ubuntu-latest", "macos-latest"]
arch: [arm64, armv7, amd64]
steps:
- name: Checkout code
uses: actions/checkout@v4
@@ -155,7 +206,7 @@ jobs:
repo-token: ${{ secrets.GITHUB_TOKEN }}
# - name: Install latest Bash (macOS only)
# if: runner.os == 'macOS'
# if: runner.os == 'macOS' && matrix.arch == 'arm64'
# run: |
# /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
# brew update
@@ -175,7 +226,7 @@ jobs:
sudo sleep 20
# - name: Set up Docker for macOS
# if: runner.os == 'macOS'
# if: runner.os == 'macOS' && matrix.arch == 'arm64'
# run: |
# brew install --cask docker
# open /Applications/Docker.app
@@ -201,24 +252,27 @@ jobs:
- name: Build, Start, Check Services and Print Logs for Ubuntu
if: runner.os == 'Linux'
run: |
sudo make init && \
sudo make build && \
sudo make start && \
sudo make check || \
(echo "An error occurred, printing logs:" && sudo cat ./_output/logs/* 2>/dev/null)
sudo make build
sudo make start
sudo make check
# - name: Build, Start, Check Services and Print Logs for macOS
# if: runner.os == 'macOS'
# run: |
# make init && \
# make build && \
# make start && \
# make check || \
# (echo "An error occurred, printing logs:" && sudo cat ./_output/logs/* 2>/dev/null)
- name: Restart Services and Print Logs for Ubuntu
if: runner.os == 'Linux' && matrix.arch == 'amd64'
run: |
sudo make restart
sudo make check
- name: Build, Start, Check Services and Print Logs for macOS
if: runner.os == 'macOS' && matrix.arch == 'arm64'
run: |
make build
openim-test-build-image:
name: Build OpenIM Docker Image
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
environment:
name: openim
steps:
@@ -239,4 +293,10 @@ jobs:
- name: Test Docker Build
run: |
sudo make init
sudo make image
sudo make image
- name: Get OpenIM Docker Images Status
id: docker_processes
run: |
sudo docker images
sudo docker ps
+1 -1
View File
@@ -95,7 +95,7 @@ stop:
## restart: Restart openim (make init configuration file is initialized) ✨
.PHONY: restart
restart: clean stop build init start check
restart: clean stop build start check
## multiarch: Build binaries for multiple platforms. See option PLATFORMS. ✨
.PHONY: multiarch
+4
View File
@@ -97,6 +97,10 @@ It's crafted in Golang and supports cross-platform deployment, ensuring a cohere
## :rocket: Quick Start
We support many platforms. Here are the addresses for quick experience on the web side
👉 **[OpenIM online web demo](https://web-enterprise.rentsoft.cn/)**
You can quickly learn OpenIM engineering solutions, all it takes is one simple command:
```bash
+25 -1
View File
@@ -3,12 +3,36 @@
before:
hooks:
- make clean
# You may remove this if you don't use go modules.
- make tidy
- make copyright.add
# you may remove this if you don't need go generate
- go generate ./...
git:
# What should be used to sort tags when gathering the current and previous
# tags if there are more than one tag in the same commit.
#
# Default: '-version:refname'
tag_sort: -version:creatordate
# What should be used to specify prerelease suffix while sorting tags when gathering
# the current and previous tags if there are more than one tag in the same commit.
#
# Since: v1.17
prerelease_suffix: "-"
# Tags to be ignored by GoReleaser.
# This means that GoReleaser will not pick up tags that match any of the
# provided values as either previous or current tags.
#
# Templates: allowed.
# Since: v1.21.
ignore_tags:
- nightly
# - "{{.Env.IGNORE_TAG}}"
snapshot:
name_template: "{{ incpatch .Version }}-next"
@@ -495,4 +519,4 @@ checksum:
algorithm: sha256
release:
prerelease: auto
prerelease: auto
@@ -0,0 +1,45 @@
# 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.
# OpenIM base image: https://github.com/openim-sigs/openim-base-image
# Set go mod installation source and proxy
FROM golang:1.20 AS builder
ARG GO111MODULE=on
ARG GOPROXY=https://goproxy.cn,direct
WORKDIR /openim/openim-server
ENV GO111MODULE=$GO111MODULE
ENV GOPROXY=$GOPROXY
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN make build BINS=openim-rpc-encryption
RUN cp /openim/openim-server/_output/bin/platforms/$(go env GOOS)/$(go env GOARCH)/openim-rpc-encryption /usr/bin/openim-rpc-encryption
# FROM ghcr.io/openim-sigs/openim-bash-image:latest
FROM ghcr.io/openim-sigs/openim-bash-image:latest
WORKDIR /openim/openim-server
COPY --from=builder /usr/bin/openim-rpc-encryption ./bin/openim-rpc-encryption
ENTRYPOINT ["./bin/openim-rpc-encryption"]
@@ -0,0 +1,45 @@
# 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.
# OpenIM base image: https://github.com/openim-sigs/openim-base-image
# Set go mod installation source and proxy
FROM golang:1.20 AS builder
ARG GO111MODULE=on
ARG GOPROXY=https://goproxy.cn,direct
WORKDIR /openim/openim-server
ENV GO111MODULE=$GO111MODULE
ENV GOPROXY=$GOPROXY
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN make build BINS=openim-rpc-extend-msg
RUN cp /openim/openim-server/_output/bin/platforms/$(go env GOOS)/$(go env GOARCH)/openim-rpc-extend-msg /usr/bin/openim-rpc-extend-msg
# FROM ghcr.io/openim-sigs/openim-bash-image:latest
FROM ghcr.io/openim-sigs/openim-bash-image:latest
WORKDIR /openim/openim-server
COPY --from=builder /usr/bin/openim-rpc-extend-msg ./bin/openim-rpc-extend-msg
ENTRYPOINT ["./bin/openim-rpc-extend-msg"]
+9 -1
View File
@@ -312,6 +312,14 @@ callback:
enable: false
timeout: 5
failedContinue: true
beforeUpdateUserInfoEx:
enable: false
timeout: 5
failedContinue: true
afterUpdateUserInfoEx:
enable: false
timeout: 5
failedContinue: true
afterSendSingleMsg:
enable: false
timeout: 5
@@ -498,7 +506,7 @@ callback:
# The number of ports needs to be consistent with msg_transfer_service_num in script/path_info.sh
prometheus:
enable: false
prometheusUrl: 172.28.0.1:13000
grafanaUrl: 172.28.0.1:13000
apiPrometheusPort: [20100]
userPrometheusPort: [ 20110 ]
friendPrometheusPort: [ 20120 ]
+1 -1
View File
@@ -165,7 +165,7 @@ export MINIO_ENDPOINT="http://im-minio:9000"
export MINIO_SIGN_ENDPOINT="https://openim.server.com/im-minio-api"
mkdir ./charts/generated-configs
../scripts/genconfig.sh ../scripts/install/environment.sh ./templates/openim.yaml > ./charts/generated-configs/config.yaml
../scripts/genconfig.sh ../scripts/install/environment.sh ./templates/config.yaml > ./charts/generated-configs/config.yaml
cp ../config/notification.yaml ./charts/generated-configs/notification.yaml
../scripts/genconfig.sh ../scripts/install/environment.sh ./templates/helm-image.yaml > ./charts/generated-configs/helm-image.yaml
```
File diff suppressed because it is too large Load Diff
@@ -14,7 +14,7 @@
# -----------------------------------------------------------------
# TODO: This config file is the template file
# --| source: deployments/templates/openim.yaml
# --| source: deployments/templates/config.yaml
# --| env: scripts/install/environment
# --| target: config/config.yaml
# -----------------------------------------------------------------
@@ -53,8 +53,8 @@ mongo:
# Maximum connection pool size
address: [ ${MONGO_ADDRESS}:${MONGO_PORT} ]
database: ${MONGO_DATABASE}
username: ${MONGO_USERNAME}
password: ${MONGO_PASSWORD}
username: ${MONGO_OPENIM_USERNAME}
password: ${MONGO_OPENIM_PASSWORD}
maxPoolSize: ${MONGO_MAX_POOL_SIZE}
###################### Redis configuration information ######################
@@ -247,6 +247,14 @@ manager:
userID: [ "${MANAGER_USERID_1}", "${MANAGER_USERID_2}", "${MANAGER_USERID_3}" ]
nickname: [ "${NICKNAME_1}", "${NICKNAME_2}", "${NICKNAME_3}" ]
# chatAdmin, use for send notification
#
# Built-in app system notification account ID
# Built-in app system notification account nickname
im-admin:
userID: [ "${IM_ADMIN_USERID}" ]
nickname: [ "${IM_ADMIN_NAME}" ]
# Multi-platform login policy
# For each platform(Android, iOS, Windows, Mac, web), only one can be online at a time
multiLoginPolicy: ${MULTILOGIN_POLICY}
@@ -307,13 +315,21 @@ iosPush:
# Timeout in seconds
# Whether to continue execution if callback fails
callback:
url: ""
url: "http://127.0.0.1:10008/callbackExample"
beforeSendSingleMsg:
enable: ${CALLBACK_ENABLE}
timeout: ${CALLBACK_TIMEOUT}
failedContinue: ${CALLBACK_FAILED_CONTINUE}
beforeUpdateUserInfoEx:
enable: ${CALLBACK_ENABLE}
timeout: ${CALLBACK_TIMEOUT}
failedContinue: ${CALLBACK_FAILED_CONTINUE}
afterUpdateUserInfoEx:
enable: ${CALLBACK_ENABLE}
timeout: ${CALLBACK_TIMEOUT}
failedContinue: ${CALLBACK_FAILED_CONTINUE}
afterSendSingleMsg:
enable: ${CALLBACK_ENABLE}
enable: true
timeout: ${CALLBACK_TIMEOUT}
failedContinue: ${CALLBACK_FAILED_CONTINUE}
beforeSendGroupMsg:
@@ -498,7 +514,7 @@ callback:
# The number of ports needs to be consistent with msg_transfer_service_num in script/path_info.sh
prometheus:
enable: ${PROMETHEUS_ENABLE}
prometheusUrl: ${PROMETHEUS_URL}
grafanaUrl: ${GRAFANA_URL}
apiPrometheusPort: [${API_PROM_PORT}]
userPrometheusPort: [ ${USER_PROM_PORT} ]
friendPrometheusPort: [ ${FRIEND_PROM_PORT} ]
+82 -105
View File
@@ -1,4 +1,4 @@
# Copyright © 2023 OpenIM. All rights reserved.
# 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.
@@ -12,31 +12,26 @@
# See the License for the specific language governing permissions and
# limitations under the License.
# ======================================
# ========= Basic Configuration ========
# ======================================
# -----------------------------------------------------------------------------
# General Configuration
# This section contains general configuration options for the entire environment.
# These options can be set via environment variables. If both environment variables
# and settings in this .env file exist, the environment variables take precedence.
# -----------------------------------------------------------------------------
# ==========================
# General Configuration
# ==========================
# These settings apply to the overall environment.
# The user for authentication or system operations.
# Default: OPENIM_USER=root
USER=${OPENIM_USER}
# Password associated with the specified user for authentication.
# Default: PASSWORD=openIM123
PASSWORD=${PASSWORD}
# Base URL for the application programming interface (API).
# Default: API_URL=http://172.28.0.1:10002
API_URL=${API_URL}
# Directory path for storing data files or related information.
# Default: DATA_DIR=./
# Data storage directory for persistent data.
# Example: DATA_DIR=/path/to/data
DATA_DIR=${DATA_DIR}
# Choose the appropriate image address, the default is GITHUB image,
# you can choose docker hub, for Chinese users can choose Ali Cloud
# export IMAGE_REGISTRY="ghcr.io/openimsdk"
# export IMAGE_REGISTRY="openim"
# export IMAGE_REGISTRY="registry.cn-hangzhou.aliyuncs.com/openimsdk"
# Docker image registry. Uncomment the preferred one.
# Options: ghcr.io/openimsdk, openim, registry.cn-hangzhou.aliyuncs.com/openimsdk
# IMAGE_REGISTRY="ghcr.io/openimsdk"
# IMAGE_REGISTRY="openim"
# IMAGE_REGISTRY="registry.cn-hangzhou.aliyuncs.com/openimsdk"
IMAGE_REGISTRY=${IMAGE_REGISTRY}
# ======================================
@@ -47,10 +42,9 @@ IMAGE_REGISTRY=${IMAGE_REGISTRY}
# Default: DOCKER_BRIDGE_SUBNET=172.28.0.0/16
DOCKER_BRIDGE_SUBNET=${DOCKER_BRIDGE_SUBNET}
# Gateway for the Docker network.
# Default: DOCKER_BRIDGE_GATEWAY=172.28.0.1
# Set and specify the IP addresses of some containers. Generally speaking,
# you do not need to modify these configurations to facilitate debugging
DOCKER_BRIDGE_GATEWAY=${DOCKER_BRIDGE_GATEWAY}
MONGO_NETWORK_ADDRESS=${MONGO_NETWORK_ADDRESS}
REDIS_NETWORK_ADDRESS=${REDIS_NETWORK_ADDRESS}
KAFKA_NETWORK_ADDRESS=${KAFKA_NETWORK_ADDRESS}
@@ -65,45 +59,66 @@ NODE_EXPORTER_NETWORK_ADDRESS=${NODE_EXPORTER_NETWORK_ADDRESS}
OPENIM_ADMIN_FRONT_NETWORK_ADDRESS=${OPENIM_ADMIN_FRONT_NETWORK_ADDRESS}
ALERT_MANAGER_NETWORK_ADDRESS=${ALERT_MANAGER_NETWORK_ADDRESS}
# ===============================================
# = Component Extension Configuration =
# ===============================================
# ==============================================================================
# Configuration Update Instructions
# ==============================================================================
# This header outlines the methods to update common variables in config.yaml and .env files.
# These instructions are vital for maintaining the OpenIM environment's configuration.
#
# METHOD 1: Regenerate All Configurations
# ----------------------------------------
# Use this method to regenerate all configurations.
# Steps:
# 1. Delete existing config files:
# - openim-server/config/config.yaml
# - openim-chat/config/config.yaml
# 2. Modify the .env file as required.
# 3. Run 'docker compose up -d'. This will regenerate:
# - config/config.yaml
#
# METHOD 2: Modify Individual Configuration Files
# -----------------------------------------------
# Use this method to update specific configuration files.
# Steps:
# 1. Modify the .env file as necessary.
# 2. Update the corresponding entries in:
# - config/config.yaml
# 3. Restart the services with 'docker compose up -d'.
# 4. Special Note: If you modify OPENIM_IP, API_OPENIM_PORT, or MINIO_PORT in .env,
# ensure to update the corresponding services and configurations accordingly.
#
# It is essential to follow these methods to ensure consistent and correct application behavior.
# ==============================================================================
# Local IP address of the service. Modify if necessary.
# Example: OPENIM_IP=172.28.0.1
OPENIM_IP=${OPENIM_IP}
# ============ Component Extension Configuration ==========
# ----- ZooKeeper Configuration -----
# Address or hostname for the ZooKeeper service.
# Default: ZOOKEEPER_ADDRESS=172.28.0.1
ZOOKEEPER_ADDRESS=${ZOOKEEPER_NETWORK_ADDRESS}
# Port for ZooKeeper service.
# Default: ZOOKEEPER_PORT=12181
ZOOKEEPER_PORT=${ZOOKEEPER_PORT}
# ----- MongoDB Configuration -----
# Address or hostname for the MongoDB service.
# Default: MONGO_ADDRESS=172.28.0.1
MONGO_ADDRESS=${MONGO_NETWORK_ADDRESS}
# Port on which MongoDB service is running.
# MongoDB service port configuration.
# Default: MONGO_PORT=37017
# MONGO_PORT=${MONGO_PORT}
# Username to authenticate with the MongoDB service.
# Default: MONGO_USERNAME=root
# MONGO_USERNAME=${MONGO_USERNAME}
# Password to authenticate with the MongoDB service.
# Password for MongoDB admin user. Used for service authentication.
# Default: MONGO_PASSWORD=openIM123
MONGO_PASSWORD=${MONGO_PASSWORD}
# Name of the database in MongoDB to be used.
# Default: MONGO_DATABASE=openIM_v3
# Username for a regular OpenIM user in MongoDB.
# Default: MONGO_OPENIM_USERNAME=openIM
MONGO_OPENIM_USERNAME=${MONGO_OPENIM_USERNAME}
# Password for a regular OpenIM user in MongoDB.
# Default: MONGO_OPENIM_PASSWORD=openIM123456
MONGO_OPENIM_PASSWORD=${MONGO_OPENIM_PASSWORD}
# Specifies the database name to be used within MongoDB.
# Default: MONGO_DATABASE=openim_v3
MONGO_DATABASE=${MONGO_DATABASE}
# ----- Redis Configuration -----
# Address or hostname for the Redis service.
# Default: REDIS_ADDRESS=172.28.0.1
REDIS_ADDRESS=${REDIS_NETWORK_ADDRESS}
# Port on which Redis in-memory data structure store is running.
# Default: REDIS_PORT=16379
@@ -113,11 +128,6 @@ REDIS_PORT=${REDIS_PORT}
# Default: REDIS_PASSWORD=openIM123
REDIS_PASSWORD=${REDIS_PASSWORD}
# ----- Kafka Configuration -----
# Address or hostname for the Kafka service.
# Default: KAFKA_ADDRESS=172.28.0.1
KAFKA_ADDRESS=${KAFKA_NETWORK_ADDRESS}
# Kakfa username to authenticate with the Kafka service.
# KAFKA_USERNAME=${KAFKA_USERNAME}
@@ -129,20 +139,13 @@ KAFKA_PORT=${KAFKA_PORT}
# Default: KAFKA_LATESTMSG_REDIS_TOPIC=latestMsgToRedis
KAFKA_LATESTMSG_REDIS_TOPIC=${KAFKA_LATESTMSG_REDIS_TOPIC}
# Topic in Kafka for pushing messages (e.g. notifications or updates).
# Default: KAFKA_MSG_PUSH_TOPIC=msgToPush
KAFKA_MSG_PUSH_TOPIC=${KAFKA_MSG_PUSH_TOPIC}
# Topic in Kafka for storing offline messages in MongoDB.
# Default: KAFKA_OFFLINEMSG_MONGO_TOPIC=offlineMsgToMongoMysql
KAFKA_OFFLINEMSG_MONGO_TOPIC=${KAFKA_OFFLINEMSG_MONGO_TOPIC}
# ----- MinIO Configuration ----
# Address or hostname for the MinIO object storage service.
# Default: MINIO_ADDRESS=172.28.0.1
MINIO_ADDRESS=${MINIO_NETWORK_ADDRESS}
# Port on which MinIO object storage service is running.
# MINIO_PORT
# ----------
# MINIO_PORT sets the port for the MinIO object storage service.
# Upon changing this port, the MinIO endpoint URLs in the `config/config.yaml` file must be updated
# to reflect this change. The endpoints include both the 'endpoint' and 'signEndpoint'
# under the MinIO configuration.
#
# Default: MINIO_PORT=10005
MINIO_PORT=${MINIO_PORT}
@@ -155,19 +158,11 @@ MINIO_PORT=${MINIO_PORT}
MINIO_SECRET_KEY=${MINIO_SECRET_KEY}
# ----- Prometheus Configuration -----
# Address or hostname for the Prometheus service.
# Default: PROMETHEUS_ADDRESS=172.28.0.1
PROMETHEUS_ADDRESS=${PROMETHEUS_NETWORK_ADDRESS}
# Port on which Prometheus service is running.
# Default: PROMETHEUS_PORT=19090
PROMETHEUS_PORT=${PROMETHEUS_PORT}
# ----- Grafana Configuration -----
# Address or hostname for the Grafana service.
# Default: GRAFANA_ADDRESS=172.28.0.1
GRAFANA_ADDRESS=${GRAFANA_NETWORK_ADDRESS}
# Port on which Grafana service is running.
# Default: GRAFANA_PORT=13000
GRAFANA_PORT=${GRAFANA_PORT}
@@ -176,35 +171,26 @@ GRAFANA_PORT=${GRAFANA_PORT}
# ============ OpenIM Web ===============
# ======================================
# Path to the OpenIM web distribution.
# Default: OPENIM_WEB_DIST_PATH=/app/dist
OPENIM_WEB_DIST_PATH=${OPENIM_WEB_DIST_PATH}
# Port on which OpenIM web service is running.
# Default: OPENIM_WEB_PORT=11001
OPENIM_WEB_PORT=${OPENIM_WEB_PORT}
# Address or hostname for the OpenIM web service.
# Default: OPENIM_WEB_ADDRESS=172.28.0.1
OPENIM_WEB_ADDRESS=${OPENIM_WEB_NETWORK_ADDRESS}
# ======================================
# ========= OpenIM Server ==============
# ======================================
# Address or hostname for the OpenIM server.
# Default: OPENIM_SERVER_ADDRESS=172.28.0.1
OPENIM_SERVER_ADDRESS=${OPENIM_SERVER_NETWORK_ADDRESS}
# Port for the OpenIM WebSockets.
# Default: OPENIM_WS_PORT=10001
OPENIM_WS_PORT=${OPENIM_WS_PORT}
# Port for the OpenIM API.
# API_OPENIM_PORT
# ---------------
# This variable defines the port on which the OpenIM API service will listen.
# When changing this port, it's essential to update the apiURL in the config.yaml file
# to ensure the API service is accessible at the new port.
#
# Default: API_OPENIM_PORT=10002
API_OPENIM_PORT=${API_OPENIM_PORT}
# ======================================
# ========== OpenIM Chat ===============
# ======================================
@@ -213,18 +199,13 @@ API_OPENIM_PORT=${API_OPENIM_PORT}
# Default: CHAT_IMAGE_VERSION=main
CHAT_IMAGE_VERSION=${CHAT_IMAGE_VERSION}
# Address or hostname for the OpenIM chat service.
# Default: OPENIM_CHAT_ADDRESS=172.28.0.1
OPENIM_CHAT_ADDRESS=${OPENIM_CHAT_NETWORK_ADDRESS}
# Port for the OpenIM chat API.
# Default: OPENIM_CHAT_API_PORT=10008
OPENIM_CHAT_API_PORT=${OPENIM_CHAT_API_PORT}
# Directory path for storing data files or related information for OpenIM chat.
# Default: OPENIM_CHAT_DATA_DIR=./openim-chat/main
OPENIM_CHAT_DATA_DIR=${OPENIM_CHAT_DATA_DIR}
# Port for the OpenIM admin API.
# Default: OPENIM_ADMIN_API_PORT=10009
OPENIM_ADMIN_API_PORT=${OPENIM_ADMIN_API_PORT}
# ======================================
# ========== OpenIM Admin ==============
@@ -233,10 +214,6 @@ OPENIM_CHAT_DATA_DIR=${OPENIM_CHAT_DATA_DIR}
# Branch name for OpenIM server.
# Default: SERVER_IMAGE_VERSION=main
SERVER_IMAGE_VERSION=${SERVER_IMAGE_VERSION}
# Port for the OpenIM admin API.
# Default: OPENIM_ADMIN_API_PORT=10009
OPENIM_ADMIN_API_PORT=${OPENIM_ADMIN_API_PORT}
# Port for the node exporter.
# Default: NODE_EXPORTER_PORT=19100
@@ -256,4 +233,4 @@ OPENIM_ADMIN_FRONT_PORT=${OPENIM_ADMIN_FRONT_PORT}
# Port for the alertmanager.
# Default: ALERT_MANAGER_PORT=19093
ALERT_MANAGER_PORT=${ALERT_MANAGER_PORT}
ALERT_MANAGER_PORT=${ALERT_MANAGER_PORT}
+10 -10
View File
@@ -44,12 +44,12 @@ scrape_configs:
# prometheus fetches application services
- job_name: 'openimserver-openim-api'
static_configs:
- targets: [ '${OPENIM_SERVER_ADDRESS}:${API_PROM_PORT}' ]
- targets: [ '${DOCKER_BRIDGE_GATEWAY}:${API_PROM_PORT}' ]
labels:
namespace: 'default'
- job_name: 'openimserver-openim-msggateway'
static_configs:
- targets: [ '${OPENIM_SERVER_ADDRESS}:${MSG_GATEWAY_PROM_PORT}' ]
- targets: [ '${DOCKER_BRIDGE_GATEWAY}:${MSG_GATEWAY_PROM_PORT}' ]
labels:
namespace: 'default'
- job_name: 'openimserver-openim-msgtransfer'
@@ -59,41 +59,41 @@ scrape_configs:
namespace: 'default'
- job_name: 'openimserver-openim-push'
static_configs:
- targets: [ '${OPENIM_SERVER_ADDRESS}:${PUSH_PROM_PORT}' ]
- targets: [ '${DOCKER_BRIDGE_GATEWAY}:${PUSH_PROM_PORT}' ]
labels:
namespace: 'default'
- job_name: 'openimserver-openim-rpc-auth'
static_configs:
- targets: [ '${OPENIM_SERVER_ADDRESS}:${AUTH_PROM_PORT}' ]
- targets: [ '${DOCKER_BRIDGE_GATEWAY}:${AUTH_PROM_PORT}' ]
labels:
namespace: 'default'
- job_name: 'openimserver-openim-rpc-conversation'
static_configs:
- targets: [ '${OPENIM_SERVER_ADDRESS}:${CONVERSATION_PROM_PORT}' ]
- targets: [ '${DOCKER_BRIDGE_GATEWAY}:${CONVERSATION_PROM_PORT}' ]
labels:
namespace: 'default'
- job_name: 'openimserver-openim-rpc-friend'
static_configs:
- targets: [ '${OPENIM_SERVER_ADDRESS}:${FRIEND_PROM_PORT}' ]
- targets: [ '${DOCKER_BRIDGE_GATEWAY}:${FRIEND_PROM_PORT}' ]
labels:
namespace: 'default'
- job_name: 'openimserver-openim-rpc-group'
static_configs:
- targets: [ '${OPENIM_SERVER_ADDRESS}:${GROUP_PROM_PORT}' ]
- targets: [ '${DOCKER_BRIDGE_GATEWAY}:${GROUP_PROM_PORT}' ]
labels:
namespace: 'default'
- job_name: 'openimserver-openim-rpc-msg'
static_configs:
- targets: [ '${OPENIM_SERVER_ADDRESS}:${MESSAGE_PROM_PORT}' ]
- targets: [ '${DOCKER_BRIDGE_GATEWAY}:${MESSAGE_PROM_PORT}' ]
labels:
namespace: 'default'
- job_name: 'openimserver-openim-rpc-third'
static_configs:
- targets: [ '${OPENIM_SERVER_ADDRESS}:${THIRD_PROM_PORT}' ]
- targets: [ '${DOCKER_BRIDGE_GATEWAY}:${THIRD_PROM_PORT}' ]
labels:
namespace: 'default'
- job_name: 'openimserver-openim-rpc-user'
static_configs:
- targets: [ '${OPENIM_SERVER_ADDRESS}:${USER_PROM_PORT}' ]
- targets: [ '${DOCKER_BRIDGE_GATEWAY}:${USER_PROM_PORT}' ]
labels:
namespace: 'default'
+99 -14
View File
@@ -16,18 +16,20 @@ services:
ports:
- "${MONGO_PORT:-37017}:27017"
container_name: mongo
command: --wiredTigerCacheSizeGB 1 --auth
command: ["/bin/bash", "-c", "/docker-entrypoint-initdb.d/mongo-init.sh || true; docker-entrypoint.sh mongod --wiredTigerCacheSizeGB 1 --auth"]
volumes:
- "${DATA_DIR:-./}/components/mongodb/data/db:/data/db"
- "${DATA_DIR:-./}/components/mongodb/data/logs:/data/logs"
- "${DATA_DIR:-./}/components/mongodb/data/conf:/etc/mongo"
- ./scripts/mongo-init.sh:/docker-entrypoint-initdb.d/mongo-init.sh:ro
- "./scripts/mongo-init.sh:/docker-entrypoint-initdb.d/mongo-init.sh:ro"
environment:
- TZ=Asia/Shanghai
- wiredTigerCacheSizeGB=1
- MONGO_INITDB_ROOT_USERNAME=${MONGO_USERNAME:-root}
- MONGO_INITDB_ROOT_PASSWORD=${MONGO_PASSWORD:-openIM123}
- MONGO_INITDB_DATABASE=${MONGO_DATABASE:-openIM_v3}
- MONGO_OPENIM_USERNAME=${MONGO_OPENIM_USERNAME:-openIM} # Non-root username
- MONGO_OPENIM_PASSWORD=${MONGO_OPENIM_PASSWORD:-openIM123456} # Non-root password
restart: always
networks:
server:
@@ -92,7 +94,7 @@ services:
ipv4_address: ${KAFKA_NETWORK_ADDRESS:-172.28.0.4}
minio:
image: minio/minio:${MINIO_IMAGE_VERSION:-latest}
image: minio/minio:${MINIO_IMAGE_VERSION:-RELEASE.2024-01-11T07-46-16Z}
ports:
- "${MINIO_PORT:-10005}:9000"
- "9090:9090"
@@ -110,21 +112,104 @@ services:
ipv4_address: ${MINIO_NETWORK_ADDRESS:-172.28.0.6}
openim-web:
image: ${IMAGE_REGISTRY:-ghcr.io/openimsdk}/openim-web:${OPENIM_WEB_IMAGE_VERSION:-latest}
image: ${IMAGE_REGISTRY:-ghcr.io/openimsdk}/openim-web:${OPENIM_WEB_IMAGE_VERSION:-v3.5.0-docker}
container_name: openim-web
environment:
- OPENIM_WEB_DIST_PATH=${OPENIM_WEB_DIST_PATH:-/app/dist}
- OPENIM_WEB_PORT=${OPENIM_WEB_PORT:-11001}
restart: always
ports:
- "${OPENIM_WEB_PORT:-11001}:11001"
- "${OPENIM_WEB_PORT:-11001}:80"
networks:
server:
ipv4_address: ${OPENIM_WEB_NETWORK_ADDRESS:-172.28.0.7}
### TODO: Uncomment, or deploy using openim docker: https://github.com/openimsdk/openim-docker
# Uncomment and configure the following services as needed
# openim-server:
# image: ${IMAGE_REGISTRY:-ghcr.io/openimsdk}/openim-server:${SERVER_IMAGE_VERSION:-main}
# container_name: openim-server
# ports:
# - "${OPENIM_WS_PORT:-10001}:${OPENIM_WS_PORT:-10001}"
# - "${API_OPENIM_PORT:-10002}:${API_OPENIM_PORT:-10002}"
# - "${API_PROM_PORT:-20100}:${API_PROM_PORT:-20100}"
# - "${USER_PROM_PORT:-20110}:${USER_PROM_PORT:-20110}"
# - "${FRIEND_PROM_PORT:-20120}:${FRIEND_PROM_PORT:-20120}"
# - "${MESSAGE_PROM_PORT:-20130}:${MESSAGE_PROM_PORT:-20130}"
# - "${MSG_GATEWAY_PROM_PORT:-20140}:${MSG_GATEWAY_PROM_PORT:-20140}"
# - "${GROUP_PROM_PORT:-20150}:${GROUP_PROM_PORT:-20150}"
# - "${AUTH_PROM_PORT:-20160}:${AUTH_PROM_PORT:-20160}"
# - "${PUSH_PROM_PORT:-20170}:${PUSH_PROM_PORT:-20170}"
# - "${CONVERSATION_PROM_PORT:-20230}:${CONVERSATION_PROM_PORT:-20230}"
# - "${RTC_PROM_PORT:-21300}:${RTC_PROM_PORT:-21300}"
# - "${THIRD_PROM_PORT:-21301}:${THIRD_PROM_PORT:-21301}"
# - "21400-21403:21400-21403"
# healthcheck:
# test: ["CMD", "/openim/openim-server/scripts/check-all.sh"]
# interval: 120s
# timeout: 30s
# retries: 5
# env_file:
# - .env
# environment:
# - OPENIM_IP=${OPENIM_IP:-127.0.0.1}
# volumes:
# - "${DATA_DIR:-./}/openim-server/logs:/openim/openim-server/logs"
# - "${DATA_DIR:-./}/openim-server/_output/logs:/openim/openim-server/_output/logs"
# - "${DATA_DIR:-./}/openim-server/config:/openim/openim-server/config"
# restart: always
# depends_on:
# - kafka
# - mysql
# - mongodb
# - redis
# - minio
# logging:
# driver: json-file
# options:
# max-size: "1g"
# max-file: "2"
# networks:
# server:
# ipv4_address: ${OPENIM_SERVER_NETWORK_ADDRESS:-172.28.0.8}
# openim-chat:
# image: ${IMAGE_REGISTRY:-ghcr.io/openimsdk}/openim-chat:${CHAT_IMAGE_VERSION:-main}
# container_name: openim-chat
# healthcheck:
# test: ["CMD", "/openim/openim-chat/scripts/check_all.sh"]
# interval: 60s
# timeout: 30s
# retries: 5
# env_file:
# - .env
# environment:
# - ZOOKEEPER_ADDRESS=${DOCKER_BRIDGE_GATEWAY:-172.28.0.1}
# - ZOOKEEPER_PORT=${ZOOKEEPER_PORT:-12181}
# - OPENIM_SERVER_ADDRESS=http://${OPENIM_SERVER_ADDRESS:-172.28.0.1}
# - API_OPENIM_PORT=${API_OPENIM_PORT:-10002}
# - MYSQL_ADDRESS=${DOCKER_BRIDGE_GATEWAY:-172.28.0.1}
# - MYSQL_PORT=${MYSQL_PORT:-13306}
# - REDIS_ADDRESS=${DOCKER_BRIDGE_GATEWAY:-172.28.0.1}
# - REDIS_PORT=${REDIS_PORT:-16379}
# ports:
# - "${OPENIM_CHAT_API_PORT:-10008}:10008"
# - "${OPENIM_ADMIN_API_PORT:-10009}:10009"
# volumes:
# - "${DATA_DIR:-./}/components/openim-chat/logs:/openim/openim-chat/logs"
# - "${DATA_DIR:-./}/components/openim-chat/config:/openim/openim-chat/config"
# restart: always
# # user: root:root
# logging:
# driver: json-file
# options:
# max-size: "1g"
# max-file: "2"
# networks:
# server:
# ipv4_address: ${OPENIM_CHAT_NETWORK_ADDRESS:-172.28.0.9}
# openim-admin:
# image: ${IMAGE_REGISTRY:-ghcr.io/openimsdk}/openim-admin-front:v3.4.0
# # https://github.com/openimsdk/open-im-server/issues/1662
# image: ${IMAGE_REGISTRY:-ghcr.io/openimsdk}/openim-admin:${ADMIN_FRONT_VERSION:-toc-base-open-docker.35}
# container_name: openim-admin
# restart: always
# ports:
@@ -139,8 +224,8 @@ services:
# hostname: prometheus
# restart: always
# volumes:
# - ./config/prometheus.yml:/etc/prometheus/prometheus.yml
# - ./config/instance-down-rules.yml:/etc/prometheus/instance-down-rules.yml
# - "${DATA_DIR:-./}/config/instance-down-rules.yml:/etc/prometheus/instance-down-rules.yml"
# - "${DATA_DIR:-./}/config/prometheus.yml:/etc/prometheus/prometheus.yml"
# ports:
# - "${PROMETHEUS_PORT:-19090}:9090"
# networks:
@@ -153,8 +238,8 @@ services:
# hostname: alertmanager
# restart: always
# volumes:
# - ./config/alertmanager.yml:/etc/alertmanager/alertmanager.yml
# - ./config/email.tmpl:/etc/alertmanager/email.tmpl
# - ${DATA_DIR:-./}/config/alertmanager.yml:/etc/alertmanager/alertmanager.yml
# - ${DATA_DIR:-./}/config/email.tmpl:/etc/alertmanager/email.tmpl
# ports:
# - "${ALERT_MANAGER_PORT:-19093}:9093"
# networks:
@@ -170,7 +255,7 @@ services:
# ports:
# - "${GRAFANA_PORT:-13000}:3000"
# volumes:
# - ${DATA_DIR:-./}/components/grafana:/var/lib/grafana
# - "${DATA_DIR:-./}/components/grafana:/var/lib/grafana"
# networks:
# server:
# ipv4_address: ${GRAFANA_NETWORK_ADDRESS:-172.28.0.11}
+1 -1
View File
@@ -31,7 +31,7 @@ docs/guide/en-US/cmd/openim/openim-rpc-user_list.md
docs/guide/en-US/cmd/openim/openim-rpc-user_update.md
docs/guide/en-US/cmd/openim/openim_validate.md
docs/guide/en-US/cmd/openim/openim_version.md
docs/guide/en-US/yaml/openim/openim.yaml
docs/guide/en-US/yaml/openim/config.yaml
docs/guide/en-US/yaml/openim/openim_color.yaml
docs/guide/en-US/yaml/openim/openim_completion.yaml
docs/guide/en-US/yaml/openim/openim_info.yaml
+56 -47
View File
@@ -89,7 +89,7 @@ While the first two methods will be our main focus, it's worth noting that the t
### 1.2. <a name='SourceCodeDeployment'></a>Source Code Deployment
In the source code deployment method, the configuration generation process involves executing `make init`, which fundamentally runs the script `./scripts/init-config.sh`. This script utilizes variables defined in the [`environment.sh`](https://github.com/openimsdk/open-im-server/blob/main/scripts/install/environment.sh) script to render the [`openim.yaml`](https://github.com/openimsdk/open-im-server/blob/main/deployments/templates/openim.yaml) template file, subsequently generating the [`config.yaml`](https://github.com/openimsdk/open-im-server/blob/main/config/config.yaml) configuration file.
In the source code deployment method, the configuration generation process involves executing `make init`, which fundamentally runs the script `./scripts/init-config.sh`. This script utilizes variables defined in the [`environment.sh`](https://github.com/openimsdk/open-im-server/blob/main/scripts/install/environment.sh) script to render the [`config.yaml`](https://github.com/openimsdk/open-im-server/blob/main/deployments/templates/config.yaml) template file, subsequently generating the [`config.yaml`](https://github.com/openimsdk/open-im-server/blob/main/config/config.yaml) configuration file.
### 1.3. <a name='DockerComposeDeployment'></a>Docker Compose Deployment
@@ -104,16 +104,19 @@ Docker deployment offers a slightly more intricate template. Within the [openim-
Configuration file modifications can be made by specifying corresponding environment variables, for instance:
```bash
export CHAT_IMAGE_VERSION="main"
export SERVER_IMAGE_VERSION="main"
export CHAT_IMAGE_VERSION="main"
export SERVER_IMAGE_VERSION="main"
```
These variables are stored within the [`environment.sh`](https://github.com/OpenIMSDK/openim-docker/blob/main/scripts/install/environment.sh) configuration:
These variables are stored within the [`environment.sh`](https://github.com/OpenIMSDK/open-im-server/blob/main/scripts/install/environment.sh) configuration:
```bash
readonly CHAT_IMAGE_VERSION=${CHAT_IMAGE_VERSION:-'main'}
readonly SERVER_IMAGE_VERSION=${SERVER_IMAGE_VERSION:-'main'}
```
> [!IMPORTANT]
> Can learn to read our mirror version strategy: https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/images.md
Setting a variable, e.g., `export CHAT_IMAGE_VERSION="release-v1.3"`, will prioritize `CHAT_IMAGE_VERSION="release-v1.3"` as the variable value. Ultimately, the chosen image version is determined, and rendering is achieved through `make init` (or `./scripts/init-config.sh`).
@@ -127,7 +130,7 @@ For convenience, configuration through modifying environment variables is recomm
+ PASSWORD
+ **Description**: Password for mysql, mongodb, redis, and minio.
+ **Description**: Password for mongodb, redis, and minio.
+ **Default**: `openIM123`
+ Notes:
+ Minimum password length: 8 characters.
@@ -139,20 +142,22 @@ For convenience, configuration through modifying environment variables is recomm
+ OPENIM_USER
+ **Description**: Username for mysql, mongodb, redis, and minio.
+ **Description**: Username for redis, and minio.
+ **Default**: `root`
```bash
export OPENIM_USER="root"
```
+ API_URL
> mongo is `openIM`, use `export MONGO_OPENIM_USERNAME="openIM"` to modify
+ OPENIM_IP
+ **Description**: API address.
+ **Note**: If the server has an external IP, it will be automatically obtained. For internal networks, set this variable to the IP serving internally.
```bash
export API_URL="http://ip:10002"
export OPENIM_IP="ip"
```
+ DATA_DIR
@@ -304,8 +309,10 @@ This section involves setting up MongoDB, including its port, address, and crede
| -------------- | -------------- | ----------------------- |
| MONGO_PORT | "27017" | Port used by MongoDB. |
| MONGO_ADDRESS | [Generated IP] | IP address for MongoDB. |
| MONGO_USERNAME | [User Defined] | Username for MongoDB. |
| MONGO_PASSWORD | [User Defined] | Password for MongoDB. |
| MONGO_USERNAME | [User Defined] | Admin Username for MongoDB. |
| MONGO_PASSWORD | [User Defined] | Admin Password for MongoDB. |
| MONGO_OPENIM_PASSWORD | [User Defined] | OpenIM Username for MongoDB. |
| MONGO_OPENIM_PASSWORD | [User Defined] | OpenIM Password for MongoDB. |
### 2.8. <a name='TencentCloudCOSConfiguration'></a>Tencent Cloud COS Configuration
@@ -441,7 +448,7 @@ This section involves configuring the log settings, including storage location,
| Parameter | Example Value | Description |
| ------------------------- | ------------------------ | --------------------------------- |
| LOG_STORAGE_LOCATION | ""${OPENIM_ROOT}"/logs/" | Location for storing logs |
| LOG_STORAGE_LOCATION | "${OPENIM_ROOT}/logs/" | Location for storing logs |
| LOG_ROTATION_TIME | "24" | Log rotation time (in hours) |
| LOG_REMAIN_ROTATION_COUNT | "2" | Number of log rotations to retain |
| LOG_REMAIN_LOG_LEVEL | "6" | Log level to retain |
@@ -453,43 +460,45 @@ This section involves configuring the log settings, including storage location,
This section involves setting up additional configuration variables for Websocket, Push Notifications, and Chat.
| Parameter | Example Value | Description |
|-------------------------|-------------------|------------------------------------|
| WEBSOCKET_MAX_CONN_NUM | "100000" | Maximum Websocket connections |
| WEBSOCKET_MAX_MSG_LEN | "4096" | Maximum Websocket message length |
| WEBSOCKET_TIMEOUT | "10" | Websocket timeout |
| PUSH_ENABLE | "getui" | Push notification enable status |
| GETUI_PUSH_URL | [Generated URL] | GeTui Push Notification URL |
| GETUI_MASTER_SECRET | [User Defined] | GeTui Master Secret |
| GETUI_APP_KEY | [User Defined] | GeTui Application Key |
| GETUI_INTENT | [User Defined] | GeTui Push Intent |
| GETUI_CHANNEL_ID | [User Defined] | GeTui Channel ID |
| GETUI_CHANNEL_NAME | [User Defined] | GeTui Channel Name |
| FCM_SERVICE_ACCOUNT | "x.json" | FCM Service Account |
| JPNS_APP_KEY | [User Defined] | JPNS Application Key |
| JPNS_MASTER_SECRET | [User Defined] | JPNS Master Secret |
| JPNS_PUSH_URL | [User Defined] | JPNS Push Notification URL |
| JPNS_PUSH_INTENT | [User Defined] | JPNS Push Intent |
| MANAGER_USERID_1 | "openIM123456" | Administrator ID 1 |
| MANAGER_USERID_2 | "openIM654321" | Administrator ID 2 |
| MANAGER_USERID_3 | "openIMAdmin" | Administrator ID 3 |
| NICKNAME_1 | "system1" | Nickname 1 |
| NICKNAME_2 | "system2" | Nickname 2 |
| NICKNAME_3 | "system3" | Nickname 3 |
| MULTILOGIN_POLICY | "1" | Multi-login Policy |
| CHAT_PERSISTENCE_MYSQL | "true" | Chat Persistence in MySQL |
| MSG_CACHE_TIMEOUT | "86400" | Message Cache Timeout |
| GROUP_MSG_READ_RECEIPT | "true" | Group Message Read Receipt Enable |
| Parameter | Example Value | Description |
|-------------------------|-------------------|----------------------------------|
| WEBSOCKET_MAX_CONN_NUM | "100000" | Maximum Websocket connections |
| WEBSOCKET_MAX_MSG_LEN | "4096" | Maximum Websocket message length |
| WEBSOCKET_TIMEOUT | "10" | Websocket timeout |
| PUSH_ENABLE | "getui" | Push notification enable status |
| GETUI_PUSH_URL | [Generated URL] | GeTui Push Notification URL |
| GETUI_MASTER_SECRET | [User Defined] | GeTui Master Secret |
| GETUI_APP_KEY | [User Defined] | GeTui Application Key |
| GETUI_INTENT | [User Defined] | GeTui Push Intent |
| GETUI_CHANNEL_ID | [User Defined] | GeTui Channel ID |
| GETUI_CHANNEL_NAME | [User Defined] | GeTui Channel Name |
| FCM_SERVICE_ACCOUNT | "x.json" | FCM Service Account |
| JPNS_APP_KEY | [User Defined] | JPNS Application Key |
| JPNS_MASTER_SECRET | [User Defined] | JPNS Master Secret |
| JPNS_PUSH_URL | [User Defined] | JPNS Push Notification URL |
| JPNS_PUSH_INTENT | [User Defined] | JPNS Push Intent |
| MANAGER_USERID_1 | "openIM123456" | Administrator ID 1 |
| MANAGER_USERID_2 | "openIM654321" | Administrator ID 2 |
| MANAGER_USERID_3 | "openIMAdmin" | Administrator ID 3 |
| NICKNAME_1 | "system1" | Nickname 1 |
| NICKNAME_2 | "system2" | Nickname 2 |
| NICKNAME_3 | "system3" | Nickname 3 |
| IM_ADMIN_USERID | "imAdmin" | IM Administrator ID |
| IM_ADMIN_NAME | "imAdmin" | IM Administrator Nickname |
| MULTILOGIN_POLICY | "1" | Multi-login Policy |
| CHAT_PERSISTENCE_MYSQL | "true" | Chat Persistence in MySQL |
| MSG_CACHE_TIMEOUT | "86400" | Message Cache Timeout |
| GROUP_MSG_READ_RECEIPT | "true" | Group Message Read Receipt Enable |
| SINGLE_MSG_READ_RECEIPT | "true" | Single Message Read Receipt Enable |
| RETAIN_CHAT_RECORDS | "365" | Retain Chat Records (in days) |
| CHAT_RECORDS_CLEAR_TIME | [Cron Expression] | Chat Records Clear Time |
| MSG_DESTRUCT_TIME | [Cron Expression] | Message Destruct Time |
| SECRET | "${PASSWORD}" | Secret Key |
| TOKEN_EXPIRE | "90" | Token Expiry Time |
| FRIEND_VERIFY | "false" | Friend Verification Enable |
| IOS_PUSH_SOUND | "xxx" | iOS |
| CALLBACK_ENABLE | "false" | Enable callback |
| CALLBACK_TIMEOUT | "5" | Maximum timeout for callback call |
| RETAIN_CHAT_RECORDS | "365" | Retain Chat Records (in days) |
| CHAT_RECORDS_CLEAR_TIME | [Cron Expression] | Chat Records Clear Time |
| MSG_DESTRUCT_TIME | [Cron Expression] | Message Destruct Time |
| SECRET | "${PASSWORD}" | Secret Key |
| TOKEN_EXPIRE | "90" | Token Expiry Time |
| FRIEND_VERIFY | "false" | Friend Verification Enable |
| IOS_PUSH_SOUND | "xxx" | iOS |
| CALLBACK_ENABLE | "false" | Enable callback |
| CALLBACK_TIMEOUT | "5" | Maximum timeout for callback call |
| CALLBACK_FAILED_CONTINUE| "true" | fails to continue to the next step |
### 2.20. <a name='PrometheusConfiguration-1'></a>Prometheus Configuration
+1 -1
View File
@@ -75,7 +75,7 @@ It is critical that our full community is actively engaged on enhancements in th
- Be aware the cherry pick script assumes you have a git remote called `upstream` that points at the openim-server github org.
Please see our [recommended Git workflow](https://github.com/openimsdk/open-im-server/blob/main/docs/contributors/github-workflow.md#workflow).
Please see our [recommended Git workflow](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/github-workflow.md#workflow).
- You will need to run the cherry pick script separately for each patch release you want to cherry pick to. Cherry picks should be applied to all [active](https://github.com/openimsdk/open-im-server/releases) release branches where the fix is applicable.
+2 -3
View File
@@ -8,7 +8,6 @@ description: |
commit hygiene.
---
![Git workflow](git_workflow.png)
## 1. Fork in the cloud
@@ -28,10 +27,10 @@ neither `$GOPATH/src/github.com/${your github profile name}/`
nor any other pattern will work.
```sh
export working_dir="$(go env GOPATH)/src/k8s.io"
export working_dir="$(go env GOPATH)/src/github.com/openimsdk"
```
If you already do Go development on github, the `k8s.io` directory
If you already do Go development on github, the `github.com/openimsdk` directory
will be a sibling to your existing `github.com` directory.
Set `user` to match your github profile name:
+1
View File
@@ -26,6 +26,7 @@ We provide multiple versions of our images to meet different project requirement
1. `main`: This image corresponds to the latest version of the main branch in OpenIM. It is updated frequently, making it perfect for users who want to stay at the cutting edge of our features.
2. `release-v3.*`: This is the image that corresponds to the latest version of OpenIM's stable release branch. It's ideal for users who prefer a balance between new features and stability.
3. `v3.*.*`: These images are specific to each tag in OpenIM. They are preserved in their original state and are never overwritten. These are the go-to images for users who need a specific, unchanging version of OpenIM.
4. The image versions adhere to Semantic Versioning 2.0.0 strategy. Taking the `openim-server` image as an example, available at [openim-server container package](https://github.com/openimsdk/open-im-server/pkgs/container/openim-server): upon tagging with v3.5.0, the CI automatically releases the following tags - `openim-server:3`, `openim-server:3.5`, `openim-server:3.5.0`, `openim-server:v3.5.0`, `openim-server:latest`, and `sha-e0244d9`. It's important to note that only `sha-e0244d9` is absolutely unique, whereas `openim-server:v3.5.0` and `openim-server:3.5.0` maintain a degree of uniqueness.
### Multi-Architecture Images
+2 -2
View File
@@ -23,8 +23,8 @@ In the `scripts/init_config.sh` file, we defined some template files. These temp
```
# Defines an associative array where the keys are the template files and the values are the corresponding output files.
declare -A TEMPLATES=(
[""${OPENIM_ROOT}"/scripts/template/config-tmpl/env.template"]="${OPENIM_OUTPUT_SUBPATH}/bin/.env"
[""${OPENIM_ROOT}"/scripts/template/config-tmpl/config.yaml"]="${OPENIM_OUTPUT_SUBPATH}/bin/config.yaml"
["${OPENIM_ROOT}/scripts/template/config-tmpl/env.template"]="${OPENIM_OUTPUT_SUBPATH}/bin/.env"
["${OPENIM_ROOT}/scripts/template/config-tmpl/config.yaml"]="${OPENIM_OUTPUT_SUBPATH}/bin/config.yaml"
)
```
@@ -0,0 +1,258 @@
# Mac Developer Deployment Guide for OpenIM
## Introduction
This guide aims to assist Mac-based developers in contributing effectively to OpenIM. It covers the setup of a development environment tailored for Mac, including the use of GitHub for development workflow and `devcontainer` for a consistent development experience.
Before contributing to OpenIM through issues and pull requests, make sure you are familiar with GitHub and the [pull request workflow](https://docs.github.com/en/get-started/quickstart/github-flow).
## Prerequisites
### System Requirements
- macOS (latest stable version recommended)
- Internet connection
- Administrator access
### Knowledge Requirements
- Basic understanding of Git and GitHub
- Familiarity with Docker and containerization
- Experience with Go programming language
## Setting up the Development Environment
### Installing Homebrew
Homebrew is an essential package manager for macOS. Install it using:
```sh
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
```
### Installing and Configuring Git
1. Install Git:
```sh
brew install git
```
2. Configure Git with your user details:
```sh
git config --global user.name "Your Name"
git config --global user.email "your.email@example.com"
```
### Setting Up the Devcontainer
`Devcontainers` provide a Docker-based isolated development environment.
Read [README.md](https://github.com/openimsdk/open-im-server/tree/main/.devcontainer) in the `.devcontainer` directory of the project to learn more about the devcontainer.
To set it up:
1. Install Docker Desktop for Mac from [Docker Hub](https://docs.docker.com/desktop/install/mac-install/).
2. Install Visual Studio Code and the Remote - Containers extension.
3. Open the cloned OpenIM repository in VS Code.
4. VS Code will prompt to reopen the project in a container. Accept this to set up the environment automatically.
### Installing Go and Dependencies
Use Homebrew to install Go:
```sh
brew install go
```
Ensure the version of Go is compatible with the version required by OpenIM (refer to the main documentation for version requirements).
### Additional Tools
Install other required tools like Docker, Vagrant, and necessary GNU utils as described in the main documentation.
## Mac Deployment openim-chat and openim-server
To integrate the Chinese document into an English document for Linux deployment, we will first translate the content and then adapt it to suit the Linux environment. Here's how the translated and adapted content might look:
### Ensure a Clean Environment
- It's recommended to execute in a new directory.
- Run `ps -ef | grep openim` to ensure no OpenIM processes are running.
- Run `ps -ef | grep chat` to check for absence of chat-related processes.
- Execute `docker ps` to verify there are no related containers running.
### Source Code Deployment
#### Deploying openim-server
Source code deployment is slightly more complex because Docker's networking on Linux differs from Mac.
```bash
git clone https://github.com/openimsdk/open-im-server
cd open-im-server
export OPENIM_IP="Your IP" # If it's a cloud server, setting might not be needed
make init # Generates configuration files
```
Before deploying openim-server, modify the Kafka logic in the docker-compose.yml file. Replace:
```yaml
- KAFKA_CFG_ADVERTISED_LISTENERS=PLAINTEXT://kafka:9092,EXTERNAL://${DOCKER_BRIDGE_GATEWAY:-172.28.0.1}:${KAFKA_PORT:-19094}
```
With:
```yaml
- KAFKA_CFG_ADVERTISED_LISTENERS=PLAINTEXT://kafka:9092,EXTERNAL://127.0.0.1:${KAFKA_PORT:-19094}
```
Then start the service:
```bash
docker compose up -d
```
Before starting the openim-server source, set `config/config.yaml` by replacing all instances of `172.28.0.1` with `127.0.0.1`:
```bash
vim config/config.yaml -c "%s/172\.28\.0\.1/127.0.0.1/g" -c "wq"
```
Then start openim-server:
```bash
make start
```
To check the startup:
```bash
make check
```
<aside>
🚧 To avoid mishaps, it's best to wait five minutes before running `make check` again.
</aside>
#### Deploying openim-chat
There are several ways to deploy openim-chat, either by source code or using Docker.
Navigate back to the parent directory:
```bash
cd ..
```
First, let's look at deploying chat from source:
```bash
git clone https://github.com/openimsdk/chat
cd chat
make init # Generates configuration files
```
If openim-chat has not deployed MySQL, you will need to deploy it. Note that the official Docker Hub for MySQL does not support architectures like ARM, so you can use the newer version of the open-source edition:
```bash
docker run -d \
--name mysql \
-p 13306:3306 \
-p 3306:33060 \
-v "$(pwd)/components/mysql/data:/var/lib/mysql" \
-v "/etc/localtime:/etc/localtime" \
-e MYSQL_ROOT_PASSWORD="openIM123" \
--restart always \
mariadb:10.6
```
Before starting the source code of openim-chat, set `config/config.yaml` by replacing all instances of `172.28.0.1` with `127.0.0.1`:
```bash
vim config/config.yaml -c "%s/172\.28\.0\.1/127.0.0.1/g" -c "wq"
```
Then start openim-chat from source:
```bash
make start
```
To check, ensure the following four processes start successfully:
```bash
make check
```
### Docker Deployment
Refer to https://github.com/openimsdk/openim-docker for Docker deployment instructions, which can be followed similarly on Linux.
```bash
git clone https://github.com/openimsdk/openim-docker
cd openim-docker
export OPENIM_IP="Your IP"
make init
docker compose up -d
docker compose logs -f openim-server
docker compose logs -f openim-chat
```
## GitHub Development Workflow
### Creating a New Branch
For new features or fixes, create a new branch:
```sh
git checkout -b feat/your-feature-name
```
### Making Changes and Committing
1. Make your changes in the code.
2. Stage your changes:
```sh
git add .
```
3. Commit with a meaningful message:
```sh
git commit -m "Add a brief description of your changes"
```
### Pushing Changes and Creating Pull Requests
1. Push your branch to GitHub:
```sh
git push origin feat/your-feature-name
```
2. Go to your fork on GitHub and create a pull request to the main OpenIM repository.
### Keeping Your Fork Updated
Regularly sync your fork with the main repository:
```sh
git fetch upstream
git checkout main
git rebase upstream/main
```
More read: [https://github.com/openimsdk/open-im-server/blob/main/CONTRIBUTING.md](https://github.com/openimsdk/open-im-server/blob/main/CONTRIBUTING.md)
## Testing and Quality Assurance
Run tests as described in the OpenIM documentation to ensure your changes do not break existing functionality.
## Conclusion
This guide provides a comprehensive overview for Mac developers to set up and contribute to OpenIM. By following these steps, you can ensure a smooth and efficient development experience. Happy coding!
+92 -28
View File
@@ -6,25 +6,57 @@
Below are the base images and their versions you'll need:
- wurstmeister/kafka
- redis:7.0.0
- mongo:6.0.2
- mysql:5.7
- wurstmeister/zookeeper
- minio/minio
- [ ] bitnami/kafka:3.5.1
- [ ] redis:7.0.0
- [ ] mongo:6.0.2
- [ ] bitnami/zookeeper:3.8
- [ ] minio/minio:latest
> [!IMPORTANT]
> It is important to note that OpenIM removed mysql components from versions v3.5.0 (release-v3.5) and above, so mysql can be deployed without this requirement or above
**If you need to install more IM components or monitoring products**
OpenIM:
> [!TIP]
> If you need to install more IM components or monitoring products [images.md](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/images.md)
- [ ] ghcr.io/openimsdk/openim-web:latest
- [ ] ghcr.io/openimsdk/openim-admin:latest
- [ ] ghcr.io/openimsdk/openim-chat:latest
- [ ] ghcr.io/openimsdk/openim-server:latest
Monitoring:
- [ ] prom/prometheusv2.48.1
- [ ] prom/alertmanagerv0.23.0
- [ ] grafana/grafana10.2.2
- [ ] bitnami/node-exporter1.7.0
Use the following commands to pull these base images:
```
docker pull wurstmeister/kafka
```bash
docker pull bitnami/kafka:3.5.1
docker pull redis:7.0.0
docker pull mongo:6.0.2
docker pull mysql:5.7
docker pull wurstmeister/zookeeper
docker pull minio/minio
docker pull bitnami/zookeeper:3.8
docker pull minio/minio:latest
```
## 2. OpenIM & Chat Images
If you need to install more IM components or monitoring products:
```bash
docker pull prom/prometheus:v2.48.1
docker pull prom/alertmanager:v0.23.0
docker pull grafana/grafana:10.2.2
docker pull bitnami/node-exporter:1.7.0
```
## 2. OpenIM Images
**For detailed understanding of version management and storage of OpenIM and Chat**: [version.md](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/version.md)
@@ -42,9 +74,26 @@ docker pull ghcr.io/openimsdk/openim-server:<version-name>
- Execute the following command to pull the image:
```bash
docker pull ghcr.io/openimsdk/openim-server:<version-name>
docker pull ghcr.io/openimsdk/openim-chat:<version-name>
```
### Web Image
- Execute the following command to pull the image:
```bash
docker pull ghcr.io/openimsdk/openim-web:<version-name>
```
### Admin Image
- Execute the following command to pull the image:
```bash
docker pull ghcr.io/openimsdk/openim-admin:<version-name>
```
## 3. Image Storage Selection
**Repositories**:
@@ -71,46 +120,61 @@ You can select from the following versions:
1. **Pull images**: Execute the above `docker pull` commands to pull all required images locally.
2. **Save images**:
```
```bash
docker save -o <tar-file-name>.tar <image-name>
```
1. **Fetch code**: Clone the repository:
If you want to save all the images, use the following command:
```
git clone https://github.com/OpenIMSDK/openim-docker.git
```bash
docker save -o <tar-file-name>.tar $(docker images -q)
```
Or download the code from [Releases](https://github.com/OpenIMSDK/openim-docker/releases/).
1. **Transfer files**: Use `scp` to transfer all images and code to the intranet server.
3. **Fetch code**: Clone the repository:
```bash
git clone https://github.com/openimsdk/openim-docker.git
```
Or download the code from [Releases](https://github.com/openimsdk/openim-docker/releases/).
> Because of the difference between win and linux newlines, please do not clone the repository with win and then synchronize scp to linux.
4. **Transfer files**: Use `scp` to transfer all images and code to the intranet server.
```bash
scp <tar-file-name>.tar user@remote-ip:/path/on/remote/server
```
Or choose other transfer methods such as a hard drive.
1. **Import images**: On the intranet server:
5. **Import images**: On the intranet server:
```
```bash
docker load -i <tar-file-name>.tar
```
1. **Deploy**: Navigate to the `openim-docker` repository directory and follow the README guide for deployment.
2. **Deploy using Docker-compose**:
Import directly with shortcut commands:
```bash
for i in `ls ./`;do docker load -i $i;done
```
docker-compose up -d
# Verify
docker-compose ps
6. **Deploy**: Navigate to the `openim-docker` repository directory and follow the [README guide](https://github.com/openimsdk/openim-docker) for deployment.
7. **Deploy using docker compose**:
```bash
export OPENIM_IP="your ip" # Set Ip
make init # Init config
docker compose up -d # Deployment
docker compose ps # Verify
```
> **Note**: If you're using a version of Docker prior to 20, make sure you've installed `docker-compose`.
## 6. Reference Links
- [OpenIMSDK Issue #432](https://github.com/openimsdk/open-im-server/issues/432)
- [openimsdk Issue #432](https://github.com/openimsdk/open-im-server/issues/432)
- [Notion Link](https://nsddd.notion.site/435ee747c0bc44048da9300a2d745ad3?pvs=25)
- [OpenIMSDK Issue #474](https://github.com/openimsdk/open-im-server/issues/474)
- [openimsdk Issue #474](https://github.com/openimsdk/open-im-server/issues/474)
+27 -24
View File
@@ -111,32 +111,35 @@ Importing Grafana Dashboards is a straightforward process and is applicable to O
To monitor OpenIM in Grafana, you need to focus on three categories of key metrics, each with its specific deployment and configuration steps:
1. **OpenIM Metrics (`prometheus-dashboard.yaml`)**:
+ **Configuration File Path**: Located at `config/prometheus-dashboard.yaml`.
+ **Enabling Monitoring**: Set the environment variable `export PROMETHEUS_ENABLE=true` to enable Prometheus monitoring.
+ **More Information**: Refer to the [OpenIM Configuration Guide](https://docs.openim.io/configurations/prometheus-integration).
2. **Node Exporter**:
+ **Container Deployment**: Deploy the `quay.io/prometheus/node-exporter` container for node monitoring.
+ **Get Dashboard**: Access the [Node Exporter Full Feature Dashboard](https://grafana.com/grafana/dashboards/1860-node-exporter-full/) and import it using YAML file download or ID import.
+ **Deployment Guide**: Refer to the [Node Exporter Deployment Documentation](https://prometheus.io/docs/guides/node-exporter/).
3. **Middleware Metrics**: Each middleware requires specific steps and configurations to enable monitoring. Here is a list of common middleware and links to their respective setup guides:
+ MySQL:
+ **Configuration**: Ensure MySQL has performance monitoring enabled.
+ **Link**: Refer to the [MySQL Monitoring Configuration Guide](https://grafana.com/docs/grafana/latest/datasources/mysql/).
+ Redis:
+ **Configuration**: Configure Redis to allow monitoring data export.
+ **Link**: Refer to the [Redis Monitoring Guide](https://grafana.com/docs/grafana/latest/datasources/redis/).
+ MongoDB:
+ **Configuration**: Set up monitoring metrics for MongoDB.
+ **Link**: Refer to the [MongoDB Monitoring Guide](https://grafana.com/grafana/plugins/grafana-mongodb-datasource/).
+ Kafka:
+ **Configuration**: Integrate Kafka with Prometheus monitoring.
+ **Link**: Refer to the [Kafka Monitoring Guide](https://grafana.com/grafana/plugins/grafana-kafka-datasource/).
+ Zookeeper:
+ **Configuration**: Ensure Zookeeper can be monitored by Prometheus.
+ **Link**: Refer to the [Zookeeper Monitoring Configuration](https://grafana.com/docs/grafana/latest/datasources/zookeeper/).
**OpenIM Metrics (`prometheus-dashboard.yaml`)**:
- **Configuration File Path**: Find this at `config/prometheus-dashboard.yaml`.
- **Enabling Monitoring**: Activate Prometheus monitoring by setting the environment variable: `export PROMETHEUS_ENABLE=true`.
- **More Information**: For detailed instructions, see the [OpenIM Configuration Guide](https://docs.openim.io/configurations/prometheus-integration).
**Node Exporter**:
- **Container Deployment**: Use the container `quay.io/prometheus/node-exporter` for effective node monitoring.
- **Access Dashboard**: Visit the [Node Exporter Full Feature Dashboard](https://grafana.com/grafana/dashboards/1860-node-exporter-full/) for dashboard integration either through YAML file download or ID.
- **Deployment Guide**: For deployment steps, consult the [Node Exporter Deployment Documentation](https://prometheus.io/docs/guides/node-exporter/).
**Middleware Metrics**: Different middlewares require unique steps and configurations for monitoring:
- MySQL:
- **Configuration**: Make sure MySQL is set up for performance monitoring.
- **Guide**: See the [MySQL Monitoring Configuration Guide](https://grafana.com/docs/grafana/latest/datasources/mysql/).
- Redis:
- **Configuration**: Adjust Redis settings to enable monitoring data export.
- **Guide**: Consult the [Redis Monitoring Guide](https://grafana.com/docs/grafana/latest/datasources/redis/).
- MongoDB:
- **Configuration**: Configure MongoDB for monitoring metrics.
- **Guide**: Visit the [MongoDB Monitoring Guide](https://grafana.com/grafana/plugins/grafana-mongodb-datasource/).
- Kafka:
- **Configuration**: Set up Kafka for Prometheus monitoring integration.
- **Guide**: Refer to the [Kafka Monitoring Guide](https://grafana.com/grafana/plugins/grafana-kafka-datasource/).
- Zookeeper:
- **Configuration**: Ensure Prometheus can monitor Zookeeper.
- **Guide**: Check out the [Zookeeper Monitoring Configuration](https://grafana.com/docs/grafana/latest/datasources/zookeeper/).
**Importing Steps**:
+251
View File
@@ -0,0 +1,251 @@
# OpenIM Release Automation Design Document
This document outlines the automation process for releasing OpenIM. You can use the `make release` command for automated publishing. We will discuss how to use the `make release` command and Github Actions CICD separately, while also providing insight into the design principles involved.
## Github Actions Automation
In our CICD pipeline, we have implemented logic for automating the release process using the goreleaser tool. To achieve this, follow these steps on your local machine or server:
```bash
git clone https://github.com/openimsdk/open-im-server
cd open-im-server
git tag -a v3.6.0 -s -m "release: xxx"
# For pre-release versions: git tag -a v3.6.0-rc.0 -s -m "pre-release: xxx"
git push origin v3.6.0
```
The remaining tasks are handled by automated processes:
+ Automatically complete the release publication on Github
+ Automatically build the `v3.6.0` version image and push it to aliyun, dockerhub, and github
Through these automated steps, we achieve rapid and efficient OpenIM version releases, simplifying the release process and enhancing productivity.
Certainly, here is the continuation of the document in English:
## Local Make Release Design
There are two primary scenarios for local usage:
+ Advanced compilation and release, manually executed locally
+ Quick compilation verification and version release, manually executed locally
**These two scenarios can also be combined, for example, by tagging locally and then releasing:**
```bash
git add .
git commit -a -s -m "release(v3.6.0): ......"
git tag v3.6.0
git release
git push origin main
```
In a local environment, you can use the `make release` command to complete the release process. The main implementation logic can be found in the `/data/workspaces/open-im-server/scripts/lib/release.sh` file. First, let's explore its usage through the help information.
### Help Information
To view the help information, execute the following command:
```bash
$ ./scripts/release.sh --help
Usage: release.sh [options]
Options:
-h, --help Display this help message
-se, --setup-env Execute environment setup
-vp, --verify-prereqs Execute prerequisite verification
-bc, --build-command Execute build command
-bi, --build-image Execute build image (default is not executed)
-pt, --package-tarballs Execute tarball packaging
-ut, --upload-tarballs Execute tarball upload
-gr, --github-release Execute GitHub release
-gc, --generate-changelog Execute changelog generation
```
### Default Behavior
If no options are provided, all operations are executed by default:
```bash
# If no options are provided, enable all operations by default
if [ "$#" -eq 0 ]; then
perform_setup_env=true
perform_verify_prereqs=true
perform_build_command=true
perform_package_tarballs=true
perform_upload_tarballs=true
perform_github_release=true
perform_generate_changelog=true
# TODO: Defaultly not enable build_image
# perform_build_image=true
fi
```
### Environment Variable Setup
Before starting, you need to set environment variables:
```bash
export TENCENT_SECRET_KEY=OZZ****************************
export TENCENT_SECRET_ID=AKI****************************
```
### Modifying COS Account and Password
If you need to change the COS account, password, and bucket information, please modify the following section in the `/data/workspaces/open-im-server/scripts/lib/release.sh` file:
```bash
readonly BUCKET="openim-1306374445"
readonly REGION="ap-guangzhou"
readonly COS_RELEASE_DIR="openim-release"
```
### GitHub Release Configuration
If you intend to use the GitHub Release feature, you also need to set the environment variable:
```bash
export GITHUB_TOKEN="your_github_token"
```
### Modifying GitHub Release Basic Information
If you need to modify the basic information of GitHub Release, please edit the following section in the `/data/workspaces/open-im-server/scripts/lib/release.sh` file:
```bash
# OpenIM GitHub account information
readonly OPENIM_GITHUB_ORG=openimsdk
readonly OPENIM_GITHUB_REPO=open-im-server
```
This setup allows you to configure and execute the local release process according to your specific needs.
### GitHub Release Versioning Rules
Firstly, it's important to note that GitHub Releases should primarily be for pre-release versions. However, goreleaser might provide a `prerelease: auto` option, which automatically marks versions with pre-release indicators like `-rc1`, `-beta`, etc., as pre-releases.
So, if your most recent tag does not have pre-release indicators such as `-rc1` or `-beta`, even if you use `make release` for pre-release versions, goreleaser might still consider them as formal releases.
To avoid this issue, I have added the `--draft` flag to github-release. This way, all releases are created as drafts.
## CICD Release Documentation Design
The release records still require manual composition for GitHub Release. This is different from github-release.
This approach ensures that all releases are initially created as drafts, allowing you to manually review and edit the release documentation on GitHub. This manual step provides more control and allows you to curate release notes and other information before making them public.
## Makefile Section
This document aims to elaborate and explain key sections of the OpenIM Release automation design, including the Makefile section and functions within the code. Below, we will provide a detailed explanation of the logic and functions of each section.
In the project's root directory, the Makefile imports a subdirectory:
```makefile
include scripts/make-rules/release.mk
```
And defines the `release` target as follows:
```makefile
## release: release the project ✨
.PHONY: release release: release.verify release.ensure-tag
@scripts/release.sh
```
### Importing Subdirectory
At the beginning of the Makefile, the `include scripts/make-rules/release.mk` statement imports the `release.mk` file from the subdirectory. This file contains rules and configurations related to releases to be used in subsequent operations.
### The `release` Target
The Makefile defines a target named `release`, which is used to execute the project's release operation. This target is marked as a phony target (`.PHONY`), meaning it doesn't represent an actual file or directory but serves as an identifier for executing a series of actions.
In the `release` target, two dependency targets are executed first: `release.verify` and `release.ensure-tag`. Afterward, the `scripts/release.sh` script is called to perform the actual release operation.
## Logic of `release.verify` and `release.ensure-tag`
```makefile
## release.verify: Check if a tool is installed and install it
.PHONY: release.verify
release.verify: tools.verify.git-chglog tools.verify.github-release tools.verify.coscmd tools.verify.coscli
## release.ensure-tag: ensure tag
.PHONY: release.ensure-tag
release.ensure-tag: tools.verify.gsemver
@scripts/ensure-tag.sh
```
### `release.verify` Target
The `release.verify` target is used to check and install tools. It depends on four sub-targets: `tools.verify.git-chglog`, `tools.verify.github-release`, `tools.verify.coscmd`, and `tools.verify.coscli`. These sub-targets aim to check if specific tools are installed and attempt to install them if they are not.
The purpose of this target is to ensure that the necessary tools required for the release process are available so that subsequent operations can be executed smoothly.
### `release.ensure-tag` Target
The `release.ensure-tag` target is used to ensure that the project has a version tag. It depends on the sub-target `tools.verify.gsemver`, indicating that it should check if the `gsemver` tool is installed before executing.
When the `release.ensure-tag` target is executed, it calls the `scripts/ensure-tag.sh` script to ensure that the project has a version tag. Version tags are typically used to identify specific versions of the project for management and release in version control systems.
## Logic of `release.sh` Script
```bash
openim::golang::setup_env
openim::build::verify_prereqs
openim::release::verify_prereqs
#openim::build::build_image
openim::build::build_command
openim::release::package_tarballs
openim::release::upload_tarballs
git push origin ${VERSION}
#openim::release::github_release
#openim::release::generate_changelog
```
The `release.sh` script is responsible for executing the actual release operations. Below is the logic of this script:
1. `openim::golang::setup_env`: This function sets up some configurations for the Golang development environment.
2. `openim::build::verify_prereqs`: This function is used to verify whether the prerequisites for building are met. This includes checking dependencies, environment variables, and more.
3. `openim::release::verify_prereqs`: Similar to the previous function, this one is used to verify whether the prerequisites for the release are met. It focuses on conditions relevant to the release.
4. `openim::build::build_command`: This function is responsible for building the project's command, which typically involves compiling the project or performing other build operations.
5. `openim::release::package_tarballs`: This function is used to package tarball files required for the release. These tarballs are usually used for distribution packages during the release.
6. `openim::release::upload_tarballs`: This function is used to upload the packaged tarball files, typically to a distribution platform or repository.
7. `git push origin ${VERSION}`: This line of command pushes the version tag to the remote Git repository's `origin` branch, marking this release in the version control system.
In the comments, you can see that there are some operations that are commented out, such as `openim::build::build_image`, `openim::release::github_release`, and `openim::release::generate_changelog`. These operations are related to building images, releasing to GitHub, and generating changelogs, and they can be enabled in the release process as needed.
Let's take a closer look at the function responsible for packaging the tarball files:
```bash
function openim::release::package_tarballs() {
# Clean out any old releases
rm -rf "${RELEASE_STAGE}" "${RELEASE_TARS}" "${RELEASE_IMAGES}"
mkdir -p "${RELEASE_TARS}"
openim::release::package_src_tarball &
openim::release::package_client_tarballs &
openim::release::package_openim_manifests_tarball &
openim::release::package_server_tarballs &
openim::util::wait-for-jobs || { openim::log::error "previous tarball phase failed"; return 1; }
openim::release::package_final_tarball & # _final depends on some of the previous phases
openim::util::wait-for-jobs || { openim::log::error "previous tarball phase failed"; return 1; }
}
```
The `openim::release::package_tarballs()` function is responsible for packaging the tarball files required for the release. Here is the specific logic of this function:
1. `rm -rf "${RELEASE_STAGE}" "${RELEASE_TARS}" "${RELEASE_IMAGES}"`: First, the function removes any old release directories and files to ensure a clean starting state.
2. `mkdir -p "${RELEASE_TARS}"`: Next, it creates a directory `${RELEASE_TARS}` to store the packaged tarball files. If the directory doesn't exist, it will be created.
3. `openim::release::package_final_tarball &`: This is an asynchronous operation that depends on some of the previous phases. It is likely used to package the final tarball file, which includes the contents of all previous asynchronous operations.
4. `openim::util::wait-for-jobs`: It waits for all asynchronous operations to complete. If any of the previous asynchronous operations fail, an error will be returned.
+21
View File
@@ -1,6 +1,7 @@
# OpenIM Branch Management and Versioning: A Blueprint for High-Grade Software Development
[📚 **OpenIM TOC**](#openim-branch-management-and-versioning-a-blueprint-for-high-grade-software-development)
- [OpenIM Branch Management and Versioning: A Blueprint for High-Grade Software Development](#openim-branch-management-and-versioning-a-blueprint-for-high-grade-software-development)
- [Unfolding the Mechanism of OpenIM Version Maintenance](#unfolding-the-mechanism-of-openim-version-maintenance)
- [Main Branch: The Heart of OpenIM Development](#main-branch-the-heart-of-openim-development)
- [Release Branch: The Beacon of Stability](#release-branch-the-beacon-of-stability)
@@ -8,8 +9,21 @@
- [Release Management: A Guided Tour](#release-management-a-guided-tour)
- [Milestones, Branching, and Addressing Major Bugs](#milestones-branching-and-addressing-major-bugs)
- [Version Skew Policy](#version-skew-policy)
- [Supported version skew](#supported-version-skew)
- [OpenIM Versioning, Branching, and Tag Strategy](#openim-versioning-branching-and-tag-strategy)
- [Supported Version Skew](#supported-version-skew-1)
- [openim-api](#openim-api)
- [openim-rpc-\* Components](#openim-rpc--components)
- [Other OpenIM Services](#other-openim-services)
- [Supported Component Upgrade Order](#supported-component-upgrade-order)
- [openim-api](#openim-api-1)
- [openim-rpc-\* Components](#openim-rpc--components-1)
- [Other OpenIM Services](#other-openim-services-1)
- [Conclusion](#conclusion)
- [Applying Principles: A Git Workflow Example](#applying-principles-a-git-workflow-example)
- [Release Process](#release-process)
- [Docker Images Version Management](#docker-images-version-management)
- [More](#more)
At OpenIM, we acknowledge the profound impact of implementing a robust and efficient version management system, hence we abide by the established standards of [Semantic Versioning 2.0.0](https://semver.org/lang/zh-CN/).
@@ -213,3 +227,10 @@ Throughout this process, active communication within the team is pivotal to main
## Docker Images Version Management
For more details on managing Docker image versions, visit [OpenIM Docker Images Administration](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/images.md).
## More
More on multi-branch version management design and version management design at helm charts
+ https://github.com/openimsdk/open-im-server/issues/1695
+ https://github.com/openimsdk/open-im-server/issues/1662
+9 -7
View File
@@ -4,8 +4,8 @@ go 1.19
require (
firebase.google.com/go v3.13.0+incompatible
github.com/OpenIMSDK/protocol v0.0.31
github.com/OpenIMSDK/tools v0.0.20
github.com/OpenIMSDK/protocol v0.0.47
github.com/OpenIMSDK/tools v0.0.29
github.com/bwmarrin/snowflake v0.3.0 // indirect
github.com/dtm-labs/rockscache v0.1.1
github.com/gin-gonic/gin v1.9.1
@@ -38,6 +38,7 @@ require (
github.com/aliyun/aliyun-oss-go-sdk v2.2.9+incompatible
github.com/go-redis/redis v6.15.9+incompatible
github.com/redis/go-redis/v9 v9.2.1
github.com/stathat/consistent v1.0.0
github.com/tencentyun/cos-go-sdk-v5 v0.7.45
go.uber.org/automaxprocs v1.5.3
golang.org/x/sync v0.4.0
@@ -126,13 +127,13 @@ require (
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d // indirect
go.opencensus.io v0.24.0 // indirect
go.uber.org/atomic v1.7.0 // indirect
go.uber.org/multierr v1.6.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
golang.org/x/arch v0.3.0 // indirect
golang.org/x/net v0.17.0 // indirect
golang.org/x/oauth2 v0.13.0 // indirect
golang.org/x/sys v0.14.0 // indirect
golang.org/x/text v0.13.0 // indirect
golang.org/x/time v0.3.0 // indirect
golang.org/x/sys v0.15.0 // indirect
golang.org/x/text v0.14.0 // indirect
golang.org/x/time v0.5.0 // indirect
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/genproto v0.0.0-20231002182017-d307bd883b97 // indirect
@@ -141,6 +142,7 @@ require (
gopkg.in/src-d/go-billy.v4 v4.3.2 // indirect
gopkg.in/warnings.v0 v0.1.2 // indirect
gorm.io/gorm v1.23.8 // indirect
stathat.com/c/consistent v1.0.0 // indirect
)
require (
@@ -151,6 +153,6 @@ require (
github.com/spf13/cobra v1.7.0
github.com/ugorji/go/codec v1.2.11 // indirect
go.uber.org/zap v1.24.0 // indirect
golang.org/x/crypto v0.14.0 // indirect
golang.org/x/crypto v0.17.0 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
)
+19 -15
View File
@@ -18,10 +18,10 @@ firebase.google.com/go v3.13.0+incompatible/go.mod h1:xlah6XbEyW6tbfSklcfe5FHJIw
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/IBM/sarama v1.41.3 h1:MWBEJ12vHC8coMjdEXFq/6ftO6DUZnQlFYcxtOJFa7c=
github.com/IBM/sarama v1.41.3/go.mod h1:Xxho9HkHd4K/MDUo/T/sOqwtX/17D33++E9Wib6hUdQ=
github.com/OpenIMSDK/protocol v0.0.31 h1:ax43x9aqA6EKNXNukS5MT5BSTqkUmwO4uTvbJLtzCgE=
github.com/OpenIMSDK/protocol v0.0.31/go.mod h1:F25dFrwrIx3lkNoiuf6FkCfxuwf8L4Z8UIsdTHP/r0Y=
github.com/OpenIMSDK/tools v0.0.20 h1:zBTjQZRJ5lR1FIzP9mtWyAvh5dKsmJXQugi4p8X/97k=
github.com/OpenIMSDK/tools v0.0.20/go.mod h1:eg+q4A34Qmu73xkY0mt37FHGMCMfC6CtmOnm0kFEGFI=
github.com/OpenIMSDK/protocol v0.0.48 h1:8MIMjyzJRsruYhVv2ZKArFiOveroaofDOb3dlAdgjsw=
github.com/OpenIMSDK/protocol v0.0.48/go.mod h1:F25dFrwrIx3lkNoiuf6FkCfxuwf8L4Z8UIsdTHP/r0Y=
github.com/OpenIMSDK/tools v0.0.29 h1:NS4PEwYl9sX3SWsMjDOLVxMo3LcTWREMr+2cjzWjcqc=
github.com/OpenIMSDK/tools v0.0.29/go.mod h1:eg+q4A34Qmu73xkY0mt37FHGMCMfC6CtmOnm0kFEGFI=
github.com/QcloudApi/qcloud_sign_golang v0.0.0-20141224014652-e4130a326409/go.mod h1:1pk82RBxDY/JZnPQrtqHlUFfCctgdorsd9M06fMynOM=
github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7 h1:uSoVVbwJiQipAclBbw+8quDsfcvFjOpI5iCf4p/cqCs=
github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs=
@@ -308,6 +308,8 @@ github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/src-d/gcfg v1.4.0 h1:xXbNR5AlLSA315x2UO+fTSSAXCDf+Ar38/6oyGbDKQ4=
github.com/src-d/gcfg v1.4.0/go.mod h1:p/UMsR43ujA89BJY9duynAwIpvqEujIH/jFlfL7jWoI=
github.com/stathat/consistent v1.0.0 h1:ZFJ1QTRn8npNBKW065raSZ8xfOqhpb8vLOkfp4CcL/U=
github.com/stathat/consistent v1.0.0/go.mod h1:uajTPbgSygZBJ+V+0mY7meZ8i0XAcZs7AQ6V121XSxw=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
@@ -354,8 +356,8 @@ go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/automaxprocs v1.5.3 h1:kWazyxZUrS3Gs4qUpbwo5kEIMGe/DAvi5Z4tl2NW4j8=
go.uber.org/automaxprocs v1.5.3/go.mod h1:eRbA25aqJrxAbsLO0xy5jVwPt7FQnRgjW+efnwa1WM0=
go.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI=
go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4=
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60=
go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg=
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
@@ -369,8 +371,8 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc=
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k=
golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/image v0.13.0 h1:3cge/F/QTkNLauhf2QoE9zp+7sr+ZcL4HnoZmdwg9sg=
golang.org/x/image v0.13.0/go.mod h1:6mmbMOeV28HuMTgA6OSRkdXKYw/t5W9Uwn2Yv1r3Yxk=
@@ -436,12 +438,12 @@ golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q=
golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.13.0 h1:bb+I9cTfFazGW51MZqBVmZy7+JEJMouUHTUSKVQLBek=
golang.org/x/term v0.15.0 h1:y/Oo/a/q3IXu26lQgl04j/gjuBDOBlx7X6Om1j2CPW4=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
@@ -449,10 +451,10 @@ golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k=
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4=
golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
@@ -536,3 +538,5 @@ gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
stathat.com/c/consistent v1.0.0 h1:ezyc51EGcRPJUxfHGSgJjWzJdj3NiMU9pNfLNGiXV0c=
stathat.com/c/consistent v1.0.0/go.mod h1:QkzMWzcbB+yQBL2AttO6sgsQS/JSTapcDISJalmCDS0=
+4
View File
@@ -33,6 +33,10 @@ func (o *ConversationApi) GetAllConversations(c *gin.Context) {
a2r.Call(conversation.ConversationClient.GetAllConversations, o.Client, c)
}
func (o *ConversationApi) GetSortedConversationList(c *gin.Context) {
a2r.Call(conversation.ConversationClient.GetSortedConversationList, o.Client, c)
}
func (o *ConversationApi) GetConversation(c *gin.Context) {
a2r.Call(conversation.ConversationClient.GetConversation, o.Client, c)
}
+3
View File
@@ -92,3 +92,6 @@ func (o *FriendApi) GetFriendIDs(c *gin.Context) {
func (o *FriendApi) GetSpecifiedFriendsInfo(c *gin.Context) {
a2r.Call(friend.FriendClient.GetSpecifiedFriendsInfo, o.Client, c)
}
func (o *FriendApi) UpdateFriends(c *gin.Context) {
a2r.Call(friend.FriendClient.UpdateFriends, o.Client, c)
}
+32 -5
View File
@@ -164,14 +164,15 @@ func (m *MessageApi) getSendMsgReq(c *gin.Context, req apistruct.SendMsg) (sendM
data = apistruct.VideoElem{}
case constant.File:
data = apistruct.FileElem{}
case constant.AtText:
data = apistruct.AtElem{}
case constant.Custom:
data = apistruct.CustomElem{}
case constant.OANotification:
data = apistruct.OANotificationElem{}
req.SessionType = constant.NotificationChatType
if !authverify.IsManagerUserID(req.SendID) {
return nil, errs.ErrNoPermission.
Wrap("only app manager can as sender send OANotificationElem")
if err = m.userRpcClient.GetNotificationByID(c, req.SendID); err != nil {
return nil, err
}
default:
return nil, errs.ErrArgs.WithDetail("not support err contentType")
@@ -186,38 +187,63 @@ func (m *MessageApi) getSendMsgReq(c *gin.Context, req apistruct.SendMsg) (sendM
return m.newUserSendMsgReq(c, &req), nil
}
// SendMessage handles the sending of a message. It's an HTTP handler function to be used with Gin framework.
func (m *MessageApi) SendMessage(c *gin.Context) {
// Initialize a request struct for sending a message.
req := apistruct.SendMsgReq{}
// Bind the JSON request body to the request struct.
if err := c.BindJSON(&req); err != nil {
// Respond with an error if request body binding fails.
apiresp.GinError(c, errs.ErrArgs.WithDetail(err.Error()).Wrap())
return
}
// Check if the user has the app manager role.
if !authverify.IsAppManagerUid(c) {
// Respond with a permission error if the user is not an app manager.
apiresp.GinError(c, errs.ErrNoPermission.Wrap("only app manager can send message"))
return
}
// Prepare the message request with additional required data.
sendMsgReq, err := m.getSendMsgReq(c, req.SendMsg)
if err != nil {
// Log and respond with an error if preparation fails.
log.ZError(c, "decodeData failed", err)
apiresp.GinError(c, err)
return
}
// Set the receiver ID in the message data.
sendMsgReq.MsgData.RecvID = req.RecvID
// Declare a variable to store the message sending status.
var status int
// Attempt to send the message using the client.
respPb, err := m.Client.SendMsg(c, sendMsgReq)
if err != nil {
// Set the status to failed and respond with an error if sending fails.
status = constant.MsgSendFailed
log.ZError(c, "send message err", err)
apiresp.GinError(c, err)
return
}
// Set the status to successful if the message is sent.
status = constant.MsgSendSuccessed
// Attempt to update the message sending status in the system.
_, err = m.Client.SetSendMsgStatus(c, &msg.SetSendMsgStatusReq{
Status: int32(status),
})
if err != nil {
// Log the error if updating the status fails.
log.ZError(c, "SetSendMsgStatus failed", err)
}
// Respond with a success message and the response payload.
apiresp.GinSuccess(c, respPb)
}
@@ -225,13 +251,14 @@ func (m *MessageApi) SendBusinessNotification(c *gin.Context) {
req := struct {
Key string `json:"key"`
Data string `json:"data"`
SendUserID string `json:"sendUserID"`
RecvUserID string `json:"recvUserID"`
SendUserID string `json:"sendUserID" binding:"required"`
RecvUserID string `json:"recvUserID" binding:"required"`
}{}
if err := c.BindJSON(&req); err != nil {
apiresp.GinError(c, errs.ErrArgs.WithDetail(err.Error()).Wrap())
return
}
if !authverify.IsAppManagerUid(c) {
apiresp.GinError(c, errs.ErrNoPermission.Wrap("only app manager can send message"))
return
+14
View File
@@ -67,6 +67,7 @@ func NewGinRouter(discov discoveryregistry.SvcDiscoveryRegistry, rdb redis.Unive
{
userRouterGroup.POST("/user_register", u.UserRegister)
userRouterGroup.POST("/update_user_info", ParseToken, u.UpdateUserInfo)
userRouterGroup.POST("/update_user_info_ex", ParseToken, u.UpdateUserInfoEx)
userRouterGroup.POST("/set_global_msg_recv_opt", ParseToken, u.SetGlobalRecvMessageOpt)
userRouterGroup.POST("/get_users_info", ParseToken, u.GetUsersPublicInfo)
userRouterGroup.POST("/get_all_users_uid", ParseToken, u.GetAllUsersID)
@@ -77,6 +78,15 @@ func NewGinRouter(discov discoveryregistry.SvcDiscoveryRegistry, rdb redis.Unive
userRouterGroup.POST("/subscribe_users_status", ParseToken, u.SubscriberStatus)
userRouterGroup.POST("/get_users_status", ParseToken, u.GetUserStatus)
userRouterGroup.POST("/get_subscribe_users_status", ParseToken, u.GetSubscribeUsersStatus)
userRouterGroup.POST("/process_user_command_add", ParseToken, u.ProcessUserCommandAdd)
userRouterGroup.POST("/process_user_command_delete", ParseToken, u.ProcessUserCommandDelete)
userRouterGroup.POST("/process_user_command_update", ParseToken, u.ProcessUserCommandUpdate)
userRouterGroup.POST("/process_user_command_get", ParseToken, u.ProcessUserCommandGet)
userRouterGroup.POST("/add_notification_account", ParseToken, u.AddNotificationAccount)
userRouterGroup.POST("/update_notification_account", ParseToken, u.UpdateNotificationAccountInfo)
userRouterGroup.POST("/search_notification_account", ParseToken, u.SearchNotificationAccount)
}
// friend routing group
friendRouterGroup := r.Group("/friend", ParseToken)
@@ -98,6 +108,7 @@ func NewGinRouter(discov discoveryregistry.SvcDiscoveryRegistry, rdb redis.Unive
friendRouterGroup.POST("/is_friend", f.IsFriend)
friendRouterGroup.POST("/get_friend_id", f.GetFriendIDs)
friendRouterGroup.POST("/get_specified_friends_info", f.GetSpecifiedFriendsInfo)
friendRouterGroup.POST("/update_friends", f.UpdateFriends)
}
g := NewGroupApi(*groupRpc)
groupRouterGroup := r.Group("/group", ParseToken)
@@ -161,6 +172,8 @@ func NewGinRouter(discov discoveryregistry.SvcDiscoveryRegistry, rdb redis.Unive
objectGroup.POST("/auth_sign", t.AuthSign)
objectGroup.POST("/complete_multipart_upload", t.CompleteMultipartUpload)
objectGroup.POST("/access_url", t.AccessURL)
objectGroup.POST("/initiate_form_data", t.InitiateFormData)
objectGroup.POST("/complete_form_data", t.CompleteFormData)
objectGroup.GET("/*name", t.ObjectRedirect)
}
// Message
@@ -191,6 +204,7 @@ func NewGinRouter(discov discoveryregistry.SvcDiscoveryRegistry, rdb redis.Unive
conversationGroup := r.Group("/conversation", ParseToken)
{
c := NewConversationApi(*conversationRpc)
conversationGroup.POST("/get_sorted_conversation_list", c.GetSortedConversationList)
conversationGroup.POST("/get_all_conversations", c.GetAllConversations)
conversationGroup.POST("/get_conversation", c.GetConversation)
conversationGroup.POST("/get_conversations", c.GetConversations)
+9 -1
View File
@@ -71,6 +71,14 @@ func (o *ThirdApi) AccessURL(c *gin.Context) {
a2r.Call(third.ThirdClient.AccessURL, o.Client, c)
}
func (o *ThirdApi) InitiateFormData(c *gin.Context) {
a2r.Call(third.ThirdClient.InitiateFormData, o.Client, c)
}
func (o *ThirdApi) CompleteFormData(c *gin.Context) {
a2r.Call(third.ThirdClient.CompleteFormData, o.Client, c)
}
func (o *ThirdApi) ObjectRedirect(c *gin.Context) {
name := c.Param("name")
if name == "" {
@@ -122,5 +130,5 @@ func (o *ThirdApi) SearchLogs(c *gin.Context) {
}
func GetPrometheus(c *gin.Context) {
c.Redirect(http.StatusFound, config2.Config.Prometheus.PrometheusUrl)
c.Redirect(http.StatusFound, config2.Config.Prometheus.GrafanaUrl)
}
+36 -3
View File
@@ -15,8 +15,6 @@
package api
import (
"github.com/gin-gonic/gin"
"github.com/OpenIMSDK/protocol/constant"
"github.com/OpenIMSDK/protocol/msggateway"
"github.com/OpenIMSDK/protocol/user"
@@ -24,6 +22,7 @@ import (
"github.com/OpenIMSDK/tools/apiresp"
"github.com/OpenIMSDK/tools/errs"
"github.com/OpenIMSDK/tools/log"
"github.com/gin-gonic/gin"
"github.com/openimsdk/open-im-server/v3/pkg/common/config"
"github.com/openimsdk/open-im-server/v3/pkg/rpcclient"
@@ -42,7 +41,9 @@ func (u *UserApi) UserRegister(c *gin.Context) {
func (u *UserApi) UpdateUserInfo(c *gin.Context) {
a2r.Call(user.UserClient.UpdateUserInfo, u.Client, c)
}
func (u *UserApi) UpdateUserInfoEx(c *gin.Context) {
a2r.Call(user.UserClient.UpdateUserInfoEx, u.Client, c)
}
func (u *UserApi) SetGlobalRecvMessageOpt(c *gin.Context) {
a2r.Call(user.UserClient.SetGlobalRecvMessageOpt, u.Client, c)
}
@@ -199,3 +200,35 @@ func (u *UserApi) GetUserStatus(c *gin.Context) {
func (u *UserApi) GetSubscribeUsersStatus(c *gin.Context) {
a2r.Call(user.UserClient.GetSubscribeUsersStatus, u.Client, c)
}
// ProcessUserCommandAdd user general function add
func (u *UserApi) ProcessUserCommandAdd(c *gin.Context) {
a2r.Call(user.UserClient.ProcessUserCommandAdd, u.Client, c)
}
// ProcessUserCommandDelete user general function delete
func (u *UserApi) ProcessUserCommandDelete(c *gin.Context) {
a2r.Call(user.UserClient.ProcessUserCommandDelete, u.Client, c)
}
// ProcessUserCommandUpdate user general function update
func (u *UserApi) ProcessUserCommandUpdate(c *gin.Context) {
a2r.Call(user.UserClient.ProcessUserCommandUpdate, u.Client, c)
}
// ProcessUserCommandGet user general function get
func (u *UserApi) ProcessUserCommandGet(c *gin.Context) {
a2r.Call(user.UserClient.ProcessUserCommandGet, u.Client, c)
}
func (u *UserApi) AddNotificationAccount(c *gin.Context) {
a2r.Call(user.UserClient.AddNotificationAccount, u.Client, c)
}
func (u *UserApi) UpdateNotificationAccountInfo(c *gin.Context) {
a2r.Call(user.UserClient.UpdateNotificationAccountInfo, u.Client, c)
}
func (u *UserApi) SearchNotificationAccount(c *gin.Context) {
a2r.Call(user.UserClient.SearchNotificationAccount, u.Client, c)
}
+4
View File
@@ -87,6 +87,7 @@ func newClient(ctx *UserConnContext, conn LongConn, isCompress bool) *Client {
}
}
// ResetClient updates the client's state with new connection and context information.
func (c *Client) ResetClient(
ctx *UserConnContext,
conn LongConn,
@@ -108,11 +109,13 @@ func (c *Client) ResetClient(
c.token = token
}
// pingHandler handles ping messages and sends pong responses.
func (c *Client) pingHandler(_ string) error {
_ = c.conn.SetReadDeadline(pongWait)
return c.writePongMsg()
}
// readMessage continuously reads messages from the connection.
func (c *Client) readMessage() {
defer func() {
if r := recover(); r != nil {
@@ -164,6 +167,7 @@ func (c *Client) readMessage() {
}
}
// handleMessage processes a single message received by the client.
func (c *Client) handleMessage(message []byte) error {
if c.IsCompress {
var err error
+1
View File
@@ -26,6 +26,7 @@ const (
Compression = "compression"
GzipCompressionProtocol = "gzip"
BackgroundStatus = "isBackground"
MsgResp = "isMsgResp"
)
const (
+85 -67
View File
@@ -16,7 +16,10 @@ package msggateway
import (
"context"
"encoding/json"
"errors"
"fmt"
"github.com/OpenIMSDK/tools/apiresp"
"net/http"
"os"
"os/signal"
@@ -288,12 +291,13 @@ func (ws *WsServer) registerClient(client *Client) {
}
wg := sync.WaitGroup{}
wg.Add(1)
go func() {
defer wg.Done()
_ = ws.sendUserOnlineInfoToOtherNode(client.ctx, client)
}()
if config.Config.Envs.Discovery == "zookeeper" {
wg.Add(1)
go func() {
defer wg.Done()
_ = ws.sendUserOnlineInfoToOtherNode(client.ctx, client)
}()
}
wg.Add(1)
go func() {
defer wg.Done()
@@ -341,11 +345,7 @@ func (ws *WsServer) multiTerminalLoginChecker(clientOK bool, oldClients []*Clien
if !clientOK {
return
}
isDeleteUser := ws.clients.deleteClients(newClient.UserID, oldClients)
if isDeleteUser {
ws.onlineUserNum.Add(-1)
}
ws.clients.deleteClients(newClient.UserID, oldClients)
for _, c := range oldClients {
err := c.KickOnlineMessage()
if err != nil {
@@ -421,84 +421,102 @@ func (ws *WsServer) unregisterClient(client *Client) {
)
}
func (ws *WsServer) wsHandler(w http.ResponseWriter, r *http.Request) {
connContext := newContext(w, r)
func (ws *WsServer) ParseWSArgs(r *http.Request) (args *WSArgs, err error) {
var v WSArgs
defer func() {
args = &v
}()
query := r.URL.Query()
v.MsgResp, _ = strconv.ParseBool(query.Get(MsgResp))
if ws.onlineUserConnNum.Load() >= ws.wsMaxConnNum {
httpError(connContext, errs.ErrConnOverMaxNumLimit)
return
return nil, errs.ErrConnOverMaxNumLimit.Wrap("over max conn num limit")
}
var (
token string
userID string
platformIDStr string
exists bool
compression bool
)
token, exists = connContext.Query(Token)
if !exists {
httpError(connContext, errs.ErrConnArgsErr)
return
if v.Token = query.Get(Token); v.Token == "" {
return nil, errs.ErrConnArgsErr.Wrap("token is empty")
}
userID, exists = connContext.Query(WsUserID)
if !exists {
httpError(connContext, errs.ErrConnArgsErr)
return
if v.UserID = query.Get(WsUserID); v.UserID == "" {
return nil, errs.ErrConnArgsErr.Wrap("sendID is empty")
}
platformIDStr, exists = connContext.Query(PlatformID)
if !exists {
httpError(connContext, errs.ErrConnArgsErr)
return
platformIDStr := query.Get(PlatformID)
if platformIDStr == "" {
return nil, errs.ErrConnArgsErr.Wrap("platformID is empty")
}
platformID, err := strconv.Atoi(platformIDStr)
if err != nil {
httpError(connContext, errs.ErrConnArgsErr)
return
return nil, errs.ErrConnArgsErr.Wrap("platformID is not int")
}
if err = authverify.WsVerifyToken(token, userID, platformID); err != nil {
httpError(connContext, err)
return
v.PlatformID = platformID
if err = authverify.WsVerifyToken(v.Token, v.UserID, platformID); err != nil {
return nil, err
}
m, err := ws.cache.GetTokensWithoutError(context.Background(), userID, platformID)
if query.Get(Compression) == GzipCompressionProtocol {
v.Compression = true
}
if r.Header.Get(Compression) == GzipCompressionProtocol {
v.Compression = true
}
m, err := ws.cache.GetTokensWithoutError(context.Background(), v.UserID, platformID)
if err != nil {
httpError(connContext, err)
return
return nil, err
}
if v, ok := m[token]; ok {
if v, ok := m[v.Token]; ok {
switch v {
case constant.NormalToken:
case constant.KickedToken:
httpError(connContext, errs.ErrTokenKicked.Wrap())
return
return nil, errs.ErrTokenKicked.Wrap()
default:
httpError(connContext, errs.ErrTokenUnknown.Wrap())
return nil, errs.ErrTokenUnknown.Wrap(fmt.Sprintf("token status is %d", v))
}
} else {
return nil, errs.ErrTokenNotExist.Wrap()
}
return &v, nil
}
type WSArgs struct {
Token string
UserID string
PlatformID int
Compression bool
MsgResp bool
}
func (ws *WsServer) wsHandler(w http.ResponseWriter, r *http.Request) {
connContext := newContext(w, r)
args, pErr := ws.ParseWSArgs(r)
var wsLongConn *GWebSocket
if args.MsgResp {
wsLongConn = newGWebSocket(WebSocket, ws.handshakeTimeout, ws.writeBufferSize)
if err := wsLongConn.GenerateLongConn(w, r); err != nil {
httpError(connContext, err)
return
}
data, err := json.Marshal(apiresp.ParseError(pErr))
if err != nil {
_ = wsLongConn.Close()
return
}
if err := wsLongConn.WriteMessage(MessageText, data); err != nil {
_ = wsLongConn.Close()
return
}
if pErr != nil {
_ = wsLongConn.Close()
return
}
} else {
httpError(connContext, errs.ErrTokenNotExist.Wrap())
return
}
wsLongConn := newGWebSocket(WebSocket, ws.handshakeTimeout, ws.writeBufferSize)
err = wsLongConn.GenerateLongConn(w, r)
if err != nil {
httpError(connContext, err)
return
}
compressProtoc, exists := connContext.Query(Compression)
if exists {
if compressProtoc == GzipCompressionProtocol {
compression = true
if pErr != nil {
httpError(connContext, pErr)
return
}
}
compressProtoc, exists = connContext.GetHeader(Compression)
if exists {
if compressProtoc == GzipCompressionProtocol {
compression = true
wsLongConn = newGWebSocket(WebSocket, ws.handshakeTimeout, ws.writeBufferSize)
if err := wsLongConn.GenerateLongConn(w, r); err != nil {
httpError(connContext, err)
return
}
}
client := ws.clientPool.Get().(*Client)
client.ResetClient(connContext, wsLongConn, connContext.GetBackground(), compression, ws, token)
client.ResetClient(connContext, wsLongConn, connContext.GetBackground(), args.Compression, ws, args.Token)
ws.registerChan <- client
go client.readMessage()
}
+6 -5
View File
@@ -67,13 +67,14 @@ func (c *ConsumerHandler) handleMs2PsChat(ctx context.Context, msg []byte) {
case constant.SuperGroupChatType:
err = c.pusher.Push2SuperGroup(ctx, pbData.MsgData.GroupID, pbData.MsgData)
default:
var pushUserIDs []string
if pbData.MsgData.SendID != pbData.MsgData.RecvID {
pushUserIDs = []string{pbData.MsgData.SendID, pbData.MsgData.RecvID}
var pushUserIDList []string
isSenderSync := utils.GetSwitchFromOptions(pbData.MsgData.Options, constant.IsSenderSync)
if !isSenderSync || pbData.MsgData.SendID == pbData.MsgData.RecvID {
pushUserIDList = append(pushUserIDList, pbData.MsgData.RecvID)
} else {
pushUserIDs = []string{pbData.MsgData.SendID}
pushUserIDList = append(pushUserIDList, pbData.MsgData.RecvID, pbData.MsgData.SendID)
}
err = c.pusher.Push2User(ctx, pushUserIDs, pbData.MsgData)
err = c.pusher.Push2User(ctx, pushUserIDList, pbData.MsgData)
}
if err != nil {
if err == errNoOfflinePusher {
+1 -2
View File
@@ -16,9 +16,8 @@ package push
import (
"context"
"sync"
"github.com/OpenIMSDK/tools/utils"
"sync"
"google.golang.org/grpc"
+118 -21
View File
@@ -18,6 +18,7 @@ import (
"context"
"encoding/json"
"errors"
"google.golang.org/grpc"
"sync"
"golang.org/x/sync/errgroup"
@@ -142,6 +143,47 @@ func (p *Pusher) UnmarshalNotificationElem(bytes []byte, t any) error {
return json.Unmarshal([]byte(notification.Detail), t)
}
/*
k8s deployment,offline push group messages function
*/
func (p *Pusher) k8sOfflinePush2SuperGroup(ctx context.Context, groupID string, msg *sdkws.MsgData, wsResults []*msggateway.SingleMsgToUserResults) error {
var needOfflinePushUserIDs []string
for _, v := range wsResults {
if !v.OnlinePush {
needOfflinePushUserIDs = append(needOfflinePushUserIDs, v.UserID)
}
}
if len(needOfflinePushUserIDs) > 0 {
var offlinePushUserIDs []string
err := callbackOfflinePush(ctx, needOfflinePushUserIDs, msg, &offlinePushUserIDs)
if err != nil {
return err
}
if len(offlinePushUserIDs) > 0 {
needOfflinePushUserIDs = offlinePushUserIDs
}
if msg.ContentType != constant.SignalingNotification {
resp, err := p.conversationRpcClient.Client.GetConversationOfflinePushUserIDs(
ctx,
&conversation.GetConversationOfflinePushUserIDsReq{ConversationID: utils.GenGroupConversationID(groupID), UserIDs: needOfflinePushUserIDs},
)
if err != nil {
return err
}
if len(resp.UserIDs) > 0 {
err = p.offlinePushMsg(ctx, groupID, msg, resp.UserIDs)
if err != nil {
log.ZError(ctx, "offlinePushMsg failed", err, "groupID", groupID, "msg", msg)
return err
}
}
}
}
return nil
}
func (p *Pusher) Push2SuperGroup(ctx context.Context, groupID string, msg *sdkws.MsgData) (err error) {
log.ZDebug(ctx, "Get super group msg from msg_transfer and push msg", "msg", msg.String(), "groupID", groupID)
var pushToUserIDs []string
@@ -205,7 +247,10 @@ func (p *Pusher) Push2SuperGroup(ctx context.Context, groupID string, msg *sdkws
log.ZDebug(ctx, "get conn and online push success", "result", wsResults, "msg", msg)
isOfflinePush := utils.GetSwitchFromOptions(msg.Options, constant.IsOfflinePush)
if isOfflinePush {
if isOfflinePush && config.Config.Envs.Discovery == "k8s" {
return p.k8sOfflinePush2SuperGroup(ctx, groupID, msg, wsResults)
}
if isOfflinePush && config.Config.Envs.Discovery == "zookeeper" {
var (
onlineSuccessUserIDs = []string{msg.SendID}
webAndPcBackgroundUserIDs []string
@@ -239,14 +284,7 @@ func (p *Pusher) Push2SuperGroup(ctx context.Context, groupID string, msg *sdkws
}
needOfflinePushUserIDs := utils.DifferenceString(onlineSuccessUserIDs, pushToUserIDs)
if msg.ContentType != constant.SignalingNotification {
notNotificationUserIDs, err := p.conversationLocalCache.GetRecvMsgNotNotifyUserIDs(ctx, groupID)
if err != nil {
return err
}
needOfflinePushUserIDs = utils.SliceSub(needOfflinePushUserIDs, notNotificationUserIDs)
}
// Use offline push messaging
if len(needOfflinePushUserIDs) > 0 {
var offlinePushUserIDs []string
@@ -258,30 +296,89 @@ func (p *Pusher) Push2SuperGroup(ctx context.Context, groupID string, msg *sdkws
if len(offlinePushUserIDs) > 0 {
needOfflinePushUserIDs = offlinePushUserIDs
}
resp, err := p.conversationRpcClient.Client.GetConversationOfflinePushUserIDs(
ctx,
&conversation.GetConversationOfflinePushUserIDsReq{ConversationID: utils.GenGroupConversationID(groupID), UserIDs: needOfflinePushUserIDs},
)
if err != nil {
return err
}
if len(resp.UserIDs) > 0 {
err = p.offlinePushMsg(ctx, groupID, msg, resp.UserIDs)
if msg.ContentType != constant.SignalingNotification {
resp, err := p.conversationRpcClient.Client.GetConversationOfflinePushUserIDs(
ctx,
&conversation.GetConversationOfflinePushUserIDsReq{ConversationID: utils.GenGroupConversationID(groupID), UserIDs: needOfflinePushUserIDs},
)
if err != nil {
log.ZError(ctx, "offlinePushMsg failed", err, "groupID", groupID, "msg", msg)
return err
}
if _, err := p.GetConnsAndOnlinePush(ctx, msg, utils.IntersectString(resp.UserIDs, webAndPcBackgroundUserIDs)); err != nil {
log.ZError(ctx, "offlinePushMsg failed", err, "groupID", groupID, "msg", msg, "userIDs", utils.IntersectString(needOfflinePushUserIDs, webAndPcBackgroundUserIDs))
return err
if len(resp.UserIDs) > 0 {
err = p.offlinePushMsg(ctx, groupID, msg, resp.UserIDs)
if err != nil {
log.ZError(ctx, "offlinePushMsg failed", err, "groupID", groupID, "msg", msg)
return err
}
if _, err := p.GetConnsAndOnlinePush(ctx, msg, utils.IntersectString(resp.UserIDs, webAndPcBackgroundUserIDs)); err != nil {
log.ZError(ctx, "offlinePushMsg failed", err, "groupID", groupID, "msg", msg, "userIDs", utils.IntersectString(needOfflinePushUserIDs, webAndPcBackgroundUserIDs))
return err
}
}
}
}
}
return nil
}
func (p *Pusher) k8sOnlinePush(ctx context.Context, msg *sdkws.MsgData, pushToUserIDs []string) (wsResults []*msggateway.SingleMsgToUserResults, err error) {
var usersHost = make(map[string][]string)
for _, v := range pushToUserIDs {
tHost, err := p.discov.GetUserIdHashGatewayHost(ctx, v)
if err != nil {
log.ZError(ctx, "get msggateway hash error", err)
return nil, err
}
tUsers, tbl := usersHost[tHost]
if tbl {
tUsers = append(tUsers, v)
usersHost[tHost] = tUsers
} else {
usersHost[tHost] = []string{v}
}
}
log.ZDebug(ctx, "genUsers send hosts struct:", "usersHost", usersHost)
var usersConns = make(map[*grpc.ClientConn][]string)
for host, userIds := range usersHost {
tconn, _ := p.discov.GetConn(ctx, host)
usersConns[tconn] = userIds
}
var (
mu sync.Mutex
wg = errgroup.Group{}
maxWorkers = config.Config.Push.MaxConcurrentWorkers
)
if maxWorkers < 3 {
maxWorkers = 3
}
wg.SetLimit(maxWorkers)
for conn, userIds := range usersConns {
tcon := conn
tuserIds := userIds
wg.Go(func() error {
input := &msggateway.OnlineBatchPushOneMsgReq{MsgData: msg, PushToUserIDs: tuserIds}
msgClient := msggateway.NewMsgGatewayClient(tcon)
reply, err := msgClient.SuperGroupOnlineBatchPushOneMsg(ctx, input)
if err != nil {
return nil
}
log.ZDebug(ctx, "push result", "reply", reply)
if reply != nil && reply.SinglePushResult != nil {
mu.Lock()
wsResults = append(wsResults, reply.SinglePushResult...)
mu.Unlock()
}
return nil
})
}
_ = wg.Wait()
return wsResults, nil
}
func (p *Pusher) GetConnsAndOnlinePush(ctx context.Context, msg *sdkws.MsgData, pushToUserIDs []string) (wsResults []*msggateway.SingleMsgToUserResults, err error) {
if config.Config.Envs.Discovery == "k8s" {
return p.k8sOnlinePush(ctx, msg, pushToUserIDs)
}
conns, err := p.discov.GetConns(ctx, config.Config.RpcRegisterName.OpenImMessageGatewayName)
log.ZDebug(ctx, "get gateway conn", "conn length", len(conns))
if err != nil {
+180
View File
@@ -17,6 +17,8 @@ package conversation
import (
"context"
"errors"
"github.com/OpenIMSDK/protocol/sdkws"
"sort"
"github.com/OpenIMSDK/tools/tx"
@@ -41,6 +43,8 @@ import (
)
type conversationServer struct {
msgRpcClient *rpcclient.MessageRpcClient
user *rpcclient.UserRpcClient
groupRpcClient *rpcclient.GroupRpcClient
conversationDatabase controller.ConversationDatabase
conversationNotificationSender *notification.ConversationNotificationSender
@@ -61,7 +65,10 @@ func Start(client discoveryregistry.SvcDiscoveryRegistry, server *grpc.Server) e
}
groupRpcClient := rpcclient.NewGroupRpcClient(client)
msgRpcClient := rpcclient.NewMessageRpcClient(client)
userRpcClient := rpcclient.NewUserRpcClient(client)
pbconversation.RegisterConversationServer(server, &conversationServer{
msgRpcClient: &msgRpcClient,
user: &userRpcClient,
conversationNotificationSender: notification.NewConversationNotificationSender(&msgRpcClient),
groupRpcClient: &groupRpcClient,
conversationDatabase: controller.NewConversationDatabase(conversationDB, cache.NewConversationRedis(rdb, cache.GetDefaultOpt(), conversationDB), tx.NewMongo(mongo.GetClient())),
@@ -82,6 +89,80 @@ func (c *conversationServer) GetConversation(ctx context.Context, req *pbconvers
return resp, nil
}
func (m *conversationServer) GetSortedConversationList(ctx context.Context, req *pbconversation.GetSortedConversationListReq) (resp *pbconversation.GetSortedConversationListResp, err error) {
log.ZDebug(ctx, "GetSortedConversationList", "seqs", req, "userID", req.UserID)
var conversationIDs []string
if len(req.ConversationIDs) == 0 {
conversationIDs, err = m.conversationDatabase.GetConversationIDs(ctx, req.UserID)
if err != nil {
return nil, err
}
} else {
conversationIDs = req.ConversationIDs
}
conversations, err := m.conversationDatabase.FindConversations(ctx, req.UserID, conversationIDs)
if err != nil {
return nil, err
}
if len(conversations) == 0 {
return nil, errs.ErrRecordNotFound.Wrap()
}
maxSeqs, err := m.msgRpcClient.GetMaxSeqs(ctx, conversationIDs)
if err != nil {
return nil, err
}
chatLogs, err := m.msgRpcClient.GetMsgByConversationIDs(ctx, conversationIDs, maxSeqs)
if err != nil {
return nil, err
}
conversationMsg, err := m.getConversationInfo(ctx, chatLogs, req.UserID)
if err != nil {
return nil, err
}
hasReadSeqs, err := m.msgRpcClient.GetHasReadSeqs(ctx, req.UserID, conversationIDs)
if err != nil {
return nil, err
}
var unreadTotal int64
conversation_unreadCount := make(map[string]int64)
for conversationID, maxSeq := range maxSeqs {
unreadCount := maxSeq - hasReadSeqs[conversationID]
conversation_unreadCount[conversationID] = unreadCount
unreadTotal += unreadCount
}
conversation_isPinTime := make(map[int64]string)
conversation_notPinTime := make(map[int64]string)
for _, v := range conversations {
conversationID := v.ConversationID
time := conversationMsg[conversationID].MsgInfo.LatestMsgRecvTime
conversationMsg[conversationID].RecvMsgOpt = v.RecvMsgOpt
if v.IsPinned {
conversationMsg[conversationID].IsPinned = v.IsPinned
conversation_isPinTime[time] = conversationID
continue
}
conversation_notPinTime[time] = conversationID
}
resp = &pbconversation.GetSortedConversationListResp{
ConversationTotal: int64(len(chatLogs)),
ConversationElems: []*pbconversation.ConversationElem{},
UnreadTotal: unreadTotal,
}
m.conversationSort(conversation_isPinTime, resp, conversation_unreadCount, conversationMsg)
m.conversationSort(conversation_notPinTime, resp, conversation_unreadCount, conversationMsg)
resp.ConversationElems = utils.Paginate(resp.ConversationElems, int(req.Pagination.GetPageNumber()), int(req.Pagination.GetShowNumber()))
return resp, nil
}
func (c *conversationServer) GetAllConversations(ctx context.Context, req *pbconversation.GetAllConversationsReq) (*pbconversation.GetAllConversationsResp, error) {
conversations, err := c.conversationDatabase.GetUserAllConversation(ctx, req.OwnerUserID)
if err != nil {
@@ -348,3 +429,102 @@ func (c *conversationServer) GetConversationOfflinePushUserIDs(
}
return &pbconversation.GetConversationOfflinePushUserIDsResp{UserIDs: utils.Keys(userIDSet)}, nil
}
func (c *conversationServer) conversationSort(
conversations map[int64]string,
resp *pbconversation.GetSortedConversationListResp,
conversation_unreadCount map[string]int64,
conversationMsg map[string]*pbconversation.ConversationElem,
) {
keys := []int64{}
for key := range conversations {
keys = append(keys, key)
}
sort.Slice(keys[:], func(i, j int) bool {
return keys[i] > keys[j]
})
index := 0
cons := make([]*pbconversation.ConversationElem, len(conversations))
for _, v := range keys {
conversationID := conversations[v]
conversationElem := conversationMsg[conversationID]
conversationElem.UnreadCount = conversation_unreadCount[conversationID]
cons[index] = conversationElem
index++
}
resp.ConversationElems = append(resp.ConversationElems, cons...)
}
func (c *conversationServer) getConversationInfo(
ctx context.Context,
chatLogs map[string]*sdkws.MsgData,
userID string) (map[string]*pbconversation.ConversationElem, error) {
var (
sendIDs []string
groupIDs []string
sendMap = make(map[string]*sdkws.UserInfo)
groupMap = make(map[string]*sdkws.GroupInfo)
conversationMsg = make(map[string]*pbconversation.ConversationElem)
)
for _, chatLog := range chatLogs {
switch chatLog.SessionType {
case constant.SingleChatType:
if chatLog.SendID == userID {
sendIDs = append(sendIDs, chatLog.RecvID)
}
sendIDs = append(sendIDs, chatLog.SendID)
case constant.GroupChatType, constant.SuperGroupChatType:
groupIDs = append(groupIDs, chatLog.GroupID)
sendIDs = append(sendIDs, chatLog.SendID)
}
}
if len(sendIDs) != 0 {
sendInfos, err := c.user.GetUsersInfo(ctx, sendIDs)
if err != nil {
return nil, err
}
for _, sendInfo := range sendInfos {
sendMap[sendInfo.UserID] = sendInfo
}
}
if len(groupIDs) != 0 {
groupInfos, err := c.groupRpcClient.GetGroupInfos(ctx, groupIDs, false)
if err != nil {
return nil, err
}
for _, groupInfo := range groupInfos {
groupMap[groupInfo.GroupID] = groupInfo
}
}
for conversationID, chatLog := range chatLogs {
pbchatLog := &pbconversation.ConversationElem{}
msgInfo := &pbconversation.MsgInfo{}
if err := utils.CopyStructFields(msgInfo, chatLog); err != nil {
return nil, err
}
switch chatLog.SessionType {
case constant.SingleChatType:
if chatLog.SendID == userID {
msgInfo.FaceURL = sendMap[chatLog.RecvID].FaceURL
msgInfo.SenderName = sendMap[chatLog.RecvID].Nickname
break
}
msgInfo.FaceURL = sendMap[chatLog.SendID].FaceURL
msgInfo.SenderName = sendMap[chatLog.SendID].Nickname
case constant.GroupChatType, constant.SuperGroupChatType:
msgInfo.GroupName = groupMap[chatLog.GroupID].GroupName
msgInfo.GroupFaceURL = groupMap[chatLog.GroupID].FaceURL
msgInfo.GroupMemberCount = groupMap[chatLog.GroupID].MemberCount
msgInfo.GroupID = chatLog.GroupID
msgInfo.GroupType = groupMap[chatLog.GroupID].GroupType
msgInfo.SenderName = sendMap[chatLog.SendID].Nickname
}
pbchatLog.ConversationID = conversationID
msgInfo.LatestMsgRecvTime = chatLog.SendTime
pbchatLog.MsgInfo = msgInfo
conversationMsg[conversationID] = pbchatLog
}
return conversationMsg, nil
}
+1
View File
@@ -79,6 +79,7 @@ func (s *friendServer) AddBlack(ctx context.Context, req *pbfriend.AddBlackReq)
BlockUserID: req.BlackUserID,
OperatorUserID: mcontext.GetOpUserID(ctx),
CreateTime: time.Now(),
Ex: req.Ex,
}
if err := s.blackDatabase.Create(ctx, []*relation.BlackModel{&black}); err != nil {
return nil, err
+41 -4
View File
@@ -53,10 +53,6 @@ type friendServer struct {
RegisterCenter registry.SvcDiscoveryRegistry
}
func (s *friendServer) PinFriends(ctx context.Context, req *pbfriend.PinFriendsReq) (*pbfriend.PinFriendsResp, error) {
return nil, errs.ErrInternalServer.Wrap("not implemented")
}
func Start(client registry.SvcDiscoveryRegistry, server *grpc.Server) error {
// Initialize MongoDB
mongo, err := unrelation.NewMongo()
@@ -411,6 +407,7 @@ func (s *friendServer) GetSpecifiedFriendsInfo(ctx context.Context, req *pbfrien
}
var friendInfo *sdkws.FriendInfo
if friend := friendMap[userID]; friend != nil {
friendInfo = &sdkws.FriendInfo{
OwnerUserID: friend.OwnerUserID,
Remark: friend.Remark,
@@ -418,6 +415,7 @@ func (s *friendServer) GetSpecifiedFriendsInfo(ctx context.Context, req *pbfrien
AddSource: friend.AddSource,
OperatorUserID: friend.OperatorUserID,
Ex: friend.Ex,
IsPinned: friend.IsPinned,
}
}
var blackInfo *sdkws.BlackInfo
@@ -438,3 +436,42 @@ func (s *friendServer) GetSpecifiedFriendsInfo(ctx context.Context, req *pbfrien
}
return resp, nil
}
func (s *friendServer) UpdateFriends(
ctx context.Context,
req *pbfriend.UpdateFriendsReq,
) (*pbfriend.UpdateFriendsResp, error) {
if len(req.FriendUserIDs) == 0 {
return nil, errs.ErrArgs.Wrap("friendIDList is empty")
}
if utils.Duplicate(req.FriendUserIDs) {
return nil, errs.ErrArgs.Wrap("friendIDList repeated")
}
_, err := s.friendDatabase.FindFriendsWithError(ctx, req.OwnerUserID, req.FriendUserIDs)
if err != nil {
return nil, err
}
val := make(map[string]any)
if req.IsPinned != nil {
val["is_pinned"] = req.IsPinned.Value
}
if req.Remark != nil {
val["remark"] = req.Remark.Value
}
if req.Ex != nil {
val["ex"] = req.Ex.Value
}
if err = s.friendDatabase.UpdateFriends(ctx, req.OwnerUserID, req.FriendUserIDs, val); err != nil {
return nil, err
}
resp := &pbfriend.UpdateFriendsResp{}
err = s.notificationSender.FriendsInfoUpdateNotification(ctx, req.OwnerUserID, req.FriendUserIDs)
if err != nil {
return nil, errs.Wrap(err, "FriendsInfoUpdateNotification Error")
}
return resp, nil
}
+4 -4
View File
@@ -327,8 +327,6 @@ func CallbackBeforeInviteUserToGroup(ctx context.Context, req *group.InviteUserT
// Handle the scenario where certain members are refused
// You might want to update the req.Members list or handle it as per your business logic
}
utils.StructFieldNotNilReplace(req, resp)
return nil
}
@@ -395,7 +393,10 @@ func CallbackBeforeSetGroupInfo(ctx context.Context, req *group.SetGroupInfoReq)
if resp.ApplyMemberFriend != nil {
req.GroupInfoForSet.ApplyMemberFriend = wrapperspb.Int32(*resp.ApplyMemberFriend)
}
utils.StructFieldNotNilReplace(req, resp)
utils.NotNilReplace(&req.GroupInfoForSet.GroupID, &resp.GroupID)
utils.NotNilReplace(&req.GroupInfoForSet.GroupName, &resp.GroupName)
utils.NotNilReplace(&req.GroupInfoForSet.FaceURL, &resp.FaceURL)
utils.NotNilReplace(&req.GroupInfoForSet.Introduction, &resp.Introduction)
return nil
}
func CallbackAfterSetGroupInfo(ctx context.Context, req *group.SetGroupInfoReq) error {
@@ -426,6 +427,5 @@ func CallbackAfterSetGroupInfo(ctx context.Context, req *group.SetGroupInfoReq)
if err := http.CallBackPostReturn(ctx, config.Config.Callback.CallbackUrl, callbackReq, resp, config.Config.Callback.CallbackAfterSetGroupInfo); err != nil {
return err
}
utils.StructFieldNotNilReplace(req, resp)
return nil
}
+47 -10
View File
@@ -109,14 +109,11 @@ type groupServer struct {
}
func (s *groupServer) NotificationUserInfoUpdate(ctx context.Context, req *pbgroup.NotificationUserInfoUpdateReq) (*pbgroup.NotificationUserInfoUpdateResp, error) {
defer log.ZDebug(ctx, "return")
defer log.ZDebug(ctx, "NotificationUserInfoUpdate return")
members, err := s.db.FindGroupMemberUser(ctx, nil, req.UserID)
if err != nil {
return nil, err
}
if err := s.PopulateGroupMember(ctx, members...); err != nil {
return nil, err
}
groupIDs := make([]string, 0, len(members))
for _, member := range members {
if member.Nickname != "" && member.FaceURL != "" {
@@ -476,13 +473,42 @@ func (s *groupServer) GetGroupAllMember(ctx context.Context, req *pbgroup.GetGro
func (s *groupServer) GetGroupMemberList(ctx context.Context, req *pbgroup.GetGroupMemberListReq) (*pbgroup.GetGroupMemberListResp, error) {
resp := &pbgroup.GetGroupMemberListResp{}
total, members, err := s.db.PageGetGroupMember(ctx, req.GroupID, req.Pagination)
var (
total int64
members []*relationtb.GroupMemberModel
err error
)
if req.Keyword == "" {
total, members, err = s.db.PageGetGroupMember(ctx, req.GroupID, req.Pagination)
} else {
members, err = s.db.FindGroupMemberAll(ctx, req.GroupID)
}
if err != nil {
return nil, err
}
if err := s.PopulateGroupMember(ctx, members...); err != nil {
return nil, err
}
if req.Keyword != "" {
groupMembers := make([]*relationtb.GroupMemberModel, 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
}
}
GMembers := utils.Paginate(groupMembers, int(req.Pagination.GetPageNumber()), int(req.Pagination.GetShowNumber()))
resp.Members = utils.Batch(convert.Db2PbGroupMember, GMembers)
resp.Total = uint32(total)
return resp, nil
}
resp.Total = uint32(total)
resp.Members = utils.Batch(convert.Db2PbGroupMember, members)
return resp, nil
@@ -802,6 +828,7 @@ func (s *groupServer) JoinGroup(ctx context.Context, req *pbgroup.JoinGroupReq)
GroupType: string(group.GroupType),
ApplyID: req.InviterUserID,
ReqMessage: req.ReqMessage,
Ex: req.Ex,
}
if err = CallbackApplyJoinGroupBefore(ctx, reqCall); err != nil {
@@ -848,6 +875,7 @@ func (s *groupServer) JoinGroup(ctx context.Context, req *pbgroup.JoinGroupReq)
JoinSource: req.JoinSource,
ReqTime: time.Now(),
HandledTime: time.Unix(0, 0),
Ex: req.Ex,
}
if err := s.db.CreateGroupRequest(ctx, []*relationtb.GroupRequestModel{&groupRequest}); err != nil {
return nil, err
@@ -1040,20 +1068,29 @@ func (s *groupServer) TransferGroupOwner(ctx context.Context, req *pbgroup.Trans
func (s *groupServer) GetGroups(ctx context.Context, req *pbgroup.GetGroupsReq) (*pbgroup.GetGroupsResp, error) {
resp := &pbgroup.GetGroupsResp{}
var (
groups []*relationtb.GroupModel
err error
group []*relationtb.GroupModel
err error
)
if req.GroupID != "" {
groups, err = s.db.FindGroup(ctx, []string{req.GroupID})
resp.Total = uint32(len(groups))
group, err = s.db.FindGroup(ctx, []string{req.GroupID})
resp.Total = uint32(len(group))
} else {
var total int64
total, groups, err = s.db.SearchGroup(ctx, req.GroupName, req.Pagination)
total, group, err = s.db.SearchGroup(ctx, req.GroupName, req.Pagination)
resp.Total = uint32(total)
}
if err != nil {
return nil, err
}
var groups []*relationtb.GroupModel
for _, v := range group {
if v.Status == constant.GroupStatusDismissed {
resp.Total--
continue
}
groups = append(groups, v)
}
groupIDs := utils.Slice(groups, func(e *relationtb.GroupModel) string {
return e.GroupID
})
+11 -2
View File
@@ -18,6 +18,7 @@ import (
"context"
utils2 "github.com/OpenIMSDK/tools/utils"
cbapi "github.com/openimsdk/open-im-server/v3/pkg/callbackstruct"
"github.com/redis/go-redis/v9"
@@ -26,8 +27,6 @@ import (
"github.com/OpenIMSDK/protocol/sdkws"
"github.com/OpenIMSDK/tools/errs"
"github.com/OpenIMSDK/tools/log"
cbapi "github.com/openimsdk/open-im-server/v3/pkg/callbackstruct"
)
func (m *msgServer) GetConversationsHasReadAndMaxSeq(ctx context.Context, req *msg.GetConversationsHasReadAndMaxSeqReq) (resp *msg.GetConversationsHasReadAndMaxSeqResp, err error) {
@@ -125,6 +124,16 @@ func (m *msgServer) MarkMsgsAsRead(
return
}
}
req_callback := &cbapi.CallbackSingleMsgReadReq{
UserID: req.UserID,
ConversationID: req.ConversationID,
ContentType: conversation.ConversationType,
Seqs: req.Seqs,
}
if err = CallbackSingleMsgRead(ctx, req_callback); err != nil {
return nil, err
}
if err = m.sendMarkAsReadNotification(ctx, req.ConversationID, conversation.ConversationType, req.UserID,
m.conversationAndGetRecvID(conversation, req.UserID), req.Seqs, hasReadSeq); err != nil {
return
+4 -5
View File
@@ -70,7 +70,7 @@ func GetContent(msg *sdkws.MsgData) string {
}
func callbackBeforeSendSingleMsg(ctx context.Context, msg *pbchat.SendMsgReq) error {
if !config.Config.Callback.CallbackBeforeSendSingleMsg.Enable {
if !config.Config.Callback.CallbackBeforeSendSingleMsg.Enable || msg.MsgData.ContentType == constant.Typing {
return nil
}
req := &cbapi.CallbackBeforeSendSingleMsgReq{
@@ -85,7 +85,7 @@ func callbackBeforeSendSingleMsg(ctx context.Context, msg *pbchat.SendMsgReq) er
}
func callbackAfterSendSingleMsg(ctx context.Context, msg *pbchat.SendMsgReq) error {
if !config.Config.Callback.CallbackAfterSendSingleMsg.Enable {
if !config.Config.Callback.CallbackAfterSendSingleMsg.Enable || msg.MsgData.ContentType == constant.Typing {
return nil
}
req := &cbapi.CallbackAfterSendSingleMsgReq{
@@ -100,7 +100,7 @@ func callbackAfterSendSingleMsg(ctx context.Context, msg *pbchat.SendMsgReq) err
}
func callbackBeforeSendGroupMsg(ctx context.Context, msg *pbchat.SendMsgReq) error {
if !config.Config.Callback.CallbackBeforeSendSingleMsg.Enable {
if !config.Config.Callback.CallbackBeforeSendGroupMsg.Enable || msg.MsgData.ContentType == constant.Typing {
return nil
}
req := &cbapi.CallbackBeforeSendGroupMsgReq{
@@ -115,7 +115,7 @@ func callbackBeforeSendGroupMsg(ctx context.Context, msg *pbchat.SendMsgReq) err
}
func callbackAfterSendGroupMsg(ctx context.Context, msg *pbchat.SendMsgReq) error {
if !config.Config.Callback.CallbackAfterSendGroupMsg.Enable {
if !config.Config.Callback.CallbackAfterSendGroupMsg.Enable || msg.MsgData.ContentType == constant.Typing {
return nil
}
req := &cbapi.CallbackAfterSendGroupMsgReq{
@@ -202,6 +202,5 @@ func CallbackAfterRevokeMsg(ctx context.Context, req *pbchat.RevokeMsgReq) error
if err := http.CallBackPostReturn(ctx, config.Config.Callback.CallbackUrl, callbackReq, resp, config.Config.Callback.CallbackAfterRevokeMsg); err != nil {
return err
}
utils.StructFieldNotNilReplace(req, resp)
return nil
}
+24 -1
View File
@@ -16,7 +16,6 @@ package msg
import (
"context"
pbmsg "github.com/OpenIMSDK/protocol/msg"
)
@@ -30,3 +29,27 @@ func (m *msgServer) GetConversationMaxSeq(
}
return &pbmsg.GetConversationMaxSeqResp{MaxSeq: maxSeq}, nil
}
func (m *msgServer) GetMaxSeqs(ctx context.Context, req *pbmsg.GetMaxSeqsReq) (*pbmsg.SeqsInfoResp, error) {
maxSeqs, err := m.MsgDatabase.GetMaxSeqs(ctx, req.ConversationIDs)
if err != nil {
return nil, err
}
return &pbmsg.SeqsInfoResp{MaxSeqs: maxSeqs}, nil
}
func (m *msgServer) GetHasReadSeqs(ctx context.Context, req *pbmsg.GetHasReadSeqsReq) (*pbmsg.SeqsInfoResp, error) {
hasReadSeqs, err := m.MsgDatabase.GetHasReadSeqs(ctx, req.UserID, req.ConversationIDs)
if err != nil {
return nil, err
}
return &pbmsg.SeqsInfoResp{MaxSeqs: hasReadSeqs}, nil
}
func (m *msgServer) GetMsgByConversationIDs(ctx context.Context, req *pbmsg.GetMsgByConversationIDsReq) (*pbmsg.GetMsgByConversationIDsResp, error) {
Msgs, err := m.MsgDatabase.FindOneByDocIDs(ctx, req.ConversationIDs, req.MaxSeqs)
if err != nil {
return nil, err
}
return &pbmsg.GetMsgByConversationIDsResp{MsgDatas: Msgs}, nil
}
+113
View File
@@ -16,6 +16,12 @@ package third
import (
"context"
"encoding/base64"
"encoding/hex"
"encoding/json"
"github.com/google/uuid"
"github.com/openimsdk/open-im-server/v3/pkg/authverify"
"path"
"strconv"
"time"
@@ -179,6 +185,113 @@ func (t *thirdServer) AccessURL(ctx context.Context, req *third.AccessURLReq) (*
}, nil
}
func (t *thirdServer) InitiateFormData(ctx context.Context, req *third.InitiateFormDataReq) (*third.InitiateFormDataResp, error) {
if req.Name == "" {
return nil, errs.ErrArgs.Wrap("name is empty")
}
if req.Size <= 0 {
return nil, errs.ErrArgs.Wrap("size must be greater than 0")
}
if err := checkUploadName(ctx, req.Name); err != nil {
return nil, err
}
var duration time.Duration
opUserID := mcontext.GetOpUserID(ctx)
var key string
if authverify.IsManagerUserID(opUserID) {
if req.Millisecond <= 0 {
duration = time.Minute * 10
} else {
duration = time.Millisecond * time.Duration(req.Millisecond)
}
if req.Absolute {
key = req.Name
}
} else {
duration = time.Minute * 10
}
uid, err := uuid.NewRandom()
if err != nil {
return nil, err
}
if key == "" {
date := time.Now().Format("20060102")
key = path.Join(cont.DirectPath, date, opUserID, hex.EncodeToString(uid[:])+path.Ext(req.Name))
}
mate := FormDataMate{
Name: req.Name,
Size: req.Size,
ContentType: req.ContentType,
Group: req.Group,
Key: key,
}
mateData, err := json.Marshal(&mate)
if err != nil {
return nil, err
}
resp, err := t.s3dataBase.FormData(ctx, key, req.Size, req.ContentType, duration)
if err != nil {
return nil, err
}
return &third.InitiateFormDataResp{
Id: base64.RawStdEncoding.EncodeToString(mateData),
Url: resp.URL,
File: resp.File,
Header: toPbMapArray(resp.Header),
FormData: resp.FormData,
Expires: resp.Expires.UnixMilli(),
SuccessCodes: utils.Slice(resp.SuccessCodes, func(code int) int32 {
return int32(code)
}),
}, nil
}
func (t *thirdServer) CompleteFormData(ctx context.Context, req *third.CompleteFormDataReq) (*third.CompleteFormDataResp, error) {
if req.Id == "" {
return nil, errs.ErrArgs.Wrap("id is empty")
}
data, err := base64.RawStdEncoding.DecodeString(req.Id)
if err != nil {
return nil, errs.ErrArgs.Wrap("invalid id " + err.Error())
}
var mate FormDataMate
if err := json.Unmarshal(data, &mate); err != nil {
return nil, errs.ErrArgs.Wrap("invalid id " + err.Error())
}
if err := checkUploadName(ctx, mate.Name); err != nil {
return nil, err
}
info, err := t.s3dataBase.StatObject(ctx, mate.Key)
if err != nil {
return nil, err
}
if info.Size > 0 && info.Size != mate.Size {
return nil, errs.ErrData.Wrap("file size mismatch")
}
obj := &relation.ObjectModel{
Name: mate.Name,
UserID: mcontext.GetOpUserID(ctx),
Hash: "etag_" + info.ETag,
Key: info.Key,
Size: info.Size,
ContentType: mate.ContentType,
Group: mate.Group,
CreateTime: time.Now(),
}
if err := t.s3dataBase.SetObject(ctx, obj); err != nil {
return nil, err
}
return &third.CompleteFormDataResp{Url: t.apiAddress(mate.Name)}, nil
}
func (t *thirdServer) apiAddress(name string) string {
return t.apiURL + name
}
type FormDataMate struct {
Name string `json:"name"`
Size int64 `json:"size"`
ContentType string `json:"contentType"`
Group string `json:"group"`
Key string `json:"key"`
}
+3
View File
@@ -29,6 +29,9 @@ import (
)
func toPbMapArray(m map[string][]string) []*third.KeyValues {
if len(m) == 0 {
return nil
}
res := make([]*third.KeyValues, 0, len(m))
for key := range m {
res = append(res, &third.KeyValues{
+35 -2
View File
@@ -16,7 +16,6 @@ package user
import (
"context"
pbuser "github.com/OpenIMSDK/protocol/user"
"github.com/OpenIMSDK/tools/utils"
@@ -44,7 +43,6 @@ func CallbackBeforeUpdateUserInfo(ctx context.Context, req *pbuser.UpdateUserInf
utils.NotNilReplace(&req.UserInfo.Nickname, resp.Nickname)
return nil
}
func CallbackAfterUpdateUserInfo(ctx context.Context, req *pbuser.UpdateUserInfoReq) error {
if !config.Config.Callback.CallbackAfterUpdateUserInfo.Enable {
return nil
@@ -61,6 +59,41 @@ func CallbackAfterUpdateUserInfo(ctx context.Context, req *pbuser.UpdateUserInfo
}
return nil
}
func CallbackBeforeUpdateUserInfoEx(ctx context.Context, req *pbuser.UpdateUserInfoExReq) error {
if !config.Config.Callback.CallbackBeforeUpdateUserInfoEx.Enable {
return nil
}
cbReq := &cbapi.CallbackBeforeUpdateUserInfoExReq{
CallbackCommand: cbapi.CallbackBeforeUpdateUserInfoExCommand,
UserID: req.UserInfo.UserID,
FaceURL: req.UserInfo.FaceURL,
Nickname: req.UserInfo.Nickname,
}
resp := &cbapi.CallbackBeforeUpdateUserInfoExResp{}
if err := http.CallBackPostReturn(ctx, config.Config.Callback.CallbackUrl, cbReq, resp, config.Config.Callback.CallbackBeforeUpdateUserInfoEx); err != nil {
return err
}
utils.NotNilReplace(req.UserInfo.FaceURL, resp.FaceURL)
utils.NotNilReplace(req.UserInfo.Ex, resp.Ex)
utils.NotNilReplace(req.UserInfo.Nickname, resp.Nickname)
return nil
}
func CallbackAfterUpdateUserInfoEx(ctx context.Context, req *pbuser.UpdateUserInfoExReq) error {
if !config.Config.Callback.CallbackAfterUpdateUserInfoEx.Enable {
return nil
}
cbReq := &cbapi.CallbackAfterUpdateUserInfoExReq{
CallbackCommand: cbapi.CallbackAfterUpdateUserInfoExCommand,
UserID: req.UserInfo.UserID,
FaceURL: req.UserInfo.FaceURL,
Nickname: req.UserInfo.Nickname,
}
resp := &cbapi.CallbackAfterUpdateUserInfoExResp{}
if err := http.CallBackPostReturn(ctx, config.Config.Callback.CallbackUrl, cbReq, resp, config.Config.Callback.CallbackBeforeUpdateUserInfoEx); err != nil {
return err
}
return nil
}
func CallbackBeforeUserRegister(ctx context.Context, req *pbuser.UserRegisterReq) error {
if !config.Config.Callback.CallbackBeforeUserRegister.Enable {
+229 -15
View File
@@ -17,6 +17,9 @@ package user
import (
"context"
"errors"
"github.com/OpenIMSDK/tools/pagination"
"github.com/openimsdk/open-im-server/v3/pkg/common/db/table/relation"
"math/rand"
"strings"
"time"
@@ -56,20 +59,9 @@ type userServer struct {
RegisterCenter registry.SvcDiscoveryRegistry
}
func (s *userServer) ProcessUserCommandAdd(ctx context.Context, req *pbuser.ProcessUserCommandAddReq) (*pbuser.ProcessUserCommandAddResp, error) {
return nil, errs.ErrInternalServer.Wrap("not implemented")
}
func (s *userServer) ProcessUserCommandUpdate(ctx context.Context, req *pbuser.ProcessUserCommandUpdateReq) (*pbuser.ProcessUserCommandUpdateResp, error) {
return nil, errs.ErrInternalServer.Wrap("not implemented")
}
func (s *userServer) ProcessUserCommandDelete(ctx context.Context, req *pbuser.ProcessUserCommandDeleteReq) (*pbuser.ProcessUserCommandDeleteResp, error) {
return nil, errs.ErrInternalServer.Wrap("not implemented")
}
func (s *userServer) ProcessUserCommandGet(ctx context.Context, req *pbuser.ProcessUserCommandGetReq) (*pbuser.ProcessUserCommandGetResp, error) {
return nil, errs.ErrInternalServer.Wrap("not implemented")
func (s *userServer) ProcessUserCommandGetAll(ctx context.Context, req *pbuser.ProcessUserCommandGetAllReq) (*pbuser.ProcessUserCommandGetAllResp, error) {
//TODO implement me
panic("implement me")
}
func Start(client registry.SvcDiscoveryRegistry, server *grpc.Server) error {
@@ -88,6 +80,12 @@ func Start(client registry.SvcDiscoveryRegistry, server *grpc.Server) error {
for k, v := range config.Config.Manager.UserID {
users = append(users, &tablerelation.UserModel{UserID: v, Nickname: config.Config.Manager.Nickname[k], AppMangerLevel: constant.AppAdmin})
}
if len(config.Config.IMAdmin.UserID) != len(config.Config.IMAdmin.Nickname) {
return errors.New("len(config.Config.AppNotificationAdmin.AppManagerUid) != len(config.Config.AppNotificationAdmin.Nickname)")
}
for k, v := range config.Config.IMAdmin.UserID {
users = append(users, &tablerelation.UserModel{UserID: v, Nickname: config.Config.IMAdmin.Nickname[k], AppMangerLevel: constant.AppNotificationAdmin})
}
userDB, err := mgo.NewUserMongo(mongo.GetDatabase())
if err != nil {
return err
@@ -157,7 +155,41 @@ func (s *userServer) UpdateUserInfo(ctx context.Context, req *pbuser.UpdateUserI
}
return resp, nil
}
func (s *userServer) UpdateUserInfoEx(ctx context.Context, req *pbuser.UpdateUserInfoExReq) (resp *pbuser.UpdateUserInfoExResp, err error) {
resp = &pbuser.UpdateUserInfoExResp{}
err = authverify.CheckAccessV3(ctx, req.UserInfo.UserID)
if err != nil {
return nil, err
}
if err = CallbackBeforeUpdateUserInfoEx(ctx, req); err != nil {
return nil, err
}
data := convert.UserPb2DBMapEx(req.UserInfo)
if err = s.UpdateByMap(ctx, req.UserInfo.UserID, data); err != nil {
return nil, err
}
_ = s.friendNotificationSender.UserInfoUpdatedNotification(ctx, req.UserInfo.UserID)
friends, err := s.friendRpcClient.GetFriendIDs(ctx, req.UserInfo.UserID)
if err != nil {
return nil, err
}
if req.UserInfo.Nickname != nil || req.UserInfo.FaceURL != nil {
if err := s.groupRpcClient.NotificationUserInfoUpdate(ctx, req.UserInfo.UserID); err != nil {
log.ZError(ctx, "NotificationUserInfoUpdate", err)
}
}
for _, friendID := range friends {
s.friendNotificationSender.FriendInfoUpdatedNotification(ctx, req.UserInfo.UserID, friendID)
}
if err := CallbackAfterUpdateUserInfoEx(ctx, req); err != nil {
return nil, err
}
if err := s.groupRpcClient.NotificationUserInfoUpdate(ctx, req.UserInfo.UserID); err != nil {
log.ZError(ctx, "NotificationUserInfoUpdate", err, "userID", req.UserInfo.UserID)
}
return resp, nil
}
func (s *userServer) SetGlobalRecvMessageOpt(ctx context.Context, req *pbuser.SetGlobalRecvMessageOptReq) (resp *pbuser.SetGlobalRecvMessageOptResp, err error) {
resp = &pbuser.SetGlobalRecvMessageOptResp{}
if _, err := s.FindWithError(ctx, []string{req.UserID}); err != nil {
@@ -202,7 +234,7 @@ func (s *userServer) AccountCheck(ctx context.Context, req *pbuser.AccountCheckR
}
func (s *userServer) GetPaginationUsers(ctx context.Context, req *pbuser.GetPaginationUsersReq) (resp *pbuser.GetPaginationUsersResp, err error) {
total, users, err := s.Page(ctx, req.Pagination)
total, users, err := s.PageFindUser(ctx, constant.IMOrdinaryUser, constant.AppOrdinaryUsers, req.Pagination)
if err != nil {
return nil, err
}
@@ -350,3 +382,185 @@ func (s *userServer) GetSubscribeUsersStatus(ctx context.Context,
}
return &pbuser.GetSubscribeUsersStatusResp{StatusList: onlineStatusList}, nil
}
// ProcessUserCommandAdd user general function add
func (s *userServer) ProcessUserCommandAdd(ctx context.Context, req *pbuser.ProcessUserCommandAddReq) (*pbuser.ProcessUserCommandAddResp, error) {
return &pbuser.ProcessUserCommandAddResp{}, nil
}
// ProcessUserCommandDelete user general function delete
func (s *userServer) ProcessUserCommandDelete(ctx context.Context, req *pbuser.ProcessUserCommandDeleteReq) (*pbuser.ProcessUserCommandDeleteResp, error) {
// Assuming you have a method in s.UserDatabase to delete a user command
err := s.UserDatabase.DeleteUserCommand(ctx, req.UserID, req.Type, req.Uuid)
if err != nil {
return nil, err
}
return &pbuser.ProcessUserCommandDeleteResp{}, nil
}
// ProcessUserCommandUpdate user general function update
func (s *userServer) ProcessUserCommandUpdate(ctx context.Context, req *pbuser.ProcessUserCommandUpdateReq) (*pbuser.ProcessUserCommandUpdateResp, error) {
return &pbuser.ProcessUserCommandUpdateResp{}, nil
}
func (s *userServer) ProcessUserCommandGet(ctx context.Context, req *pbuser.ProcessUserCommandGetReq) (*pbuser.ProcessUserCommandGetResp, error) {
// Return the response with the slice
return &pbuser.ProcessUserCommandGetResp{}, nil
}
func (s *userServer) AddNotificationAccount(ctx context.Context, req *pbuser.AddNotificationAccountReq) (*pbuser.AddNotificationAccountResp, error) {
if err := authverify.CheckIMAdmin(ctx); err != nil {
return nil, err
}
if req.UserID == "" {
for i := 0; i < 20; i++ {
userId := s.genUserID()
_, err := s.UserDatabase.FindWithError(ctx, []string{userId})
if err == nil {
continue
}
req.UserID = userId
break
}
if req.UserID == "" {
return nil, errs.ErrInternalServer.Wrap("gen user id failed")
}
} else {
_, err := s.UserDatabase.FindWithError(ctx, []string{req.UserID})
if err == nil {
return nil, errs.ErrArgs.Wrap("userID is used")
}
}
user := &tablerelation.UserModel{
UserID: req.UserID,
Nickname: req.NickName,
FaceURL: req.FaceURL,
CreateTime: time.Now(),
AppMangerLevel: constant.AppNotificationAdmin,
}
if err := s.UserDatabase.Create(ctx, []*tablerelation.UserModel{user}); err != nil {
return nil, err
}
return &pbuser.AddNotificationAccountResp{
UserID: req.UserID,
NickName: req.NickName,
FaceURL: req.FaceURL,
}, nil
}
func (s *userServer) UpdateNotificationAccountInfo(ctx context.Context, req *pbuser.UpdateNotificationAccountInfoReq) (*pbuser.UpdateNotificationAccountInfoResp, error) {
if err := authverify.CheckIMAdmin(ctx); err != nil {
return nil, err
}
if _, err := s.UserDatabase.FindWithError(ctx, []string{req.UserID}); err != nil {
return nil, errs.ErrArgs.Wrap()
}
user := map[string]interface{}{}
if req.NickName != "" {
user["nickname"] = req.NickName
}
if req.FaceURL != "" {
user["face_url"] = req.FaceURL
}
if err := s.UserDatabase.UpdateByMap(ctx, req.UserID, user); err != nil {
return nil, err
}
return &pbuser.UpdateNotificationAccountInfoResp{}, nil
}
func (s *userServer) SearchNotificationAccount(ctx context.Context, req *pbuser.SearchNotificationAccountReq) (*pbuser.SearchNotificationAccountResp, error) {
if err := authverify.CheckIMAdmin(ctx); err != nil {
return nil, err
}
var users []*relation.UserModel
var err error
if req.Keyword != "" {
users, err = s.UserDatabase.Find(ctx, []string{req.Keyword})
if err != nil {
return nil, err
}
resp := s.userModelToResp(users, req.Pagination)
if resp.Total != 0 {
return resp, nil
}
users, err = s.UserDatabase.FindByNickname(ctx, req.Keyword)
if err != nil {
return nil, err
}
resp = s.userModelToResp(users, req.Pagination)
return resp, nil
return resp, nil
}
users, err = s.UserDatabase.FindNotification(ctx, constant.AppNotificationAdmin)
if err != nil {
return nil, err
}
resp := s.userModelToResp(users, req.Pagination)
return resp, nil
}
func (s *userServer) GetNotificationAccount(ctx context.Context, req *pbuser.GetNotificationAccountReq) (*pbuser.GetNotificationAccountResp, error) {
if req.UserID == "" {
return nil, errs.ErrArgs.Wrap("userID is empty")
}
user, err := s.UserDatabase.GetUserByID(ctx, req.UserID)
if err != nil {
return nil, errs.ErrUserIDNotFound.Wrap()
}
if user.AppMangerLevel == constant.AppAdmin || user.AppMangerLevel == constant.AppNotificationAdmin {
return &pbuser.GetNotificationAccountResp{}, nil
}
return nil, errs.ErrNoPermission.Wrap("notification messages cannot be sent for this ID")
}
func (s *userServer) genUserID() string {
const l = 10
data := make([]byte, l)
rand.Read(data)
chars := []byte("0123456789")
for i := 0; i < len(data); i++ {
if i == 0 {
data[i] = chars[1:][data[i]%9]
} else {
data[i] = chars[data[i]%10]
}
}
return string(data)
}
func (s *userServer) userModelToResp(users []*relation.UserModel, pagination pagination.Pagination) *pbuser.SearchNotificationAccountResp {
accounts := make([]*pbuser.NotificationAccountInfo, 0)
var total int64
for _, v := range users {
if v.AppMangerLevel == constant.AppNotificationAdmin && !utils.IsContain(v.UserID, config.Config.IMAdmin.UserID) {
temp := &pbuser.NotificationAccountInfo{
UserID: v.UserID,
FaceURL: v.FaceURL,
NickName: v.Nickname,
}
accounts = append(accounts, temp)
total += 1
}
}
notificationAccounts := utils.Paginate(accounts, int(pagination.GetPageNumber()), int(pagination.GetShowNumber()))
return &pbuser.SearchNotificationAccountResp{Total: total, NotificationAccounts: notificationAccounts}
}
+24
View File
@@ -64,6 +64,30 @@ type SendMsgReq struct {
SendMsg
}
type GetConversationListReq struct {
// userID uniquely identifies the user.
UserID string `protobuf:"bytes,1,opt,name=userID,proto3" json:"userID,omitempty" binding:"required"`
// ConversationIDs contains a list of unique identifiers for conversations.
ConversationIDs []string `protobuf:"bytes,2,rep,name=conversationIDs,proto3" json:"conversationIDs,omitempty"`
}
type GetConversationListResp struct {
// ConversationElems is a map that associates conversation IDs with their respective details.
ConversationElems map[string]*ConversationElem `protobuf:"bytes,1,rep,name=conversationElems,proto3" json:"conversationElems,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
}
type ConversationElem struct {
// MaxSeq represents the maximum sequence number within the conversation.
MaxSeq int64 `protobuf:"varint,1,opt,name=maxSeq,proto3" json:"maxSeq,omitempty"`
// UnreadSeq represents the number of unread messages in the conversation.
UnreadSeq int64 `protobuf:"varint,2,opt,name=unreadSeq,proto3" json:"unreadSeq,omitempty"`
// LastSeqTime represents the timestamp of the last sequence in the conversation.
LastSeqTime int64 `protobuf:"varint,3,opt,name=LastSeqTime,proto3" json:"LastSeqTime,omitempty"`
}
// BatchSendMsgReq defines the structure for sending a message to multiple recipients.
type BatchSendMsgReq struct {
SendMsg
+1 -1
View File
@@ -87,7 +87,7 @@ type OANotificationElem struct {
NotificationType int32 `mapstructure:"notificationType" json:"notificationType" validate:"required"`
Text string `mapstructure:"text" json:"text" validate:"required"`
Url string `mapstructure:"url" json:"url"`
MixType int32 `mapstructure:"mixType" json:"mixType" validate:"required"`
MixType int32 `mapstructure:"mixType" json:"mixType"`
PictureElem *PictureElem `mapstructure:"pictureElem" json:"pictureElem"`
SoundElem *SoundElem `mapstructure:"soundElem" json:"soundElem"`
VideoElem *VideoElem `mapstructure:"videoElem" json:"videoElem"`
+17 -2
View File
@@ -38,6 +38,9 @@ func CheckAccessV3(ctx context.Context, ownerUserID string) (err error) {
if utils.IsContain(opUserID, config.Config.Manager.UserID) {
return nil
}
if utils.IsContain(opUserID, config.Config.IMAdmin.UserID) {
return nil
}
if opUserID == ownerUserID {
return nil
}
@@ -45,22 +48,34 @@ func CheckAccessV3(ctx context.Context, ownerUserID string) (err error) {
}
func IsAppManagerUid(ctx context.Context) bool {
return utils.IsContain(mcontext.GetOpUserID(ctx), config.Config.Manager.UserID)
return utils.IsContain(mcontext.GetOpUserID(ctx), config.Config.Manager.UserID) || utils.IsContain(mcontext.GetOpUserID(ctx), config.Config.IMAdmin.UserID)
}
func CheckAdmin(ctx context.Context) error {
if utils.IsContain(mcontext.GetOpUserID(ctx), config.Config.Manager.UserID) {
return nil
}
if utils.IsContain(mcontext.GetOpUserID(ctx), config.Config.IMAdmin.UserID) {
return nil
}
return errs.ErrNoPermission.Wrap(fmt.Sprintf("user %s is not admin userID", mcontext.GetOpUserID(ctx)))
}
func CheckIMAdmin(ctx context.Context) error {
if utils.IsContain(mcontext.GetOpUserID(ctx), config.Config.IMAdmin.UserID) {
return nil
}
if utils.IsContain(mcontext.GetOpUserID(ctx), config.Config.Manager.UserID) {
return nil
}
return errs.ErrNoPermission.Wrap(fmt.Sprintf("user %s is not CheckIMAdmin userID", mcontext.GetOpUserID(ctx)))
}
func ParseRedisInterfaceToken(redisToken any) (*tokenverify.Claims, error) {
return tokenverify.GetClaimFromToken(string(redisToken.([]uint8)), Secret())
}
func IsManagerUserID(opUserID string) bool {
return utils.IsContain(opUserID, config.Config.Manager.UserID)
return utils.IsContain(opUserID, config.Config.Manager.UserID) || utils.IsContain(opUserID, config.Config.IMAdmin.UserID)
}
func WsVerifyToken(token, userID string, platformID int) error {
+2
View File
@@ -37,6 +37,8 @@ const (
CallbackGroupMsgReadCommand = "callbackGroupMsgReadCommand"
CallbackMsgModifyCommand = "callbackMsgModifyCommand"
CallbackAfterUpdateUserInfoCommand = "callbackAfterUpdateUserInfoCommand"
CallbackAfterUpdateUserInfoExCommand = "callbackAfterUpdateUserInfoExCommand"
CallbackBeforeUpdateUserInfoExCommand = "callbackBeforeUpdateUserInfoExCommand"
CallbackBeforeUserRegisterCommand = "callbackBeforeUserRegisterCommand"
CallbackAfterUserRegisterCommand = "callbackAfterUserRegisterCommand"
CallbackTransferGroupOwnerAfter = "callbackTransferGroupOwnerAfter"
+1
View File
@@ -148,6 +148,7 @@ type CallbackJoinGroupReq struct {
GroupType string `json:"groupType"`
ApplyID string `json:"applyID"`
ReqMessage string `json:"reqMessage"`
Ex string `json:"ex"`
}
type CallbackJoinGroupResp struct {
+4 -3
View File
@@ -94,9 +94,10 @@ type CallbackGroupMsgReadResp struct {
type CallbackSingleMsgReadReq struct {
CallbackCommand `json:"callbackCommand"`
SendID string `json:"sendID"`
ReceiveID string `json:"receiveID"`
ContentType int64 `json:"contentType"`
UserID string `json:"userID"`
ConversationID string `json:"conversationID"`
ContentType int32 `json:"contentType"`
Seqs []int64 `json:"seqs"`
}
type CallbackSingleMsgReadResp struct {
+29 -1
View File
@@ -14,7 +14,10 @@
package callbackstruct
import "github.com/OpenIMSDK/protocol/sdkws"
import (
"github.com/OpenIMSDK/protocol/sdkws"
"github.com/OpenIMSDK/protocol/wrapperspb"
)
type CallbackBeforeUpdateUserInfoReq struct {
CallbackCommand `json:"callbackCommand"`
@@ -41,6 +44,31 @@ type CallbackAfterUpdateUserInfoResp struct {
CommonCallbackResp
}
type CallbackBeforeUpdateUserInfoExReq struct {
CallbackCommand `json:"callbackCommand"`
UserID string `json:"userID"`
Nickname *wrapperspb.StringValue `json:"nickName"`
FaceURL *wrapperspb.StringValue `json:"faceURL"`
Ex *wrapperspb.StringValue `json:"ex"`
}
type CallbackBeforeUpdateUserInfoExResp struct {
CommonCallbackResp
Nickname *wrapperspb.StringValue `json:"nickName"`
FaceURL *wrapperspb.StringValue `json:"faceURL"`
Ex *wrapperspb.StringValue `json:"ex"`
}
type CallbackAfterUpdateUserInfoExReq struct {
CallbackCommand `json:"callbackCommand"`
UserID string `json:"userID"`
Nickname *wrapperspb.StringValue `json:"nickName"`
FaceURL *wrapperspb.StringValue `json:"faceURL"`
Ex *wrapperspb.StringValue `json:"ex"`
}
type CallbackAfterUpdateUserInfoExResp struct {
CommonCallbackResp
}
type CallbackBeforeUserRegisterReq struct {
CallbackCommand `json:"callbackCommand"`
Secret string `json:"secret"`
+1 -1
View File
@@ -45,7 +45,7 @@ type CmdOpts struct {
func WithCronTaskLogName() func(*CmdOpts) {
return func(opts *CmdOpts) {
opts.loggerPrefixName = "OpenIM.CronTask.log.all"
opts.loggerPrefixName = "openim.crontask.log.all"
}
}
+8 -1
View File
@@ -236,6 +236,11 @@ type configStruct struct {
Nickname []string `yaml:"nickname"`
} `yaml:"manager"`
IMAdmin struct {
UserID []string `yaml:"userID"`
Nickname []string `yaml:"nickname"`
} `yaml:"im-admin"`
MultiLoginPolicy int `yaml:"multiLoginPolicy"`
ChatPersistenceMysql bool `yaml:"chatPersistenceMysql"`
MsgCacheTimeout int `yaml:"msgCacheTimeout"`
@@ -277,6 +282,8 @@ type configStruct struct {
CallbackBeforeSetFriendRemark CallBackConfig `yaml:"callbackBeforeSetFriendRemark"`
CallbackAfterSetFriendRemark CallBackConfig `yaml:"callbackAfterSetFriendRemark"`
CallbackBeforeUpdateUserInfo CallBackConfig `yaml:"beforeUpdateUserInfo"`
CallbackBeforeUpdateUserInfoEx CallBackConfig `yaml:"beforeUpdateUserInfoEx"`
CallbackAfterUpdateUserInfoEx CallBackConfig `yaml:"afterUpdateUserInfoEx"`
CallbackBeforeUserRegister CallBackConfig `yaml:"beforeUserRegister"`
CallbackAfterUpdateUserInfo CallBackConfig `yaml:"updateUserInfo"`
CallbackAfterUserRegister CallBackConfig `yaml:"afterUserRegister"`
@@ -307,7 +314,7 @@ type configStruct struct {
Prometheus struct {
Enable bool `yaml:"enable"`
PrometheusUrl string `yaml:"prometheusUrl"`
GrafanaUrl string `yaml:"grafanaUrl"`
ApiPrometheusPort []int `yaml:"apiPrometheusPort"`
UserPrometheusPort []int `yaml:"userPrometheusPort"`
FriendPrometheusPort []int `yaml:"friendPrometheusPort"`
+2 -1
View File
@@ -17,7 +17,6 @@ package convert
import (
"context"
"fmt"
"github.com/OpenIMSDK/protocol/sdkws"
"github.com/OpenIMSDK/tools/utils"
@@ -62,6 +61,7 @@ func FriendsDB2Pb(
for _, friendDB := range friendsDB {
userID = append(userID, friendDB.FriendUserID)
}
users, err := getUsers(ctx, userID)
if err != nil {
return nil, err
@@ -74,6 +74,7 @@ func FriendsDB2Pb(
friendPb.FriendUser.FaceURL = users[friend.FriendUserID].FaceURL
friendPb.FriendUser.Ex = users[friend.FriendUserID].Ex
friendPb.CreateTime = friend.CreateTime.Unix()
friendPb.IsPinned = friend.IsPinned
friendsPb = append(friendsPb, friendPb)
}
return friendsPb, nil
+22
View File
@@ -72,3 +72,25 @@ func UserPb2DBMap(user *sdkws.UserInfo) map[string]any {
}
return val
}
func UserPb2DBMapEx(user *sdkws.UserInfoWithEx) map[string]any {
if user == nil {
return nil
}
val := make(map[string]any)
// Map fields from UserInfoWithEx to val
if user.Nickname != nil {
val["nickname"] = user.Nickname.Value
}
if user.FaceURL != nil {
val["face_url"] = user.FaceURL.Value
}
if user.Ex != nil {
val["ex"] = user.Ex.Value
}
if user.GlobalRecvMsgOpt != nil {
val["global_recv_msg_opt"] = user.GlobalRecvMsgOpt.Value
}
return val
}
+14
View File
@@ -44,6 +44,8 @@ type FriendCache interface {
GetFriend(ctx context.Context, ownerUserID, friendUserID string) (friend *relationtb.FriendModel, err error)
// Delete friend when friend info changed
DelFriend(ownerUserID, friendUserID string) FriendCache
// Delete friends when friends' info changed
DelFriends(ownerUserID string, friendUserIDs []string) FriendCache
}
// FriendCacheRedis is an implementation of the FriendCache interface using Redis.
@@ -152,3 +154,15 @@ func (f *FriendCacheRedis) DelFriend(ownerUserID, friendUserID string) FriendCac
return newFriendCache
}
// DelFriends deletes multiple friend infos from the cache.
func (f *FriendCacheRedis) DelFriends(ownerUserID string, friendUserIDs []string) FriendCache {
newFriendCache := f.NewCache()
for _, friendUserID := range friendUserIDs {
key := f.getFriendKey(ownerUserID, friendUserID)
newFriendCache.AddKeys(key) // Assuming AddKeys marks the keys for deletion
}
return newFriendCache
}
+9 -1
View File
@@ -87,7 +87,15 @@ func NewRedis() (redis.UniversalClient, error) {
// overrideConfigFromEnv overrides configuration fields with environment variables if present.
func overrideConfigFromEnv() {
if envAddr := os.Getenv("REDIS_ADDRESS"); envAddr != "" {
config.Config.Redis.Address = strings.Split(envAddr, ",") // Assuming addresses are comma-separated
if envPort := os.Getenv("REDIS_PORT"); envPort != "" {
addresses := strings.Split(envAddr, ",")
for i, addr := range addresses {
addresses[i] = addr + ":" + envPort
}
config.Config.Redis.Address = addresses
} else {
config.Config.Redis.Address = strings.Split(envAddr, ",")
}
}
if envUser := os.Getenv("REDIS_USERNAME"); envUser != "" {
config.Config.Redis.Username = envUser
+1 -1
View File
@@ -279,7 +279,7 @@ func (c *conversationDatabase) CreateGroupChatConversation(ctx context.Context,
for _, v := range existConversationUserIDs {
cache = cache.DelConversations(v, conversationID)
}
return c.cache.ExecDel(ctx)
return cache.ExecDel(ctx)
})
}
+39 -12
View File
@@ -32,32 +32,50 @@ import (
)
type FriendDatabase interface {
// 检查user2是否在user1的好友列表中(inUser1Friends==true) 检查user1是否在user2的好友列表中(inUser2Friends==true)
// CheckIn checks if user2 is in user1's friend list (inUser1Friends==true) and if user1 is in user2's friend list (inUser2Friends==true)
CheckIn(ctx context.Context, user1, user2 string) (inUser1Friends bool, inUser2Friends bool, err error)
// 增加或者更新好友申请
// AddFriendRequest adds or updates a friend request
AddFriendRequest(ctx context.Context, fromUserID, toUserID string, reqMsg string, ex string) (err error)
// 先判断是否在好友表,如果在则不插入
// BecomeFriends first checks if the users are already in the friends table; if not, it inserts them as friends
BecomeFriends(ctx context.Context, ownerUserID string, friendUserIDs []string, addSource int32) (err error)
// 拒绝好友申请
// RefuseFriendRequest refuses a friend request
RefuseFriendRequest(ctx context.Context, friendRequest *relation.FriendRequestModel) (err error)
// 同意好友申请
// AgreeFriendRequest accepts a friend request
AgreeFriendRequest(ctx context.Context, friendRequest *relation.FriendRequestModel) (err error)
// 删除好友
// Delete removes a friend or friends from the owner's friend list
Delete(ctx context.Context, ownerUserID string, friendUserIDs []string) (err error)
// 更新好友备注
// UpdateRemark updates the remark for a friend
UpdateRemark(ctx context.Context, ownerUserID, friendUserID, remark string) (err error)
// 获取ownerUserID的好友列表
// PageOwnerFriends retrieves the friend list of ownerUserID with pagination
PageOwnerFriends(ctx context.Context, ownerUserID string, pagination pagination.Pagination) (total int64, friends []*relation.FriendModel, err error)
// friendUserID在哪些人的好友列表中
// PageInWhoseFriends finds the users who have friendUserID in their friend list with pagination
PageInWhoseFriends(ctx context.Context, friendUserID string, pagination pagination.Pagination) (total int64, friends []*relation.FriendModel, err error)
// 获取我发出去的好友申请
// PageFriendRequestFromMe retrieves the friend requests sent by the user with pagination
PageFriendRequestFromMe(ctx context.Context, userID string, pagination pagination.Pagination) (total int64, friends []*relation.FriendRequestModel, err error)
// 获取我收到的的好友申请
// PageFriendRequestToMe retrieves the friend requests received by the user with pagination
PageFriendRequestToMe(ctx context.Context, userID string, pagination pagination.Pagination) (total int64, friends []*relation.FriendRequestModel, err error)
// 获取某人指定好友的信息
// 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 []*relation.FriendModel, err error)
// FindFriendUserIDs retrieves the friend IDs of a user
FindFriendUserIDs(ctx context.Context, ownerUserID string) (friendUserIDs []string, err error)
// FindBothFriendRequests finds friend requests sent and received
FindBothFriendRequests(ctx context.Context, fromUserID, toUserID string) (friends []*relation.FriendRequestModel, err error)
// UpdateFriends updates fields for friends
UpdateFriends(ctx context.Context, ownerUserID string, friendUserIDs []string, val map[string]any) (err error)
}
type friendDatabase struct {
@@ -298,3 +316,12 @@ func (f *friendDatabase) FindFriendUserIDs(ctx context.Context, ownerUserID stri
func (f *friendDatabase) FindBothFriendRequests(ctx context.Context, fromUserID, toUserID string) (friends []*relation.FriendRequestModel, err error) {
return f.friendRequest.FindBothFriendRequests(ctx, fromUserID, toUserID)
}
func (f *friendDatabase) UpdateFriends(ctx context.Context, ownerUserID string, friendUserIDs []string, val map[string]any) (err error) {
if len(val) == 0 {
return nil
}
if err := f.friend.UpdateFriends(ctx, ownerUserID, friendUserIDs, val); err != nil {
return err
}
return f.cache.DelFriends(ownerUserID, friendUserIDs).ExecDel(ctx)
}
+1 -1
View File
@@ -197,7 +197,7 @@ func (g *groupDatabase) UpdateGroup(ctx context.Context, groupID string, data ma
func (g *groupDatabase) DismissGroup(ctx context.Context, groupID string, deleteMember bool) error {
return g.ctxTx.Transaction(ctx, func(ctx context.Context) error {
c := g.cache.NewCache()
if err := g.groupDB.UpdateState(ctx, groupID, constant.GroupStatusDismissed); err != nil {
if err := g.groupDB.UpdateStatus(ctx, groupID, constant.GroupStatusDismissed); err != nil {
return err
}
if deleteMember {
+16
View File
@@ -98,6 +98,7 @@ type CommonMsgDatabase interface {
SetSendMsgStatus(ctx context.Context, id string, status int32) error
GetSendMsgStatus(ctx context.Context, id string) (int32, error)
SearchMessage(ctx context.Context, req *pbmsg.SearchMessageReq) (total int32, msgData []*sdkws.MsgData, err error)
FindOneByDocIDs(ctx context.Context, docIDs []string, seqs map[string]int64) (map[string]*sdkws.MsgData, error)
// to mq
MsgToMQ(ctx context.Context, key string, msg2mq *sdkws.MsgData) error
@@ -1051,6 +1052,21 @@ func (db *commonMsgDatabase) SearchMessage(ctx context.Context, req *pbmsg.Searc
return total, totalMsgs, nil
}
func (db *commonMsgDatabase) FindOneByDocIDs(ctx context.Context, conversationIDs []string, seqs map[string]int64) (map[string]*sdkws.MsgData, error) {
totalMsgs := make(map[string]*sdkws.MsgData)
for _, conversationID := range conversationIDs {
seq := seqs[conversationID]
docID := db.msg.GetDocID(conversationID, seq)
msgs, err := db.msgDocDatabase.FindOneByDocID(ctx, docID)
if err != nil {
return nil, err
}
index := db.msg.GetMsgIndex(seq)
totalMsgs[conversationID] = convert.MsgDB2Pb(msgs.Msg[index].Msg)
}
return totalMsgs, nil
}
func (db *commonMsgDatabase) ConvertMsgsDocLen(ctx context.Context, conversationIDs []string) {
db.msgDocDatabase.ConvertMsgsDocLen(ctx, conversationIDs)
}
+10
View File
@@ -35,6 +35,8 @@ type S3Database interface {
CompleteMultipartUpload(ctx context.Context, uploadID string, parts []string) (*cont.UploadResult, error)
AccessURL(ctx context.Context, name string, expire time.Duration, opt *s3.AccessURLOption) (time.Time, string, error)
SetObject(ctx context.Context, info *relation.ObjectModel) error
StatObject(ctx context.Context, name string) (*s3.ObjectInfo, error)
FormData(ctx context.Context, name string, size int64, contentType string, duration time.Duration) (*s3.FormData, error)
}
func NewS3Database(rdb redis.UniversalClient, s3 s3.Interface, obj relation.ObjectInfoModelInterface) S3Database {
@@ -100,3 +102,11 @@ func (s *s3Database) AccessURL(ctx context.Context, name string, expire time.Dur
}
return expireTime, rawURL, nil
}
func (s *s3Database) StatObject(ctx context.Context, name string) (*s3.ObjectInfo, error) {
return s.s3.StatObject(ctx, name)
}
func (s *s3Database) FormData(ctx context.Context, name string, size int64, contentType string, duration time.Duration) (*s3.FormData, error) {
return s.s3.FormData(ctx, name, size, contentType, duration)
}
+45
View File
@@ -38,18 +38,26 @@ type UserDatabase interface {
FindWithError(ctx context.Context, userIDs []string) (users []*relation.UserModel, err error)
// Find Get the information of the specified user If the userID is not found, no error will be returned
Find(ctx context.Context, userIDs []string) (users []*relation.UserModel, err error)
// Find userInfo By Nickname
FindByNickname(ctx context.Context, nickname string) (users []*relation.UserModel, err error)
// Find notificationAccounts
FindNotification(ctx context.Context, level int64) (users []*relation.UserModel, err error)
// Create Insert multiple external guarantees that the userID is not repeated and does not exist in the db
Create(ctx context.Context, users []*relation.UserModel) (err error)
// Update update (non-zero value) external guarantee userID exists
//Update(ctx context.Context, user *relation.UserModel) (err error)
// UpdateByMap update (zero value) external guarantee userID exists
UpdateByMap(ctx context.Context, userID string, args map[string]any) (err error)
// FindUser
PageFindUser(ctx context.Context, level1 int64, level2 int64, pagination pagination.Pagination) (count int64, users []*relation.UserModel, err error)
// Page If not found, no error is returned
Page(ctx context.Context, pagination pagination.Pagination) (count int64, users []*relation.UserModel, err error)
// IsExist true as long as one exists
IsExist(ctx context.Context, userIDs []string) (exist bool, err error)
// GetAllUserID Get all user IDs
GetAllUserID(ctx context.Context, pagination pagination.Pagination) (int64, []string, error)
// Get user by userID
GetUserByID(ctx context.Context, userID string) (user *relation.UserModel, err error)
// InitOnce Inside the function, first query whether it exists in the db, if it exists, do nothing; if it does not exist, insert it
InitOnce(ctx context.Context, users []*relation.UserModel) (err error)
// CountTotal Get the total number of users
@@ -68,6 +76,12 @@ type UserDatabase interface {
GetUserStatus(ctx context.Context, userIDs []string) ([]*user.OnlineStatus, error)
// SetUserStatus Set the user status and store the user status in redis
SetUserStatus(ctx context.Context, userID string, status, platformID int32) error
//CRUD user command
AddUserCommand(ctx context.Context, userID string, Type int32, UUID string, value string) error
DeleteUserCommand(ctx context.Context, userID string, Type int32, UUID string) error
UpdateUserCommand(ctx context.Context, userID string, Type int32, UUID string, value string) error
GetUserCommands(ctx context.Context, userID string, Type int32) ([]*user.CommandInfoResp, error)
}
type userDatabase struct {
@@ -125,6 +139,16 @@ func (u *userDatabase) Find(ctx context.Context, userIDs []string) (users []*rel
return u.cache.GetUsersInfo(ctx, userIDs)
}
// Find userInfo By Nickname
func (u *userDatabase) FindByNickname(ctx context.Context, nickname string) (users []*relation.UserModel, err error) {
return u.userDB.TakeByNickname(ctx, nickname)
}
// Find notificationAccouts
func (u *userDatabase) FindNotification(ctx context.Context, level int64) (users []*relation.UserModel, err error) {
return u.userDB.TakeNotification(ctx, level)
}
// Create Insert multiple external guarantees that the userID is not repeated and does not exist in the db.
func (u *userDatabase) Create(ctx context.Context, users []*relation.UserModel) (err error) {
return u.tx.Transaction(ctx, func(ctx context.Context) error {
@@ -160,6 +184,10 @@ func (u *userDatabase) Page(ctx context.Context, pagination pagination.Paginatio
return u.userDB.Page(ctx, pagination)
}
func (u *userDatabase) PageFindUser(ctx context.Context, level1 int64, level2 int64, pagination pagination.Pagination) (count int64, users []*relation.UserModel, err error) {
return u.userDB.PageFindUser(ctx, level1, level2, pagination)
}
// IsExist Does userIDs exist? As long as there is one, it will be true.
func (u *userDatabase) IsExist(ctx context.Context, userIDs []string) (exist bool, err error) {
users, err := u.userDB.Find(ctx, userIDs)
@@ -177,6 +205,10 @@ func (u *userDatabase) GetAllUserID(ctx context.Context, pagination pagination.P
return u.userDB.GetAllUserID(ctx, pagination)
}
func (u *userDatabase) GetUserByID(ctx context.Context, userID string) (user *relation.UserModel, err error) {
return u.userDB.Take(ctx, userID)
}
// CountTotal Get the total number of users.
func (u *userDatabase) CountTotal(ctx context.Context, before *time.Time) (count int64, err error) {
return u.userDB.CountTotal(ctx, before)
@@ -227,3 +259,16 @@ func (u *userDatabase) GetUserStatus(ctx context.Context, userIDs []string) ([]*
func (u *userDatabase) SetUserStatus(ctx context.Context, userID string, status, platformID int32) error {
return u.cache.SetUserStatus(ctx, userID, status, platformID)
}
func (u *userDatabase) AddUserCommand(ctx context.Context, userID string, Type int32, UUID string, value string) error {
return u.userDB.AddUserCommand(ctx, userID, Type, UUID, value)
}
func (u *userDatabase) DeleteUserCommand(ctx context.Context, userID string, Type int32, UUID string) error {
return u.userDB.DeleteUserCommand(ctx, userID, Type, UUID)
}
func (u *userDatabase) UpdateUserCommand(ctx context.Context, userID string, Type int32, UUID string, value string) error {
return u.userDB.UpdateUserCommand(ctx, userID, Type, UUID, value)
}
func (u *userDatabase) GetUserCommands(ctx context.Context, userID string, Type int32) ([]*user.CommandInfoResp, error) {
commands, err := u.userDB.GetUserCommand(ctx, userID, Type)
return commands, err
}
+2 -1
View File
@@ -114,7 +114,8 @@ func (c *ConversationMgo) GetAllConversationIDs(ctx context.Context) ([]string,
func (c *ConversationMgo) GetAllConversationIDsNumber(ctx context.Context) (int64, error) {
counts, err := mgoutil.Aggregate[int64](ctx, c.coll, []bson.M{
{"$group": bson.M{"_id": "$conversation_id"}},
{"$project": bson.M{"_id": 0, "conversation_id": "$_id"}},
{"$group": bson.M{"_id": nil, "count": bson.M{"$sum": 1}}},
{"$project": bson.M{"_id": 0}},
})
if err != nil {
return 0, err
+20 -1
View File
@@ -16,7 +16,6 @@ package mgo
import (
"context"
"github.com/OpenIMSDK/tools/mgoutil"
"github.com/OpenIMSDK/tools/pagination"
"go.mongodb.org/mongo-driver/mongo/options"
@@ -143,3 +142,23 @@ func (f *FriendMgo) FindFriendUserIDs(ctx context.Context, ownerUserID string) (
filter := bson.M{"owner_user_id": ownerUserID}
return mgoutil.Find[string](ctx, f.coll, filter, options.Find().SetProjection(bson.M{"_id": 0, "friend_user_id": 1}))
}
func (f *FriendMgo) UpdateFriends(ctx context.Context, ownerUserID string, friendUserIDs []string, val map[string]any) error {
// Ensure there are IDs to update
if len(friendUserIDs) == 0 {
return nil // Or return an error if you expect there to always be IDs
}
// Create a filter to match documents with the specified ownerUserID and any of the friendUserIDs
filter := bson.M{
"owner_user_id": ownerUserID,
"friend_user_id": bson.M{"$in": friendUserIDs},
}
// Create an update document
update := bson.M{"$set": val}
// Perform the update operation for all matching documents
_, err := mgoutil.UpdateMany(ctx, f.coll, filter, update)
return err
}
+2 -2
View File
@@ -49,8 +49,8 @@ func (g *GroupMgo) Create(ctx context.Context, groups []*relation.GroupModel) (e
return mgoutil.InsertMany(ctx, g.coll, groups)
}
func (g *GroupMgo) UpdateState(ctx context.Context, groupID string, state int32) (err error) {
return g.UpdateMap(ctx, groupID, map[string]any{"state": state})
func (g *GroupMgo) UpdateStatus(ctx context.Context, groupID string, status int32) (err error) {
return g.UpdateMap(ctx, groupID, map[string]any{"status": status})
}
func (g *GroupMgo) UpdateMap(ctx context.Context, groupID string, args map[string]any) (err error) {
+7 -3
View File
@@ -51,7 +51,11 @@ func (g *GroupMemberMgo) Create(ctx context.Context, groupMembers []*relation.Gr
}
func (g *GroupMemberMgo) Delete(ctx context.Context, groupID string, userIDs []string) (err error) {
return mgoutil.DeleteMany(ctx, g.coll, bson.M{"group_id": groupID, "user_id": bson.M{"$in": userIDs}})
filter := bson.M{"group_id": groupID}
if len(userIDs) > 0 {
filter["user_id"] = bson.M{"$in": userIDs}
}
return mgoutil.DeleteMany(ctx, g.coll, filter)
}
func (g *GroupMemberMgo) UpdateRoleLevel(ctx context.Context, groupID string, userID string, roleLevel int32) error {
@@ -84,8 +88,8 @@ func (g *GroupMemberMgo) FindRoleLevelUserIDs(ctx context.Context, groupID strin
}
func (g *GroupMemberMgo) SearchMember(ctx context.Context, keyword string, groupID string, pagination pagination.Pagination) (total int64, groupList []*relation.GroupMemberModel, err error) {
//TODO implement me
panic("implement me")
filter := bson.M{"group_id": groupID, "nickname": bson.M{"$regex": keyword}}
return mgoutil.FindPage[*relation.GroupMemberModel](ctx, g.coll, filter, pagination)
}
func (g *GroupMemberMgo) FindUserJoinedGroupID(ctx context.Context, userID string) (groupIDs []string, err error) {
+94 -2
View File
@@ -16,6 +16,7 @@ package mgo
import (
"context"
"github.com/OpenIMSDK/protocol/user"
"time"
"github.com/OpenIMSDK/tools/mgoutil"
@@ -64,12 +65,31 @@ func (u *UserMgo) Take(ctx context.Context, userID string) (user *relation.UserM
return mgoutil.FindOne[*relation.UserModel](ctx, u.coll, bson.M{"user_id": userID})
}
func (u *UserMgo) TakeNotification(ctx context.Context, level int64) (user []*relation.UserModel, err error) {
return mgoutil.Find[*relation.UserModel](ctx, u.coll, bson.M{"app_manger_level": level})
}
func (u *UserMgo) TakeByNickname(ctx context.Context, nickname string) (user []*relation.UserModel, err error) {
return mgoutil.Find[*relation.UserModel](ctx, u.coll, bson.M{"nickname": nickname})
}
func (u *UserMgo) Page(ctx context.Context, pagination pagination.Pagination) (count int64, users []*relation.UserModel, err error) {
return mgoutil.FindPage[*relation.UserModel](ctx, u.coll, bson.M{}, pagination)
}
func (u *UserMgo) PageFindUser(ctx context.Context, level1 int64, level2 int64, pagination pagination.Pagination) (count int64, users []*relation.UserModel, err error) {
query := bson.M{
"$or": []bson.M{
{"app_manger_level": level1},
{"app_manger_level": level2},
},
}
return mgoutil.FindPage[*relation.UserModel](ctx, u.coll, query, pagination)
}
func (u *UserMgo) GetAllUserID(ctx context.Context, pagination pagination.Pagination) (int64, []string, error) {
return mgoutil.FindPage[string](ctx, u.coll, bson.M{}, pagination, options.Find().SetProjection(bson.M{"user_id": 1}))
return mgoutil.FindPage[string](ctx, u.coll, bson.M{}, pagination, options.Find().SetProjection(bson.M{"_id": 0, "user_id": 1}))
}
func (u *UserMgo) Exist(ctx context.Context, userID string) (exist bool, err error) {
@@ -77,7 +97,7 @@ func (u *UserMgo) Exist(ctx context.Context, userID string) (exist bool, err err
}
func (u *UserMgo) GetUserGlobalRecvMsgOpt(ctx context.Context, userID string) (opt int, err error) {
return mgoutil.FindOne[int](ctx, u.coll, bson.M{"user_id": userID}, options.FindOne().SetProjection(bson.M{"global_recv_msg_opt": 1}))
return mgoutil.FindOne[int](ctx, u.coll, bson.M{"user_id": userID}, options.FindOne().SetProjection(bson.M{"_id": 0, "global_recv_msg_opt": 1}))
}
func (u *UserMgo) CountTotal(ctx context.Context, before *time.Time) (count int64, err error) {
@@ -87,6 +107,78 @@ func (u *UserMgo) CountTotal(ctx context.Context, before *time.Time) (count int6
return mgoutil.Count(ctx, u.coll, bson.M{"create_time": bson.M{"$lt": before}})
}
func (u *UserMgo) AddUserCommand(ctx context.Context, userID string, Type int32, UUID string, value string) error {
collection := u.coll.Database().Collection("userCommands")
// Create a new document instead of updating an existing one
doc := bson.M{
"userID": userID,
"type": Type,
"uuid": UUID,
"createTime": time.Now().Unix(), // assuming you want the creation time in Unix timestamp
"value": value,
}
_, err := collection.InsertOne(ctx, doc)
return err
}
func (u *UserMgo) DeleteUserCommand(ctx context.Context, userID string, Type int32, UUID string) error {
collection := u.coll.Database().Collection("userCommands")
filter := bson.M{"userID": userID, "type": Type, "uuid": UUID}
_, err := collection.DeleteOne(ctx, filter)
return err
}
func (u *UserMgo) UpdateUserCommand(ctx context.Context, userID string, Type int32, UUID string, value string) error {
collection := u.coll.Database().Collection("userCommands")
filter := bson.M{"userID": userID, "type": Type, "uuid": UUID}
update := bson.M{"$set": bson.M{"value": value}}
_, err := collection.UpdateOne(ctx, filter, update)
return err
}
func (u *UserMgo) GetUserCommand(ctx context.Context, userID string, Type int32) ([]*user.CommandInfoResp, error) {
collection := u.coll.Database().Collection("userCommands")
filter := bson.M{"userID": userID, "type": Type}
cursor, err := collection.Find(ctx, filter)
if err != nil {
return nil, err
}
defer cursor.Close(ctx)
// Initialize commands as a slice of pointers
commands := []*user.CommandInfoResp{}
for cursor.Next(ctx) {
var document struct {
UUID string `bson:"uuid"`
Value string `bson:"value"`
CreateTime int64 `bson:"createTime"`
}
if err := cursor.Decode(&document); err != nil {
return nil, err
}
commandInfo := &user.CommandInfoResp{ // Change here: use a pointer to the struct
Uuid: document.UUID,
Value: document.Value,
CreateTime: document.CreateTime,
}
commands = append(commands, commandInfo)
}
if err := cursor.Err(); err != nil {
return nil, err
}
return commands, nil
}
func (u *UserMgo) CountRangeEverydayTotal(ctx context.Context, start time.Time, end time.Time) (map[string]int64, error) {
pipeline := bson.A{
bson.M{
+1
View File
@@ -17,6 +17,7 @@ package cont
const (
hashPath = "openim/data/hash/"
tempPath = "openim/temp/"
DirectPath = "openim/direct"
UploadTypeMultipart = 1 // 分片上传
UploadTypePresigned = 2 // 预签名上传
partSeparator = ","
+4
View File
@@ -279,3 +279,7 @@ func (c *Controller) AccessURL(ctx context.Context, name string, expire time.Dur
}
return c.impl.AccessURL(ctx, name, expire, opt)
}
func (c *Controller) FormData(ctx context.Context, name string, size int64, contentType string, duration time.Duration) (*s3.FormData, error) {
return c.impl.FormData(ctx, name, size, contentType, duration)
}
+69
View File
@@ -16,6 +16,11 @@ package cos
import (
"context"
"crypto/hmac"
"crypto/sha1"
"encoding/base64"
"encoding/hex"
"encoding/json"
"errors"
"fmt"
"net/http"
@@ -44,6 +49,8 @@ const (
imageWebp = "webp"
)
const successCode = http.StatusOK
const (
videoSnapshotImagePng = "png"
videoSnapshotImageJpg = "jpg"
@@ -326,3 +333,65 @@ func (c *Cos) getPresignedURL(ctx context.Context, name string, expire time.Dura
}
return c.client.Object.GetObjectURL(name), nil
}
func (c *Cos) FormData(ctx context.Context, name string, size int64, contentType string, duration time.Duration) (*s3.FormData, error) {
// https://cloud.tencent.com/document/product/436/14690
now := time.Now()
expiration := now.Add(duration)
keyTime := fmt.Sprintf("%d;%d", now.Unix(), expiration.Unix())
conditions := []any{
map[string]string{"q-sign-algorithm": "sha1"},
map[string]string{"q-ak": c.credential.SecretID},
map[string]string{"q-sign-time": keyTime},
map[string]string{"key": name},
}
if contentType != "" {
conditions = append(conditions, map[string]string{"Content-Type": contentType})
}
policy := map[string]any{
"expiration": expiration.Format("2006-01-02T15:04:05.000Z"),
"conditions": conditions,
}
policyJson, err := json.Marshal(policy)
if err != nil {
return nil, err
}
signKey := hmacSha1val(c.credential.SecretKey, keyTime)
strToSign := sha1val(string(policyJson))
signature := hmacSha1val(signKey, strToSign)
fd := &s3.FormData{
URL: c.client.BaseURL.BucketURL.String(),
File: "file",
Expires: expiration,
FormData: map[string]string{
"policy": base64.StdEncoding.EncodeToString(policyJson),
"q-sign-algorithm": "sha1",
"q-ak": c.credential.SecretID,
"q-key-time": keyTime,
"q-signature": signature,
"key": name,
"success_action_status": strconv.Itoa(successCode),
},
SuccessCodes: []int{successCode},
}
if contentType != "" {
fd.FormData["Content-Type"] = contentType
}
if c.credential.SessionToken != "" {
fd.FormData["x-cos-security-token"] = c.credential.SessionToken
}
return fd, nil
}
func hmacSha1val(key, msg string) string {
v := hmac.New(sha1.New, []byte(key))
v.Write([]byte(msg))
return hex.EncodeToString(v.Sum(nil))
}
func sha1val(msg string) string {
sha1Hash := sha1.New()
sha1Hash.Write([]byte(msg))
return hex.EncodeToString(sha1Hash.Sum(nil))
}
+50
View File
@@ -57,6 +57,8 @@ const (
imageThumbnailPath = "openim/thumbnail"
)
const successCode = http.StatusOK
func NewMinio(cache cache.MinioCache) (s3.Interface, error) {
u, err := url.Parse(config.Config.Object.Minio.Endpoint)
if err != nil {
@@ -441,3 +443,51 @@ func (m *Minio) getObjectData(ctx context.Context, name string, limit int64) ([]
}
return io.ReadAll(io.LimitReader(object, limit))
}
func (m *Minio) FormData(ctx context.Context, name string, size int64, contentType string, duration time.Duration) (*s3.FormData, error) {
if err := m.initMinio(ctx); err != nil {
return nil, err
}
policy := minio.NewPostPolicy()
if err := policy.SetKey(name); err != nil {
return nil, err
}
expires := time.Now().Add(duration)
if err := policy.SetExpires(expires); err != nil {
return nil, err
}
if size > 0 {
if err := policy.SetContentLengthRange(0, size); err != nil {
return nil, err
}
}
if err := policy.SetSuccessStatusAction(strconv.Itoa(successCode)); err != nil {
return nil, err
}
if contentType != "" {
if err := policy.SetContentType(contentType); err != nil {
return nil, err
}
}
if err := policy.SetBucket(m.bucket); err != nil {
return nil, err
}
u, fd, err := m.core.PresignedPostPolicy(ctx, policy)
if err != nil {
return nil, err
}
sign, err := url.Parse(m.signEndpoint)
if err != nil {
return nil, err
}
u.Scheme = sign.Scheme
u.Host = sign.Host
return &s3.FormData{
URL: u.String(),
File: "file",
Header: nil,
FormData: fd,
Expires: expires,
SuccessCodes: []int{successCode},
}, nil
}
+49
View File
@@ -16,8 +16,13 @@ package oss
import (
"context"
"crypto/hmac"
"crypto/sha1"
"encoding/base64"
"encoding/json"
"errors"
"fmt"
"io"
"net/http"
"net/url"
"reflect"
@@ -45,6 +50,8 @@ const (
imageWebp = "webp"
)
const successCode = http.StatusOK
const (
videoSnapshotImagePng = "png"
videoSnapshotImageJpg = "jpg"
@@ -327,3 +334,45 @@ func (o *OSS) AccessURL(ctx context.Context, name string, expire time.Duration,
params := getURLParams(*o.bucket.Client.Conn, rawParams)
return getURL(o.um, o.bucket.BucketName, name, params).String(), nil
}
func (o *OSS) FormData(ctx context.Context, name string, size int64, contentType string, duration time.Duration) (*s3.FormData, error) {
// https://help.aliyun.com/zh/oss/developer-reference/postobject?spm=a2c4g.11186623.0.0.1cb83cebkP55nn
expires := time.Now().Add(duration)
conditions := []any{
map[string]string{"bucket": o.bucket.BucketName},
map[string]string{"key": name},
}
if size > 0 {
conditions = append(conditions, []any{"content-length-range", 0, size})
}
policy := map[string]any{
"expiration": expires.Format("2006-01-02T15:04:05.000Z"),
"conditions": conditions,
}
policyJson, err := json.Marshal(policy)
if err != nil {
return nil, err
}
policyStr := base64.StdEncoding.EncodeToString(policyJson)
h := hmac.New(sha1.New, []byte(o.credentials.GetAccessKeySecret()))
if _, err := io.WriteString(h, policyStr); err != nil {
return nil, err
}
fd := &s3.FormData{
URL: o.bucketURL,
File: "file",
Expires: expires,
FormData: map[string]string{
"key": name,
"policy": policyStr,
"OSSAccessKeyId": o.credentials.GetAccessKeyID(),
"success_action_status": strconv.Itoa(successCode),
"signature": base64.StdEncoding.EncodeToString(h.Sum(nil)),
},
SuccessCodes: []int{successCode},
}
if contentType != "" {
fd.FormData["x-oss-content-type"] = contentType
}
return fd, nil
}
+11
View File
@@ -74,6 +74,15 @@ type CopyObjectInfo struct {
ETag string `json:"etag"`
}
type FormData struct {
URL string `json:"url"`
File string `json:"file"`
Header http.Header `json:"header"`
FormData map[string]string `json:"form"`
Expires time.Time `json:"expires"`
SuccessCodes []int `json:"successActionStatus"`
}
type SignPart struct {
PartNumber int `json:"partNumber"`
URL string `json:"url"`
@@ -152,4 +161,6 @@ type Interface interface {
ListUploadedParts(ctx context.Context, uploadID string, name string, partNumberMarker int, maxParts int) (*ListUploadedPartsResult, error)
AccessURL(ctx context.Context, name string, expire time.Duration, opt *AccessURLOption) (string, error)
FormData(ctx context.Context, name string, size int64, contentType string, duration time.Duration) (*FormData, error)
}
+3
View File
@@ -30,6 +30,7 @@ type FriendModel struct {
AddSource int32 `bson:"add_source"`
OperatorUserID string `bson:"operator_user_id"`
Ex string `bson:"ex"`
IsPinned bool `bson:"is_pinned"`
}
// FriendModelInterface defines the operations for managing friends in MongoDB.
@@ -56,4 +57,6 @@ type FriendModelInterface interface {
FindInWhoseFriends(ctx context.Context, friendUserID string, pagination pagination.Pagination) (total int64, friends []*FriendModel, err error)
// FindFriendUserIDs retrieves a list of friend user IDs for a given owner.
FindFriendUserIDs(ctx context.Context, ownerUserID string) (friendUserIDs []string, err error)
// UpdateFriends update friends' fields
UpdateFriends(ctx context.Context, ownerUserID string, friendUserIDs []string, val map[string]any) (err error)
}
+1 -1
View File
@@ -42,7 +42,7 @@ type GroupModel struct {
type GroupModelInterface interface {
Create(ctx context.Context, groups []*GroupModel) (err error)
UpdateMap(ctx context.Context, groupID string, args map[string]any) (err error)
UpdateState(ctx context.Context, groupID string, state int32) (err error)
UpdateStatus(ctx context.Context, groupID string, status int32) (err error)
Find(ctx context.Context, groupIDs []string) (groups []*GroupModel, err error)
Take(ctx context.Context, groupID string) (group *GroupModel, err error)
Search(ctx context.Context, keyword string, pagination pagination.Pagination) (total int64, groups []*GroupModel, err error)
+9
View File
@@ -16,6 +16,7 @@ package relation
import (
"context"
"github.com/OpenIMSDK/protocol/user"
"time"
"github.com/OpenIMSDK/tools/pagination"
@@ -52,7 +53,10 @@ type UserModelInterface interface {
UpdateByMap(ctx context.Context, userID string, args map[string]any) (err error)
Find(ctx context.Context, userIDs []string) (users []*UserModel, err error)
Take(ctx context.Context, userID string) (user *UserModel, err error)
TakeNotification(ctx context.Context, level int64) (user []*UserModel, err error)
TakeByNickname(ctx context.Context, nickname string) (user []*UserModel, err error)
Page(ctx context.Context, pagination pagination.Pagination) (count int64, users []*UserModel, err error)
PageFindUser(ctx context.Context, level1 int64, level2 int64, pagination pagination.Pagination) (count int64, users []*UserModel, err error)
Exist(ctx context.Context, userID string) (exist bool, err error)
GetAllUserID(ctx context.Context, pagination pagination.Pagination) (count int64, userIDs []string, err error)
GetUserGlobalRecvMsgOpt(ctx context.Context, userID string) (opt int, err error)
@@ -60,4 +64,9 @@ type UserModelInterface interface {
CountTotal(ctx context.Context, before *time.Time) (count int64, err error)
// 获取范围内用户增量
CountRangeEverydayTotal(ctx context.Context, start time.Time, end time.Time) (map[string]int64, error)
//CRUD user command
AddUserCommand(ctx context.Context, userID string, Type int32, UUID string, value string) error
DeleteUserCommand(ctx context.Context, userID string, Type int32, UUID string) error
UpdateUserCommand(ctx context.Context, userID string, Type int32, UUID string, value string) error
GetUserCommand(ctx context.Context, userID string, Type int32) ([]*user.CommandInfoResp, error)
}
+8 -4
View File
@@ -74,8 +74,12 @@ func buildMongoURI() string {
return uri
}
username := os.Getenv("MONGO_USERNAME")
password := os.Getenv("MONGO_PASSWORD")
if config.Config.Mongo.Uri != "" {
return config.Config.Mongo.Uri
}
username := os.Getenv("MONGO_OPENIM_USERNAME")
password := os.Getenv("MONGO_OPENIM_PASSWORD")
address := os.Getenv("MONGO_ADDRESS")
port := os.Getenv("MONGO_PORT")
database := os.Getenv("MONGO_DATABASE")
@@ -99,9 +103,9 @@ func buildMongoURI() string {
maxPoolSize = fmt.Sprint(config.Config.Mongo.MaxPoolSize)
}
uriFormat := "mongodb://%s/%s?maxPoolSize=%s&authSource=admin"
uriFormat := "mongodb://%s/%s?maxPoolSize=%s"
if username != "" && password != "" {
uriFormat = "mongodb://%s:%s@%s/%s?maxPoolSize=%s&authSource=admin"
uriFormat = "mongodb://%s:%s@%s/%s?maxPoolSize=%s"
return fmt.Sprintf(uriFormat, username, password, address, database, maxPoolSize)
}
return fmt.Sprintf(uriFormat, address, database, maxPoolSize)
@@ -24,7 +24,8 @@ import (
func setupTestEnvironment() {
os.Setenv("ZOOKEEPER_SCHEMA", "openim")
os.Setenv("ZOOKEEPER_ADDRESS", "172.28.0.1:12181")
os.Setenv("ZOOKEEPER_ADDRESS", "172.28.0.1")
os.Setenv("ZOOKEEPER_PORT", "12181")
os.Setenv("ZOOKEEPER_USERNAME", "")
os.Setenv("ZOOKEEPER_PASSWORD", "")
}
@@ -18,6 +18,7 @@ import (
"context"
"errors"
"fmt"
"github.com/stathat/consistent"
"os"
"strconv"
"strings"
@@ -31,51 +32,57 @@ import (
// K8sDR represents the Kubernetes service discovery and registration client.
type K8sDR struct {
options []grpc.DialOption
rpcRegisterAddr string
options []grpc.DialOption
rpcRegisterAddr string
gatewayHostConsistent *consistent.Consistent
}
// NewK8sDiscoveryRegister creates a new instance of K8sDR for Kubernetes service discovery and registration.
func NewK8sDiscoveryRegister() (discoveryregistry.SvcDiscoveryRegistry, error) {
return &K8sDR{}, nil
gatewayConsistent := consistent.New()
gatewayHosts := getMsgGatewayHost(context.Background())
for _, v := range gatewayHosts {
gatewayConsistent.Add(v)
}
return &K8sDR{gatewayHostConsistent: gatewayConsistent}, nil
}
// Register registers a service with Kubernetes.
func (cli *K8sDR) Register(serviceName, host string, port int, opts ...grpc.DialOption) error {
if serviceName != config.Config.RpcRegisterName.OpenImMessageGatewayName {
cli.rpcRegisterAddr = serviceName
} else {
cli.rpcRegisterAddr = cli.getSelfHost(context.Background())
cli.rpcRegisterAddr = getSelfHost(context.Background())
}
return nil
}
// UnRegister removes a service registration from Kubernetes.
func (cli *K8sDR) UnRegister() error {
return nil
}
// CreateRpcRootNodes creates root nodes for RPC in Kubernetes.
func (cli *K8sDR) CreateRpcRootNodes(serviceNames []string) error {
return nil
}
// RegisterConf2Registry registers a configuration to the registry.
func (cli *K8sDR) RegisterConf2Registry(key string, conf []byte) error {
return nil
}
// GetConfFromRegistry retrieves a configuration from the registry.
func (cli *K8sDR) GetConfFromRegistry(key string) ([]byte, error) {
return nil, nil
}
func (cli *K8sDR) getSelfHost(ctx context.Context) string {
func (cli *K8sDR) GetUserIdHashGatewayHost(ctx context.Context, userId string) (string, error) {
host, err := cli.gatewayHostConsistent.Get(userId)
if err != nil {
log.ZError(ctx, "GetUserIdHashGatewayHost error", err)
}
return host, err
}
func getSelfHost(ctx context.Context) string {
port := 88
instance := "openimserver"
selfPodName := os.Getenv("MY_POD_NAME")
@@ -95,26 +102,8 @@ func (cli *K8sDR) getSelfHost(ctx context.Context) string {
return host
}
// GetConns returns a list of gRPC client connections for a given service.
func (cli *K8sDR) GetConns(ctx context.Context, serviceName string, opts ...grpc.DialOption) ([]*grpc.ClientConn, error) {
if serviceName != config.Config.RpcRegisterName.OpenImMessageGatewayName {
conn, err := grpc.DialContext(ctx, serviceName, append(cli.options, opts...)...)
return []*grpc.ClientConn{conn}, err
}
var ret []*grpc.ClientConn
gatewayHosts := cli.getMsgGatewayHost(ctx)
for _, host := range gatewayHosts {
conn, err := grpc.DialContext(ctx, host, append(cli.options, opts...)...)
if err != nil {
return nil, err
}
ret = append(ret, conn)
}
return ret, nil
}
// like openimserver-openim-msggateway-0.openimserver-openim-msggateway-headless.openim-lin.svc.cluster.local:88
func (cli *K8sDR) getMsgGatewayHost(ctx context.Context) []string {
func getMsgGatewayHost(ctx context.Context) []string {
port := 88
instance := "openimserver"
selfPodName := os.Getenv("MY_POD_NAME")
@@ -135,40 +124,53 @@ func (cli *K8sDR) getMsgGatewayHost(ctx context.Context) []string {
ret = append(ret, host)
}
log.ZInfo(ctx, "getMsgGatewayHost", "instance", instance, "selfPodName", selfPodName, "replicas", replicas, "ns", ns, "ret", ret)
return ret
}
// GetConn returns a single gRPC client connection for a given service.
// GetConns returns the gRPC client connections to the specified service.
func (cli *K8sDR) GetConns(ctx context.Context, serviceName string, opts ...grpc.DialOption) ([]*grpc.ClientConn, error) {
if serviceName != config.Config.RpcRegisterName.OpenImMessageGatewayName {
conn, err := grpc.DialContext(ctx, serviceName, append(cli.options, opts...)...)
return []*grpc.ClientConn{conn}, err
} else {
var ret []*grpc.ClientConn
gatewayHosts := getMsgGatewayHost(ctx)
for _, host := range gatewayHosts {
conn, err := grpc.DialContext(ctx, host, append(cli.options, opts...)...)
if err != nil {
return nil, err
} else {
ret = append(ret, conn)
}
}
return ret, nil
}
}
func (cli *K8sDR) GetConn(ctx context.Context, serviceName string, opts ...grpc.DialOption) (*grpc.ClientConn, error) {
return grpc.DialContext(ctx, serviceName, append(cli.options, opts...)...)
}
// GetSelfConnTarget returns the connection target of the client itself.
func (cli *K8sDR) GetSelfConnTarget() string {
return cli.rpcRegisterAddr
}
// AddOption adds gRPC dial options to the client.
func (cli *K8sDR) AddOption(opts ...grpc.DialOption) {
cli.options = append(cli.options, opts...)
}
// CloseConn closes a given gRPC client connection.
func (cli *K8sDR) CloseConn(conn *grpc.ClientConn) {
conn.Close()
}
// do not use this method for call rpc.
// do not use this method for call rpc
func (cli *K8sDR) GetClientLocalConns() map[string][]*grpc.ClientConn {
fmt.Println("should not call this function!!!!!!!!!!!!!!!!!!!!!!!!!")
return nil
}
// Close closes the K8sDR client.
func (cli *K8sDR) Close() {
// Close any open resources here (if applicable)
return
}
@@ -52,10 +52,18 @@ func getEnv(key, fallback string) string {
return fallback
}
// getZkAddrFromEnv returns the value of an environment variable if it exists, otherwise it returns the fallback value.
// getZkAddrFromEnv returns the Zookeeper addresses combined from the ZOOKEEPER_ADDRESS and ZOOKEEPER_PORT environment variables.
// If the environment variables are not set, it returns the fallback value.
func getZkAddrFromEnv(fallback []string) []string {
if value, exists := os.LookupEnv("ZOOKEEPER_ADDRESS"); exists {
return strings.Split(value, ",")
address, addrExists := os.LookupEnv("ZOOKEEPER_ADDRESS")
port, portExists := os.LookupEnv("ZOOKEEPER_PORT")
if addrExists && portExists {
addresses := strings.Split(address, ",")
for i, addr := range addresses {
addresses[i] = addr + ":" + port
}
return addresses
}
return fallback
}
+1 -1
View File
@@ -432,7 +432,7 @@ func computeApproximateRequestSize(r *http.Request) int {
}
s += len(r.Host)
// r.Form and r.MultipartForm are assumed to be included in r.URL.
// r.FormData and r.MultipartForm are assumed to be included in r.URL.
if r.ContentLength != -1 {
s += int(r.ContentLength)
+2 -2
View File
@@ -112,7 +112,6 @@ func callBackPostReturn(ctx context.Context, url, command string, input interfac
//v.Set(constant.CallbackCommand, command)
//url = url + "/" + v.Encode()
url = url + "/" + command
b, err := Post(ctx, url, nil, input, callbackConfig.CallbackTimeOut)
if err != nil {
if callbackConfig.CallbackFailedContinue != nil && *callbackConfig.CallbackFailedContinue {
@@ -121,13 +120,14 @@ func callBackPostReturn(ctx context.Context, url, command string, input interfac
}
return errs.ErrNetwork.Wrap(err.Error())
}
defer log.ZDebug(ctx, "callback", "data", string(b))
if err = json.Unmarshal(b, output); err != nil {
if callbackConfig.CallbackFailedContinue != nil && *callbackConfig.CallbackFailedContinue {
log.ZWarn(ctx, "callback failed but continue", err, "url", url)
return nil
}
return errs.ErrData.Wrap(err.Error())
return errs.ErrData.WithDetail(err.Error() + "response format error")
}
return output.Parse()
+6 -7
View File
@@ -31,15 +31,14 @@ import (
"github.com/openimsdk/open-im-server/v3/pkg/common/config"
)
const (
maxRetry = 10 // Maximum number of retries for producer creation
)
const maxRetry = 10 // number of retries
var errEmptyMsg = errors.New("binary msg is empty")
var errEmptyMsg = errors.New("kafka binary msg is empty")
// Producer represents a Kafka producer.
type Producer struct {
topic string
addr []string
topic string
config *sarama.Config
producer sarama.SyncProducer
}
@@ -68,7 +67,7 @@ func NewKafkaProducer(addr []string, topic string) *Producer {
// Get Kafka configuration from environment variables or fallback to config file
kafkaUsername := getEnvOrConfig("KAFKA_USERNAME", config.Config.Kafka.Username)
kafkaPassword := getEnvOrConfig("KAFKA_PASSWORD", config.Config.Kafka.Password)
kafkaAddr := getEnvOrConfig("KAFKA_ADDRESS", addr[0]) // Assuming addr[0] contains address from config
kafkaAddr := getKafkaAddrFromEnv(addr) // Updated to use the new function
// Configure SASL authentication if credentials are provided
if kafkaUsername != "" && kafkaPassword != "" {
@@ -78,7 +77,7 @@ func NewKafkaProducer(addr []string, topic string) *Producer {
}
// Set the Kafka address
p.addr = []string{kafkaAddr}
p.addr = kafkaAddr
// Set up TLS configuration (if required)
SetupTLSConfig(p.config)
+19
View File
@@ -15,7 +15,9 @@
package kafka
import (
"fmt"
"os"
"strings"
"github.com/IBM/sarama"
@@ -44,3 +46,20 @@ func getEnvOrConfig(envName string, configValue string) string {
}
return configValue
}
// getKafkaAddrFromEnv returns the Kafka addresses combined from the KAFKA_ADDRESS and KAFKA_PORT environment variables.
// If the environment variables are not set, it returns the fallback value.
func getKafkaAddrFromEnv(fallback []string) []string {
envAddr := os.Getenv("KAFKA_ADDRESS")
envPort := os.Getenv("KAFKA_PORT")
if envAddr != "" && envPort != "" {
addresses := strings.Split(envAddr, ",")
for i, addr := range addresses {
addresses[i] = fmt.Sprintf("%s:%s", addr, envPort)
}
return addresses
}
return fallback
}
+1 -12
View File
@@ -30,10 +30,9 @@ func NewOptions(opts ...OptionsOpt) Options {
options[constant.IsOfflinePush] = false
options[constant.IsUnreadCount] = false
options[constant.IsConversationUpdate] = false
options[constant.IsSenderSync] = false
options[constant.IsSenderSync] = true
options[constant.IsNotPrivate] = false
options[constant.IsSenderConversationUpdate] = false
options[constant.IsSenderNotificationPush] = false
options[constant.IsReactionFromCache] = false
for _, opt := range opts {
opt(options)
@@ -114,12 +113,6 @@ func WithSenderConversationUpdate() OptionsOpt {
}
}
func WithSenderNotificationPush() OptionsOpt {
return func(options Options) {
options[constant.IsSenderNotificationPush] = true
}
}
func WithReactionFromCache() OptionsOpt {
return func(options Options) {
options[constant.IsReactionFromCache] = true
@@ -174,10 +167,6 @@ func (o Options) IsSenderConversationUpdate() bool {
return o.Is(constant.IsSenderConversationUpdate)
}
func (o Options) IsSenderNotificationPush() bool {
return o.Is(constant.IsSenderNotificationPush)
}
func (o Options) IsReactionFromCache() bool {
return o.Is(constant.IsReactionFromCache)
}
+35 -1
View File
@@ -17,7 +17,6 @@ package rpcclient
import (
"context"
"encoding/json"
"google.golang.org/grpc"
"google.golang.org/protobuf/proto"
@@ -68,6 +67,7 @@ func newContentTypeConf() map[int32]config.NotificationConf {
constant.BlackAddedNotification: config.Config.Notification.BlackAdded,
constant.BlackDeletedNotification: config.Config.Notification.BlackDeleted,
constant.FriendInfoUpdatedNotification: config.Config.Notification.FriendInfoUpdated,
constant.FriendsInfoUpdateNotification: config.Config.Notification.FriendInfoUpdated, //use the same FriendInfoUpdated
// conversation
constant.ConversationChangeNotification: config.Config.Notification.ConversationChanged,
constant.ConversationUnreadNotification: config.Config.Notification.ConversationChanged,
@@ -115,6 +115,7 @@ func newSessionTypeConf() map[int32]int32 {
constant.BlackAddedNotification: constant.SingleChatType,
constant.BlackDeletedNotification: constant.SingleChatType,
constant.FriendInfoUpdatedNotification: constant.SingleChatType,
constant.FriendsInfoUpdateNotification: constant.SingleChatType,
// conversation
constant.ConversationChangeNotification: constant.SingleChatType,
constant.ConversationUnreadNotification: constant.SingleChatType,
@@ -155,6 +156,30 @@ func (m *MessageRpcClient) GetMaxSeq(ctx context.Context, req *sdkws.GetMaxSeqRe
return resp, err
}
func (m *MessageRpcClient) GetMaxSeqs(ctx context.Context, conversationIDs []string) (map[string]int64, error) {
log.ZDebug(ctx, "GetMaxSeqs", "conversationIDs", conversationIDs)
resp, err := m.Client.GetMaxSeqs(ctx, &msg.GetMaxSeqsReq{
ConversationIDs: conversationIDs,
})
return resp.MaxSeqs, err
}
func (m *MessageRpcClient) GetHasReadSeqs(ctx context.Context, userID string, conversationIDs []string) (map[string]int64, error) {
resp, err := m.Client.GetHasReadSeqs(ctx, &msg.GetHasReadSeqsReq{
UserID: userID,
ConversationIDs: conversationIDs,
})
return resp.MaxSeqs, err
}
func (m *MessageRpcClient) GetMsgByConversationIDs(ctx context.Context, docIDs []string, seqs map[string]int64) (map[string]*sdkws.MsgData, error) {
resp, err := m.Client.GetMsgByConversationIDs(ctx, &msg.GetMsgByConversationIDsReq{
ConversationIDs: docIDs,
MaxSeqs: seqs,
})
return resp.MsgDatas, err
}
func (m *MessageRpcClient) PullMessageBySeqList(ctx context.Context, req *sdkws.PullMessageBySeqsReq) (*sdkws.PullMessageBySeqsResp, error) {
resp, err := m.Client.PullMessageBySeqs(ctx, req)
return resp, err
@@ -256,6 +281,7 @@ func (s *NotificationSender) NotificationWithSesstionType(ctx context.Context, s
optionsConfig.ReliabilityLevel = constant.UnreliableNotification
}
options := config.GetOptionsByNotification(optionsConfig)
s.SetOptionsByContentType(ctx, options, contentType)
msg.Options = options
offlineInfo.Title = title
offlineInfo.Desc = desc
@@ -274,3 +300,11 @@ func (s *NotificationSender) NotificationWithSesstionType(ctx context.Context, s
func (s *NotificationSender) Notification(ctx context.Context, sendID, recvID string, contentType int32, m proto.Message, opts ...NotificationOptions) error {
return s.NotificationWithSesstionType(ctx, sendID, recvID, contentType, s.sessionTypeConf[contentType], m, opts...)
}
func (s *NotificationSender) SetOptionsByContentType(_ context.Context, options map[string]bool, contentType int32) {
switch contentType {
case constant.UserStatusChangeNotification:
options[constant.IsSenderSync] = false
default:
}
}
+6 -1
View File
@@ -196,7 +196,12 @@ func (f *FriendNotificationSender) FriendRemarkSetNotification(ctx context.Conte
tips.FromToUserID.ToUserID = toUserID
return f.Notification(ctx, fromUserID, toUserID, constant.FriendRemarkSetNotification, &tips)
}
func (f *FriendNotificationSender) FriendsInfoUpdateNotification(ctx context.Context, toUserID string, friendIDs []string) error {
tips := sdkws.FriendsInfoUpdateTips{FromToUserID: &sdkws.FromToUserID{}}
tips.FromToUserID.ToUserID = toUserID
tips.FriendIDs = friendIDs
return f.Notification(ctx, toUserID, toUserID, constant.FriendsInfoUpdateNotification, &tips)
}
func (f *FriendNotificationSender) BlackAddedNotification(ctx context.Context, req *pbfriend.AddBlackReq) error {
tips := sdkws.BlackAddedTips{FromToUserID: &sdkws.FromToUserID{}}
tips.FromToUserID.FromUserID = req.OwnerUserID
+13 -3
View File
@@ -409,11 +409,16 @@ func (g *GroupNotificationSender) GroupApplicationAcceptedNotification(ctx conte
if err != nil {
return err
}
tips := &sdkws.GroupApplicationAcceptedTips{Group: group, HandleMsg: req.HandledMsg, ReceiverAs: 1}
tips := &sdkws.GroupApplicationAcceptedTips{Group: group, HandleMsg: req.HandledMsg}
if err := g.fillOpUser(ctx, &tips.OpUser, tips.Group.GroupID); err != nil {
return err
}
for _, userID := range append(userIDs, mcontext.GetOpUserID(ctx)) {
for _, userID := range append(userIDs, req.FromUserID) {
if userID == req.FromUserID {
tips.ReceiverAs = 0
} else {
tips.ReceiverAs = 1
}
err = g.Notification(ctx, mcontext.GetOpUserID(ctx), userID, constant.GroupApplicationAcceptedNotification, tips)
if err != nil {
log.ZError(ctx, "failed", err)
@@ -441,7 +446,12 @@ func (g *GroupNotificationSender) GroupApplicationRejectedNotification(ctx conte
if err := g.fillOpUser(ctx, &tips.OpUser, tips.Group.GroupID); err != nil {
return err
}
for _, userID := range append(userIDs, mcontext.GetOpUserID(ctx)) {
for _, userID := range append(userIDs, req.FromUserID) {
if userID == req.FromUserID {
tips.ReceiverAs = 0
} else {
tips.ReceiverAs = 1
}
err = g.Notification(ctx, mcontext.GetOpUserID(ctx), userID, constant.GroupApplicationRejectedNotification, tips)
if err != nil {
log.ZError(ctx, "failed", err)
+10
View File
@@ -64,6 +64,9 @@ func NewUserRpcClient(client discoveryregistry.SvcDiscoveryRegistry) UserRpcClie
// GetUsersInfo retrieves information for multiple users based on their user IDs.
func (u *UserRpcClient) GetUsersInfo(ctx context.Context, userIDs []string) ([]*sdkws.UserInfo, error) {
if len(userIDs) == 0 {
return []*sdkws.UserInfo{}, nil
}
resp, err := u.Client.GetDesignateUsers(ctx, &user.GetDesignateUsersReq{
UserIDs: userIDs,
})
@@ -179,3 +182,10 @@ func (u *UserRpcClient) SetUserStatus(ctx context.Context, userID string, status
})
return err
}
func (u *UserRpcClient) GetNotificationByID(ctx context.Context, userID string) error {
_, err := u.Client.GetNotificationAccount(ctx, &user.GetNotificationAccountReq{
UserID: userID,
})
return err
}

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