Compare commits

...

88 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
155 changed files with 6929 additions and 4208 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_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_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: jobs:
CLAAssistant: CLAAssistant:
+130
View File
@@ -106,6 +106,16 @@ jobs:
ghcr.io/openimsdk/openim-api ghcr.io/openimsdk/openim-api
openim/openim-api openim/openim-api
registry.cn-hangzhou.aliyuncs.com/openimsdk/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 - name: Build and push Docker image for openim-api
uses: docker/build-push-action@v5 uses: docker/build-push-action@v5
@@ -127,6 +137,16 @@ jobs:
ghcr.io/openimsdk/openim-cmdutils ghcr.io/openimsdk/openim-cmdutils
openim/openim-cmdutils openim/openim-cmdutils
registry.cn-hangzhou.aliyuncs.com/openimsdk/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 - name: Build and push Docker image for openim-cmdutils
uses: docker/build-push-action@v5 uses: docker/build-push-action@v5
@@ -148,6 +168,16 @@ jobs:
ghcr.io/openimsdk/openim-crontask ghcr.io/openimsdk/openim-crontask
openim/openim-crontask openim/openim-crontask
registry.cn-hangzhou.aliyuncs.com/openimsdk/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 - name: Build and push Docker image for openim-crontask
uses: docker/build-push-action@v5 uses: docker/build-push-action@v5
@@ -169,6 +199,16 @@ jobs:
ghcr.io/openimsdk/openim-msggateway ghcr.io/openimsdk/openim-msggateway
openim/openim-msggateway openim/openim-msggateway
registry.cn-hangzhou.aliyuncs.com/openimsdk/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 - name: Build and push Docker image for openim-msggateway
uses: docker/build-push-action@v5 uses: docker/build-push-action@v5
@@ -190,6 +230,16 @@ jobs:
ghcr.io/openimsdk/openim-msgtransfer ghcr.io/openimsdk/openim-msgtransfer
openim/openim-msgtransfer openim/openim-msgtransfer
registry.cn-hangzhou.aliyuncs.com/openimsdk/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 - name: Build and push Docker image for openim-msgtransfer
uses: docker/build-push-action@v5 uses: docker/build-push-action@v5
@@ -211,6 +261,16 @@ jobs:
ghcr.io/openimsdk/openim-push ghcr.io/openimsdk/openim-push
openim/openim-push openim/openim-push
registry.cn-hangzhou.aliyuncs.com/openimsdk/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 - name: Build and push Docker image for openim-push
uses: docker/build-push-action@v5 uses: docker/build-push-action@v5
@@ -232,6 +292,16 @@ jobs:
ghcr.io/openimsdk/openim-rpc-auth ghcr.io/openimsdk/openim-rpc-auth
openim/openim-rpc-auth openim/openim-rpc-auth
registry.cn-hangzhou.aliyuncs.com/openimsdk/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 - name: Build and push Docker image for openim-rpc-auth
uses: docker/build-push-action@v5 uses: docker/build-push-action@v5
@@ -253,6 +323,16 @@ jobs:
ghcr.io/openimsdk/openim-rpc-conversation ghcr.io/openimsdk/openim-rpc-conversation
openim/openim-rpc-conversation openim/openim-rpc-conversation
registry.cn-hangzhou.aliyuncs.com/openimsdk/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 - name: Build and push Docker image for openim-rpc-conversation
uses: docker/build-push-action@v5 uses: docker/build-push-action@v5
@@ -274,6 +354,16 @@ jobs:
ghcr.io/openimsdk/openim-rpc-friend ghcr.io/openimsdk/openim-rpc-friend
openim/openim-rpc-friend openim/openim-rpc-friend
registry.cn-hangzhou.aliyuncs.com/openimsdk/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 - name: Build and push Docker image for openim-rpc-friend
uses: docker/build-push-action@v5 uses: docker/build-push-action@v5
@@ -295,6 +385,16 @@ jobs:
ghcr.io/openimsdk/openim-rpc-group ghcr.io/openimsdk/openim-rpc-group
openim/openim-rpc-group openim/openim-rpc-group
registry.cn-hangzhou.aliyuncs.com/openimsdk/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 - name: Build and push Docker image for openim-rpc-group
uses: docker/build-push-action@v5 uses: docker/build-push-action@v5
@@ -316,6 +416,16 @@ jobs:
ghcr.io/openimsdk/openim-rpc-msg ghcr.io/openimsdk/openim-rpc-msg
openim/openim-rpc-msg openim/openim-rpc-msg
registry.cn-hangzhou.aliyuncs.com/openimsdk/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 - name: Build and push Docker image for openim-rpc-msg
uses: docker/build-push-action@v5 uses: docker/build-push-action@v5
@@ -337,6 +447,16 @@ jobs:
ghcr.io/openimsdk/openim-rpc-third ghcr.io/openimsdk/openim-rpc-third
openim/openim-rpc-third openim/openim-rpc-third
registry.cn-hangzhou.aliyuncs.com/openimsdk/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 - name: Build and push Docker image for openim-rpc-third
uses: docker/build-push-action@v5 uses: docker/build-push-action@v5
@@ -358,6 +478,16 @@ jobs:
ghcr.io/openimsdk/openim-rpc-user ghcr.io/openimsdk/openim-rpc-user
openim/openim-rpc-user openim/openim-rpc-user
registry.cn-hangzhou.aliyuncs.com/openimsdk/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 - name: Build and push Docker image for openim-rpc-user
uses: docker/build-push-action@v5 uses: docker/build-push-action@v5
+34 -4
View File
@@ -92,11 +92,41 @@ jobs:
- name: Exec OpenIM API test - name: Exec OpenIM API test
run: | run: |
sudo make test-api mkdir -p ./tmp
touch ./tmp/test.md
- name: Exec OpenIM E2E test 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: | 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 - name: Exec OpenIM System uninstall
run: | run: |
+1
View File
@@ -17,6 +17,7 @@ on:
issues: issues:
types: types:
- labeled - labeled
jobs: jobs:
add-comment: add-comment:
if: github.event.label.name == 'help wanted' || github.event.label.name == 'good first issue' if: github.event.label.name == 'help wanted' || github.event.label.name == 'good first issue'
+84 -30
View File
@@ -18,39 +18,42 @@ on:
push: push:
branches: branches:
- main - main
- release-*
paths-ignore: paths-ignore:
- "docs/**" - "docs/**"
- "README.md" - "README.md"
- "README_zh-CN.md" - "README_zh-CN.md"
- "**.md"
- "docs/**"
- "CONTRIBUTING.md" - "CONTRIBUTING.md"
pull_request: pull_request:
branches: branches:
- main - main
- release-*
paths-ignore: paths-ignore:
- "README.md" - "README.md"
- "README_zh-CN.md" - "README_zh-CN.md"
- "CONTRIBUTING.md" - "CONTRIBUTING/**"
- "**.md"
- "docs/**" - "docs/**"
env: env:
GO_VERSION: "1.19" GO_VERSION: "1.19"
GOLANGCI_VERSION: "v1.50.1" GOLANGCI_VERSION: "v1.50.1"
jobs: jobs:
openim: openim:
name: Test with go ${{ matrix.go_version }} on ${{ matrix.os }} name: Test with go ${{ matrix.go_version }} on ${{ matrix.os }}
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
permissions: permissions:
contents: write contents: write
pull-requests: write
environment: environment:
name: openim name: openim
strategy: strategy:
matrix: matrix:
go_version: ["1.19","1.20","1.21"] go_version: ["1.19","1.20","1.21"]
os: [ubuntu-latest] os: [ubuntu-latest]
steps: steps:
- name: Setup - name: Setup
uses: actions/checkout@v4 uses: actions/checkout@v4
@@ -67,6 +70,9 @@ jobs:
version: '3.x' # If available, use the latest major version that's compatible version: '3.x' # If available, use the latest major version that's compatible
repo-token: ${{ secrets.GITHUB_TOKEN }} repo-token: ${{ secrets.GITHUB_TOKEN }}
- name: OpenIM Scripts Verification(make verify)
run: sudo make verify
- name: Module Operations - name: Module Operations
run: | run: |
sudo make tidy sudo make tidy
@@ -83,16 +89,14 @@ jobs:
- name: Build Source - name: Build Source
run: sudo make build run: sudo make build
- name: Build multiarch PLATFORMS
if: startsWith(github.ref, 'refs/heads/release-')
run: |
sudo make multiarch
- name: Cleanup Build - name: Cleanup Build
run: sudo make clean 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 - name: Set Current Directory
id: set_directory id: set_directory
run: echo "::set-output name=directory::$(pwd)" run: echo "::set-output name=directory::$(pwd)"
@@ -109,8 +113,11 @@ jobs:
continue-on-error: true continue-on-error: true
openim-start: openim-start:
name: Test OpenIM install/start on ${{ matrix.os }} name: Test OpenIM install/start on ${{ matrix.os }}-${{ matrix.arch }}
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
permissions:
contents: write
pull-requests: write
environment: environment:
name: openim name: openim
strategy: strategy:
@@ -129,15 +136,59 @@ jobs:
run: | run: |
sudo make install 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: execute-scripts:
name: Execute OpenIM Script On ${{ matrix.os }} name: Execute OpenIM Script On ${{ matrix.os }}-${{ matrix.arch }}
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
permissions:
contents: write
pull-requests: write
environment: environment:
name: openim name: openim
strategy: strategy:
matrix: matrix:
go_version: ["1.20"] go_version: ["1.21"]
os: ["ubuntu-latest", "macos-latest"] os: ["ubuntu-latest", "macos-latest"]
arch: [arm64, armv7, amd64]
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v4 uses: actions/checkout@v4
@@ -155,7 +206,7 @@ jobs:
repo-token: ${{ secrets.GITHUB_TOKEN }} repo-token: ${{ secrets.GITHUB_TOKEN }}
# - name: Install latest Bash (macOS only) # - name: Install latest Bash (macOS only)
# if: runner.os == 'macOS' # if: runner.os == 'macOS' && matrix.arch == 'arm64'
# run: | # run: |
# /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" # /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
# brew update # brew update
@@ -175,7 +226,7 @@ jobs:
sudo sleep 20 sudo sleep 20
# - name: Set up Docker for macOS # - name: Set up Docker for macOS
# if: runner.os == 'macOS' # if: runner.os == 'macOS' && matrix.arch == 'arm64'
# run: | # run: |
# brew install --cask docker # brew install --cask docker
# open /Applications/Docker.app # open /Applications/Docker.app
@@ -201,30 +252,27 @@ jobs:
- name: Build, Start, Check Services and Print Logs for Ubuntu - name: Build, Start, Check Services and Print Logs for Ubuntu
if: runner.os == 'Linux' if: runner.os == 'Linux'
run: | run: |
sudo make init && \ sudo make build
sudo make build && \ sudo make start
sudo make start && \ sudo make check
sudo make check || \
(echo "An error occurred, printing logs:" && sudo cat ./_output/logs/* 2>/dev/null)
- name: Restart Services and Print Logs for Ubuntu - name: Restart Services and Print Logs for Ubuntu
if: runner.os == 'Linux' if: runner.os == 'Linux' && matrix.arch == 'amd64'
run: | run: |
sudo make restart sudo make restart
sudo make check sudo make check
# - name: Build, Start, Check Services and Print Logs for macOS - name: Build, Start, Check Services and Print Logs for macOS
# if: runner.os == 'macOS' if: runner.os == 'macOS' && matrix.arch == 'arm64'
# run: | run: |
# make init && \ make build
# make build && \
# make start && \
# make check || \
# (echo "An error occurred, printing logs:" && sudo cat ./_output/logs/* 2>/dev/null)
openim-test-build-image: openim-test-build-image:
name: Build OpenIM Docker Image name: Build OpenIM Docker Image
runs-on: ubuntu-latest runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
environment: environment:
name: openim name: openim
steps: steps:
@@ -246,3 +294,9 @@ jobs:
run: | run: |
sudo make init 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
+4
View File
@@ -97,6 +97,10 @@ It's crafted in Golang and supports cross-platform deployment, ensuring a cohere
## :rocket: Quick Start ## :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: You can quickly learn OpenIM engineering solutions, all it takes is one simple command:
```bash ```bash
+25 -1
View File
@@ -3,12 +3,36 @@
before: before:
hooks: hooks:
- make clean
# You may remove this if you don't use go modules. # You may remove this if you don't use go modules.
- make tidy - make tidy
- make copyright.add - make copyright.add
# you may remove this if you don't need go generate # you may remove this if you don't need go generate
- 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: snapshot:
name_template: "{{ incpatch .Version }}-next" name_template: "{{ incpatch .Version }}-next"
@@ -495,4 +519,4 @@ checksum:
algorithm: sha256 algorithm: sha256
release: 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 enable: false
timeout: 5 timeout: 5
failedContinue: true failedContinue: true
beforeUpdateUserInfoEx:
enable: false
timeout: 5
failedContinue: true
afterUpdateUserInfoEx:
enable: false
timeout: 5
failedContinue: true
afterSendSingleMsg: afterSendSingleMsg:
enable: false enable: false
timeout: 5 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 # The number of ports needs to be consistent with msg_transfer_service_num in script/path_info.sh
prometheus: prometheus:
enable: false enable: false
prometheusUrl: 172.28.0.1:13000 grafanaUrl: 172.28.0.1:13000
apiPrometheusPort: [20100] apiPrometheusPort: [20100]
userPrometheusPort: [ 20110 ] userPrometheusPort: [ 20110 ]
friendPrometheusPort: [ 20120 ] 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" export MINIO_SIGN_ENDPOINT="https://openim.server.com/im-minio-api"
mkdir ./charts/generated-configs 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 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 ../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 # TODO: This config file is the template file
# --| source: deployments/templates/openim.yaml # --| source: deployments/templates/config.yaml
# --| env: scripts/install/environment # --| env: scripts/install/environment
# --| target: config/config.yaml # --| target: config/config.yaml
# ----------------------------------------------------------------- # -----------------------------------------------------------------
@@ -53,8 +53,8 @@ mongo:
# Maximum connection pool size # Maximum connection pool size
address: [ ${MONGO_ADDRESS}:${MONGO_PORT} ] address: [ ${MONGO_ADDRESS}:${MONGO_PORT} ]
database: ${MONGO_DATABASE} database: ${MONGO_DATABASE}
username: ${MONGO_USERNAME} username: ${MONGO_OPENIM_USERNAME}
password: ${MONGO_PASSWORD} password: ${MONGO_OPENIM_PASSWORD}
maxPoolSize: ${MONGO_MAX_POOL_SIZE} maxPoolSize: ${MONGO_MAX_POOL_SIZE}
###################### Redis configuration information ###################### ###################### Redis configuration information ######################
@@ -315,13 +315,21 @@ iosPush:
# Timeout in seconds # Timeout in seconds
# Whether to continue execution if callback fails # Whether to continue execution if callback fails
callback: callback:
url: "" url: "http://127.0.0.1:10008/callbackExample"
beforeSendSingleMsg: beforeSendSingleMsg:
enable: ${CALLBACK_ENABLE} enable: ${CALLBACK_ENABLE}
timeout: ${CALLBACK_TIMEOUT} timeout: ${CALLBACK_TIMEOUT}
failedContinue: ${CALLBACK_FAILED_CONTINUE} 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: afterSendSingleMsg:
enable: ${CALLBACK_ENABLE} enable: true
timeout: ${CALLBACK_TIMEOUT} timeout: ${CALLBACK_TIMEOUT}
failedContinue: ${CALLBACK_FAILED_CONTINUE} failedContinue: ${CALLBACK_FAILED_CONTINUE}
beforeSendGroupMsg: beforeSendGroupMsg:
@@ -506,7 +514,7 @@ callback:
# The number of ports needs to be consistent with msg_transfer_service_num in script/path_info.sh # The number of ports needs to be consistent with msg_transfer_service_num in script/path_info.sh
prometheus: prometheus:
enable: ${PROMETHEUS_ENABLE} enable: ${PROMETHEUS_ENABLE}
prometheusUrl: ${PROMETHEUS_URL} grafanaUrl: ${GRAFANA_URL}
apiPrometheusPort: [${API_PROM_PORT}] apiPrometheusPort: [${API_PROM_PORT}]
userPrometheusPort: [ ${USER_PROM_PORT} ] userPrometheusPort: [ ${USER_PROM_PORT} ]
friendPrometheusPort: [ ${FRIEND_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"); # Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with 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 # See the License for the specific language governing permissions and
# limitations under the License. # 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. # Data storage directory for persistent data.
# Default: OPENIM_USER=root # Example: DATA_DIR=/path/to/data
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_DIR=${DATA_DIR} DATA_DIR=${DATA_DIR}
# Choose the appropriate image address, the default is GITHUB image, # Docker image registry. Uncomment the preferred one.
# you can choose docker hub, for Chinese users can choose Ali Cloud # Options: ghcr.io/openimsdk, openim, registry.cn-hangzhou.aliyuncs.com/openimsdk
# export IMAGE_REGISTRY="ghcr.io/openimsdk" # IMAGE_REGISTRY="ghcr.io/openimsdk"
# export IMAGE_REGISTRY="openim" # IMAGE_REGISTRY="openim"
# export IMAGE_REGISTRY="registry.cn-hangzhou.aliyuncs.com/openimsdk" # IMAGE_REGISTRY="registry.cn-hangzhou.aliyuncs.com/openimsdk"
IMAGE_REGISTRY=${IMAGE_REGISTRY} IMAGE_REGISTRY=${IMAGE_REGISTRY}
# ====================================== # ======================================
@@ -47,10 +42,9 @@ IMAGE_REGISTRY=${IMAGE_REGISTRY}
# Default: DOCKER_BRIDGE_SUBNET=172.28.0.0/16 # Default: DOCKER_BRIDGE_SUBNET=172.28.0.0/16
DOCKER_BRIDGE_SUBNET=${DOCKER_BRIDGE_SUBNET} DOCKER_BRIDGE_SUBNET=${DOCKER_BRIDGE_SUBNET}
# Gateway for the Docker network. # Set and specify the IP addresses of some containers. Generally speaking,
# Default: DOCKER_BRIDGE_GATEWAY=172.28.0.1 # you do not need to modify these configurations to facilitate debugging
DOCKER_BRIDGE_GATEWAY=${DOCKER_BRIDGE_GATEWAY} DOCKER_BRIDGE_GATEWAY=${DOCKER_BRIDGE_GATEWAY}
MONGO_NETWORK_ADDRESS=${MONGO_NETWORK_ADDRESS} MONGO_NETWORK_ADDRESS=${MONGO_NETWORK_ADDRESS}
REDIS_NETWORK_ADDRESS=${REDIS_NETWORK_ADDRESS} REDIS_NETWORK_ADDRESS=${REDIS_NETWORK_ADDRESS}
KAFKA_NETWORK_ADDRESS=${KAFKA_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} OPENIM_ADMIN_FRONT_NETWORK_ADDRESS=${OPENIM_ADMIN_FRONT_NETWORK_ADDRESS}
ALERT_MANAGER_NETWORK_ADDRESS=${ALERT_MANAGER_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 ----- # ----- 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. # Port for ZooKeeper service.
# Default: ZOOKEEPER_PORT=12181 # Default: ZOOKEEPER_PORT=12181
ZOOKEEPER_PORT=${ZOOKEEPER_PORT} ZOOKEEPER_PORT=${ZOOKEEPER_PORT}
# ----- MongoDB Configuration ----- # MongoDB service port 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.
# Default: MONGO_PORT=37017 # Default: MONGO_PORT=37017
# MONGO_PORT=${MONGO_PORT} # MONGO_PORT=${MONGO_PORT}
# Username to authenticate with the MongoDB service. # Password for MongoDB admin user. Used for service authentication.
# Default: MONGO_USERNAME=root
# MONGO_USERNAME=${MONGO_USERNAME}
# Password to authenticate with the MongoDB service.
# Default: MONGO_PASSWORD=openIM123 # Default: MONGO_PASSWORD=openIM123
MONGO_PASSWORD=${MONGO_PASSWORD} MONGO_PASSWORD=${MONGO_PASSWORD}
# Name of the database in MongoDB to be used. # Username for a regular OpenIM user in MongoDB.
# Default: MONGO_DATABASE=openIM_v3 # 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} MONGO_DATABASE=${MONGO_DATABASE}
# ----- Redis Configuration ----- # ----- 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. # Port on which Redis in-memory data structure store is running.
# Default: REDIS_PORT=16379 # Default: REDIS_PORT=16379
@@ -113,11 +128,6 @@ REDIS_PORT=${REDIS_PORT}
# Default: REDIS_PASSWORD=openIM123 # Default: REDIS_PASSWORD=openIM123
REDIS_PASSWORD=${REDIS_PASSWORD} 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. # Kakfa username to authenticate with the Kafka service.
# KAFKA_USERNAME=${KAFKA_USERNAME} # KAFKA_USERNAME=${KAFKA_USERNAME}
@@ -129,20 +139,13 @@ KAFKA_PORT=${KAFKA_PORT}
# Default: KAFKA_LATESTMSG_REDIS_TOPIC=latestMsgToRedis # Default: KAFKA_LATESTMSG_REDIS_TOPIC=latestMsgToRedis
KAFKA_LATESTMSG_REDIS_TOPIC=${KAFKA_LATESTMSG_REDIS_TOPIC} KAFKA_LATESTMSG_REDIS_TOPIC=${KAFKA_LATESTMSG_REDIS_TOPIC}
# Topic in Kafka for pushing messages (e.g. notifications or updates). # MINIO_PORT
# Default: KAFKA_MSG_PUSH_TOPIC=msgToPush # ----------
KAFKA_MSG_PUSH_TOPIC=${KAFKA_MSG_PUSH_TOPIC} # 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
# Topic in Kafka for storing offline messages in MongoDB. # to reflect this change. The endpoints include both the 'endpoint' and 'signEndpoint'
# Default: KAFKA_OFFLINEMSG_MONGO_TOPIC=offlineMsgToMongoMysql # under the MinIO configuration.
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.
# Default: MINIO_PORT=10005 # Default: MINIO_PORT=10005
MINIO_PORT=${MINIO_PORT} MINIO_PORT=${MINIO_PORT}
@@ -155,19 +158,11 @@ MINIO_PORT=${MINIO_PORT}
MINIO_SECRET_KEY=${MINIO_SECRET_KEY} MINIO_SECRET_KEY=${MINIO_SECRET_KEY}
# ----- Prometheus Configuration ----- # ----- 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. # Port on which Prometheus service is running.
# Default: PROMETHEUS_PORT=19090 # Default: PROMETHEUS_PORT=19090
PROMETHEUS_PORT=${PROMETHEUS_PORT} PROMETHEUS_PORT=${PROMETHEUS_PORT}
# ----- Grafana Configuration ----- # ----- 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. # Port on which Grafana service is running.
# Default: GRAFANA_PORT=13000 # Default: GRAFANA_PORT=13000
GRAFANA_PORT=${GRAFANA_PORT} GRAFANA_PORT=${GRAFANA_PORT}
@@ -176,35 +171,26 @@ GRAFANA_PORT=${GRAFANA_PORT}
# ============ OpenIM Web =============== # ============ 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. # Port on which OpenIM web service is running.
# Default: OPENIM_WEB_PORT=11001 # Default: OPENIM_WEB_PORT=11001
OPENIM_WEB_PORT=${OPENIM_WEB_PORT} 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 ============== # ========= 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. # Port for the OpenIM WebSockets.
# Default: OPENIM_WS_PORT=10001 # Default: OPENIM_WS_PORT=10001
OPENIM_WS_PORT=${OPENIM_WS_PORT} 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 # Default: API_OPENIM_PORT=10002
API_OPENIM_PORT=${API_OPENIM_PORT} API_OPENIM_PORT=${API_OPENIM_PORT}
# ====================================== # ======================================
# ========== OpenIM Chat =============== # ========== OpenIM Chat ===============
# ====================================== # ======================================
@@ -213,18 +199,13 @@ API_OPENIM_PORT=${API_OPENIM_PORT}
# Default: CHAT_IMAGE_VERSION=main # Default: CHAT_IMAGE_VERSION=main
CHAT_IMAGE_VERSION=${CHAT_IMAGE_VERSION} 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. # Port for the OpenIM chat API.
# Default: OPENIM_CHAT_API_PORT=10008 # Default: OPENIM_CHAT_API_PORT=10008
OPENIM_CHAT_API_PORT=${OPENIM_CHAT_API_PORT} OPENIM_CHAT_API_PORT=${OPENIM_CHAT_API_PORT}
# Directory path for storing data files or related information for OpenIM chat. # Port for the OpenIM admin API.
# Default: OPENIM_CHAT_DATA_DIR=./openim-chat/main # Default: OPENIM_ADMIN_API_PORT=10009
OPENIM_CHAT_DATA_DIR=${OPENIM_CHAT_DATA_DIR} OPENIM_ADMIN_API_PORT=${OPENIM_ADMIN_API_PORT}
# ====================================== # ======================================
# ========== OpenIM Admin ============== # ========== OpenIM Admin ==============
@@ -233,10 +214,6 @@ OPENIM_CHAT_DATA_DIR=${OPENIM_CHAT_DATA_DIR}
# Branch name for OpenIM server. # Branch name for OpenIM server.
# Default: SERVER_IMAGE_VERSION=main # Default: SERVER_IMAGE_VERSION=main
SERVER_IMAGE_VERSION=${SERVER_IMAGE_VERSION} 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. # Port for the node exporter.
# Default: NODE_EXPORTER_PORT=19100 # Default: NODE_EXPORTER_PORT=19100
@@ -256,4 +233,4 @@ OPENIM_ADMIN_FRONT_PORT=${OPENIM_ADMIN_FRONT_PORT}
# Port for the alertmanager. # Port for the alertmanager.
# Default: ALERT_MANAGER_PORT=19093 # Default: ALERT_MANAGER_PORT=19093
ALERT_MANAGER_PORT=${ALERT_MANAGER_PORT} ALERT_MANAGER_PORT=${ALERT_MANAGER_PORT}
+99 -14
View File
@@ -16,18 +16,20 @@ services:
ports: ports:
- "${MONGO_PORT:-37017}:27017" - "${MONGO_PORT:-37017}:27017"
container_name: mongo 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: volumes:
- "${DATA_DIR:-./}/components/mongodb/data/db:/data/db" - "${DATA_DIR:-./}/components/mongodb/data/db:/data/db"
- "${DATA_DIR:-./}/components/mongodb/data/logs:/data/logs" - "${DATA_DIR:-./}/components/mongodb/data/logs:/data/logs"
- "${DATA_DIR:-./}/components/mongodb/data/conf:/etc/mongo" - "${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: environment:
- TZ=Asia/Shanghai - TZ=Asia/Shanghai
- wiredTigerCacheSizeGB=1 - wiredTigerCacheSizeGB=1
- MONGO_INITDB_ROOT_USERNAME=${MONGO_USERNAME:-root} - MONGO_INITDB_ROOT_USERNAME=${MONGO_USERNAME:-root}
- MONGO_INITDB_ROOT_PASSWORD=${MONGO_PASSWORD:-openIM123} - MONGO_INITDB_ROOT_PASSWORD=${MONGO_PASSWORD:-openIM123}
- MONGO_INITDB_DATABASE=${MONGO_DATABASE:-openIM_v3} - 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 restart: always
networks: networks:
server: server:
@@ -92,7 +94,7 @@ services:
ipv4_address: ${KAFKA_NETWORK_ADDRESS:-172.28.0.4} ipv4_address: ${KAFKA_NETWORK_ADDRESS:-172.28.0.4}
minio: minio:
image: minio/minio:${MINIO_IMAGE_VERSION:-latest} image: minio/minio:${MINIO_IMAGE_VERSION:-RELEASE.2024-01-11T07-46-16Z}
ports: ports:
- "${MINIO_PORT:-10005}:9000" - "${MINIO_PORT:-10005}:9000"
- "9090:9090" - "9090:9090"
@@ -110,21 +112,104 @@ services:
ipv4_address: ${MINIO_NETWORK_ADDRESS:-172.28.0.6} ipv4_address: ${MINIO_NETWORK_ADDRESS:-172.28.0.6}
openim-web: 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 container_name: openim-web
environment:
- OPENIM_WEB_DIST_PATH=${OPENIM_WEB_DIST_PATH:-/app/dist}
- OPENIM_WEB_PORT=${OPENIM_WEB_PORT:-11001}
restart: always restart: always
ports: ports:
- "${OPENIM_WEB_PORT:-11001}:11001" - "${OPENIM_WEB_PORT:-11001}:80"
networks: networks:
server: server:
ipv4_address: ${OPENIM_WEB_NETWORK_ADDRESS:-172.28.0.7} 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 # 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: # 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 # container_name: openim-admin
# restart: always # restart: always
# ports: # ports:
@@ -139,8 +224,8 @@ services:
# hostname: prometheus # hostname: prometheus
# restart: always # restart: always
# volumes: # volumes:
# - ./config/prometheus.yml:/etc/prometheus/prometheus.yml # - "${DATA_DIR:-./}/config/instance-down-rules.yml:/etc/prometheus/instance-down-rules.yml"
# - ./config/instance-down-rules.yml:/etc/prometheus/instance-down-rules.yml # - "${DATA_DIR:-./}/config/prometheus.yml:/etc/prometheus/prometheus.yml"
# ports: # ports:
# - "${PROMETHEUS_PORT:-19090}:9090" # - "${PROMETHEUS_PORT:-19090}:9090"
# networks: # networks:
@@ -153,8 +238,8 @@ services:
# hostname: alertmanager # hostname: alertmanager
# restart: always # restart: always
# volumes: # volumes:
# - ./config/alertmanager.yml:/etc/alertmanager/alertmanager.yml # - ${DATA_DIR:-./}/config/alertmanager.yml:/etc/alertmanager/alertmanager.yml
# - ./config/email.tmpl:/etc/alertmanager/email.tmpl # - ${DATA_DIR:-./}/config/email.tmpl:/etc/alertmanager/email.tmpl
# ports: # ports:
# - "${ALERT_MANAGER_PORT:-19093}:9093" # - "${ALERT_MANAGER_PORT:-19093}:9093"
# networks: # networks:
@@ -170,7 +255,7 @@ services:
# ports: # ports:
# - "${GRAFANA_PORT:-13000}:3000" # - "${GRAFANA_PORT:-13000}:3000"
# volumes: # volumes:
# - ${DATA_DIR:-./}/components/grafana:/var/lib/grafana # - "${DATA_DIR:-./}/components/grafana:/var/lib/grafana"
# networks: # networks:
# server: # server:
# ipv4_address: ${GRAFANA_NETWORK_ADDRESS:-172.28.0.11} # 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-rpc-user_update.md
docs/guide/en-US/cmd/openim/openim_validate.md docs/guide/en-US/cmd/openim/openim_validate.md
docs/guide/en-US/cmd/openim/openim_version.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_color.yaml
docs/guide/en-US/yaml/openim/openim_completion.yaml docs/guide/en-US/yaml/openim/openim_completion.yaml
docs/guide/en-US/yaml/openim/openim_info.yaml docs/guide/en-US/yaml/openim/openim_info.yaml
+18 -11
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 ### 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 ### 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: Configuration file modifications can be made by specifying corresponding environment variables, for instance:
```bash ```bash
export CHAT_IMAGE_VERSION="main" export CHAT_IMAGE_VERSION="main"
export SERVER_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 ```bash
readonly CHAT_IMAGE_VERSION=${CHAT_IMAGE_VERSION:-'main'} readonly CHAT_IMAGE_VERSION=${CHAT_IMAGE_VERSION:-'main'}
readonly SERVER_IMAGE_VERSION=${SERVER_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`). 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 + PASSWORD
+ **Description**: Password for mysql, mongodb, redis, and minio. + **Description**: Password for mongodb, redis, and minio.
+ **Default**: `openIM123` + **Default**: `openIM123`
+ Notes: + Notes:
+ Minimum password length: 8 characters. + Minimum password length: 8 characters.
@@ -139,20 +142,22 @@ For convenience, configuration through modifying environment variables is recomm
+ OPENIM_USER + OPENIM_USER
+ **Description**: Username for mysql, mongodb, redis, and minio. + **Description**: Username for redis, and minio.
+ **Default**: `root` + **Default**: `root`
```bash ```bash
export OPENIM_USER="root" export OPENIM_USER="root"
``` ```
+ API_URL > mongo is `openIM`, use `export MONGO_OPENIM_USERNAME="openIM"` to modify
+ OPENIM_IP
+ **Description**: API address. + **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. + **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 ```bash
export API_URL="http://ip:10002" export OPENIM_IP="ip"
``` ```
+ DATA_DIR + 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_PORT | "27017" | Port used by MongoDB. |
| MONGO_ADDRESS | [Generated IP] | IP address for MongoDB. | | MONGO_ADDRESS | [Generated IP] | IP address for MongoDB. |
| MONGO_USERNAME | [User Defined] | Username for MongoDB. | | MONGO_USERNAME | [User Defined] | Admin Username for MongoDB. |
| MONGO_PASSWORD | [User Defined] | Password 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 ### 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 | | 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_ROTATION_TIME | "24" | Log rotation time (in hours) |
| LOG_REMAIN_ROTATION_COUNT | "2" | Number of log rotations to retain | | LOG_REMAIN_ROTATION_COUNT | "2" | Number of log rotations to retain |
| LOG_REMAIN_LOG_LEVEL | "6" | Log level to retain | | LOG_REMAIN_LOG_LEVEL | "6" | Log level to retain |
+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. - 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. - 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. commit hygiene.
--- ---
![Git workflow](git_workflow.png)
## 1. Fork in the cloud ## 1. Fork in the cloud
@@ -28,10 +27,10 @@ neither `$GOPATH/src/github.com/${your github profile name}/`
nor any other pattern will work. nor any other pattern will work.
```sh ```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. will be a sibling to your existing `github.com` directory.
Set `user` to match your github profile name: 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. 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. 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. 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 ### 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. # Defines an associative array where the keys are the template files and the values are the corresponding output files.
declare -A TEMPLATES=( declare -A TEMPLATES=(
[""${OPENIM_ROOT}"/scripts/template/config-tmpl/env.template"]="${OPENIM_OUTPUT_SUBPATH}/bin/.env" ["${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/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: Below are the base images and their versions you'll need:
- wurstmeister/kafka - [ ] bitnami/kafka:3.5.1
- redis:7.0.0 - [ ] redis:7.0.0
- mongo:6.0.2 - [ ] mongo:6.0.2
- mysql:5.7 - [ ] bitnami/zookeeper:3.8
- wurstmeister/zookeeper - [ ] minio/minio:latest
- minio/minio
> [!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: Use the following commands to pull these base images:
``` ```bash
docker pull wurstmeister/kafka docker pull bitnami/kafka:3.5.1
docker pull redis:7.0.0 docker pull redis:7.0.0
docker pull mongo:6.0.2 docker pull mongo:6.0.2
docker pull mysql:5.7 docker pull mysql:5.7
docker pull wurstmeister/zookeeper docker pull bitnami/zookeeper:3.8
docker pull minio/minio 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) **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: - Execute the following command to pull the image:
```bash ```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 ## 3. Image Storage Selection
**Repositories**: **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. 1. **Pull images**: Execute the above `docker pull` commands to pull all required images locally.
2. **Save images**: 2. **Save images**:
``` ```bash
docker save -o <tar-file-name>.tar <image-name> 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:
``` ```bash
git clone https://github.com/OpenIMSDK/openim-docker.git docker save -o <tar-file-name>.tar $(docker images -q)
``` ```
Or download the code from [Releases](https://github.com/OpenIMSDK/openim-docker/releases/). 3. **Fetch code**: Clone the repository:
1. **Transfer files**: Use `scp` to transfer all images and code to the intranet server.
```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 scp <tar-file-name>.tar user@remote-ip:/path/on/remote/server
``` ```
Or choose other transfer methods such as a hard drive. 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 docker load -i <tar-file-name>.tar
``` ```
1. **Deploy**: Navigate to the `openim-docker` repository directory and follow the README guide for deployment. Import directly with shortcut commands:
2. **Deploy using Docker-compose**:
```bash
for i in `ls ./`;do docker load -i $i;done
``` ```
docker-compose up -d
# Verify 6. **Deploy**: Navigate to the `openim-docker` repository directory and follow the [README guide](https://github.com/openimsdk/openim-docker) for deployment.
docker-compose ps
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`. > **Note**: If you're using a version of Docker prior to 20, make sure you've installed `docker-compose`.
## 6. Reference Links ## 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) - [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: 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`)**: **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/).
- **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**: **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 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 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) - [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) - [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) - [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) - [Release Management: A Guided Tour](#release-management-a-guided-tour)
- [Milestones, Branching, and Addressing Major Bugs](#milestones-branching-and-addressing-major-bugs) - [Milestones, Branching, and Addressing Major Bugs](#milestones-branching-and-addressing-major-bugs)
- [Version Skew Policy](#version-skew-policy) - [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) - [Applying Principles: A Git Workflow Example](#applying-principles-a-git-workflow-example)
- [Release Process](#release-process)
- [Docker Images Version Management](#docker-images-version-management) - [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/). 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 ## 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). 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
+7 -9
View File
@@ -4,6 +4,8 @@ go 1.19
require ( require (
firebase.google.com/go v3.13.0+incompatible firebase.google.com/go v3.13.0+incompatible
github.com/OpenIMSDK/protocol v0.0.47
github.com/OpenIMSDK/tools v0.0.29
github.com/bwmarrin/snowflake v0.3.0 // indirect github.com/bwmarrin/snowflake v0.3.0 // indirect
github.com/dtm-labs/rockscache v0.1.1 github.com/dtm-labs/rockscache v0.1.1
github.com/gin-gonic/gin v1.9.1 github.com/gin-gonic/gin v1.9.1
@@ -33,8 +35,6 @@ require github.com/google/uuid v1.3.1
require ( require (
github.com/IBM/sarama v1.41.3 github.com/IBM/sarama v1.41.3
github.com/OpenIMSDK/protocol v0.0.36
github.com/OpenIMSDK/tools v0.0.21
github.com/aliyun/aliyun-oss-go-sdk v2.2.9+incompatible github.com/aliyun/aliyun-oss-go-sdk v2.2.9+incompatible
github.com/go-redis/redis v6.15.9+incompatible github.com/go-redis/redis v6.15.9+incompatible
github.com/redis/go-redis/v9 v9.2.1 github.com/redis/go-redis/v9 v9.2.1
@@ -127,13 +127,13 @@ require (
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d // indirect github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d // indirect
go.opencensus.io v0.24.0 // indirect go.opencensus.io v0.24.0 // indirect
go.uber.org/atomic v1.7.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/arch v0.3.0 // indirect
golang.org/x/net v0.17.0 // indirect golang.org/x/net v0.17.0 // indirect
golang.org/x/oauth2 v0.13.0 // indirect golang.org/x/oauth2 v0.13.0 // indirect
golang.org/x/sys v0.14.0 // indirect golang.org/x/sys v0.15.0 // indirect
golang.org/x/text v0.13.0 // indirect golang.org/x/text v0.14.0 // indirect
golang.org/x/time v0.3.0 // indirect golang.org/x/time v0.5.0 // indirect
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect
google.golang.org/appengine v1.6.7 // indirect google.golang.org/appengine v1.6.7 // indirect
google.golang.org/genproto v0.0.0-20231002182017-d307bd883b97 // indirect google.golang.org/genproto v0.0.0-20231002182017-d307bd883b97 // indirect
@@ -153,8 +153,6 @@ require (
github.com/spf13/cobra v1.7.0 github.com/spf13/cobra v1.7.0
github.com/ugorji/go/codec v1.2.11 // indirect github.com/ugorji/go/codec v1.2.11 // indirect
go.uber.org/zap v1.24.0 // 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 gopkg.in/ini.v1 v1.67.0 // indirect
) )
replace github.com/OpenIMSDK/protocol v0.0.36 => github.com/luhaoling/protocol v0.0.0-20231222100538-d625562d53d5
+15 -15
View File
@@ -18,8 +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/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 h1:MWBEJ12vHC8coMjdEXFq/6ftO6DUZnQlFYcxtOJFa7c=
github.com/IBM/sarama v1.41.3/go.mod h1:Xxho9HkHd4K/MDUo/T/sOqwtX/17D33++E9Wib6hUdQ= github.com/IBM/sarama v1.41.3/go.mod h1:Xxho9HkHd4K/MDUo/T/sOqwtX/17D33++E9Wib6hUdQ=
github.com/OpenIMSDK/tools v0.0.21 h1:iTapc2mIEVH/xl5Nd6jfwPub11Pgp44tVcE1rjB3a48= github.com/OpenIMSDK/protocol v0.0.48 h1:8MIMjyzJRsruYhVv2ZKArFiOveroaofDOb3dlAdgjsw=
github.com/OpenIMSDK/tools v0.0.21/go.mod h1:eg+q4A34Qmu73xkY0mt37FHGMCMfC6CtmOnm0kFEGFI= 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/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 h1:uSoVVbwJiQipAclBbw+8quDsfcvFjOpI5iCf4p/cqCs=
github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs= github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs=
@@ -225,8 +227,6 @@ github.com/lestrrat-go/strftime v1.0.6 h1:CFGsDEt1pOpFNU+TJB0nhz9jl+K0hZSLE205Ah
github.com/lestrrat-go/strftime v1.0.6/go.mod h1:f7jQKgV5nnJpYgdEasS+/y7EsTb8ykN2z68n3TtcTaw= github.com/lestrrat-go/strftime v1.0.6/go.mod h1:f7jQKgV5nnJpYgdEasS+/y7EsTb8ykN2z68n3TtcTaw=
github.com/lithammer/shortuuid v3.0.0+incompatible h1:NcD0xWW/MZYXEHa6ITy6kaXN5nwm/V115vj2YXfhS0w= github.com/lithammer/shortuuid v3.0.0+incompatible h1:NcD0xWW/MZYXEHa6ITy6kaXN5nwm/V115vj2YXfhS0w=
github.com/lithammer/shortuuid v3.0.0+incompatible/go.mod h1:FR74pbAuElzOUuenUHTK2Tciko1/vKuIKS9dSkDrA4w= github.com/lithammer/shortuuid v3.0.0+incompatible/go.mod h1:FR74pbAuElzOUuenUHTK2Tciko1/vKuIKS9dSkDrA4w=
github.com/luhaoling/protocol v0.0.0-20231222100538-d625562d53d5 h1:nmrJmAgQsCAxKgw109kaTcBV4rMWDRvqOson0ehw708=
github.com/luhaoling/protocol v0.0.0-20231222100538-d625562d53d5/go.mod h1:F25dFrwrIx3lkNoiuf6FkCfxuwf8L4Z8UIsdTHP/r0Y=
github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo=
@@ -356,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 h1:kWazyxZUrS3Gs4qUpbwo5kEIMGe/DAvi5Z4tl2NW4j8=
go.uber.org/automaxprocs v1.5.3/go.mod h1:eRbA25aqJrxAbsLO0xy5jVwPt7FQnRgjW+efnwa1WM0= 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/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI=
go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= 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 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60=
go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg= 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= golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
@@ -371,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-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.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.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc= golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k=
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= 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/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 h1:3cge/F/QTkNLauhf2QoE9zp+7sr+ZcL4HnoZmdwg9sg=
golang.org/x/image v0.13.0/go.mod h1:6mmbMOeV28HuMTgA6OSRkdXKYw/t5W9Uwn2Yv1r3Yxk= golang.org/x/image v0.13.0/go.mod h1:6mmbMOeV28HuMTgA6OSRkdXKYw/t5W9Uwn2Yv1r3Yxk=
@@ -438,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.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.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.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.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 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-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.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.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.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.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
@@ -451,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.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= 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.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 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-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-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
+4
View File
@@ -33,6 +33,10 @@ func (o *ConversationApi) GetAllConversations(c *gin.Context) {
a2r.Call(conversation.ConversationClient.GetAllConversations, o.Client, c) 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) { func (o *ConversationApi) GetConversation(c *gin.Context) {
a2r.Call(conversation.ConversationClient.GetConversation, o.Client, c) 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) { func (o *FriendApi) GetSpecifiedFriendsInfo(c *gin.Context) {
a2r.Call(friend.FriendClient.GetSpecifiedFriendsInfo, o.Client, c) a2r.Call(friend.FriendClient.GetSpecifiedFriendsInfo, o.Client, c)
} }
func (o *FriendApi) UpdateFriends(c *gin.Context) {
a2r.Call(friend.FriendClient.UpdateFriends, o.Client, c)
}
+30 -2
View File
@@ -164,6 +164,8 @@ func (m *MessageApi) getSendMsgReq(c *gin.Context, req apistruct.SendMsg) (sendM
data = apistruct.VideoElem{} data = apistruct.VideoElem{}
case constant.File: case constant.File:
data = apistruct.FileElem{} data = apistruct.FileElem{}
case constant.AtText:
data = apistruct.AtElem{}
case constant.Custom: case constant.Custom:
data = apistruct.CustomElem{} data = apistruct.CustomElem{}
case constant.OANotification: case constant.OANotification:
@@ -185,38 +187,63 @@ func (m *MessageApi) getSendMsgReq(c *gin.Context, req apistruct.SendMsg) (sendM
return m.newUserSendMsgReq(c, &req), nil 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) { func (m *MessageApi) SendMessage(c *gin.Context) {
// Initialize a request struct for sending a message.
req := apistruct.SendMsgReq{} req := apistruct.SendMsgReq{}
// Bind the JSON request body to the request struct.
if err := c.BindJSON(&req); err != nil { 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()) apiresp.GinError(c, errs.ErrArgs.WithDetail(err.Error()).Wrap())
return return
} }
// Check if the user has the app manager role.
if !authverify.IsAppManagerUid(c) { 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")) apiresp.GinError(c, errs.ErrNoPermission.Wrap("only app manager can send message"))
return return
} }
// Prepare the message request with additional required data.
sendMsgReq, err := m.getSendMsgReq(c, req.SendMsg) sendMsgReq, err := m.getSendMsgReq(c, req.SendMsg)
if err != nil { if err != nil {
// Log and respond with an error if preparation fails.
log.ZError(c, "decodeData failed", err) log.ZError(c, "decodeData failed", err)
apiresp.GinError(c, err) apiresp.GinError(c, err)
return return
} }
// Set the receiver ID in the message data.
sendMsgReq.MsgData.RecvID = req.RecvID sendMsgReq.MsgData.RecvID = req.RecvID
// Declare a variable to store the message sending status.
var status int var status int
// Attempt to send the message using the client.
respPb, err := m.Client.SendMsg(c, sendMsgReq) respPb, err := m.Client.SendMsg(c, sendMsgReq)
if err != nil { if err != nil {
// Set the status to failed and respond with an error if sending fails.
status = constant.MsgSendFailed status = constant.MsgSendFailed
log.ZError(c, "send message err", err) log.ZError(c, "send message err", err)
apiresp.GinError(c, err) apiresp.GinError(c, err)
return return
} }
// Set the status to successful if the message is sent.
status = constant.MsgSendSuccessed status = constant.MsgSendSuccessed
// Attempt to update the message sending status in the system.
_, err = m.Client.SetSendMsgStatus(c, &msg.SetSendMsgStatusReq{ _, err = m.Client.SetSendMsgStatus(c, &msg.SetSendMsgStatusReq{
Status: int32(status), Status: int32(status),
}) })
if err != nil { if err != nil {
// Log the error if updating the status fails.
log.ZError(c, "SetSendMsgStatus failed", err) log.ZError(c, "SetSendMsgStatus failed", err)
} }
// Respond with a success message and the response payload.
apiresp.GinSuccess(c, respPb) apiresp.GinSuccess(c, respPb)
} }
@@ -224,13 +251,14 @@ func (m *MessageApi) SendBusinessNotification(c *gin.Context) {
req := struct { req := struct {
Key string `json:"key"` Key string `json:"key"`
Data string `json:"data"` Data string `json:"data"`
SendUserID string `json:"sendUserID"` SendUserID string `json:"sendUserID" binding:"required"`
RecvUserID string `json:"recvUserID"` RecvUserID string `json:"recvUserID" binding:"required"`
}{} }{}
if err := c.BindJSON(&req); err != nil { if err := c.BindJSON(&req); err != nil {
apiresp.GinError(c, errs.ErrArgs.WithDetail(err.Error()).Wrap()) apiresp.GinError(c, errs.ErrArgs.WithDetail(err.Error()).Wrap())
return return
} }
if !authverify.IsAppManagerUid(c) { if !authverify.IsAppManagerUid(c) {
apiresp.GinError(c, errs.ErrNoPermission.Wrap("only app manager can send message")) apiresp.GinError(c, errs.ErrNoPermission.Wrap("only app manager can send message"))
return return
+5 -1
View File
@@ -67,6 +67,7 @@ func NewGinRouter(discov discoveryregistry.SvcDiscoveryRegistry, rdb redis.Unive
{ {
userRouterGroup.POST("/user_register", u.UserRegister) userRouterGroup.POST("/user_register", u.UserRegister)
userRouterGroup.POST("/update_user_info", ParseToken, u.UpdateUserInfo) 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("/set_global_msg_recv_opt", ParseToken, u.SetGlobalRecvMessageOpt)
userRouterGroup.POST("/get_users_info", ParseToken, u.GetUsersPublicInfo) userRouterGroup.POST("/get_users_info", ParseToken, u.GetUsersPublicInfo)
userRouterGroup.POST("/get_all_users_uid", ParseToken, u.GetAllUsersID) userRouterGroup.POST("/get_all_users_uid", ParseToken, u.GetAllUsersID)
@@ -107,7 +108,7 @@ func NewGinRouter(discov discoveryregistry.SvcDiscoveryRegistry, rdb redis.Unive
friendRouterGroup.POST("/is_friend", f.IsFriend) friendRouterGroup.POST("/is_friend", f.IsFriend)
friendRouterGroup.POST("/get_friend_id", f.GetFriendIDs) friendRouterGroup.POST("/get_friend_id", f.GetFriendIDs)
friendRouterGroup.POST("/get_specified_friends_info", f.GetSpecifiedFriendsInfo) friendRouterGroup.POST("/get_specified_friends_info", f.GetSpecifiedFriendsInfo)
//friendRouterGroup.POST("/set_pin_friend", f.SetPinFriends) friendRouterGroup.POST("/update_friends", f.UpdateFriends)
} }
g := NewGroupApi(*groupRpc) g := NewGroupApi(*groupRpc)
groupRouterGroup := r.Group("/group", ParseToken) groupRouterGroup := r.Group("/group", ParseToken)
@@ -171,6 +172,8 @@ func NewGinRouter(discov discoveryregistry.SvcDiscoveryRegistry, rdb redis.Unive
objectGroup.POST("/auth_sign", t.AuthSign) objectGroup.POST("/auth_sign", t.AuthSign)
objectGroup.POST("/complete_multipart_upload", t.CompleteMultipartUpload) objectGroup.POST("/complete_multipart_upload", t.CompleteMultipartUpload)
objectGroup.POST("/access_url", t.AccessURL) 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) objectGroup.GET("/*name", t.ObjectRedirect)
} }
// Message // Message
@@ -201,6 +204,7 @@ func NewGinRouter(discov discoveryregistry.SvcDiscoveryRegistry, rdb redis.Unive
conversationGroup := r.Group("/conversation", ParseToken) conversationGroup := r.Group("/conversation", ParseToken)
{ {
c := NewConversationApi(*conversationRpc) c := NewConversationApi(*conversationRpc)
conversationGroup.POST("/get_sorted_conversation_list", c.GetSortedConversationList)
conversationGroup.POST("/get_all_conversations", c.GetAllConversations) conversationGroup.POST("/get_all_conversations", c.GetAllConversations)
conversationGroup.POST("/get_conversation", c.GetConversation) conversationGroup.POST("/get_conversation", c.GetConversation)
conversationGroup.POST("/get_conversations", c.GetConversations) 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) 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) { func (o *ThirdApi) ObjectRedirect(c *gin.Context) {
name := c.Param("name") name := c.Param("name")
if name == "" { if name == "" {
@@ -122,5 +130,5 @@ func (o *ThirdApi) SearchLogs(c *gin.Context) {
} }
func GetPrometheus(c *gin.Context) { func GetPrometheus(c *gin.Context) {
c.Redirect(http.StatusFound, config2.Config.Prometheus.PrometheusUrl) c.Redirect(http.StatusFound, config2.Config.Prometheus.GrafanaUrl)
} }
+3 -1
View File
@@ -41,7 +41,9 @@ func (u *UserApi) UserRegister(c *gin.Context) {
func (u *UserApi) UpdateUserInfo(c *gin.Context) { func (u *UserApi) UpdateUserInfo(c *gin.Context) {
a2r.Call(user.UserClient.UpdateUserInfo, u.Client, c) 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) { func (u *UserApi) SetGlobalRecvMessageOpt(c *gin.Context) {
a2r.Call(user.UserClient.SetGlobalRecvMessageOpt, u.Client, c) a2r.Call(user.UserClient.SetGlobalRecvMessageOpt, 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( func (c *Client) ResetClient(
ctx *UserConnContext, ctx *UserConnContext,
conn LongConn, conn LongConn,
@@ -108,11 +109,13 @@ func (c *Client) ResetClient(
c.token = token c.token = token
} }
// pingHandler handles ping messages and sends pong responses.
func (c *Client) pingHandler(_ string) error { func (c *Client) pingHandler(_ string) error {
_ = c.conn.SetReadDeadline(pongWait) _ = c.conn.SetReadDeadline(pongWait)
return c.writePongMsg() return c.writePongMsg()
} }
// readMessage continuously reads messages from the connection.
func (c *Client) readMessage() { func (c *Client) readMessage() {
defer func() { defer func() {
if r := recover(); r != nil { 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 { func (c *Client) handleMessage(message []byte) error {
if c.IsCompress { if c.IsCompress {
var err error var err error
+1
View File
@@ -26,6 +26,7 @@ const (
Compression = "compression" Compression = "compression"
GzipCompressionProtocol = "gzip" GzipCompressionProtocol = "gzip"
BackgroundStatus = "isBackground" BackgroundStatus = "isBackground"
MsgResp = "isMsgResp"
) )
const ( const (
+78 -61
View File
@@ -16,7 +16,10 @@ package msggateway
import ( import (
"context" "context"
"encoding/json"
"errors" "errors"
"fmt"
"github.com/OpenIMSDK/tools/apiresp"
"net/http" "net/http"
"os" "os"
"os/signal" "os/signal"
@@ -342,11 +345,7 @@ func (ws *WsServer) multiTerminalLoginChecker(clientOK bool, oldClients []*Clien
if !clientOK { if !clientOK {
return return
} }
ws.clients.deleteClients(newClient.UserID, oldClients)
isDeleteUser := ws.clients.deleteClients(newClient.UserID, oldClients)
if isDeleteUser {
ws.onlineUserNum.Add(-1)
}
for _, c := range oldClients { for _, c := range oldClients {
err := c.KickOnlineMessage() err := c.KickOnlineMessage()
if err != nil { if err != nil {
@@ -422,84 +421,102 @@ func (ws *WsServer) unregisterClient(client *Client) {
) )
} }
func (ws *WsServer) wsHandler(w http.ResponseWriter, r *http.Request) { func (ws *WsServer) ParseWSArgs(r *http.Request) (args *WSArgs, err error) {
connContext := newContext(w, r) var v WSArgs
defer func() {
args = &v
}()
query := r.URL.Query()
v.MsgResp, _ = strconv.ParseBool(query.Get(MsgResp))
if ws.onlineUserConnNum.Load() >= ws.wsMaxConnNum { if ws.onlineUserConnNum.Load() >= ws.wsMaxConnNum {
httpError(connContext, errs.ErrConnOverMaxNumLimit) return nil, errs.ErrConnOverMaxNumLimit.Wrap("over max conn num limit")
return
} }
var ( if v.Token = query.Get(Token); v.Token == "" {
token string return nil, errs.ErrConnArgsErr.Wrap("token is empty")
userID string
platformIDStr string
exists bool
compression bool
)
token, exists = connContext.Query(Token)
if !exists {
httpError(connContext, errs.ErrConnArgsErr)
return
} }
userID, exists = connContext.Query(WsUserID) if v.UserID = query.Get(WsUserID); v.UserID == "" {
if !exists { return nil, errs.ErrConnArgsErr.Wrap("sendID is empty")
httpError(connContext, errs.ErrConnArgsErr)
return
} }
platformIDStr, exists = connContext.Query(PlatformID) platformIDStr := query.Get(PlatformID)
if !exists { if platformIDStr == "" {
httpError(connContext, errs.ErrConnArgsErr) return nil, errs.ErrConnArgsErr.Wrap("platformID is empty")
return
} }
platformID, err := strconv.Atoi(platformIDStr) platformID, err := strconv.Atoi(platformIDStr)
if err != nil { if err != nil {
httpError(connContext, errs.ErrConnArgsErr) return nil, errs.ErrConnArgsErr.Wrap("platformID is not int")
return
} }
if err = authverify.WsVerifyToken(token, userID, platformID); err != nil { v.PlatformID = platformID
httpError(connContext, err) if err = authverify.WsVerifyToken(v.Token, v.UserID, platformID); err != nil {
return 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 { if err != nil {
httpError(connContext, err) return nil, err
return
} }
if v, ok := m[token]; ok { if v, ok := m[v.Token]; ok {
switch v { switch v {
case constant.NormalToken: case constant.NormalToken:
case constant.KickedToken: case constant.KickedToken:
httpError(connContext, errs.ErrTokenKicked.Wrap()) return nil, errs.ErrTokenKicked.Wrap()
return
default: 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 return
} }
} else { } else {
httpError(connContext, errs.ErrTokenNotExist.Wrap()) if pErr != nil {
return httpError(connContext, pErr)
} 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
} }
} wsLongConn = newGWebSocket(WebSocket, ws.handshakeTimeout, ws.writeBufferSize)
compressProtoc, exists = connContext.GetHeader(Compression) if err := wsLongConn.GenerateLongConn(w, r); err != nil {
if exists { httpError(connContext, err)
if compressProtoc == GzipCompressionProtocol { return
compression = true
} }
} }
client := ws.clientPool.Get().(*Client) 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 ws.registerChan <- client
go client.readMessage() go client.readMessage()
} }
+180
View File
@@ -17,6 +17,8 @@ package conversation
import ( import (
"context" "context"
"errors" "errors"
"github.com/OpenIMSDK/protocol/sdkws"
"sort"
"github.com/OpenIMSDK/tools/tx" "github.com/OpenIMSDK/tools/tx"
@@ -41,6 +43,8 @@ import (
) )
type conversationServer struct { type conversationServer struct {
msgRpcClient *rpcclient.MessageRpcClient
user *rpcclient.UserRpcClient
groupRpcClient *rpcclient.GroupRpcClient groupRpcClient *rpcclient.GroupRpcClient
conversationDatabase controller.ConversationDatabase conversationDatabase controller.ConversationDatabase
conversationNotificationSender *notification.ConversationNotificationSender conversationNotificationSender *notification.ConversationNotificationSender
@@ -61,7 +65,10 @@ func Start(client discoveryregistry.SvcDiscoveryRegistry, server *grpc.Server) e
} }
groupRpcClient := rpcclient.NewGroupRpcClient(client) groupRpcClient := rpcclient.NewGroupRpcClient(client)
msgRpcClient := rpcclient.NewMessageRpcClient(client) msgRpcClient := rpcclient.NewMessageRpcClient(client)
userRpcClient := rpcclient.NewUserRpcClient(client)
pbconversation.RegisterConversationServer(server, &conversationServer{ pbconversation.RegisterConversationServer(server, &conversationServer{
msgRpcClient: &msgRpcClient,
user: &userRpcClient,
conversationNotificationSender: notification.NewConversationNotificationSender(&msgRpcClient), conversationNotificationSender: notification.NewConversationNotificationSender(&msgRpcClient),
groupRpcClient: &groupRpcClient, groupRpcClient: &groupRpcClient,
conversationDatabase: controller.NewConversationDatabase(conversationDB, cache.NewConversationRedis(rdb, cache.GetDefaultOpt(), conversationDB), tx.NewMongo(mongo.GetClient())), 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 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) { func (c *conversationServer) GetAllConversations(ctx context.Context, req *pbconversation.GetAllConversationsReq) (*pbconversation.GetAllConversationsResp, error) {
conversations, err := c.conversationDatabase.GetUserAllConversation(ctx, req.OwnerUserID) conversations, err := c.conversationDatabase.GetUserAllConversation(ctx, req.OwnerUserID)
if err != nil { if err != nil {
@@ -348,3 +429,102 @@ func (c *conversationServer) GetConversationOfflinePushUserIDs(
} }
return &pbconversation.GetConversationOfflinePushUserIDsResp{UserIDs: utils.Keys(userIDSet)}, nil 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
}
+20 -18
View File
@@ -53,11 +53,6 @@ type friendServer struct {
RegisterCenter registry.SvcDiscoveryRegistry RegisterCenter registry.SvcDiscoveryRegistry
} }
func (s *friendServer) UpdateFriends(ctx context.Context, req *pbfriend.UpdateFriendsReq) (*pbfriend.UpdateFriendsResp, error) {
//TODO implement me
panic("implement me")
}
func Start(client registry.SvcDiscoveryRegistry, server *grpc.Server) error { func Start(client registry.SvcDiscoveryRegistry, server *grpc.Server) error {
// Initialize MongoDB // Initialize MongoDB
mongo, err := unrelation.NewMongo() mongo, err := unrelation.NewMongo()
@@ -441,7 +436,7 @@ func (s *friendServer) GetSpecifiedFriendsInfo(ctx context.Context, req *pbfrien
} }
return resp, nil return resp, nil
} }
func (s *friendServer) PinFriends( func (s *friendServer) UpdateFriends(
ctx context.Context, ctx context.Context,
req *pbfriend.UpdateFriendsReq, req *pbfriend.UpdateFriendsReq,
) (*pbfriend.UpdateFriendsResp, error) { ) (*pbfriend.UpdateFriendsResp, error) {
@@ -451,25 +446,32 @@ func (s *friendServer) PinFriends(
if utils.Duplicate(req.FriendUserIDs) { if utils.Duplicate(req.FriendUserIDs) {
return nil, errs.ErrArgs.Wrap("friendIDList repeated") return nil, errs.ErrArgs.Wrap("friendIDList repeated")
} }
var isPinned bool
if req.IsPinned != nil {
isPinned = req.IsPinned.Value
} else {
return nil, errs.ErrArgs.Wrap("isPinned is nil")
}
//check whther in friend list
_, err := s.friendDatabase.FindFriendsWithError(ctx, req.OwnerUserID, req.FriendUserIDs) _, err := s.friendDatabase.FindFriendsWithError(ctx, req.OwnerUserID, req.FriendUserIDs)
if err != nil { if err != nil {
return nil, err return nil, err
} }
//set friendslist friend pin status to isPinned val := make(map[string]any)
for _, friendID := range req.FriendUserIDs {
if err := s.friendDatabase.UpdateFriendPinStatus(ctx, req.OwnerUserID, friendID, isPinned); err != nil { if req.IsPinned != nil {
return nil, err 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{} 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 return resp, nil
} }
+45 -10
View File
@@ -109,14 +109,11 @@ type groupServer struct {
} }
func (s *groupServer) NotificationUserInfoUpdate(ctx context.Context, req *pbgroup.NotificationUserInfoUpdateReq) (*pbgroup.NotificationUserInfoUpdateResp, error) { 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) members, err := s.db.FindGroupMemberUser(ctx, nil, req.UserID)
if err != nil { if err != nil {
return nil, err return nil, err
} }
if err := s.PopulateGroupMember(ctx, members...); err != nil {
return nil, err
}
groupIDs := make([]string, 0, len(members)) groupIDs := make([]string, 0, len(members))
for _, member := range members { for _, member := range members {
if member.Nickname != "" && member.FaceURL != "" { 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) { func (s *groupServer) GetGroupMemberList(ctx context.Context, req *pbgroup.GetGroupMemberListReq) (*pbgroup.GetGroupMemberListResp, error) {
resp := &pbgroup.GetGroupMemberListResp{} 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 { if err != nil {
return nil, err return nil, err
} }
if err := s.PopulateGroupMember(ctx, members...); err != nil { if err := s.PopulateGroupMember(ctx, members...); err != nil {
return nil, err 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.Total = uint32(total)
resp.Members = utils.Batch(convert.Db2PbGroupMember, members) resp.Members = utils.Batch(convert.Db2PbGroupMember, members)
return resp, nil return resp, nil
@@ -1042,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) { func (s *groupServer) GetGroups(ctx context.Context, req *pbgroup.GetGroupsReq) (*pbgroup.GetGroupsResp, error) {
resp := &pbgroup.GetGroupsResp{} resp := &pbgroup.GetGroupsResp{}
var ( var (
groups []*relationtb.GroupModel group []*relationtb.GroupModel
err error err error
) )
if req.GroupID != "" { if req.GroupID != "" {
groups, err = s.db.FindGroup(ctx, []string{req.GroupID}) group, err = s.db.FindGroup(ctx, []string{req.GroupID})
resp.Total = uint32(len(groups)) resp.Total = uint32(len(group))
} else { } else {
var total int64 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) resp.Total = uint32(total)
} }
if err != nil { if err != nil {
return nil, err 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 { groupIDs := utils.Slice(groups, func(e *relationtb.GroupModel) string {
return e.GroupID return e.GroupID
}) })
+11 -2
View File
@@ -18,6 +18,7 @@ import (
"context" "context"
utils2 "github.com/OpenIMSDK/tools/utils" utils2 "github.com/OpenIMSDK/tools/utils"
cbapi "github.com/openimsdk/open-im-server/v3/pkg/callbackstruct"
"github.com/redis/go-redis/v9" "github.com/redis/go-redis/v9"
@@ -26,8 +27,6 @@ import (
"github.com/OpenIMSDK/protocol/sdkws" "github.com/OpenIMSDK/protocol/sdkws"
"github.com/OpenIMSDK/tools/errs" "github.com/OpenIMSDK/tools/errs"
"github.com/OpenIMSDK/tools/log" "github.com/OpenIMSDK/tools/log"
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) { func (m *msgServer) GetConversationsHasReadAndMaxSeq(ctx context.Context, req *msg.GetConversationsHasReadAndMaxSeqReq) (resp *msg.GetConversationsHasReadAndMaxSeqResp, err error) {
@@ -125,6 +124,16 @@ func (m *msgServer) MarkMsgsAsRead(
return 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, if err = m.sendMarkAsReadNotification(ctx, req.ConversationID, conversation.ConversationType, req.UserID,
m.conversationAndGetRecvID(conversation, req.UserID), req.Seqs, hasReadSeq); err != nil { m.conversationAndGetRecvID(conversation, req.UserID), req.Seqs, hasReadSeq); err != nil {
return return
+4 -4
View File
@@ -70,7 +70,7 @@ func GetContent(msg *sdkws.MsgData) string {
} }
func callbackBeforeSendSingleMsg(ctx context.Context, msg *pbchat.SendMsgReq) error { 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 return nil
} }
req := &cbapi.CallbackBeforeSendSingleMsgReq{ 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 { 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 return nil
} }
req := &cbapi.CallbackAfterSendSingleMsgReq{ 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 { 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 return nil
} }
req := &cbapi.CallbackBeforeSendGroupMsgReq{ 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 { 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 return nil
} }
req := &cbapi.CallbackAfterSendGroupMsgReq{ req := &cbapi.CallbackAfterSendGroupMsgReq{
+24 -1
View File
@@ -16,7 +16,6 @@ package msg
import ( import (
"context" "context"
pbmsg "github.com/OpenIMSDK/protocol/msg" pbmsg "github.com/OpenIMSDK/protocol/msg"
) )
@@ -30,3 +29,27 @@ func (m *msgServer) GetConversationMaxSeq(
} }
return &pbmsg.GetConversationMaxSeqResp{MaxSeq: maxSeq}, nil 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 ( import (
"context" "context"
"encoding/base64"
"encoding/hex"
"encoding/json"
"github.com/google/uuid"
"github.com/openimsdk/open-im-server/v3/pkg/authverify"
"path"
"strconv" "strconv"
"time" "time"
@@ -179,6 +185,113 @@ func (t *thirdServer) AccessURL(ctx context.Context, req *third.AccessURLReq) (*
}, nil }, 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 { func (t *thirdServer) apiAddress(name string) string {
return t.apiURL + name 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"`
}
-10
View File
@@ -101,16 +101,6 @@ type thirdServer struct {
defaultExpire time.Duration defaultExpire time.Duration
} }
func (t *thirdServer) InitiateFormData(ctx context.Context, req *third.InitiateFormDataReq) (*third.InitiateFormDataResp, error) {
//TODO implement me
panic("implement me")
}
func (t *thirdServer) CompleteFormData(ctx context.Context, req *third.CompleteFormDataReq) (*third.CompleteFormDataResp, error) {
//TODO implement me
panic("implement me")
}
func (t *thirdServer) FcmUpdateToken(ctx context.Context, req *third.FcmUpdateTokenReq) (resp *third.FcmUpdateTokenResp, err error) { func (t *thirdServer) FcmUpdateToken(ctx context.Context, req *third.FcmUpdateTokenReq) (resp *third.FcmUpdateTokenResp, err error) {
err = t.thirdDatabase.FcmUpdateToken(ctx, req.Account, int(req.PlatformID), req.FcmToken, req.ExpireTime) err = t.thirdDatabase.FcmUpdateToken(ctx, req.Account, int(req.PlatformID), req.FcmToken, req.ExpireTime)
if err != nil { if err != nil {
+3
View File
@@ -29,6 +29,9 @@ import (
) )
func toPbMapArray(m map[string][]string) []*third.KeyValues { func toPbMapArray(m map[string][]string) []*third.KeyValues {
if len(m) == 0 {
return nil
}
res := make([]*third.KeyValues, 0, len(m)) res := make([]*third.KeyValues, 0, len(m))
for key := range m { for key := range m {
res = append(res, &third.KeyValues{ res = append(res, &third.KeyValues{
+35 -2
View File
@@ -16,7 +16,6 @@ package user
import ( import (
"context" "context"
pbuser "github.com/OpenIMSDK/protocol/user" pbuser "github.com/OpenIMSDK/protocol/user"
"github.com/OpenIMSDK/tools/utils" "github.com/OpenIMSDK/tools/utils"
@@ -44,7 +43,6 @@ func CallbackBeforeUpdateUserInfo(ctx context.Context, req *pbuser.UpdateUserInf
utils.NotNilReplace(&req.UserInfo.Nickname, resp.Nickname) utils.NotNilReplace(&req.UserInfo.Nickname, resp.Nickname)
return nil return nil
} }
func CallbackAfterUpdateUserInfo(ctx context.Context, req *pbuser.UpdateUserInfoReq) error { func CallbackAfterUpdateUserInfo(ctx context.Context, req *pbuser.UpdateUserInfoReq) error {
if !config.Config.Callback.CallbackAfterUpdateUserInfo.Enable { if !config.Config.Callback.CallbackAfterUpdateUserInfo.Enable {
return nil return nil
@@ -61,6 +59,41 @@ func CallbackAfterUpdateUserInfo(ctx context.Context, req *pbuser.UpdateUserInfo
} }
return nil 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 { func CallbackBeforeUserRegister(ctx context.Context, req *pbuser.UserRegisterReq) error {
if !config.Config.Callback.CallbackBeforeUserRegister.Enable { if !config.Config.Callback.CallbackBeforeUserRegister.Enable {
+110 -64
View File
@@ -17,6 +17,8 @@ package user
import ( import (
"context" "context"
"errors" "errors"
"github.com/OpenIMSDK/tools/pagination"
"github.com/openimsdk/open-im-server/v3/pkg/common/db/table/relation"
"math/rand" "math/rand"
"strings" "strings"
"time" "time"
@@ -57,6 +59,11 @@ type userServer struct {
RegisterCenter registry.SvcDiscoveryRegistry RegisterCenter registry.SvcDiscoveryRegistry
} }
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 { func Start(client registry.SvcDiscoveryRegistry, server *grpc.Server) error {
rdb, err := cache.NewRedis() rdb, err := cache.NewRedis()
if err != nil { if err != nil {
@@ -148,7 +155,41 @@ func (s *userServer) UpdateUserInfo(ctx context.Context, req *pbuser.UpdateUserI
} }
return resp, nil 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) { func (s *userServer) SetGlobalRecvMessageOpt(ctx context.Context, req *pbuser.SetGlobalRecvMessageOptReq) (resp *pbuser.SetGlobalRecvMessageOptResp, err error) {
resp = &pbuser.SetGlobalRecvMessageOptResp{} resp = &pbuser.SetGlobalRecvMessageOptResp{}
if _, err := s.FindWithError(ctx, []string{req.UserID}); err != nil { if _, err := s.FindWithError(ctx, []string{req.UserID}); err != nil {
@@ -193,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) { 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 { if err != nil {
return nil, err return nil, err
} }
@@ -344,11 +385,6 @@ func (s *userServer) GetSubscribeUsersStatus(ctx context.Context,
// ProcessUserCommandAdd user general function add // ProcessUserCommandAdd user general function add
func (s *userServer) ProcessUserCommandAdd(ctx context.Context, req *pbuser.ProcessUserCommandAddReq) (*pbuser.ProcessUserCommandAddResp, error) { func (s *userServer) ProcessUserCommandAdd(ctx context.Context, req *pbuser.ProcessUserCommandAddReq) (*pbuser.ProcessUserCommandAddResp, error) {
// Assuming you have a method in s.UserDatabase to add a user command
err := s.UserDatabase.AddUserCommand(ctx, req.UserID, req.Type, req.Uuid, req.Value)
if err != nil {
return nil, err
}
return &pbuser.ProcessUserCommandAddResp{}, nil return &pbuser.ProcessUserCommandAddResp{}, nil
} }
@@ -360,42 +396,19 @@ func (s *userServer) ProcessUserCommandDelete(ctx context.Context, req *pbuser.P
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &pbuser.ProcessUserCommandDeleteResp{}, nil return &pbuser.ProcessUserCommandDeleteResp{}, nil
} }
// ProcessUserCommandUpdate user general function update // ProcessUserCommandUpdate user general function update
func (s *userServer) ProcessUserCommandUpdate(ctx context.Context, req *pbuser.ProcessUserCommandUpdateReq) (*pbuser.ProcessUserCommandUpdateResp, error) { func (s *userServer) ProcessUserCommandUpdate(ctx context.Context, req *pbuser.ProcessUserCommandUpdateReq) (*pbuser.ProcessUserCommandUpdateResp, error) {
// Assuming you have a method in s.UserDatabase to update a user command
err := s.UserDatabase.UpdateUserCommand(ctx, req.UserID, req.Type, req.Uuid, req.Value)
if err != nil {
return nil, err
}
return &pbuser.ProcessUserCommandUpdateResp{}, nil return &pbuser.ProcessUserCommandUpdateResp{}, nil
} }
func (s *userServer) ProcessUserCommandGet(ctx context.Context, req *pbuser.ProcessUserCommandGetReq) (*pbuser.ProcessUserCommandGetResp, error) { func (s *userServer) ProcessUserCommandGet(ctx context.Context, req *pbuser.ProcessUserCommandGetReq) (*pbuser.ProcessUserCommandGetResp, error) {
// Fetch user commands from the database
commands, err := s.UserDatabase.GetUserCommands(ctx, req.UserID, req.Type)
if err != nil {
return nil, err
}
// Initialize commandInfoSlice as an empty slice
commandInfoSlice := make([]*pbuser.CommandInfoResp, 0, len(commands))
for _, command := range commands {
// No need to use index since command is already a pointer
commandInfoSlice = append(commandInfoSlice, &pbuser.CommandInfoResp{
Uuid: command.Uuid,
Value: command.Value,
CreateTime: command.CreateTime,
})
}
// Return the response with the slice // Return the response with the slice
return &pbuser.ProcessUserCommandGetResp{KVArray: commandInfoSlice}, nil return &pbuser.ProcessUserCommandGetResp{}, nil
} }
func (s *userServer) AddNotificationAccount(ctx context.Context, req *pbuser.AddNotificationAccountReq) (*pbuser.AddNotificationAccountResp, error) { func (s *userServer) AddNotificationAccount(ctx context.Context, req *pbuser.AddNotificationAccountReq) (*pbuser.AddNotificationAccountResp, error) {
@@ -403,22 +416,28 @@ func (s *userServer) AddNotificationAccount(ctx context.Context, req *pbuser.Add
return nil, err return nil, err
} }
var userID string if req.UserID == "" {
for i := 0; i < 20; i++ { for i := 0; i < 20; i++ {
userId := s.genUserID() userId := s.genUserID()
_, err := s.UserDatabase.FindWithError(ctx, []string{userId}) _, err := s.UserDatabase.FindWithError(ctx, []string{userId})
if err == nil { if err == nil {
continue 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")
} }
userID = userId
break
}
if userID == "" {
return nil, errs.ErrInternalServer.Wrap("gen user id failed")
} }
user := &tablerelation.UserModel{ user := &tablerelation.UserModel{
UserID: userID, UserID: req.UserID,
Nickname: req.NickName, Nickname: req.NickName,
FaceURL: req.FaceURL, FaceURL: req.FaceURL,
CreateTime: time.Now(), CreateTime: time.Now(),
@@ -428,7 +447,11 @@ func (s *userServer) AddNotificationAccount(ctx context.Context, req *pbuser.Add
return nil, err return nil, err
} }
return &pbuser.AddNotificationAccountResp{}, nil 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) { func (s *userServer) UpdateNotificationAccountInfo(ctx context.Context, req *pbuser.UpdateNotificationAccountInfoReq) (*pbuser.UpdateNotificationAccountInfoResp, error) {
@@ -462,31 +485,34 @@ func (s *userServer) SearchNotificationAccount(ctx context.Context, req *pbuser.
return nil, err return nil, err
} }
_, users, err := s.UserDatabase.Page(ctx, req.Pagination) 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 { if err != nil {
return nil, err return nil, err
} }
var total int64 resp := s.userModelToResp(users, req.Pagination)
accounts := make([]*pbuser.NotificationAccountInfo, 0, len(users)) return resp, nil
for _, v := range users {
if v.AppMangerLevel != constant.AppNotificationAdmin {
continue
}
temp := &pbuser.NotificationAccountInfo{
UserID: v.UserID,
FaceURL: v.FaceURL,
NickName: v.Nickname,
}
accounts = append(accounts, temp)
total += 1
}
return &pbuser.SearchNotificationAccountResp{Total: total, NotificationAccounts: accounts}, nil
}
func (s *userServer) UpdateUserInfoEx(ctx context.Context, req *pbuser.UpdateUserInfoExReq) (*pbuser.UpdateUserInfoExResp, error) {
//TODO implement me
panic("implement me")
} }
func (s *userServer) GetNotificationAccount(ctx context.Context, req *pbuser.GetNotificationAccountReq) (*pbuser.GetNotificationAccountResp, error) { func (s *userServer) GetNotificationAccount(ctx context.Context, req *pbuser.GetNotificationAccountReq) (*pbuser.GetNotificationAccountResp, error) {
@@ -518,3 +544,23 @@ func (s *userServer) genUserID() string {
} }
return string(data) 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 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. // BatchSendMsgReq defines the structure for sending a message to multiple recipients.
type BatchSendMsgReq struct { type BatchSendMsgReq struct {
SendMsg SendMsg
+8 -2
View File
@@ -38,6 +38,9 @@ func CheckAccessV3(ctx context.Context, ownerUserID string) (err error) {
if utils.IsContain(opUserID, config.Config.Manager.UserID) { if utils.IsContain(opUserID, config.Config.Manager.UserID) {
return nil return nil
} }
if utils.IsContain(opUserID, config.Config.IMAdmin.UserID) {
return nil
}
if opUserID == ownerUserID { if opUserID == ownerUserID {
return nil return nil
} }
@@ -45,13 +48,16 @@ func CheckAccessV3(ctx context.Context, ownerUserID string) (err error) {
} }
func IsAppManagerUid(ctx context.Context) bool { 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 { func CheckAdmin(ctx context.Context) error {
if utils.IsContain(mcontext.GetOpUserID(ctx), config.Config.Manager.UserID) { if utils.IsContain(mcontext.GetOpUserID(ctx), config.Config.Manager.UserID) {
return nil 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))) return errs.ErrNoPermission.Wrap(fmt.Sprintf("user %s is not admin userID", mcontext.GetOpUserID(ctx)))
} }
func CheckIMAdmin(ctx context.Context) error { func CheckIMAdmin(ctx context.Context) error {
@@ -69,7 +75,7 @@ func ParseRedisInterfaceToken(redisToken any) (*tokenverify.Claims, error) {
} }
func IsManagerUserID(opUserID string) bool { 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 { func WsVerifyToken(token, userID string, platformID int) error {
+2
View File
@@ -37,6 +37,8 @@ const (
CallbackGroupMsgReadCommand = "callbackGroupMsgReadCommand" CallbackGroupMsgReadCommand = "callbackGroupMsgReadCommand"
CallbackMsgModifyCommand = "callbackMsgModifyCommand" CallbackMsgModifyCommand = "callbackMsgModifyCommand"
CallbackAfterUpdateUserInfoCommand = "callbackAfterUpdateUserInfoCommand" CallbackAfterUpdateUserInfoCommand = "callbackAfterUpdateUserInfoCommand"
CallbackAfterUpdateUserInfoExCommand = "callbackAfterUpdateUserInfoExCommand"
CallbackBeforeUpdateUserInfoExCommand = "callbackBeforeUpdateUserInfoExCommand"
CallbackBeforeUserRegisterCommand = "callbackBeforeUserRegisterCommand" CallbackBeforeUserRegisterCommand = "callbackBeforeUserRegisterCommand"
CallbackAfterUserRegisterCommand = "callbackAfterUserRegisterCommand" CallbackAfterUserRegisterCommand = "callbackAfterUserRegisterCommand"
CallbackTransferGroupOwnerAfter = "callbackTransferGroupOwnerAfter" CallbackTransferGroupOwnerAfter = "callbackTransferGroupOwnerAfter"
+4 -3
View File
@@ -94,9 +94,10 @@ type CallbackGroupMsgReadResp struct {
type CallbackSingleMsgReadReq struct { type CallbackSingleMsgReadReq struct {
CallbackCommand `json:"callbackCommand"` CallbackCommand `json:"callbackCommand"`
SendID string `json:"sendID"` UserID string `json:"userID"`
ReceiveID string `json:"receiveID"` ConversationID string `json:"conversationID"`
ContentType int64 `json:"contentType"` ContentType int32 `json:"contentType"`
Seqs []int64 `json:"seqs"`
} }
type CallbackSingleMsgReadResp struct { type CallbackSingleMsgReadResp struct {
+29 -1
View File
@@ -14,7 +14,10 @@
package callbackstruct package callbackstruct
import "github.com/OpenIMSDK/protocol/sdkws" import (
"github.com/OpenIMSDK/protocol/sdkws"
"github.com/OpenIMSDK/protocol/wrapperspb"
)
type CallbackBeforeUpdateUserInfoReq struct { type CallbackBeforeUpdateUserInfoReq struct {
CallbackCommand `json:"callbackCommand"` CallbackCommand `json:"callbackCommand"`
@@ -41,6 +44,31 @@ type CallbackAfterUpdateUserInfoResp struct {
CommonCallbackResp 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 { type CallbackBeforeUserRegisterReq struct {
CallbackCommand `json:"callbackCommand"` CallbackCommand `json:"callbackCommand"`
Secret string `json:"secret"` Secret string `json:"secret"`
+1 -1
View File
@@ -45,7 +45,7 @@ type CmdOpts struct {
func WithCronTaskLogName() func(*CmdOpts) { func WithCronTaskLogName() func(*CmdOpts) {
return func(opts *CmdOpts) { return func(opts *CmdOpts) {
opts.loggerPrefixName = "OpenIM.CronTask.log.all" opts.loggerPrefixName = "openim.crontask.log.all"
} }
} }
+3 -1
View File
@@ -282,6 +282,8 @@ type configStruct struct {
CallbackBeforeSetFriendRemark CallBackConfig `yaml:"callbackBeforeSetFriendRemark"` CallbackBeforeSetFriendRemark CallBackConfig `yaml:"callbackBeforeSetFriendRemark"`
CallbackAfterSetFriendRemark CallBackConfig `yaml:"callbackAfterSetFriendRemark"` CallbackAfterSetFriendRemark CallBackConfig `yaml:"callbackAfterSetFriendRemark"`
CallbackBeforeUpdateUserInfo CallBackConfig `yaml:"beforeUpdateUserInfo"` CallbackBeforeUpdateUserInfo CallBackConfig `yaml:"beforeUpdateUserInfo"`
CallbackBeforeUpdateUserInfoEx CallBackConfig `yaml:"beforeUpdateUserInfoEx"`
CallbackAfterUpdateUserInfoEx CallBackConfig `yaml:"afterUpdateUserInfoEx"`
CallbackBeforeUserRegister CallBackConfig `yaml:"beforeUserRegister"` CallbackBeforeUserRegister CallBackConfig `yaml:"beforeUserRegister"`
CallbackAfterUpdateUserInfo CallBackConfig `yaml:"updateUserInfo"` CallbackAfterUpdateUserInfo CallBackConfig `yaml:"updateUserInfo"`
CallbackAfterUserRegister CallBackConfig `yaml:"afterUserRegister"` CallbackAfterUserRegister CallBackConfig `yaml:"afterUserRegister"`
@@ -312,7 +314,7 @@ type configStruct struct {
Prometheus struct { Prometheus struct {
Enable bool `yaml:"enable"` Enable bool `yaml:"enable"`
PrometheusUrl string `yaml:"prometheusUrl"` GrafanaUrl string `yaml:"grafanaUrl"`
ApiPrometheusPort []int `yaml:"apiPrometheusPort"` ApiPrometheusPort []int `yaml:"apiPrometheusPort"`
UserPrometheusPort []int `yaml:"userPrometheusPort"` UserPrometheusPort []int `yaml:"userPrometheusPort"`
FriendPrometheusPort []int `yaml:"friendPrometheusPort"` FriendPrometheusPort []int `yaml:"friendPrometheusPort"`
+23 -1
View File
@@ -64,7 +64,7 @@ func UserPb2DBMap(user *sdkws.UserInfo) map[string]any {
"global_recv_msg_opt": user.GlobalRecvMsgOpt, "global_recv_msg_opt": user.GlobalRecvMsgOpt,
} }
for key, value := range fields { for key, value := range fields {
if v, ok := value.(string); ok { if v, ok := value.(string); ok && v != "" {
val[key] = v val[key] = v
} else if v, ok := value.(int32); ok && v != 0 { } else if v, ok := value.(int32); ok && v != 0 {
val[key] = v val[key] = v
@@ -72,3 +72,25 @@ func UserPb2DBMap(user *sdkws.UserInfo) map[string]any {
} }
return val 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) GetFriend(ctx context.Context, ownerUserID, friendUserID string) (friend *relationtb.FriendModel, err error)
// Delete friend when friend info changed // Delete friend when friend info changed
DelFriend(ownerUserID, friendUserID string) FriendCache 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. // FriendCacheRedis is an implementation of the FriendCache interface using Redis.
@@ -152,3 +154,15 @@ func (f *FriendCacheRedis) DelFriend(ownerUserID, friendUserID string) FriendCac
return newFriendCache 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. // overrideConfigFromEnv overrides configuration fields with environment variables if present.
func overrideConfigFromEnv() { func overrideConfigFromEnv() {
if envAddr := os.Getenv("REDIS_ADDRESS"); envAddr != "" { 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 != "" { if envUser := os.Getenv("REDIS_USERNAME"); envUser != "" {
config.Config.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 { for _, v := range existConversationUserIDs {
cache = cache.DelConversations(v, conversationID) cache = cache.DelConversations(v, conversationID)
} }
return c.cache.ExecDel(ctx) return cache.ExecDel(ctx)
}) })
} }
+36 -16
View File
@@ -32,33 +32,50 @@ import (
) )
type FriendDatabase interface { 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) 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) 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) 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) RefuseFriendRequest(ctx context.Context, friendRequest *relation.FriendRequestModel) (err error)
// 同意好友申请
// AgreeFriendRequest accepts a friend request
AgreeFriendRequest(ctx context.Context, friendRequest *relation.FriendRequestModel) (err error) 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) 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) 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) 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) 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) 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) 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) 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) 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) FindBothFriendRequests(ctx context.Context, fromUserID, toUserID string) (friends []*relation.FriendRequestModel, err error)
UpdateFriendPinStatus(ctx context.Context, ownerUserID string, friendUserID string, isPinned bool) (err error)
// UpdateFriends updates fields for friends
UpdateFriends(ctx context.Context, ownerUserID string, friendUserIDs []string, val map[string]any) (err error)
} }
type friendDatabase struct { type friendDatabase struct {
@@ -299,9 +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) { func (f *friendDatabase) FindBothFriendRequests(ctx context.Context, fromUserID, toUserID string) (friends []*relation.FriendRequestModel, err error) {
return f.friendRequest.FindBothFriendRequests(ctx, fromUserID, toUserID) return f.friendRequest.FindBothFriendRequests(ctx, fromUserID, toUserID)
} }
func (f *friendDatabase) UpdateFriendPinStatus(ctx context.Context, ownerUserID string, friendUserID string, isPinned bool) (err error) { func (f *friendDatabase) UpdateFriends(ctx context.Context, ownerUserID string, friendUserIDs []string, val map[string]any) (err error) {
if err := f.friend.UpdatePinStatus(ctx, ownerUserID, friendUserID, isPinned); err != nil { if len(val) == 0 {
return nil
}
if err := f.friend.UpdateFriends(ctx, ownerUserID, friendUserIDs, val); err != nil {
return err return err
} }
return f.cache.DelFriend(ownerUserID, friendUserID).ExecDel(ctx) 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 { func (g *groupDatabase) DismissGroup(ctx context.Context, groupID string, deleteMember bool) error {
return g.ctxTx.Transaction(ctx, func(ctx context.Context) error { return g.ctxTx.Transaction(ctx, func(ctx context.Context) error {
c := g.cache.NewCache() 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 return err
} }
if deleteMember { if deleteMember {
+16
View File
@@ -98,6 +98,7 @@ type CommonMsgDatabase interface {
SetSendMsgStatus(ctx context.Context, id string, status int32) error SetSendMsgStatus(ctx context.Context, id string, status int32) error
GetSendMsgStatus(ctx context.Context, id string) (int32, error) GetSendMsgStatus(ctx context.Context, id string) (int32, error)
SearchMessage(ctx context.Context, req *pbmsg.SearchMessageReq) (total int32, msgData []*sdkws.MsgData, err 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 // to mq
MsgToMQ(ctx context.Context, key string, msg2mq *sdkws.MsgData) error 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 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) { func (db *commonMsgDatabase) ConvertMsgsDocLen(ctx context.Context, conversationIDs []string) {
db.msgDocDatabase.ConvertMsgsDocLen(ctx, conversationIDs) 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) 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) AccessURL(ctx context.Context, name string, expire time.Duration, opt *s3.AccessURLOption) (time.Time, string, error)
SetObject(ctx context.Context, info *relation.ObjectModel) 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 { 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 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)
}
+20
View File
@@ -38,12 +38,18 @@ type UserDatabase interface {
FindWithError(ctx context.Context, userIDs []string) (users []*relation.UserModel, err error) 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 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(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 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) Create(ctx context.Context, users []*relation.UserModel) (err error)
// Update update (non-zero value) external guarantee userID exists // Update update (non-zero value) external guarantee userID exists
//Update(ctx context.Context, user *relation.UserModel) (err error) //Update(ctx context.Context, user *relation.UserModel) (err error)
// UpdateByMap update (zero value) external guarantee userID exists // UpdateByMap update (zero value) external guarantee userID exists
UpdateByMap(ctx context.Context, userID string, args map[string]any) (err error) 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 If not found, no error is returned
Page(ctx context.Context, pagination pagination.Pagination) (count int64, users []*relation.UserModel, err error) Page(ctx context.Context, pagination pagination.Pagination) (count int64, users []*relation.UserModel, err error)
// IsExist true as long as one exists // IsExist true as long as one exists
@@ -133,6 +139,16 @@ func (u *userDatabase) Find(ctx context.Context, userIDs []string) (users []*rel
return u.cache.GetUsersInfo(ctx, userIDs) 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. // 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) { func (u *userDatabase) Create(ctx context.Context, users []*relation.UserModel) (err error) {
return u.tx.Transaction(ctx, func(ctx context.Context) error { return u.tx.Transaction(ctx, func(ctx context.Context) error {
@@ -168,6 +184,10 @@ func (u *userDatabase) Page(ctx context.Context, pagination pagination.Paginatio
return u.userDB.Page(ctx, pagination) 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. // 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) { func (u *userDatabase) IsExist(ctx context.Context, userIDs []string) (exist bool, err error) {
users, err := u.userDB.Find(ctx, userIDs) users, err := u.userDB.Find(ctx, userIDs)
+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) { func (c *ConversationMgo) GetAllConversationIDsNumber(ctx context.Context) (int64, error) {
counts, err := mgoutil.Aggregate[int64](ctx, c.coll, []bson.M{ counts, err := mgoutil.Aggregate[int64](ctx, c.coll, []bson.M{
{"$group": bson.M{"_id": "$conversation_id"}}, {"$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 { if err != nil {
return 0, err return 0, err
+16 -14
View File
@@ -16,7 +16,6 @@ package mgo
import ( import (
"context" "context"
"github.com/OpenIMSDK/tools/errs"
"github.com/OpenIMSDK/tools/mgoutil" "github.com/OpenIMSDK/tools/mgoutil"
"github.com/OpenIMSDK/tools/pagination" "github.com/OpenIMSDK/tools/pagination"
"go.mongodb.org/mongo-driver/mongo/options" "go.mongodb.org/mongo-driver/mongo/options"
@@ -144,19 +143,22 @@ func (f *FriendMgo) FindFriendUserIDs(ctx context.Context, ownerUserID string) (
return mgoutil.Find[string](ctx, f.coll, filter, options.Find().SetProjection(bson.M{"_id": 0, "friend_user_id": 1})) return mgoutil.Find[string](ctx, f.coll, filter, options.Find().SetProjection(bson.M{"_id": 0, "friend_user_id": 1}))
} }
// UpdatePinStatus update friend's pin status func (f *FriendMgo) UpdateFriends(ctx context.Context, ownerUserID string, friendUserIDs []string, val map[string]any) error {
func (f *FriendMgo) UpdatePinStatus(ctx context.Context, ownerUserID string, friendUserID string, isPinned bool) (err error) { // Ensure there are IDs to update
if len(friendUserIDs) == 0 {
filter := bson.M{"owner_user_id": ownerUserID, "friend_user_id": friendUserID} return nil // Or return an error if you expect there to always be IDs
// Create an update operation to set the "is_pinned" field to isPinned for all documents.
update := bson.M{"$set": bson.M{"is_pinned": isPinned}}
// Perform the update operation for all documents in the collection.
_, err = f.coll.UpdateMany(ctx, filter, update)
if err != nil {
return errs.Wrap(err, "update pin error")
} }
return nil // 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) return mgoutil.InsertMany(ctx, g.coll, groups)
} }
func (g *GroupMgo) UpdateState(ctx context.Context, groupID string, state int32) (err error) { func (g *GroupMgo) UpdateStatus(ctx context.Context, groupID string, status int32) (err error) {
return g.UpdateMap(ctx, groupID, map[string]any{"state": state}) 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) { 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) { 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 { 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) { func (g *GroupMemberMgo) SearchMember(ctx context.Context, keyword string, groupID string, pagination pagination.Pagination) (total int64, groupList []*relation.GroupMemberModel, err error) {
//TODO implement me filter := bson.M{"group_id": groupID, "nickname": bson.M{"$regex": keyword}}
panic("implement me") return mgoutil.FindPage[*relation.GroupMemberModel](ctx, g.coll, filter, pagination)
} }
func (g *GroupMemberMgo) FindUserJoinedGroupID(ctx context.Context, userID string) (groupIDs []string, err error) { func (g *GroupMemberMgo) FindUserJoinedGroupID(ctx context.Context, userID string) (groupIDs []string, err error) {
+21 -2
View File
@@ -65,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}) 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) { 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) 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) { 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) { func (u *UserMgo) Exist(ctx context.Context, userID string) (exist bool, err error) {
@@ -78,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) { 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) { func (u *UserMgo) CountTotal(ctx context.Context, before *time.Time) (count int64, err error) {
+1
View File
@@ -17,6 +17,7 @@ package cont
const ( const (
hashPath = "openim/data/hash/" hashPath = "openim/data/hash/"
tempPath = "openim/temp/" tempPath = "openim/temp/"
DirectPath = "openim/direct"
UploadTypeMultipart = 1 // 分片上传 UploadTypeMultipart = 1 // 分片上传
UploadTypePresigned = 2 // 预签名上传 UploadTypePresigned = 2 // 预签名上传
partSeparator = "," 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) 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 ( import (
"context" "context"
"crypto/hmac"
"crypto/sha1"
"encoding/base64"
"encoding/hex"
"encoding/json"
"errors" "errors"
"fmt" "fmt"
"net/http" "net/http"
@@ -44,6 +49,8 @@ const (
imageWebp = "webp" imageWebp = "webp"
) )
const successCode = http.StatusOK
const ( const (
videoSnapshotImagePng = "png" videoSnapshotImagePng = "png"
videoSnapshotImageJpg = "jpg" 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 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" imageThumbnailPath = "openim/thumbnail"
) )
const successCode = http.StatusOK
func NewMinio(cache cache.MinioCache) (s3.Interface, error) { func NewMinio(cache cache.MinioCache) (s3.Interface, error) {
u, err := url.Parse(config.Config.Object.Minio.Endpoint) u, err := url.Parse(config.Config.Object.Minio.Endpoint)
if err != nil { 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)) 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 ( import (
"context" "context"
"crypto/hmac"
"crypto/sha1"
"encoding/base64"
"encoding/json"
"errors" "errors"
"fmt" "fmt"
"io"
"net/http" "net/http"
"net/url" "net/url"
"reflect" "reflect"
@@ -45,6 +50,8 @@ const (
imageWebp = "webp" imageWebp = "webp"
) )
const successCode = http.StatusOK
const ( const (
videoSnapshotImagePng = "png" videoSnapshotImagePng = "png"
videoSnapshotImageJpg = "jpg" 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) params := getURLParams(*o.bucket.Client.Conn, rawParams)
return getURL(o.um, o.bucket.BucketName, name, params).String(), nil 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"` 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 { type SignPart struct {
PartNumber int `json:"partNumber"` PartNumber int `json:"partNumber"`
URL string `json:"url"` 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) 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) 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)
} }
+2 -2
View File
@@ -57,6 +57,6 @@ type FriendModelInterface interface {
FindInWhoseFriends(ctx context.Context, friendUserID string, pagination pagination.Pagination) (total int64, friends []*FriendModel, err error) 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 retrieves a list of friend user IDs for a given owner.
FindFriendUserIDs(ctx context.Context, ownerUserID string) (friendUserIDs []string, err error) FindFriendUserIDs(ctx context.Context, ownerUserID string) (friendUserIDs []string, err error)
// UpdatePinStatus update friend's pin status // UpdateFriends update friends' fields
UpdatePinStatus(ctx context.Context, ownerUserID string, friendUserID string, isPinned bool) (err error) 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 { type GroupModelInterface interface {
Create(ctx context.Context, groups []*GroupModel) (err error) Create(ctx context.Context, groups []*GroupModel) (err error)
UpdateMap(ctx context.Context, groupID string, args map[string]any) (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) Find(ctx context.Context, groupIDs []string) (groups []*GroupModel, err error)
Take(ctx context.Context, groupID string) (group *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) Search(ctx context.Context, keyword string, pagination pagination.Pagination) (total int64, groups []*GroupModel, err error)
+3
View File
@@ -53,7 +53,10 @@ type UserModelInterface interface {
UpdateByMap(ctx context.Context, userID string, args map[string]any) (err error) UpdateByMap(ctx context.Context, userID string, args map[string]any) (err error)
Find(ctx context.Context, userIDs []string) (users []*UserModel, err error) Find(ctx context.Context, userIDs []string) (users []*UserModel, err error)
Take(ctx context.Context, userID string) (user *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) 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) Exist(ctx context.Context, userID string) (exist bool, err error)
GetAllUserID(ctx context.Context, pagination pagination.Pagination) (count int64, userIDs []string, 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) GetUserGlobalRecvMsgOpt(ctx context.Context, userID string) (opt int, err error)
+8 -4
View File
@@ -74,8 +74,12 @@ func buildMongoURI() string {
return uri return uri
} }
username := os.Getenv("MONGO_USERNAME") if config.Config.Mongo.Uri != "" {
password := os.Getenv("MONGO_PASSWORD") return config.Config.Mongo.Uri
}
username := os.Getenv("MONGO_OPENIM_USERNAME")
password := os.Getenv("MONGO_OPENIM_PASSWORD")
address := os.Getenv("MONGO_ADDRESS") address := os.Getenv("MONGO_ADDRESS")
port := os.Getenv("MONGO_PORT") port := os.Getenv("MONGO_PORT")
database := os.Getenv("MONGO_DATABASE") database := os.Getenv("MONGO_DATABASE")
@@ -99,9 +103,9 @@ func buildMongoURI() string {
maxPoolSize = fmt.Sprint(config.Config.Mongo.MaxPoolSize) maxPoolSize = fmt.Sprint(config.Config.Mongo.MaxPoolSize)
} }
uriFormat := "mongodb://%s/%s?maxPoolSize=%s&authSource=admin" uriFormat := "mongodb://%s/%s?maxPoolSize=%s"
if username != "" && password != "" { 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, username, password, address, database, maxPoolSize)
} }
return fmt.Sprintf(uriFormat, address, database, maxPoolSize) return fmt.Sprintf(uriFormat, address, database, maxPoolSize)
@@ -24,7 +24,8 @@ import (
func setupTestEnvironment() { func setupTestEnvironment() {
os.Setenv("ZOOKEEPER_SCHEMA", "openim") 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_USERNAME", "")
os.Setenv("ZOOKEEPER_PASSWORD", "") os.Setenv("ZOOKEEPER_PASSWORD", "")
} }
@@ -55,14 +55,17 @@ func (cli *K8sDR) Register(serviceName, host string, port int, opts ...grpc.Dial
return nil return nil
} }
func (cli *K8sDR) UnRegister() error { func (cli *K8sDR) UnRegister() error {
return nil return nil
} }
func (cli *K8sDR) CreateRpcRootNodes(serviceNames []string) error { func (cli *K8sDR) CreateRpcRootNodes(serviceNames []string) error {
return nil return nil
} }
func (cli *K8sDR) RegisterConf2Registry(key string, conf []byte) error { func (cli *K8sDR) RegisterConf2Registry(key string, conf []byte) error {
return nil return nil
@@ -123,6 +126,8 @@ func getMsgGatewayHost(ctx context.Context) []string {
log.ZInfo(ctx, "getMsgGatewayHost", "instance", instance, "selfPodName", selfPodName, "replicas", replicas, "ns", ns, "ret", ret) log.ZInfo(ctx, "getMsgGatewayHost", "instance", instance, "selfPodName", selfPodName, "replicas", replicas, "ns", ns, "ret", ret)
return ret return ret
} }
// 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) { func (cli *K8sDR) GetConns(ctx context.Context, serviceName string, opts ...grpc.DialOption) ([]*grpc.ClientConn, error) {
if serviceName != config.Config.RpcRegisterName.OpenImMessageGatewayName { if serviceName != config.Config.RpcRegisterName.OpenImMessageGatewayName {
@@ -142,6 +147,7 @@ func (cli *K8sDR) GetConns(ctx context.Context, serviceName string, opts ...grpc
return ret, nil return ret, nil
} }
} }
func (cli *K8sDR) GetConn(ctx context.Context, serviceName string, opts ...grpc.DialOption) (*grpc.ClientConn, error) { func (cli *K8sDR) GetConn(ctx context.Context, serviceName string, opts ...grpc.DialOption) (*grpc.ClientConn, error) {
return grpc.DialContext(ctx, serviceName, append(cli.options, opts...)...) return grpc.DialContext(ctx, serviceName, append(cli.options, opts...)...)
@@ -151,9 +157,11 @@ func (cli *K8sDR) GetSelfConnTarget() string {
return cli.rpcRegisterAddr return cli.rpcRegisterAddr
} }
func (cli *K8sDR) AddOption(opts ...grpc.DialOption) { func (cli *K8sDR) AddOption(opts ...grpc.DialOption) {
cli.options = append(cli.options, opts...) cli.options = append(cli.options, opts...)
} }
func (cli *K8sDR) CloseConn(conn *grpc.ClientConn) { func (cli *K8sDR) CloseConn(conn *grpc.ClientConn) {
conn.Close() conn.Close()
} }
@@ -52,10 +52,18 @@ func getEnv(key, fallback string) string {
return fallback 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 { func getZkAddrFromEnv(fallback []string) []string {
if value, exists := os.LookupEnv("ZOOKEEPER_ADDRESS"); exists { address, addrExists := os.LookupEnv("ZOOKEEPER_ADDRESS")
return strings.Split(value, ",") 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 return fallback
} }
+1 -1
View File
@@ -432,7 +432,7 @@ func computeApproximateRequestSize(r *http.Request) int {
} }
s += len(r.Host) 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 { if r.ContentLength != -1 {
s += int(r.ContentLength) s += int(r.ContentLength)
+6 -7
View File
@@ -31,15 +31,14 @@ import (
"github.com/openimsdk/open-im-server/v3/pkg/common/config" "github.com/openimsdk/open-im-server/v3/pkg/common/config"
) )
const ( const maxRetry = 10 // number of retries
maxRetry = 10 // Maximum number of retries for producer creation
)
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 { type Producer struct {
topic string
addr []string addr []string
topic string
config *sarama.Config config *sarama.Config
producer sarama.SyncProducer 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 // Get Kafka configuration from environment variables or fallback to config file
kafkaUsername := getEnvOrConfig("KAFKA_USERNAME", config.Config.Kafka.Username) kafkaUsername := getEnvOrConfig("KAFKA_USERNAME", config.Config.Kafka.Username)
kafkaPassword := getEnvOrConfig("KAFKA_PASSWORD", config.Config.Kafka.Password) 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 // Configure SASL authentication if credentials are provided
if kafkaUsername != "" && kafkaPassword != "" { if kafkaUsername != "" && kafkaPassword != "" {
@@ -78,7 +77,7 @@ func NewKafkaProducer(addr []string, topic string) *Producer {
} }
// Set the Kafka address // Set the Kafka address
p.addr = []string{kafkaAddr} p.addr = kafkaAddr
// Set up TLS configuration (if required) // Set up TLS configuration (if required)
SetupTLSConfig(p.config) SetupTLSConfig(p.config)
+19
View File
@@ -15,7 +15,9 @@
package kafka package kafka
import ( import (
"fmt"
"os" "os"
"strings"
"github.com/IBM/sarama" "github.com/IBM/sarama"
@@ -44,3 +46,20 @@ func getEnvOrConfig(envName string, configValue string) string {
} }
return configValue 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.IsOfflinePush] = false
options[constant.IsUnreadCount] = false options[constant.IsUnreadCount] = false
options[constant.IsConversationUpdate] = false options[constant.IsConversationUpdate] = false
options[constant.IsSenderSync] = false options[constant.IsSenderSync] = true
options[constant.IsNotPrivate] = false options[constant.IsNotPrivate] = false
options[constant.IsSenderConversationUpdate] = false options[constant.IsSenderConversationUpdate] = false
options[constant.IsSenderNotificationPush] = false
options[constant.IsReactionFromCache] = false options[constant.IsReactionFromCache] = false
for _, opt := range opts { for _, opt := range opts {
opt(options) opt(options)
@@ -114,12 +113,6 @@ func WithSenderConversationUpdate() OptionsOpt {
} }
} }
func WithSenderNotificationPush() OptionsOpt {
return func(options Options) {
options[constant.IsSenderNotificationPush] = true
}
}
func WithReactionFromCache() OptionsOpt { func WithReactionFromCache() OptionsOpt {
return func(options Options) { return func(options Options) {
options[constant.IsReactionFromCache] = true options[constant.IsReactionFromCache] = true
@@ -174,10 +167,6 @@ func (o Options) IsSenderConversationUpdate() bool {
return o.Is(constant.IsSenderConversationUpdate) return o.Is(constant.IsSenderConversationUpdate)
} }
func (o Options) IsSenderNotificationPush() bool {
return o.Is(constant.IsSenderNotificationPush)
}
func (o Options) IsReactionFromCache() bool { func (o Options) IsReactionFromCache() bool {
return o.Is(constant.IsReactionFromCache) return o.Is(constant.IsReactionFromCache)
} }
+35 -1
View File
@@ -17,7 +17,6 @@ package rpcclient
import ( import (
"context" "context"
"encoding/json" "encoding/json"
"google.golang.org/grpc" "google.golang.org/grpc"
"google.golang.org/protobuf/proto" "google.golang.org/protobuf/proto"
@@ -68,6 +67,7 @@ func newContentTypeConf() map[int32]config.NotificationConf {
constant.BlackAddedNotification: config.Config.Notification.BlackAdded, constant.BlackAddedNotification: config.Config.Notification.BlackAdded,
constant.BlackDeletedNotification: config.Config.Notification.BlackDeleted, constant.BlackDeletedNotification: config.Config.Notification.BlackDeleted,
constant.FriendInfoUpdatedNotification: config.Config.Notification.FriendInfoUpdated, constant.FriendInfoUpdatedNotification: config.Config.Notification.FriendInfoUpdated,
constant.FriendsInfoUpdateNotification: config.Config.Notification.FriendInfoUpdated, //use the same FriendInfoUpdated
// conversation // conversation
constant.ConversationChangeNotification: config.Config.Notification.ConversationChanged, constant.ConversationChangeNotification: config.Config.Notification.ConversationChanged,
constant.ConversationUnreadNotification: config.Config.Notification.ConversationChanged, constant.ConversationUnreadNotification: config.Config.Notification.ConversationChanged,
@@ -115,6 +115,7 @@ func newSessionTypeConf() map[int32]int32 {
constant.BlackAddedNotification: constant.SingleChatType, constant.BlackAddedNotification: constant.SingleChatType,
constant.BlackDeletedNotification: constant.SingleChatType, constant.BlackDeletedNotification: constant.SingleChatType,
constant.FriendInfoUpdatedNotification: constant.SingleChatType, constant.FriendInfoUpdatedNotification: constant.SingleChatType,
constant.FriendsInfoUpdateNotification: constant.SingleChatType,
// conversation // conversation
constant.ConversationChangeNotification: constant.SingleChatType, constant.ConversationChangeNotification: constant.SingleChatType,
constant.ConversationUnreadNotification: constant.SingleChatType, constant.ConversationUnreadNotification: constant.SingleChatType,
@@ -155,6 +156,30 @@ func (m *MessageRpcClient) GetMaxSeq(ctx context.Context, req *sdkws.GetMaxSeqRe
return resp, err 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) { func (m *MessageRpcClient) PullMessageBySeqList(ctx context.Context, req *sdkws.PullMessageBySeqsReq) (*sdkws.PullMessageBySeqsResp, error) {
resp, err := m.Client.PullMessageBySeqs(ctx, req) resp, err := m.Client.PullMessageBySeqs(ctx, req)
return resp, err return resp, err
@@ -256,6 +281,7 @@ func (s *NotificationSender) NotificationWithSesstionType(ctx context.Context, s
optionsConfig.ReliabilityLevel = constant.UnreliableNotification optionsConfig.ReliabilityLevel = constant.UnreliableNotification
} }
options := config.GetOptionsByNotification(optionsConfig) options := config.GetOptionsByNotification(optionsConfig)
s.SetOptionsByContentType(ctx, options, contentType)
msg.Options = options msg.Options = options
offlineInfo.Title = title offlineInfo.Title = title
offlineInfo.Desc = desc 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 { 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...) 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 tips.FromToUserID.ToUserID = toUserID
return f.Notification(ctx, fromUserID, toUserID, constant.FriendRemarkSetNotification, &tips) 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 { func (f *FriendNotificationSender) BlackAddedNotification(ctx context.Context, req *pbfriend.AddBlackReq) error {
tips := sdkws.BlackAddedTips{FromToUserID: &sdkws.FromToUserID{}} tips := sdkws.BlackAddedTips{FromToUserID: &sdkws.FromToUserID{}}
tips.FromToUserID.FromUserID = req.OwnerUserID tips.FromToUserID.FromUserID = req.OwnerUserID
+13 -3
View File
@@ -409,11 +409,16 @@ func (g *GroupNotificationSender) GroupApplicationAcceptedNotification(ctx conte
if err != nil { if err != nil {
return err 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 { if err := g.fillOpUser(ctx, &tips.OpUser, tips.Group.GroupID); err != nil {
return err 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) err = g.Notification(ctx, mcontext.GetOpUserID(ctx), userID, constant.GroupApplicationAcceptedNotification, tips)
if err != nil { if err != nil {
log.ZError(ctx, "failed", err) 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 { if err := g.fillOpUser(ctx, &tips.OpUser, tips.Group.GroupID); err != nil {
return err 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) err = g.Notification(ctx, mcontext.GetOpUserID(ctx), userID, constant.GroupApplicationRejectedNotification, tips)
if err != nil { if err != nil {
log.ZError(ctx, "failed", err) log.ZError(ctx, "failed", err)
+3
View File
@@ -64,6 +64,9 @@ func NewUserRpcClient(client discoveryregistry.SvcDiscoveryRegistry) UserRpcClie
// GetUsersInfo retrieves information for multiple users based on their user IDs. // GetUsersInfo retrieves information for multiple users based on their user IDs.
func (u *UserRpcClient) GetUsersInfo(ctx context.Context, userIDs []string) ([]*sdkws.UserInfo, error) { 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{ resp, err := u.Client.GetDesignateUsers(ctx, &user.GetDesignateUsersReq{
UserIDs: userIDs, UserIDs: userIDs,
}) })
+3 -1
View File
@@ -3,4 +3,6 @@ go.mod
go.sum go.sum
third_party/ third_party/
translations/ translations/
log logs
.git
.golangci.yml
+45 -45
View File
@@ -23,7 +23,7 @@ trap 'openim::util::onCtrlC' INT
print_with_delay() { print_with_delay() {
text="$1" text="$1"
delay="$2" delay="$2"
for i in $(seq 0 $((${#text}-1))); do for i in $(seq 0 $((${#text}-1))); do
printf "${text:$i:1}" printf "${text:$i:1}"
sleep $delay sleep $delay
@@ -34,7 +34,7 @@ print_with_delay() {
print_progress() { print_progress() {
total="$1" total="$1"
delay="$2" delay="$2"
printf "[" printf "["
for i in $(seq 1 $total); do for i in $(seq 1 $total); do
printf "#" printf "#"
@@ -44,14 +44,14 @@ print_progress() {
} }
function openim_logo() { function openim_logo() {
# Set text color to cyan for header and URL # Set text color to cyan for header and URL
echo -e "\033[0;36m" echo -e "\033[0;36m"
# Display fancy ASCII Art logo
# look http://patorjk.com/software/taag/#p=display&h=1&v=1&f=Doh&t=OpenIM
print_with_delay '
# Display fancy ASCII Art logo
# look http://patorjk.com/software/taag/#p=display&h=1&v=1&f=Doh&t=OpenIM
print_with_delay '
OOOOOOOOO IIIIIIIIIIMMMMMMMM MMMMMMMM OOOOOOOOO IIIIIIIIIIMMMMMMMM MMMMMMMM
OO:::::::::OO I::::::::IM:::::::M M:::::::M OO:::::::::OO I::::::::IM:::::::M M:::::::M
OO:::::::::::::OO I::::::::IM::::::::M M::::::::M OO:::::::::::::OO I::::::::IM::::::::M M::::::::M
@@ -68,45 +68,45 @@ O:::::::OOO:::::::O p:::::ppppp:::::::pe::::::::e n::::n n::::nII:
OO:::::::::::::OO p::::::::::::::::p e::::::::eeeeeeee n::::n n::::nI::::::::IM::::::M M::::::M OO:::::::::::::OO p::::::::::::::::p e::::::::eeeeeeee n::::n n::::nI::::::::IM::::::M M::::::M
OO:::::::::OO p::::::::::::::pp ee:::::::::::::e n::::n n::::nI::::::::IM::::::M M::::::M OO:::::::::OO p::::::::::::::pp ee:::::::::::::e n::::n n::::nI::::::::IM::::::M M::::::M
OOOOOOOOO p::::::pppppppp eeeeeeeeeeeeee nnnnnn nnnnnnIIIIIIIIIIMMMMMMMM MMMMMMMM OOOOOOOOO p::::::pppppppp eeeeeeeeeeeeee nnnnnn nnnnnnIIIIIIIIIIMMMMMMMM MMMMMMMM
p:::::p p:::::p
p:::::p p:::::p
p:::::::p p:::::::p
p:::::::p p:::::::p
p:::::::p p:::::::p
ppppppppp ppppppppp
' 0.0001
# Display product URL ' 0.0001
print_with_delay "Discover more and contribute at: https://github.com/openimsdk/open-im-server" 0.01
# Display product URL
# Reset text color back to normal print_with_delay "Discover more and contribute at: https://github.com/openimsdk/open-im-server" 0.01
echo -e "\033[0m"
# Reset text color back to normal
# Set text color to green for product description echo -e "\033[0m"
echo -e "\033[1;32m"
# Set text color to green for product description
print_with_delay "Open-IM-Server: Reinventing Instant Messaging" 0.01 echo -e "\033[1;32m"
print_progress 50 0.02
print_with_delay "Open-IM-Server: Reinventing Instant Messaging" 0.01
print_with_delay "Open-IM-Server is not just a product; it's a revolution. It's about bringing the power of seamless," 0.01 print_progress 50 0.02
print_with_delay "real-time messaging to your fingertips. And it's about joining a global community of developers, dedicated to pushing the boundaries of what's possible." 0.01
print_with_delay "Open-IM-Server is not just a product; it's a revolution. It's about bringing the power of seamless," 0.01
print_progress 50 0.02 print_with_delay "real-time messaging to your fingertips. And it's about joining a global community of developers, dedicated to pushing the boundaries of what's possible." 0.01
# Reset text color back to normal print_progress 50 0.02
echo -e "\033[0m"
# Reset text color back to normal
# Set text color to yellow for the Slack link echo -e "\033[0m"
echo -e "\033[1;33m"
# Set text color to yellow for the Slack link
print_with_delay "Join our developer community on Slack: https://join.slack.com/t/openimsdk/shared_invite/zt-22720d66b-o_FvKxMTGXtcnnnHiMqe9Q" 0.01 echo -e "\033[1;33m"
# Reset text color back to normal print_with_delay "Join our developer community on Slack: https://join.slack.com/t/openimsdk/shared_invite/zt-22720d66b-o_FvKxMTGXtcnnnHiMqe9Q" 0.01
echo -e "\033[0m"
# Reset text color back to normal
echo -e "\033[0m"
} }
function main() { function main() {
openim_logo openim_logo
} }
main "$@" main "$@"
+173
View File
@@ -0,0 +1,173 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#**************************************************************************
# Copyright (C) 2011, Paul Lutus *
# *
# This program is free software; you can redistribute it and/or modify *
# it under the terms of the GNU General Public License as published by *
# the Free Software Foundation; either version 2 of the License, or *
# (at your option) any later version. *
# *
# This program is distributed in the hope that it will be useful, *
# but WITHOUT ANY WARRANTY; without even the implied warranty of *
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
# GNU General Public License for more details. *
# *
# You should have received a copy of the GNU General Public License *
# along with this program; if not, write to the *
# Free Software Foundation, Inc., *
# 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
#**************************************************************************
import re
import sys
PVERSION = '1.0'
class BeautifyBash:
def __init__(self):
self.tab_str = ' '
self.tab_size = 2
def read_file(self, fp):
with open(fp) as f:
return f.read()
def write_file(self, fp, data):
with open(fp, 'w') as f:
f.write(data)
def beautify_string(self, data, path=''):
tab = 0
case_stack = []
in_here_doc = False
defer_ext_quote = False
in_ext_quote = False
ext_quote_string = ''
here_string = ''
output = []
line = 1
for record in re.split('\n', data):
record = record.rstrip()
stripped_record = record.strip()
# collapse multiple quotes between ' ... '
test_record = re.sub(r'\'.*?\'', '', stripped_record)
# collapse multiple quotes between " ... "
test_record = re.sub(r'".*?"', '', test_record)
# collapse multiple quotes between ` ... `
test_record = re.sub(r'`.*?`', '', test_record)
# collapse multiple quotes between \` ... ' (weird case)
test_record = re.sub(r'\\`.*?\'', '', test_record)
# strip out any escaped single characters
test_record = re.sub(r'\\.', '', test_record)
# remove '#' comments
test_record = re.sub(r'(\A|\s)(#.*)', '', test_record, 1)
if(not in_here_doc):
if(re.search('<<-?', test_record)):
here_string = re.sub(
'.*<<-?\s*[\'|"]?([_|\w]+)[\'|"]?.*', '\\1', stripped_record, 1)
in_here_doc = (len(here_string) > 0)
if(in_here_doc): # pass on with no changes
output.append(record)
# now test for here-doc termination string
if(re.search(here_string, test_record) and not re.search('<<', test_record)):
in_here_doc = False
else: # not in here doc
if(in_ext_quote):
if(re.search(ext_quote_string, test_record)):
# provide line after quotes
test_record = re.sub(
'.*%s(.*)' % ext_quote_string, '\\1', test_record, 1)
in_ext_quote = False
else: # not in ext quote
if(re.search(r'(\A|\s)(\'|")', test_record)):
# apply only after this line has been processed
defer_ext_quote = True
ext_quote_string = re.sub(
'.*([\'"]).*', '\\1', test_record, 1)
# provide line before quote
test_record = re.sub(
'(.*)%s.*' % ext_quote_string, '\\1', test_record, 1)
if(in_ext_quote):
# pass on unchanged
output.append(record)
else: # not in ext quote
inc = len(re.findall(
'(\s|\A|;)(case|then|do)(;|\Z|\s)', test_record))
inc += len(re.findall('(\{|\(|\[)', test_record))
outc = len(re.findall(
'(\s|\A|;)(esac|fi|done|elif)(;|\)|\||\Z|\s)', test_record))
outc += len(re.findall('(\}|\)|\])', test_record))
if(re.search(r'\besac\b', test_record)):
if(len(case_stack) == 0):
sys.stderr.write(
'File %s: error: "esac" before "case" in line %d.\n' % (
path, line)
)
else:
outc += case_stack.pop()
# sepcial handling for bad syntax within case ... esac
if(len(case_stack) > 0):
if(re.search('\A[^(]*\)', test_record)):
# avoid overcount
outc -= 2
case_stack[-1] += 1
if(re.search(';;', test_record)):
outc += 1
case_stack[-1] -= 1
# an ad-hoc solution for the "else" keyword
else_case = (
0, -1)[re.search('^(else)', test_record) != None]
net = inc - outc
tab += min(net, 0)
extab = tab + else_case
extab = max(0, extab)
output.append(
(self.tab_str * self.tab_size * extab) + stripped_record)
tab += max(net, 0)
if(defer_ext_quote):
in_ext_quote = True
defer_ext_quote = False
if(re.search(r'\bcase\b', test_record)):
case_stack.append(0)
line += 1
error = (tab != 0)
if(error):
sys.stderr.write(
'File %s: error: indent/outdent mismatch: %d.\n' % (path, tab))
return '\n'.join(output), error
def beautify_file(self, path):
error = False
if(path == '-'):
data = sys.stdin.read()
result, error = self.beautify_string(data, '(stdin)')
sys.stdout.write(result)
else: # named file
data = self.read_file(path)
result, error = self.beautify_string(data, path)
if(data != result):
# make a backup copy
self.write_file(path + '~', data)
self.write_file(path, result)
return error
def main(self):
error = False
sys.argv.pop(0)
if(len(sys.argv) < 1):
sys.stderr.write(
'usage: shell script filenames or \"-\" for stdin.\n')
else:
for path in sys.argv:
error |= self.beautify_file(path)
sys.exit((0, 1)[error])
# if not called as a module
if(__name__ == '__main__'):
BeautifyBash().main()
+9 -9
View File
@@ -30,8 +30,8 @@ OPENIM_ROOT=$(dirname "${BASH_SOURCE[0]}")/..
source "${OPENIM_ROOT}/scripts/lib/init.sh" source "${OPENIM_ROOT}/scripts/lib/init.sh"
# CPU core number # CPU core number
pushd ""${OPENIM_ROOT}"/tools/ncpu" >/dev/null pushd "${OPENIM_ROOT}/tools/ncpu" >/dev/null
cpu_count=$(go run .) cpu_count=$(go run .)
popd >/dev/null popd >/dev/null
openim::color::echo ${GREEN_PREFIX} "======> cpu_count=$cpu_count" openim::color::echo ${GREEN_PREFIX} "======> cpu_count=$cpu_count"
@@ -42,7 +42,7 @@ compile_count=$((cpu_count / 2))
# For help output # For help output
ARGHELP="" ARGHELP=""
if [[ "$#" -gt 0 ]]; then if [[ "$#" -gt 0 ]]; then
ARGHELP="'$*'" ARGHELP="'$*'"
fi fi
openim::color::echo $COLOR_CYAN "NOTE: $0 has been replaced by 'make multiarch' or 'make build'" openim::color::echo $COLOR_CYAN "NOTE: $0 has been replaced by 'make multiarch' or 'make build'"
@@ -61,15 +61,15 @@ echo " ./scripts/build-all-service.sh BINS=openim-api V=1 DEBUG=1"
echo echo
if [ -z "$*" ]; then if [ -z "$*" ]; then
openim::log::info "no args, build all service" openim::log::info "no args, build all service"
make --no-print-directory -C "${OPENIM_ROOT}" -j$compile_count build make --no-print-directory -C "${OPENIM_ROOT}" -j$compile_count build
else else
openim::log::info "build service: $*" openim::log::info "build service: $*"
make --no-print-directory -C "${OPENIM_ROOT}" -j$compile_count build "$*" make --no-print-directory -C "${OPENIM_ROOT}" -j$compile_count build "$*"
fi fi
if [ $? -eq 0 ]; then if [ $? -eq 0 ]; then
openim::log::success "all service build success, run 'make start' or './scripts/start-all.sh'" openim::log::success "all service build success, run 'make start' or './scripts/start-all.sh'"
else else
openim::log::error "make build Error, script exits" openim::log::error "make build Error, script exits"
fi fi
+27 -23
View File
@@ -14,10 +14,10 @@
# limitations under the License. # limitations under the License.
# This script is check openim service is running normally # This script is check openim service is running normally
# #
# Usage: `scripts/check-all.sh`. # Usage: `scripts/check-all.sh`.
# Encapsulated as: `make check`. # Encapsulated as: `make check`.
# READ: https://github.com/openimsdk/open-im-server/tree/main/scripts/install/environment.sh # READ: https://github.com/openimsdk/open-im-server/tree/main/scripts/install/environment.sh
set -o errexit set -o errexit
set -o nounset set -o nounset
@@ -30,43 +30,47 @@ OPENIM_VERBOSE=4
openim::log::info "\n# Begin to check all openim service" openim::log::info "\n# Begin to check all openim service"
# OpenIM status openim::log::status "Check all dependent service ports"
# Elegant printing function
# Elegant printing function # Elegant printing function
print_services_and_ports() { print_services_and_ports() {
service_names=("$1[@]") local service_names=("$@")
service_ports=("$2[@]") local half_length=$((${#service_names[@]} / 2))
local service_ports=("${service_names[@]:half_length}")
echo "+-------------------------+----------+"
echo "| Service Name | Port |" echo "+-------------------------+----------+"
echo "+-------------------------+----------+" echo "| Service Name | Port |"
echo "+-------------------------+----------+"
for index in "${!service_names}"; do
printf "| %-23s | %-8s |\n" "${!service_names[$index]}" "${!service_ports[$index]}" for ((index=0; index < half_length; index++)); do
done printf "| %-23s | %-8s |\n" "${service_names[$index]}" "${service_ports[$index]}"
done
echo "+-------------------------+----------+"
echo "+-------------------------+----------+"
} }
# Assuming OPENIM_SERVER_NAME_TARGETS and OPENIM_SERVER_PORT_TARGETS are defined
# Similarly for OPENIM_DEPENDENCY_TARGETS and OPENIM_DEPENDENCY_PORT_TARGETS
# Print out services and their ports # Print out services and their ports
print_services_and_ports OPENIM_SERVER_NAME_TARGETS OPENIM_SERVER_PORT_TARGETS print_services_and_ports "${OPENIM_SERVER_NAME_TARGETS[@]}" "${OPENIM_SERVER_PORT_TARGETS[@]}"
# Print out dependencies and their ports # Print out dependencies and their ports
print_services_and_ports OPENIM_DEPENDENCY_TARGETS OPENIM_DEPENDENCY_PORT_TARGETS print_services_and_ports "${OPENIM_DEPENDENCY_TARGETS[@]}" "${OPENIM_DEPENDENCY_PORT_TARGETS[@]}"
# OpenIM check # OpenIM check
echo "++ The port being checked: ${OPENIM_SERVER_PORT_LISTARIES[@]}" echo "++ The port being checked: ${OPENIM_SERVER_PORT_LISTARIES[@]}"
openim::log::info "\n## Check all dependent service ports" openim::log::info "\n## Check all dependent service ports"
echo "+++ The port being checked: ${OPENIM_DEPENDENCY_PORT_LISTARIES[@]}" echo "++ The port being checked: ${OPENIM_DEPENDENCY_PORT_LISTARIES[@]}"
set +e set +e
# Later, after discarding Docker, the Docker keyword is unreliable, and Kubepods is used # Later, after discarding Docker, the Docker keyword is unreliable, and Kubepods is used
if grep -qE 'docker|kubepods' /proc/1/cgroup || [ -f /.dockerenv ]; then if grep -qE 'docker|kubepods' /proc/1/cgroup || [ -f /.dockerenv ]; then
openim::color::echo ${COLOR_CYAN} "Environment in the interior of the container" openim::color::echo ${COLOR_CYAN} "Environment in the interior of the container"
else else
openim::color::echo ${COLOR_CYAN} "The environment is outside the container" openim::color::echo ${COLOR_CYAN} "The environment is outside the container"
openim::util::check_ports ${OPENIM_DEPENDENCY_PORT_LISTARIES[@]} || return 0 openim::util::check_ports ${OPENIM_DEPENDENCY_PORT_LISTARIES[@]} || return 0
fi fi
if [[ $? -ne 0 ]]; then if [[ $? -ne 0 ]]; then
@@ -89,4 +93,4 @@ else
echo "++++ Check all openim service ports successfully !" echo "++++ Check all openim service ports successfully !"
fi fi
set -e set -e
+66 -66
View File
@@ -118,7 +118,7 @@ function return_to_kansas {
openim::log::status "Aborting in-progress git am." openim::log::status "Aborting in-progress git am."
git am --abort >/dev/null 2>&1 || true git am --abort >/dev/null 2>&1 || true
fi fi
# return to the starting branch and delete the PR text file # return to the starting branch and delete the PR text file
if [[ -z "${DRY_RUN}" ]]; then if [[ -z "${DRY_RUN}" ]]; then
echo echo
@@ -137,7 +137,7 @@ function make-a-pr() {
rel="$(basename "${BRANCH}")" rel="$(basename "${BRANCH}")"
echo echo
openim::log::status "Creating a pull request on GitHub at ${GITHUB_USER}:${NEWBRANCH}" openim::log::status "Creating a pull request on GitHub at ${GITHUB_USER}:${NEWBRANCH}"
local numandtitle local numandtitle
numandtitle=$(printf '%s\n' "${SUBJECTS[@]}") numandtitle=$(printf '%s\n' "${SUBJECTS[@]}")
prtext=$(cat <<EOF prtext=$(cat <<EOF
@@ -153,7 +153,7 @@ For details on the cherry pick process, see the [cherry pick requests](https://g
EOF EOF
) )
gh pr create --title="Automated cherry pick of ${numandtitle}" --body="${prtext}" --head "${GITHUB_USER}:${NEWBRANCH}" --base "${rel}" --repo="${MAIN_REPO_ORG}/${MAIN_REPO_NAME}" gh pr create --title="Automated cherry pick of ${numandtitle}" --body="${prtext}" --head "${GITHUB_USER}:${NEWBRANCH}" --base "${rel}" --repo="${MAIN_REPO_ORG}/${MAIN_REPO_NAME}"
} }
git checkout -b "${NEWBRANCHUNIQ}" "${BRANCH}" git checkout -b "${NEWBRANCHUNIQ}" "${BRANCH}"
@@ -161,84 +161,84 @@ cleanbranch="${NEWBRANCHUNIQ}"
gitamcleanup=true gitamcleanup=true
for pull in "${PULLS[@]}"; do for pull in "${PULLS[@]}"; do
openim::log::status "Downloading patch to /tmp/${pull}.patch (in case you need to do this again)" openim::log::status "Downloading patch to /tmp/${pull}.patch (in case you need to do this again)"
curl -o "/tmp/${pull}.patch" -sSL "https://github.com/${MAIN_REPO_ORG}/${MAIN_REPO_NAME}/pull/${pull}.patch" curl -o "/tmp/${pull}.patch" -sSL "https://github.com/${MAIN_REPO_ORG}/${MAIN_REPO_NAME}/pull/${pull}.patch"
echo echo
openim::log::status "About to attempt cherry pick of PR. To reattempt:" openim::log::status "About to attempt cherry pick of PR. To reattempt:"
echo " $ git am -3 /tmp/${pull}.patch" echo " $ git am -3 /tmp/${pull}.patch"
echo echo
git am -3 "/tmp/${pull}.patch" || { git am -3 "/tmp/${pull}.patch" || {
conflicts=false conflicts=false
while unmerged=$(git status --porcelain | grep ^U) && [[ -n ${unmerged} ]] \ while unmerged=$(git status --porcelain | grep ^U) && [[ -n ${unmerged} ]] \
|| [[ -e "${REBASEMAGIC}" ]]; do || [[ -e "${REBASEMAGIC}" ]]; do
conflicts=true # <-- We should have detected conflicts once conflicts=true # <-- We should have detected conflicts once
echo echo
openim::log::status "Conflicts detected:" openim::log::status "Conflicts detected:"
echo echo
(git status --porcelain | grep ^U) || echo "!!! None. Did you git am --continue?" (git status --porcelain | grep ^U) || echo "!!! None. Did you git am --continue?"
echo echo
openim::log::status "Please resolve the conflicts in another window (and remember to 'git add / git am --continue')" openim::log::status "Please resolve the conflicts in another window (and remember to 'git add / git am --continue')"
read -p "+++ Proceed (anything other than 'y' aborts the cherry-pick)? [y/n] " -r read -p "+++ Proceed (anything other than 'y' aborts the cherry-pick)? [y/n] " -r
echo echo
if ! [[ "${REPLY}" =~ ^[yY]$ ]]; then if ! [[ "${REPLY}" =~ ^[yY]$ ]]; then
echo "Aborting." >&2 echo "Aborting." >&2
exit 1
fi
done
if [[ "${conflicts}" != "true" ]]; then
echo "!!! git am failed, likely because of an in-progress 'git am' or 'git rebase'"
exit 1 exit 1
fi fi
} done
if [[ "${conflicts}" != "true" ]]; then
echo "!!! git am failed, likely because of an in-progress 'git am' or 'git rebase'"
exit 1
fi
}
# set the subject # set the subject
subject=$(grep -m 1 "^Subject" "/tmp/${pull}.patch" | sed -e 's/Subject: \[PATCH//g' | sed 's/.*] //') subject=$(grep -m 1 "^Subject" "/tmp/${pull}.patch" | sed -e 's/Subject: \[PATCH//g' | sed 's/.*] //')
SUBJECTS+=("#${pull}: ${subject}") SUBJECTS+=("#${pull}: ${subject}")
# remove the patch file from /tmp # remove the patch file from /tmp
rm -f "/tmp/${pull}.patch" rm -f "/tmp/${pull}.patch"
done done
gitamcleanup=false gitamcleanup=false
# Re-generate docs (if needed) # Re-generate docs (if needed)
if [[ -n "${REGENERATE_DOCS}" ]]; then if [[ -n "${REGENERATE_DOCS}" ]]; then
echo
echo "Regenerating docs..."
if ! scripts/generate-docs.sh; then
echo echo
echo "Regenerating docs..." echo "scripts/gendoc.sh FAILED to complete."
if ! scripts/generate-docs.sh; then exit 1
echo fi
echo "scripts/gendoc.sh FAILED to complete."
exit 1
fi
fi fi
if [[ -n "${DRY_RUN}" ]]; then if [[ -n "${DRY_RUN}" ]]; then
openim::log::error "!!! Skipping git push and PR creation because you set DRY_RUN." openim::log::error "!!! Skipping git push and PR creation because you set DRY_RUN."
echo "To return to the branch you were in when you invoked this script:" echo "To return to the branch you were in when you invoked this script:"
echo echo
echo " git checkout ${STARTINGBRANCH}" echo " git checkout ${STARTINGBRANCH}"
echo echo
echo "To delete this branch:" echo "To delete this branch:"
echo echo
echo " git branch -D ${NEWBRANCHUNIQ}" echo " git branch -D ${NEWBRANCHUNIQ}"
exit 0 exit 0
fi fi
if git remote -v | grep ^"${FORK_REMOTE}" | grep "${MAIN_REPO_ORG}/${MAIN_REPO_NAME}.git"; then if git remote -v | grep ^"${FORK_REMOTE}" | grep "${MAIN_REPO_ORG}/${MAIN_REPO_NAME}.git"; then
echo "!!! You have ${FORK_REMOTE} configured as your ${MAIN_REPO_ORG}/${MAIN_REPO_NAME}.git" echo "!!! You have ${FORK_REMOTE} configured as your ${MAIN_REPO_ORG}/${MAIN_REPO_NAME}.git"
echo "This isn't normal. Leaving you with push instructions:" echo "This isn't normal. Leaving you with push instructions:"
echo echo
openim::log::status "First manually push the branch this script created:" openim::log::status "First manually push the branch this script created:"
echo echo
echo " git push REMOTE ${NEWBRANCHUNIQ}:${NEWBRANCH}" echo " git push REMOTE ${NEWBRANCHUNIQ}:${NEWBRANCH}"
echo echo
echo "where REMOTE is your personal fork (maybe ${UPSTREAM_REMOTE}? Consider swapping those.)." echo "where REMOTE is your personal fork (maybe ${UPSTREAM_REMOTE}? Consider swapping those.)."
echo "OR consider setting UPSTREAM_REMOTE and FORK_REMOTE to different values." echo "OR consider setting UPSTREAM_REMOTE and FORK_REMOTE to different values."
echo echo
make-a-pr make-a-pr
cleanbranch="" cleanbranch=""
exit 0 exit 0
fi fi
echo echo
@@ -248,8 +248,8 @@ echo " git push ${FORK_REMOTE} ${NEWBRANCHUNIQ}:${NEWBRANCH}"
echo echo
read -p "+++ Proceed (anything other than 'y' aborts the cherry-pick)? [y/n] " -r read -p "+++ Proceed (anything other than 'y' aborts the cherry-pick)? [y/n] " -r
if ! [[ "${REPLY}" =~ ^[yY]$ ]]; then if ! [[ "${REPLY}" =~ ^[yY]$ ]]; then
echo "Aborting." >&2 echo "Aborting." >&2
exit 1 exit 1
fi fi
git push "${FORK_REMOTE}" -f "${NEWBRANCHUNIQ}:${NEWBRANCH}" git push "${FORK_REMOTE}" -f "${NEWBRANCHUNIQ}:${NEWBRANCH}"
+287 -286
View File
@@ -42,7 +42,7 @@ OPENIM_ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")"/.. && pwd -P)
# Constants # Constants
readonly OPENIM_BUILD_IMAGE_REPO=openim-build readonly OPENIM_BUILD_IMAGE_REPO=openim-build
#readonly OPENIM_BUILD_IMAGE_CROSS_TAG="$(cat ""${OPENIM_ROOT}"/build/build-image/cross/VERSION")" #readonly OPENIM_BUILD_IMAGE_CROSS_TAG="$(cat "${OPENIM_ROOT}/build/build-image/cross/VERSION")"
readonly OPENIM_DOCKER_REGISTRY="${OPENIM_DOCKER_REGISTRY:-k8s.gcr.io}" readonly OPENIM_DOCKER_REGISTRY="${OPENIM_DOCKER_REGISTRY:-k8s.gcr.io}"
readonly OPENIM_BASE_IMAGE_REGISTRY="${OPENIM_BASE_IMAGE_REGISTRY:-us.gcr.io/k8s-artifacts-prod/build-image}" readonly OPENIM_BASE_IMAGE_REGISTRY="${OPENIM_BASE_IMAGE_REGISTRY:-us.gcr.io/k8s-artifacts-prod/build-image}"
@@ -53,7 +53,7 @@ readonly OPENIM_BASE_IMAGE_REGISTRY="${OPENIM_BASE_IMAGE_REGISTRY:-us.gcr.io/k8s
# #
# Increment/change this number if you change the build image (anything under # Increment/change this number if you change the build image (anything under
# build/build-image) or change the set of volumes in the data container. # build/build-image) or change the set of volumes in the data container.
#readonly OPENIM_BUILD_IMAGE_VERSION_BASE="$(cat ""${OPENIM_ROOT}"/build/build-image/VERSION")" #readonly OPENIM_BUILD_IMAGE_VERSION_BASE="$(cat "${OPENIM_ROOT}/build/build-image/VERSION")"
#readonly OPENIM_BUILD_IMAGE_VERSION="${OPENIM_BUILD_IMAGE_VERSION_BASE}-${OPENIM_BUILD_IMAGE_CROSS_TAG}" #readonly OPENIM_BUILD_IMAGE_VERSION="${OPENIM_BUILD_IMAGE_VERSION_BASE}-${OPENIM_BUILD_IMAGE_CROSS_TAG}"
# Here we map the output directories across both the local and remote _output # Here we map the output directories across both the local and remote _output
@@ -66,9 +66,10 @@ readonly OPENIM_BASE_IMAGE_REGISTRY="${OPENIM_BASE_IMAGE_REGISTRY:-us.gcr.io/k8s
# is really remote, this is the stuff that has to be copied # is really remote, this is the stuff that has to be copied
# back. # back.
# OUT_DIR can come in from the Makefile, so honor it. # OUT_DIR can come in from the Makefile, so honor it.
readonly LOCAL_OUTPUT_ROOT=""${OPENIM_ROOT}"/${OUT_DIR:-_output}" readonly LOCAL_OUTPUT_ROOT="${OPENIM_ROOT}/${OUT_DIR:-_output}"
readonly LOCAL_OUTPUT_SUBPATH="${LOCAL_OUTPUT_ROOT}/platforms" readonly LOCAL_OUTPUT_SUBPATH="${LOCAL_OUTPUT_ROOT}/bin"
readonly LOCAL_OUTPUT_BINPATH="${LOCAL_OUTPUT_SUBPATH}" readonly LOCAL_OUTPUT_BINPATH="${LOCAL_OUTPUT_SUBPATH}/platforms"
readonly LOCAL_OUTPUT_BINTOOLSPATH="${LOCAL_OUTPUT_SUBPATH}/tools"
readonly LOCAL_OUTPUT_GOPATH="${LOCAL_OUTPUT_SUBPATH}/go" readonly LOCAL_OUTPUT_GOPATH="${LOCAL_OUTPUT_SUBPATH}/go"
readonly LOCAL_OUTPUT_IMAGE_STAGING="${LOCAL_OUTPUT_ROOT}/images" readonly LOCAL_OUTPUT_IMAGE_STAGING="${LOCAL_OUTPUT_ROOT}/images"
@@ -86,28 +87,28 @@ readonly OPENIM_CONTAINER_RSYNC_PORT=8730
# #
# $1 - server architecture # $1 - server architecture
openim::build::get_docker_wrapped_binaries() { openim::build::get_docker_wrapped_binaries() {
local arch=$1 local arch=$1
local debian_base_version=v2.1.0 local debian_base_version=v2.1.0
local debian_iptables_version=v12.1.0 local debian_iptables_version=v12.1.0
### If you change any of these lists, please also update DOCKERIZED_BINARIES ### If you change any of these lists, please also update DOCKERIZED_BINARIES
### in build/BUILD. And openim::golang::server_image_targets ### in build/BUILD. And openim::golang::server_image_targets
local targets=( local targets=(
"openim-api,${OPENIM_BASE_IMAGE_REGISTRY}/debian-base-${arch}:${debian_base_version}" "openim-api,${OPENIM_BASE_IMAGE_REGISTRY}/debian-base-${arch}:${debian_base_version}"
"openim-cmdutils,${OPENIM_BASE_IMAGE_REGISTRY}/debian-base-${arch}:${debian_base_version}" "openim-cmdutils,${OPENIM_BASE_IMAGE_REGISTRY}/debian-base-${arch}:${debian_base_version}"
"openim-crontask,${OPENIM_BASE_IMAGE_REGISTRY}/debian-base-${arch}:${debian_base_version}" "openim-crontask,${OPENIM_BASE_IMAGE_REGISTRY}/debian-base-${arch}:${debian_base_version}"
"openim-msggateway,${OPENIM_BASE_IMAGE_REGISTRY}/debian-base-${arch}:${debian_base_version}" "openim-msggateway,${OPENIM_BASE_IMAGE_REGISTRY}/debian-base-${arch}:${debian_base_version}"
"openim-msgtransfer,${OPENIM_BASE_IMAGE_REGISTRY}/debian-base-${arch}:${debian_base_version}" "openim-msgtransfer,${OPENIM_BASE_IMAGE_REGISTRY}/debian-base-${arch}:${debian_base_version}"
"openim-push,${OPENIM_BASE_IMAGE_REGISTRY}/debian-base-${arch}:${debian_base_version}" "openim-push,${OPENIM_BASE_IMAGE_REGISTRY}/debian-base-${arch}:${debian_base_version}"
"openim-rpc-auth,${OPENIM_BASE_IMAGE_REGISTRY}/debian-base-${arch}:${debian_base_version}" "openim-rpc-auth,${OPENIM_BASE_IMAGE_REGISTRY}/debian-base-${arch}:${debian_base_version}"
"openim-rpc-conversation,${OPENIM_BASE_IMAGE_REGISTRY}/debian-base-${arch}:${debian_base_version}" "openim-rpc-conversation,${OPENIM_BASE_IMAGE_REGISTRY}/debian-base-${arch}:${debian_base_version}"
"openim-rpc-friend,${OPENIM_BASE_IMAGE_REGISTRY}/debian-base-${arch}:${debian_base_version}" "openim-rpc-friend,${OPENIM_BASE_IMAGE_REGISTRY}/debian-base-${arch}:${debian_base_version}"
"openim-rpc-group,${OPENIM_BASE_IMAGE_REGISTRY}/debian-base-${arch}:${debian_base_version}" "openim-rpc-group,${OPENIM_BASE_IMAGE_REGISTRY}/debian-base-${arch}:${debian_base_version}"
"openim-rpc-msg,${OPENIM_BASE_IMAGE_REGISTRY}/debian-base-${arch}:${debian_base_version}" "openim-rpc-msg,${OPENIM_BASE_IMAGE_REGISTRY}/debian-base-${arch}:${debian_base_version}"
"openim-rpc-third,${OPENIM_BASE_IMAGE_REGISTRY}/debian-base-${arch}:${debian_base_version}" "openim-rpc-third,${OPENIM_BASE_IMAGE_REGISTRY}/debian-base-${arch}:${debian_base_version}"
"openim-rpc-user,${OPENIM_BASE_IMAGE_REGISTRY}/debian-base-${arch}:${debian_base_version}" "openim-rpc-user,${OPENIM_BASE_IMAGE_REGISTRY}/debian-base-${arch}:${debian_base_version}"
) )
echo "${targets[@]}" echo "${targets[@]}"
} }
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
@@ -132,170 +133,170 @@ openim::build::get_docker_wrapped_binaries() {
# DOCKER_MOUNT_ARGS # DOCKER_MOUNT_ARGS
# LOCAL_OUTPUT_BUILD_CONTEXT # LOCAL_OUTPUT_BUILD_CONTEXT
function openim::build::verify_prereqs() { function openim::build::verify_prereqs() {
local -r require_docker=${1:-true} local -r require_docker=${1:-true}
openim::log::status "Verifying Prerequisites...." openim::log::status "Verifying Prerequisites...."
openim::build::ensure_tar || return 1 openim::build::ensure_tar || return 1
openim::build::ensure_rsync || return 1 openim::build::ensure_rsync || return 1
if ${require_docker}; then if ${require_docker}; then
openim::build::ensure_docker_in_path || return 1 openim::build::ensure_docker_in_path || return 1
openim::util::ensure_docker_daemon_connectivity || return 1 openim::util::ensure_docker_daemon_connectivity || return 1
if (( OPENIM_VERBOSE > 6 )); then if (( OPENIM_VERBOSE > 6 )); then
openim::log::status "Docker Version:" openim::log::status "Docker Version:"
"${DOCKER[@]}" version | openim::log::info_from_stdin "${DOCKER[@]}" version | openim::log::info_from_stdin
fi
fi fi
fi
OPENIM_GIT_BRANCH=$(git symbolic-ref --short -q HEAD 2>/dev/null || true) OPENIM_GIT_BRANCH=$(git symbolic-ref --short -q HEAD 2>/dev/null || true)
OPENIM_ROOT_HASH=$(openim::build::short_hash "${HOSTNAME:-}:"${OPENIM_ROOT}":${OPENIM_GIT_BRANCH}") OPENIM_ROOT_HASH=$(openim::build::short_hash "${HOSTNAME:-}:${OPENIM_ROOT}:${OPENIM_GIT_BRANCH}")
OPENIM_BUILD_IMAGE_TAG_BASE="build-${OPENIM_ROOT_HASH}" OPENIM_BUILD_IMAGE_TAG_BASE="build-${OPENIM_ROOT_HASH}"
#OPENIM_BUILD_IMAGE_TAG="${OPENIM_BUILD_IMAGE_TAG_BASE}-${OPENIM_BUILD_IMAGE_VERSION}" #OPENIM_BUILD_IMAGE_TAG="${OPENIM_BUILD_IMAGE_TAG_BASE}-${OPENIM_BUILD_IMAGE_VERSION}"
#OPENIM_BUILD_IMAGE="${OPENIM_BUILD_IMAGE_REPO}:${OPENIM_BUILD_IMAGE_TAG}" #OPENIM_BUILD_IMAGE="${OPENIM_BUILD_IMAGE_REPO}:${OPENIM_BUILD_IMAGE_TAG}"
OPENIM_BUILD_CONTAINER_NAME_BASE="openim-build-${OPENIM_ROOT_HASH}" OPENIM_BUILD_CONTAINER_NAME_BASE="openim-build-${OPENIM_ROOT_HASH}"
#OPENIM_BUILD_CONTAINER_NAME="${OPENIM_BUILD_CONTAINER_NAME_BASE}-${OPENIM_BUILD_IMAGE_VERSION}" #OPENIM_BUILD_CONTAINER_NAME="${OPENIM_BUILD_CONTAINER_NAME_BASE}-${OPENIM_BUILD_IMAGE_VERSION}"
OPENIM_RSYNC_CONTAINER_NAME_BASE="openim-rsync-${OPENIM_ROOT_HASH}" OPENIM_RSYNC_CONTAINER_NAME_BASE="openim-rsync-${OPENIM_ROOT_HASH}"
#OPENIM_RSYNC_CONTAINER_NAME="${OPENIM_RSYNC_CONTAINER_NAME_BASE}-${OPENIM_BUILD_IMAGE_VERSION}" #OPENIM_RSYNC_CONTAINER_NAME="${OPENIM_RSYNC_CONTAINER_NAME_BASE}-${OPENIM_BUILD_IMAGE_VERSION}"
OPENIM_DATA_CONTAINER_NAME_BASE="openim-build-data-${OPENIM_ROOT_HASH}" OPENIM_DATA_CONTAINER_NAME_BASE="openim-build-data-${OPENIM_ROOT_HASH}"
#OPENIM_DATA_CONTAINER_NAME="${OPENIM_DATA_CONTAINER_NAME_BASE}-${OPENIM_BUILD_IMAGE_VERSION}" #OPENIM_DATA_CONTAINER_NAME="${OPENIM_DATA_CONTAINER_NAME_BASE}-${OPENIM_BUILD_IMAGE_VERSION}"
#DOCKER_MOUNT_ARGS=(--volumes-from "${OPENIM_DATA_CONTAINER_NAME}") #DOCKER_MOUNT_ARGS=(--volumes-from "${OPENIM_DATA_CONTAINER_NAME}")
#LOCAL_OUTPUT_BUILD_CONTEXT="${LOCAL_OUTPUT_IMAGE_STAGING}/${OPENIM_BUILD_IMAGE}" #LOCAL_OUTPUT_BUILD_CONTEXT="${LOCAL_OUTPUT_IMAGE_STAGING}/${OPENIM_BUILD_IMAGE}"
openim::version::get_version_vars openim::version::get_version_vars
#openim::version::save_version_vars ""${OPENIM_ROOT}"/.dockerized-openim-version-defs" #openim::version::save_version_vars "${OPENIM_ROOT}/.dockerized-openim-version-defs"
} }
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
# Utility functions # Utility functions
function openim::build::docker_available_on_osx() { function openim::build::docker_available_on_osx() {
if [[ -z "${DOCKER_HOST}" ]]; then if [[ -z "${DOCKER_HOST}" ]]; then
if [[ -S "/var/run/docker.sock" ]]; then if [[ -S "/var/run/docker.sock" ]]; then
openim::log::status "Using Docker for MacOS" openim::log::status "Using Docker for MacOS"
return 0 return 0
fi
openim::log::status "No docker host is set. Checking options for setting one..."
if [[ -z "$(which docker-machine)" ]]; then
openim::log::status "It looks like you're running Mac OS X, yet neither Docker for Mac nor docker-machine can be found."
openim::log::status "See: https://docs.docker.com/engine/installation/mac/ for installation instructions."
return 1
elif [[ -n "$(which docker-machine)" ]]; then
openim::build::prepare_docker_machine
fi
fi fi
openim::log::status "No docker host is set. Checking options for setting one..."
if [[ -z "$(which docker-machine)" ]]; then
openim::log::status "It looks like you're running Mac OS X, yet neither Docker for Mac nor docker-machine can be found."
openim::log::status "See: https://docs.docker.com/engine/installation/mac/ for installation instructions."
return 1
elif [[ -n "$(which docker-machine)" ]]; then
openim::build::prepare_docker_machine
fi
fi
} }
function openim::build::prepare_docker_machine() { function openim::build::prepare_docker_machine() {
openim::log::status "docker-machine was found." openim::log::status "docker-machine was found."
local available_memory_bytes local available_memory_bytes
available_memory_bytes=$(sysctl -n hw.memsize 2>/dev/null) available_memory_bytes=$(sysctl -n hw.memsize 2>/dev/null)
local bytes_in_mb=1048576 local bytes_in_mb=1048576
# Give virtualbox 1/2 the system memory. Its necessary to divide by 2, instead # Give virtualbox 1/2 the system memory. Its necessary to divide by 2, instead
# of multiple by .5, because bash can only multiply by ints. # of multiple by .5, because bash can only multiply by ints.
local memory_divisor=2 local memory_divisor=2
local virtualbox_memory_mb=$(( available_memory_bytes / (bytes_in_mb * memory_divisor) )) local virtualbox_memory_mb=$(( available_memory_bytes / (bytes_in_mb * memory_divisor) ))
docker-machine inspect "${DOCKER_MACHINE_NAME}" &> /dev/null || { docker-machine inspect "${DOCKER_MACHINE_NAME}" &> /dev/null || {
openim::log::status "Creating a machine to build OPENIM" openim::log::status "Creating a machine to build OPENIM"
docker-machine create --driver "${DOCKER_MACHINE_DRIVER}" \ docker-machine create --driver "${DOCKER_MACHINE_DRIVER}" \
--virtualbox-memory "${virtualbox_memory_mb}" \ --virtualbox-memory "${virtualbox_memory_mb}" \
--engine-env HTTP_PROXY="${OPENIMRNETES_HTTP_PROXY:-}" \ --engine-env HTTP_PROXY="${OPENIMRNETES_HTTP_PROXY:-}" \
--engine-env HTTPS_PROXY="${OPENIMRNETES_HTTPS_PROXY:-}" \ --engine-env HTTPS_PROXY="${OPENIMRNETES_HTTPS_PROXY:-}" \
--engine-env NO_PROXY="${OPENIMRNETES_NO_PROXY:-127.0.0.1}" \ --engine-env NO_PROXY="${OPENIMRNETES_NO_PROXY:-127.0.0.1}" \
"${DOCKER_MACHINE_NAME}" > /dev/null || { "${DOCKER_MACHINE_NAME}" > /dev/null || {
openim::log::error "Something went wrong creating a machine." openim::log::error "Something went wrong creating a machine."
openim::log::error "Try the following: " openim::log::error "Try the following: "
openim::log::error "docker-machine create -d ${DOCKER_MACHINE_DRIVER} --virtualbox-memory ${virtualbox_memory_mb} ${DOCKER_MACHINE_NAME}" openim::log::error "docker-machine create -d ${DOCKER_MACHINE_DRIVER} --virtualbox-memory ${virtualbox_memory_mb} ${DOCKER_MACHINE_NAME}"
return 1 return 1
}
} }
docker-machine start "${DOCKER_MACHINE_NAME}" &> /dev/null }
# it takes `docker-machine env` a few seconds to work if the machine was just started docker-machine start "${DOCKER_MACHINE_NAME}" &> /dev/null
local docker_machine_out # it takes `docker-machine env` a few seconds to work if the machine was just started
while ! docker_machine_out=$(docker-machine env "${DOCKER_MACHINE_NAME}" 2>&1); do local docker_machine_out
if [[ ${docker_machine_out} =~ "Error checking TLS connection" ]]; then while ! docker_machine_out=$(docker-machine env "${DOCKER_MACHINE_NAME}" 2>&1); do
echo "${docker_machine_out}" if [[ ${docker_machine_out} =~ "Error checking TLS connection" ]]; then
docker-machine regenerate-certs "${DOCKER_MACHINE_NAME}" echo "${docker_machine_out}"
else docker-machine regenerate-certs "${DOCKER_MACHINE_NAME}"
sleep 1 else
fi sleep 1
done fi
eval "$(docker-machine env "${DOCKER_MACHINE_NAME}")" done
openim::log::status "A Docker host using docker-machine named '${DOCKER_MACHINE_NAME}' is ready to go!" eval "$(docker-machine env "${DOCKER_MACHINE_NAME}")"
return 0 openim::log::status "A Docker host using docker-machine named '${DOCKER_MACHINE_NAME}' is ready to go!"
return 0
} }
function openim::build::is_gnu_sed() { function openim::build::is_gnu_sed() {
[[ $(sed --version 2>&1) == *GNU* ]] [[ $(sed --version 2>&1) == *GNU* ]]
} }
function openim::build::ensure_rsync() { function openim::build::ensure_rsync() {
if [[ -z "$(which rsync)" ]]; then if [[ -z "$(which rsync)" ]]; then
openim::log::error "Can't find 'rsync' in PATH, please fix and retry." openim::log::error "Can't find 'rsync' in PATH, please fix and retry."
return 1 return 1
fi fi
} }
function openim::build::update_dockerfile() { function openim::build::update_dockerfile() {
if openim::build::is_gnu_sed; then if openim::build::is_gnu_sed; then
sed_opts=(-i) sed_opts=(-i)
else else
sed_opts=(-i '') sed_opts=(-i '')
fi fi
sed "${sed_opts[@]}" "s/OPENIM_BUILD_IMAGE_CROSS_TAG/${OPENIM_BUILD_IMAGE_CROSS_TAG}/" "${LOCAL_OUTPUT_BUILD_CONTEXT}/Dockerfile" sed "${sed_opts[@]}" "s/OPENIM_BUILD_IMAGE_CROSS_TAG/${OPENIM_BUILD_IMAGE_CROSS_TAG}/" "${LOCAL_OUTPUT_BUILD_CONTEXT}/Dockerfile"
} }
function openim::build::set_proxy() { function openim::build::set_proxy() {
if [[ -n "${OPENIMRNETES_HTTPS_PROXY:-}" ]]; then if [[ -n "${OPENIMRNETES_HTTPS_PROXY:-}" ]]; then
echo "ENV https_proxy $OPENIMRNETES_HTTPS_PROXY" >> "${LOCAL_OUTPUT_BUILD_CONTEXT}/Dockerfile" echo "ENV https_proxy $OPENIMRNETES_HTTPS_PROXY" >> "${LOCAL_OUTPUT_BUILD_CONTEXT}/Dockerfile"
fi fi
if [[ -n "${OPENIMRNETES_HTTP_PROXY:-}" ]]; then if [[ -n "${OPENIMRNETES_HTTP_PROXY:-}" ]]; then
echo "ENV http_proxy $OPENIMRNETES_HTTP_PROXY" >> "${LOCAL_OUTPUT_BUILD_CONTEXT}/Dockerfile" echo "ENV http_proxy $OPENIMRNETES_HTTP_PROXY" >> "${LOCAL_OUTPUT_BUILD_CONTEXT}/Dockerfile"
fi fi
if [[ -n "${OPENIMRNETES_NO_PROXY:-}" ]]; then if [[ -n "${OPENIMRNETES_NO_PROXY:-}" ]]; then
echo "ENV no_proxy $OPENIMRNETES_NO_PROXY" >> "${LOCAL_OUTPUT_BUILD_CONTEXT}/Dockerfile" echo "ENV no_proxy $OPENIMRNETES_NO_PROXY" >> "${LOCAL_OUTPUT_BUILD_CONTEXT}/Dockerfile"
fi fi
} }
function openim::build::ensure_docker_in_path() { function openim::build::ensure_docker_in_path() {
if [[ -z "$(which docker)" ]]; then if [[ -z "$(which docker)" ]]; then
openim::log::error "Can't find 'docker' in PATH, please fix and retry." openim::log::error "Can't find 'docker' in PATH, please fix and retry."
openim::log::error "See https://docs.docker.com/installation/#installation for installation instructions." openim::log::error "See https://docs.docker.com/installation/#installation for installation instructions."
return 1 return 1
fi fi
} }
function openim::build::ensure_tar() { function openim::build::ensure_tar() {
if [[ -n "${TAR:-}" ]]; then if [[ -n "${TAR:-}" ]]; then
return return
fi fi
# Find gnu tar if it is available, bomb out if not. # Find gnu tar if it is available, bomb out if not.
TAR=tar TAR=tar
if which gtar &>/dev/null; then if which gtar &>/dev/null; then
TAR=gtar TAR=gtar
else else
if which gnutar &>/dev/null; then if which gnutar &>/dev/null; then
TAR=gnutar TAR=gnutar
fi
fi
if ! "${TAR}" --version | grep -q GNU; then
echo " !!! Cannot find GNU tar. Build on Linux or install GNU tar"
echo " on Mac OS X (brew install gnu-tar)."
return 1
fi fi
fi
if ! "${TAR}" --version | grep -q GNU; then
echo " !!! Cannot find GNU tar. Build on Linux or install GNU tar"
echo " on Mac OS X (brew install gnu-tar)."
return 1
fi
} }
function openim::build::has_docker() { function openim::build::has_docker() {
which docker &> /dev/null which docker &> /dev/null
} }
function openim::build::has_ip() { function openim::build::has_ip() {
which ip &> /dev/null && ip -Version | grep 'iproute2' &> /dev/null which ip &> /dev/null && ip -Version | grep 'iproute2' &> /dev/null
} }
# Detect if a specific image exists # Detect if a specific image exists
@@ -303,12 +304,12 @@ function openim::build::has_ip() {
# $1 - image repo name # $1 - image repo name
# $2 - image tag # $2 - image tag
function openim::build::docker_image_exists() { function openim::build::docker_image_exists() {
[[ -n $1 && -n $2 ]] || { [[ -n $1 && -n $2 ]] || {
openim::log::error "Internal error. Image not specified in docker_image_exists." openim::log::error "Internal error. Image not specified in docker_image_exists."
exit 2 exit 2
} }
[[ $("${DOCKER[@]}" images -q "${1}:${2}") ]] [[ $("${DOCKER[@]}" images -q "${1}:${2}") ]]
} }
# Delete all images that match a tag prefix except for the "current" version # Delete all images that match a tag prefix except for the "current" version
@@ -317,21 +318,21 @@ function openim::build::docker_image_exists() {
# $2: The tag base. We consider any image that matches $2* # $2: The tag base. We consider any image that matches $2*
# $3: The current image not to delete if provided # $3: The current image not to delete if provided
function openim::build::docker_delete_old_images() { function openim::build::docker_delete_old_images() {
# In Docker 1.12, we can replace this with # In Docker 1.12, we can replace this with
# docker images "$1" --format "{{.Tag}}" # docker images "$1" --format "{{.Tag}}"
for tag in $("${DOCKER[@]}" images "${1}" | tail -n +2 | awk '{print $2}') ; do for tag in $("${DOCKER[@]}" images "${1}" | tail -n +2 | awk '{print $2}') ; do
if [[ "${tag}" != "${2}"* ]] ; then if [[ "${tag}" != "${2}"* ]] ; then
V=3 openim::log::status "Keeping image ${1}:${tag}" V=3 openim::log::status "Keeping image ${1}:${tag}"
continue continue
fi fi
if [[ -z "${3:-}" || "${tag}" != "${3}" ]] ; then if [[ -z "${3:-}" || "${tag}" != "${3}" ]] ; then
V=2 openim::log::status "Deleting image ${1}:${tag}" V=2 openim::log::status "Deleting image ${1}:${tag}"
"${DOCKER[@]}" rmi "${1}:${tag}" >/dev/null "${DOCKER[@]}" rmi "${1}:${tag}" >/dev/null
else else
V=3 openim::log::status "Keeping image ${1}:${tag}" V=3 openim::log::status "Keeping image ${1}:${tag}"
fi fi
done done
} }
# Stop and delete all containers that match a pattern # Stop and delete all containers that match a pattern
@@ -339,36 +340,36 @@ function openim::build::docker_delete_old_images() {
# $1: The base container prefix # $1: The base container prefix
# $2: The current container to keep, if provided # $2: The current container to keep, if provided
function openim::build::docker_delete_old_containers() { function openim::build::docker_delete_old_containers() {
# In Docker 1.12 we can replace this line with # In Docker 1.12 we can replace this line with
# docker ps -a --format="{{.Names}}" # docker ps -a --format="{{.Names}}"
for container in $("${DOCKER[@]}" ps -a | tail -n +2 | awk '{print $NF}') ; do for container in $("${DOCKER[@]}" ps -a | tail -n +2 | awk '{print $NF}') ; do
if [[ "${container}" != "${1}"* ]] ; then if [[ "${container}" != "${1}"* ]] ; then
V=3 openim::log::status "Keeping container ${container}" V=3 openim::log::status "Keeping container ${container}"
continue continue
fi fi
if [[ -z "${2:-}" || "${container}" != "${2}" ]] ; then if [[ -z "${2:-}" || "${container}" != "${2}" ]] ; then
V=2 openim::log::status "Deleting container ${container}" V=2 openim::log::status "Deleting container ${container}"
openim::build::destroy_container "${container}" openim::build::destroy_container "${container}"
else else
V=3 openim::log::status "Keeping container ${container}" V=3 openim::log::status "Keeping container ${container}"
fi fi
done done
} }
# Takes $1 and computes a short has for it. Useful for unique tag generation # Takes $1 and computes a short has for it. Useful for unique tag generation
function openim::build::short_hash() { function openim::build::short_hash() {
[[ $# -eq 1 ]] || { [[ $# -eq 1 ]] || {
openim::log::error "Internal error. No data based to short_hash." openim::log::error "Internal error. No data based to short_hash."
exit 2 exit 2
} }
local short_hash local short_hash
if which md5 >/dev/null 2>&1; then if which md5 >/dev/null 2>&1; then
short_hash=$(md5 -q -s "$1") short_hash=$(md5 -q -s "$1")
else else
short_hash=$(echo -n "$1" | md5sum) short_hash=$(echo -n "$1" | md5sum)
fi fi
echo "${short_hash:0:10}" echo "${short_hash:0:10}"
} }
# Pedantically kill, wait-on and remove a container. The -f -v options # Pedantically kill, wait-on and remove a container. The -f -v options
@@ -376,15 +377,15 @@ function openim::build::short_hash() {
# container, wait to ensure it's stopped, then try the remove. This is # container, wait to ensure it's stopped, then try the remove. This is
# a workaround for bug https://github.com/docker/docker/issues/3968. # a workaround for bug https://github.com/docker/docker/issues/3968.
function openim::build::destroy_container() { function openim::build::destroy_container() {
"${DOCKER[@]}" kill "$1" >/dev/null 2>&1 || true "${DOCKER[@]}" kill "$1" >/dev/null 2>&1 || true
if [[ $("${DOCKER[@]}" version --format '{{.Server.Version}}') = 17.06.0* ]]; then if [[ $("${DOCKER[@]}" version --format '{{.Server.Version}}') = 17.06.0* ]]; then
# Workaround https://github.com/moby/moby/issues/33948. # Workaround https://github.com/moby/moby/issues/33948.
# TODO: remove when 17.06.0 is not relevant anymore # TODO: remove when 17.06.0 is not relevant anymore
DOCKER_API_VERSION=v1.29 "${DOCKER[@]}" wait "$1" >/dev/null 2>&1 || true DOCKER_API_VERSION=v1.29 "${DOCKER[@]}" wait "$1" >/dev/null 2>&1 || true
else else
"${DOCKER[@]}" wait "$1" >/dev/null 2>&1 || true "${DOCKER[@]}" wait "$1" >/dev/null 2>&1 || true
fi fi
"${DOCKER[@]}" rm -f -v "$1" >/dev/null 2>&1 || true "${DOCKER[@]}" rm -f -v "$1" >/dev/null 2>&1 || true
} }
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
@@ -392,47 +393,47 @@ function openim::build::destroy_container() {
function openim::build::clean() { function openim::build::clean() {
if openim::build::has_docker ; then if openim::build::has_docker ; then
openim::build::docker_delete_old_containers "${OPENIM_BUILD_CONTAINER_NAME_BASE}" openim::build::docker_delete_old_containers "${OPENIM_BUILD_CONTAINER_NAME_BASE}"
openim::build::docker_delete_old_containers "${OPENIM_RSYNC_CONTAINER_NAME_BASE}" openim::build::docker_delete_old_containers "${OPENIM_RSYNC_CONTAINER_NAME_BASE}"
openim::build::docker_delete_old_containers "${OPENIM_DATA_CONTAINER_NAME_BASE}" openim::build::docker_delete_old_containers "${OPENIM_DATA_CONTAINER_NAME_BASE}"
openim::build::docker_delete_old_images "${OPENIM_BUILD_IMAGE_REPO}" "${OPENIM_BUILD_IMAGE_TAG_BASE}" openim::build::docker_delete_old_images "${OPENIM_BUILD_IMAGE_REPO}" "${OPENIM_BUILD_IMAGE_TAG_BASE}"
V=2 openim::log::status "Cleaning all untagged docker images"
"${DOCKER[@]}" rmi "$("${DOCKER[@]}" images -q --filter 'dangling=true')" 2> /dev/null || true
fi
V=2 openim::log::status "Cleaning all untagged docker images" if [[ -d "${LOCAL_OUTPUT_ROOT}" ]]; then
"${DOCKER[@]}" rmi "$("${DOCKER[@]}" images -q --filter 'dangling=true')" 2> /dev/null || true openim::log::status "Removing _output directory"
fi rm -rf "${LOCAL_OUTPUT_ROOT}"
fi
if [[ -d "${LOCAL_OUTPUT_ROOT}" ]]; then
openim::log::status "Removing _output directory"
rm -rf "${LOCAL_OUTPUT_ROOT}"
fi
} }
# Set up the context directory for the openim-build image and build it. # Set up the context directory for the openim-build image and build it.
function openim::build::build_image() { function openim::build::build_image() {
mkdir -p "${LOCAL_OUTPUT_BUILD_CONTEXT}" mkdir -p "${LOCAL_OUTPUT_BUILD_CONTEXT}"
# Make sure the context directory owned by the right user for syncing sources to container. # Make sure the context directory owned by the right user for syncing sources to container.
chown -R "${USER_ID}":"${GROUP_ID}" "${LOCAL_OUTPUT_BUILD_CONTEXT}" chown -R "${USER_ID}":"${GROUP_ID}" "${LOCAL_OUTPUT_BUILD_CONTEXT}"
cp /etc/localtime "${LOCAL_OUTPUT_BUILD_CONTEXT}/" cp /etc/localtime "${LOCAL_OUTPUT_BUILD_CONTEXT}/"
cp ""${OPENIM_ROOT}"/build/build-image/Dockerfile" "${LOCAL_OUTPUT_BUILD_CONTEXT}/Dockerfile" cp "${OPENIM_ROOT}/build/build-image/Dockerfile" "${LOCAL_OUTPUT_BUILD_CONTEXT}/Dockerfile"
cp ""${OPENIM_ROOT}"/build/build-image/rsyncd.sh" "${LOCAL_OUTPUT_BUILD_CONTEXT}/" cp "${OPENIM_ROOT}/build/build-image/rsyncd.sh" "${LOCAL_OUTPUT_BUILD_CONTEXT}/"
dd if=/dev/urandom bs=512 count=1 2>/dev/null | LC_ALL=C tr -dc 'A-Za-z0-9' | dd bs=32 count=1 2>/dev/null > "${LOCAL_OUTPUT_BUILD_CONTEXT}/rsyncd.password" dd if=/dev/urandom bs=512 count=1 2>/dev/null | LC_ALL=C tr -dc 'A-Za-z0-9' | dd bs=32 count=1 2>/dev/null > "${LOCAL_OUTPUT_BUILD_CONTEXT}/rsyncd.password"
chmod go= "${LOCAL_OUTPUT_BUILD_CONTEXT}/rsyncd.password" chmod go= "${LOCAL_OUTPUT_BUILD_CONTEXT}/rsyncd.password"
openim::build::update_dockerfile openim::build::update_dockerfile
openim::build::set_proxy openim::build::set_proxy
openim::build::docker_build "${OPENIM_BUILD_IMAGE}" "${LOCAL_OUTPUT_BUILD_CONTEXT}" 'false' openim::build::docker_build "${OPENIM_BUILD_IMAGE}" "${LOCAL_OUTPUT_BUILD_CONTEXT}" 'false'
# Clean up old versions of everything # Clean up old versions of everything
openim::build::docker_delete_old_containers "${OPENIM_BUILD_CONTAINER_NAME_BASE}" "${OPENIM_BUILD_CONTAINER_NAME}" openim::build::docker_delete_old_containers "${OPENIM_BUILD_CONTAINER_NAME_BASE}" "${OPENIM_BUILD_CONTAINER_NAME}"
openim::build::docker_delete_old_containers "${OPENIM_RSYNC_CONTAINER_NAME_BASE}" "${OPENIM_RSYNC_CONTAINER_NAME}" openim::build::docker_delete_old_containers "${OPENIM_RSYNC_CONTAINER_NAME_BASE}" "${OPENIM_RSYNC_CONTAINER_NAME}"
openim::build::docker_delete_old_containers "${OPENIM_DATA_CONTAINER_NAME_BASE}" "${OPENIM_DATA_CONTAINER_NAME}" openim::build::docker_delete_old_containers "${OPENIM_DATA_CONTAINER_NAME_BASE}" "${OPENIM_DATA_CONTAINER_NAME}"
openim::build::docker_delete_old_images "${OPENIM_BUILD_IMAGE_REPO}" "${OPENIM_BUILD_IMAGE_TAG_BASE}" "${OPENIM_BUILD_IMAGE_TAG}" openim::build::docker_delete_old_images "${OPENIM_BUILD_IMAGE_REPO}" "${OPENIM_BUILD_IMAGE_TAG_BASE}" "${OPENIM_BUILD_IMAGE_TAG}"
openim::build::ensure_data_container openim::build::ensure_data_container
openim::build::sync_to_container openim::build::sync_to_container
} }
# Build a docker image from a Dockerfile. # Build a docker image from a Dockerfile.
@@ -440,14 +441,14 @@ function openim::build::build_image() {
# $2 is the location of the "context" directory, with the Dockerfile at the root. # $2 is the location of the "context" directory, with the Dockerfile at the root.
# $3 is the value to set the --pull flag for docker build; true by default # $3 is the value to set the --pull flag for docker build; true by default
function openim::build::docker_build() { function openim::build::docker_build() {
local -r image=$1 local -r image=$1
local -r context_dir=$2 local -r context_dir=$2
local -r pull="${3:-true}" local -r pull="${3:-true}"
local -ra build_cmd=("${DOCKER[@]}" build -t "${image}" "--pull=${pull}" "${context_dir}") local -ra build_cmd=("${DOCKER[@]}" build -t "${image}" "--pull=${pull}" "${context_dir}")
openim::log::status "Building Docker image ${image}" openim::log::status "Building Docker image ${image}"
local docker_output local docker_output
docker_output=$("${build_cmd[@]}" 2>&1) || { docker_output=$("${build_cmd[@]}" 2>&1) || {
cat <<EOF >&2 cat <<EOF >&2
+++ Docker build command failed for ${image} +++ Docker build command failed for ${image}
@@ -458,61 +459,61 @@ To retry manually, run:
${build_cmd[*]} ${build_cmd[*]}
EOF EOF
return 1 return 1
} }
} }
function openim::build::ensure_data_container() { function openim::build::ensure_data_container() {
# If the data container exists AND exited successfully, we can use it. # If the data container exists AND exited successfully, we can use it.
# Otherwise nuke it and start over. # Otherwise nuke it and start over.
local ret=0 local ret=0
local code=0 local code=0
code=$(docker inspect \ code=$(docker inspect \
-f '{{.State.ExitCode}}' \ -f '{{.State.ExitCode}}' \
"${OPENIM_DATA_CONTAINER_NAME}" 2>/dev/null) || ret=$? "${OPENIM_DATA_CONTAINER_NAME}" 2>/dev/null) || ret=$?
if [[ "${ret}" == 0 && "${code}" != 0 ]]; then if [[ "${ret}" == 0 && "${code}" != 0 ]]; then
openim::build::destroy_container "${OPENIM_DATA_CONTAINER_NAME}" openim::build::destroy_container "${OPENIM_DATA_CONTAINER_NAME}"
ret=1 ret=1
fi fi
if [[ "${ret}" != 0 ]]; then if [[ "${ret}" != 0 ]]; then
openim::log::status "Creating data container ${OPENIM_DATA_CONTAINER_NAME}" openim::log::status "Creating data container ${OPENIM_DATA_CONTAINER_NAME}"
# We have to ensure the directory exists, or else the docker run will # We have to ensure the directory exists, or else the docker run will
# create it as root. # create it as root.
mkdir -p "${LOCAL_OUTPUT_GOPATH}" mkdir -p "${LOCAL_OUTPUT_GOPATH}"
# We want this to run as root to be able to chown, so non-root users can # We want this to run as root to be able to chown, so non-root users can
# later use the result as a data container. This run both creates the data # later use the result as a data container. This run both creates the data
# container and chowns the GOPATH. # container and chowns the GOPATH.
# #
# The data container creates volumes for all of the directories that store # The data container creates volumes for all of the directories that store
# intermediates for the Go build. This enables incremental builds across # intermediates for the Go build. This enables incremental builds across
# Docker sessions. The *_cgo paths are re-compiled versions of the go std # Docker sessions. The *_cgo paths are re-compiled versions of the go std
# libraries for true static building. # libraries for true static building.
local -ra docker_cmd=( local -ra docker_cmd=(
"${DOCKER[@]}" run "${DOCKER[@]}" run
--volume "${REMOTE_ROOT}" # white-out the whole output dir --volume "${REMOTE_ROOT}" # white-out the whole output dir
--volume /usr/local/go/pkg/linux_386_cgo --volume /usr/local/go/pkg/linux_386_cgo
--volume /usr/local/go/pkg/linux_amd64_cgo --volume /usr/local/go/pkg/linux_amd64_cgo
--volume /usr/local/go/pkg/linux_arm_cgo --volume /usr/local/go/pkg/linux_arm_cgo
--volume /usr/local/go/pkg/linux_arm64_cgo --volume /usr/local/go/pkg/linux_arm64_cgo
--volume /usr/local/go/pkg/linux_ppc64le_cgo --volume /usr/local/go/pkg/linux_ppc64le_cgo
--volume /usr/local/go/pkg/darwin_amd64_cgo --volume /usr/local/go/pkg/darwin_amd64_cgo
--volume /usr/local/go/pkg/darwin_386_cgo --volume /usr/local/go/pkg/darwin_386_cgo
--volume /usr/local/go/pkg/windows_amd64_cgo --volume /usr/local/go/pkg/windows_amd64_cgo
--volume /usr/local/go/pkg/windows_386_cgo --volume /usr/local/go/pkg/windows_386_cgo
--name "${OPENIM_DATA_CONTAINER_NAME}" --name "${OPENIM_DATA_CONTAINER_NAME}"
--hostname "${HOSTNAME}" --hostname "${HOSTNAME}"
"${OPENIM_BUILD_IMAGE}" "${OPENIM_BUILD_IMAGE}"
chown -R "${USER_ID}":"${GROUP_ID}" chown -R "${USER_ID}":"${GROUP_ID}"
"${REMOTE_ROOT}" "${REMOTE_ROOT}"
/usr/local/go/pkg/ /usr/local/go/pkg/
) )
"${docker_cmd[@]}" "${docker_cmd[@]}"
fi fi
} }
# Build all openim commands. # Build all openim commands.
function openim::build::build_command() { function openim::build::build_command() {
openim::log::status "Running build command..." openim::log::status "Running build command..."
make -C "${OPENIM_ROOT}" multiarch make -C "${OPENIM_ROOT}" multiarch
} }
+6 -6
View File
@@ -19,11 +19,11 @@
echo "mode: atomic" > coverage.txt echo "mode: atomic" > coverage.txt
for d in $(find ./* -maxdepth 10 -type d); do for d in $(find ./* -maxdepth 10 -type d); do
if ls $d/*.go &> /dev/null; then if ls $d/*.go &> /dev/null; then
go test -coverprofile=profile.out -covermode=atomic $d go test -coverprofile=profile.out -covermode=atomic $d
if [ -f profile.out ]; then if [ -f profile.out ]; then
cat profile.out | grep -v "mode: " >> /tmp/coverage.txt cat profile.out | grep -v "mode: " >> /tmp/coverage.txt
rm profile.out rm profile.out
fi
fi fi
fi
done done
+11 -11
View File
@@ -15,16 +15,16 @@
if ! command -v pv &> /dev/null if ! command -v pv &> /dev/null
then then
echo "pv not found, installing..." echo "pv not found, installing..."
if [ -e /etc/debian_version ]; then if [ -e /etc/debian_version ]; then
sudo apt-get update sudo apt-get update
sudo apt-get install -y pv sudo apt-get install -y pv
elif [ -e /etc/redhat-release ]; then elif [ -e /etc/redhat-release ]; then
sudo yum install -y pv sudo yum install -y pv
else else
echo "Unsupported OS, please install pv manually." echo "Unsupported OS, please install pv manually."
exit 1 exit 1
fi fi
fi fi
readonly t_reset=$(tput sgr0) readonly t_reset=$(tput sgr0)
@@ -42,8 +42,8 @@ openim::util::ensure-bash-version
trap 'openim::util::onCtrlC' INT trap 'openim::util::onCtrlC' INT
function openim::util::onCtrlC() { function openim::util::onCtrlC() {
echo -e "\n${t_reset}Ctrl+C Press it. It's exiting openim make init..." echo -e "\n${t_reset}Ctrl+C Press it. It's exiting openim make init..."
exit 0 exit 0
} }
openim::util::desc "========> Welcome to the OpenIM Demo" openim::util::desc "========> Welcome to the OpenIM Demo"
+48 -48
View File
@@ -22,61 +22,61 @@ cd "$OPENIM_ROOT"
openim::util::check_docker_and_compose_versions openim::util::check_docker_and_compose_versions
progress() { progress() {
local _main_pid="$1" local _main_pid="$1"
local _length=20 local _length=20
local _ratio=1 local _ratio=1
local _colors=("31" "32" "33" "34" "35" "36" "37") local _colors=("31" "32" "33" "34" "35" "36" "37")
local _wave=("▁" "▂" "▃" "▄" "▅" "▆" "▇" "█" "▇" "▆" "▅" "▄" "▃" "▂") local _wave=("▁" "▂" "▃" "▄" "▅" "▆" "▇" "█" "▇" "▆" "▅" "▄" "▃" "▂")
while pgrep -P "$_main_pid" &> /dev/null; do while pgrep -P "$_main_pid" &> /dev/null; do
local _mark='>' local _mark='>'
local _progress_bar= local _progress_bar=
for ((i = 1; i <= _length; i++)); do for ((i = 1; i <= _length; i++)); do
if ((i > _ratio)); then if ((i > _ratio)); then
_mark='-' _mark='-'
fi fi
_progress_bar="${_progress_bar}${_mark}" _progress_bar="${_progress_bar}${_mark}"
done
local _color_idx=$((_ratio % ${#_colors[@]}))
local _color_prefix="\033[${_colors[_color_idx]}m"
local _reset_suffix="\033[0m"
local _wave_idx=$((_ratio % ${#_wave[@]}))
local _wave_progress=${_wave[_wave_idx]}
printf "Progress: ${_color_prefix}${_progress_bar}${_reset_suffix} ${_wave_progress} Countdown: %2ds \r" "$_countdown"
((_ratio++))
((_ratio > _length)) && _ratio=1
sleep 0.1
done done
local _color_idx=$((_ratio % ${#_colors[@]}))
local _color_prefix="\033[${_colors[_color_idx]}m"
local _reset_suffix="\033[0m"
local _wave_idx=$((_ratio % ${#_wave[@]}))
local _wave_progress=${_wave[_wave_idx]}
printf "Progress: ${_color_prefix}${_progress_bar}${_reset_suffix} ${_wave_progress} Countdown: %2ds \r" "$_countdown"
((_ratio++))
((_ratio > _length)) && _ratio=1
sleep 0.1
done
} }
countdown() { countdown() {
local _duration="$1" local _duration="$1"
for ((i = _duration; i >= 1; i--)); do for ((i = _duration; i >= 1; i--)); do
printf "\rCountdown: %2ds \r" "$i" printf "\rCountdown: %2ds \r" "$i"
sleep 1 sleep 1
done done
printf "\rCountdown: %2ds \r" "$_duration" printf "\rCountdown: %2ds \r" "$_duration"
} }
do_sth() { do_sth() {
echo "++++++++++++++++++++++++" echo "++++++++++++++++++++++++"
progress $$ & progress $$ &
local _progress_pid=$! local _progress_pid=$!
local _countdown=30 local _countdown=30
countdown "$_countdown" & countdown "$_countdown" &
local _countdown_pid=$! local _countdown_pid=$!
sleep 30 sleep 30
kill "$_progress_pid" "$_countdown_pid" kill "$_progress_pid" "$_countdown_pid"
"${SCRIPTS_ROOT}/check-all.sh" "${SCRIPTS_ROOT}/check-all.sh"
echo -e "${PURPLE_PREFIX}=========> Check docker-compose status ${COLOR_SUFFIX} \n" echo -e "${PURPLE_PREFIX}=========> Check docker-compose status ${COLOR_SUFFIX} \n"
} }
set -e set -e

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