mirror of
https://github.com/openimsdk/open-im-server.git
synced 2025-12-03 02:42:19 +08:00
Merge remote-tracking branch 'origin/add_set_ex_friend' into add_set_ex_friend
This commit is contained in:
commit
e41268de0f
8
.github/workflows/openimci.yml
vendored
8
.github/workflows/openimci.yml
vendored
@ -207,6 +207,12 @@ jobs:
|
|||||||
sudo make check || \
|
sudo make check || \
|
||||||
(echo "An error occurred, printing logs:" && sudo cat ./_output/logs/* 2>/dev/null)
|
(echo "An error occurred, printing logs:" && sudo cat ./_output/logs/* 2>/dev/null)
|
||||||
|
|
||||||
|
- name: Restart Services and Print Logs for Ubuntu
|
||||||
|
if: runner.os == 'Linux'
|
||||||
|
run: |
|
||||||
|
sudo make restart
|
||||||
|
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'
|
||||||
# run: |
|
# run: |
|
||||||
@ -239,4 +245,4 @@ jobs:
|
|||||||
- name: Test Docker Build
|
- name: Test Docker Build
|
||||||
run: |
|
run: |
|
||||||
sudo make init
|
sudo make init
|
||||||
sudo make image
|
sudo make image
|
||||||
|
|||||||
2
Makefile
2
Makefile
@ -95,7 +95,7 @@ stop:
|
|||||||
|
|
||||||
## restart: Restart openim (make init configuration file is initialized) ✨
|
## restart: Restart openim (make init configuration file is initialized) ✨
|
||||||
.PHONY: restart
|
.PHONY: restart
|
||||||
restart: clean stop build init start check
|
restart: clean stop build start check
|
||||||
|
|
||||||
## multiarch: Build binaries for multiple platforms. See option PLATFORMS. ✨
|
## multiarch: Build binaries for multiple platforms. See option PLATFORMS. ✨
|
||||||
.PHONY: multiarch
|
.PHONY: multiarch
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -247,6 +247,14 @@ manager:
|
|||||||
userID: [ "${MANAGER_USERID_1}", "${MANAGER_USERID_2}", "${MANAGER_USERID_3}" ]
|
userID: [ "${MANAGER_USERID_1}", "${MANAGER_USERID_2}", "${MANAGER_USERID_3}" ]
|
||||||
nickname: [ "${NICKNAME_1}", "${NICKNAME_2}", "${NICKNAME_3}" ]
|
nickname: [ "${NICKNAME_1}", "${NICKNAME_2}", "${NICKNAME_3}" ]
|
||||||
|
|
||||||
|
# chatAdmin, use for send notification
|
||||||
|
#
|
||||||
|
# Built-in app system notification account ID
|
||||||
|
# Built-in app system notification account nickname
|
||||||
|
im-admin:
|
||||||
|
userID: [ "${IM_ADMIN_USERID}" ]
|
||||||
|
nickname: [ "${IM_ADMIN_NAME}" ]
|
||||||
|
|
||||||
# Multi-platform login policy
|
# Multi-platform login policy
|
||||||
# For each platform(Android, iOS, Windows, Mac, web), only one can be online at a time
|
# For each platform(Android, iOS, Windows, Mac, web), only one can be online at a time
|
||||||
multiLoginPolicy: ${MULTILOGIN_POLICY}
|
multiLoginPolicy: ${MULTILOGIN_POLICY}
|
||||||
|
|||||||
@ -453,43 +453,45 @@ This section involves configuring the log settings, including storage location,
|
|||||||
|
|
||||||
This section involves setting up additional configuration variables for Websocket, Push Notifications, and Chat.
|
This section involves setting up additional configuration variables for Websocket, Push Notifications, and Chat.
|
||||||
|
|
||||||
| Parameter | Example Value | Description |
|
| Parameter | Example Value | Description |
|
||||||
|-------------------------|-------------------|------------------------------------|
|
|-------------------------|-------------------|----------------------------------|
|
||||||
| WEBSOCKET_MAX_CONN_NUM | "100000" | Maximum Websocket connections |
|
| WEBSOCKET_MAX_CONN_NUM | "100000" | Maximum Websocket connections |
|
||||||
| WEBSOCKET_MAX_MSG_LEN | "4096" | Maximum Websocket message length |
|
| WEBSOCKET_MAX_MSG_LEN | "4096" | Maximum Websocket message length |
|
||||||
| WEBSOCKET_TIMEOUT | "10" | Websocket timeout |
|
| WEBSOCKET_TIMEOUT | "10" | Websocket timeout |
|
||||||
| PUSH_ENABLE | "getui" | Push notification enable status |
|
| PUSH_ENABLE | "getui" | Push notification enable status |
|
||||||
| GETUI_PUSH_URL | [Generated URL] | GeTui Push Notification URL |
|
| GETUI_PUSH_URL | [Generated URL] | GeTui Push Notification URL |
|
||||||
| GETUI_MASTER_SECRET | [User Defined] | GeTui Master Secret |
|
| GETUI_MASTER_SECRET | [User Defined] | GeTui Master Secret |
|
||||||
| GETUI_APP_KEY | [User Defined] | GeTui Application Key |
|
| GETUI_APP_KEY | [User Defined] | GeTui Application Key |
|
||||||
| GETUI_INTENT | [User Defined] | GeTui Push Intent |
|
| GETUI_INTENT | [User Defined] | GeTui Push Intent |
|
||||||
| GETUI_CHANNEL_ID | [User Defined] | GeTui Channel ID |
|
| GETUI_CHANNEL_ID | [User Defined] | GeTui Channel ID |
|
||||||
| GETUI_CHANNEL_NAME | [User Defined] | GeTui Channel Name |
|
| GETUI_CHANNEL_NAME | [User Defined] | GeTui Channel Name |
|
||||||
| FCM_SERVICE_ACCOUNT | "x.json" | FCM Service Account |
|
| FCM_SERVICE_ACCOUNT | "x.json" | FCM Service Account |
|
||||||
| JPNS_APP_KEY | [User Defined] | JPNS Application Key |
|
| JPNS_APP_KEY | [User Defined] | JPNS Application Key |
|
||||||
| JPNS_MASTER_SECRET | [User Defined] | JPNS Master Secret |
|
| JPNS_MASTER_SECRET | [User Defined] | JPNS Master Secret |
|
||||||
| JPNS_PUSH_URL | [User Defined] | JPNS Push Notification URL |
|
| JPNS_PUSH_URL | [User Defined] | JPNS Push Notification URL |
|
||||||
| JPNS_PUSH_INTENT | [User Defined] | JPNS Push Intent |
|
| JPNS_PUSH_INTENT | [User Defined] | JPNS Push Intent |
|
||||||
| MANAGER_USERID_1 | "openIM123456" | Administrator ID 1 |
|
| MANAGER_USERID_1 | "openIM123456" | Administrator ID 1 |
|
||||||
| MANAGER_USERID_2 | "openIM654321" | Administrator ID 2 |
|
| MANAGER_USERID_2 | "openIM654321" | Administrator ID 2 |
|
||||||
| MANAGER_USERID_3 | "openIMAdmin" | Administrator ID 3 |
|
| MANAGER_USERID_3 | "openIMAdmin" | Administrator ID 3 |
|
||||||
| NICKNAME_1 | "system1" | Nickname 1 |
|
| NICKNAME_1 | "system1" | Nickname 1 |
|
||||||
| NICKNAME_2 | "system2" | Nickname 2 |
|
| NICKNAME_2 | "system2" | Nickname 2 |
|
||||||
| NICKNAME_3 | "system3" | Nickname 3 |
|
| NICKNAME_3 | "system3" | Nickname 3 |
|
||||||
| MULTILOGIN_POLICY | "1" | Multi-login Policy |
|
| IM_ADMIN_USERID | "imAdmin" | IM Administrator ID |
|
||||||
| CHAT_PERSISTENCE_MYSQL | "true" | Chat Persistence in MySQL |
|
| IM_ADMIN_NAME | "imAdmin" | IM Administrator Nickname |
|
||||||
| MSG_CACHE_TIMEOUT | "86400" | Message Cache Timeout |
|
| MULTILOGIN_POLICY | "1" | Multi-login Policy |
|
||||||
| GROUP_MSG_READ_RECEIPT | "true" | Group Message Read Receipt Enable |
|
| CHAT_PERSISTENCE_MYSQL | "true" | Chat Persistence in MySQL |
|
||||||
|
| MSG_CACHE_TIMEOUT | "86400" | Message Cache Timeout |
|
||||||
|
| GROUP_MSG_READ_RECEIPT | "true" | Group Message Read Receipt Enable |
|
||||||
| SINGLE_MSG_READ_RECEIPT | "true" | Single Message Read Receipt Enable |
|
| SINGLE_MSG_READ_RECEIPT | "true" | Single Message Read Receipt Enable |
|
||||||
| RETAIN_CHAT_RECORDS | "365" | Retain Chat Records (in days) |
|
| RETAIN_CHAT_RECORDS | "365" | Retain Chat Records (in days) |
|
||||||
| CHAT_RECORDS_CLEAR_TIME | [Cron Expression] | Chat Records Clear Time |
|
| CHAT_RECORDS_CLEAR_TIME | [Cron Expression] | Chat Records Clear Time |
|
||||||
| MSG_DESTRUCT_TIME | [Cron Expression] | Message Destruct Time |
|
| MSG_DESTRUCT_TIME | [Cron Expression] | Message Destruct Time |
|
||||||
| SECRET | "${PASSWORD}" | Secret Key |
|
| SECRET | "${PASSWORD}" | Secret Key |
|
||||||
| TOKEN_EXPIRE | "90" | Token Expiry Time |
|
| TOKEN_EXPIRE | "90" | Token Expiry Time |
|
||||||
| FRIEND_VERIFY | "false" | Friend Verification Enable |
|
| FRIEND_VERIFY | "false" | Friend Verification Enable |
|
||||||
| IOS_PUSH_SOUND | "xxx" | iOS |
|
| IOS_PUSH_SOUND | "xxx" | iOS |
|
||||||
| CALLBACK_ENABLE | "false" | Enable callback |
|
| CALLBACK_ENABLE | "false" | Enable callback |
|
||||||
| CALLBACK_TIMEOUT | "5" | Maximum timeout for callback call |
|
| CALLBACK_TIMEOUT | "5" | Maximum timeout for callback call |
|
||||||
| CALLBACK_FAILED_CONTINUE| "true" | fails to continue to the next step |
|
| CALLBACK_FAILED_CONTINUE| "true" | fails to continue to the next step |
|
||||||
### 2.20. <a name='PrometheusConfiguration-1'></a>Prometheus Configuration
|
### 2.20. <a name='PrometheusConfiguration-1'></a>Prometheus Configuration
|
||||||
|
|
||||||
|
|||||||
6
go.mod
6
go.mod
@ -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.39
|
||||||
|
github.com/OpenIMSDK/tools v0.0.21
|
||||||
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.37
|
|
||||||
github.com/OpenIMSDK/tools v0.0.22
|
|
||||||
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
|
||||||
@ -133,7 +133,7 @@ require (
|
|||||||
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.14.0 // indirect
|
||||||
golang.org/x/text v0.13.0 // indirect
|
golang.org/x/text v0.13.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
|
||||||
|
|||||||
8
go.sum
8
go.sum
@ -18,8 +18,8 @@ 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/protocol v0.0.37 h1:sVTnb3TjMQ3PG4m3gDI/LAM1Hkk9nI6gf7lf7UpscnI=
|
github.com/OpenIMSDK/protocol v0.0.39 h1:DfvFcNGBcfj2vtT7W3uw4U/ipnI7NecTzQdlSYGuQz8=
|
||||||
github.com/OpenIMSDK/protocol v0.0.37/go.mod h1:F25dFrwrIx3lkNoiuf6FkCfxuwf8L4Z8UIsdTHP/r0Y=
|
github.com/OpenIMSDK/protocol v0.0.39/go.mod h1:F25dFrwrIx3lkNoiuf6FkCfxuwf8L4Z8UIsdTHP/r0Y=
|
||||||
github.com/OpenIMSDK/tools v0.0.21 h1:iTapc2mIEVH/xl5Nd6jfwPub11Pgp44tVcE1rjB3a48=
|
github.com/OpenIMSDK/tools v0.0.21 h1:iTapc2mIEVH/xl5Nd6jfwPub11Pgp44tVcE1rjB3a48=
|
||||||
github.com/OpenIMSDK/tools v0.0.21/go.mod h1:eg+q4A34Qmu73xkY0mt37FHGMCMfC6CtmOnm0kFEGFI=
|
github.com/OpenIMSDK/tools v0.0.21/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=
|
||||||
@ -453,8 +453,8 @@ 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.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k=
|
||||||
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||||
golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4=
|
golang.org/x/time v0.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=
|
||||||
|
|||||||
@ -169,9 +169,8 @@ func (m *MessageApi) getSendMsgReq(c *gin.Context, req apistruct.SendMsg) (sendM
|
|||||||
case constant.OANotification:
|
case constant.OANotification:
|
||||||
data = apistruct.OANotificationElem{}
|
data = apistruct.OANotificationElem{}
|
||||||
req.SessionType = constant.NotificationChatType
|
req.SessionType = constant.NotificationChatType
|
||||||
if !authverify.IsManagerUserID(req.SendID) {
|
if err = m.userRpcClient.GetNotificationByID(c, req.SendID); err != nil {
|
||||||
return nil, errs.ErrNoPermission.
|
return nil, err
|
||||||
Wrap("only app manager can as sender send OANotificationElem")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
|||||||
@ -83,6 +83,10 @@ func NewGinRouter(discov discoveryregistry.SvcDiscoveryRegistry, rdb redis.Unive
|
|||||||
userRouterGroup.POST("/process_user_command_delete", ParseToken, u.ProcessUserCommandDelete)
|
userRouterGroup.POST("/process_user_command_delete", ParseToken, u.ProcessUserCommandDelete)
|
||||||
userRouterGroup.POST("/process_user_command_update", ParseToken, u.ProcessUserCommandUpdate)
|
userRouterGroup.POST("/process_user_command_update", ParseToken, u.ProcessUserCommandUpdate)
|
||||||
userRouterGroup.POST("/process_user_command_get", ParseToken, u.ProcessUserCommandGet)
|
userRouterGroup.POST("/process_user_command_get", ParseToken, u.ProcessUserCommandGet)
|
||||||
|
|
||||||
|
userRouterGroup.POST("/add_notification_account", ParseToken, u.AddNotificationAccount)
|
||||||
|
userRouterGroup.POST("/update_notification_account", ParseToken, u.UpdateNotificationAccountInfo)
|
||||||
|
userRouterGroup.POST("/search_notification_account", ParseToken, u.SearchNotificationAccount)
|
||||||
}
|
}
|
||||||
// friend routing group
|
// friend routing group
|
||||||
friendRouterGroup := r.Group("/friend", ParseToken)
|
friendRouterGroup := r.Group("/friend", ParseToken)
|
||||||
@ -168,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
|
||||||
|
|||||||
@ -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 == "" {
|
||||||
|
|||||||
@ -15,8 +15,6 @@
|
|||||||
package api
|
package api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/gin-gonic/gin"
|
|
||||||
|
|
||||||
"github.com/OpenIMSDK/protocol/constant"
|
"github.com/OpenIMSDK/protocol/constant"
|
||||||
"github.com/OpenIMSDK/protocol/msggateway"
|
"github.com/OpenIMSDK/protocol/msggateway"
|
||||||
"github.com/OpenIMSDK/protocol/user"
|
"github.com/OpenIMSDK/protocol/user"
|
||||||
@ -24,6 +22,7 @@ import (
|
|||||||
"github.com/OpenIMSDK/tools/apiresp"
|
"github.com/OpenIMSDK/tools/apiresp"
|
||||||
"github.com/OpenIMSDK/tools/errs"
|
"github.com/OpenIMSDK/tools/errs"
|
||||||
"github.com/OpenIMSDK/tools/log"
|
"github.com/OpenIMSDK/tools/log"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
|
||||||
"github.com/openimsdk/open-im-server/v3/pkg/common/config"
|
"github.com/openimsdk/open-im-server/v3/pkg/common/config"
|
||||||
"github.com/openimsdk/open-im-server/v3/pkg/rpcclient"
|
"github.com/openimsdk/open-im-server/v3/pkg/rpcclient"
|
||||||
@ -221,3 +220,15 @@ func (u *UserApi) ProcessUserCommandUpdate(c *gin.Context) {
|
|||||||
func (u *UserApi) ProcessUserCommandGet(c *gin.Context) {
|
func (u *UserApi) ProcessUserCommandGet(c *gin.Context) {
|
||||||
a2r.Call(user.UserClient.ProcessUserCommandGet, u.Client, c)
|
a2r.Call(user.UserClient.ProcessUserCommandGet, u.Client, c)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (u *UserApi) AddNotificationAccount(c *gin.Context) {
|
||||||
|
a2r.Call(user.UserClient.AddNotificationAccount, u.Client, c)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *UserApi) UpdateNotificationAccountInfo(c *gin.Context) {
|
||||||
|
a2r.Call(user.UserClient.UpdateNotificationAccountInfo, u.Client, c)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *UserApi) SearchNotificationAccount(c *gin.Context) {
|
||||||
|
a2r.Call(user.UserClient.SearchNotificationAccount, u.Client, c)
|
||||||
|
}
|
||||||
|
|||||||
@ -53,6 +53,10 @@ type friendServer struct {
|
|||||||
RegisterCenter registry.SvcDiscoveryRegistry
|
RegisterCenter registry.SvcDiscoveryRegistry
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *friendServer) UpdateFriends(ctx context.Context, req *pbfriend.UpdateFriendsReq) (*pbfriend.UpdateFriendsResp, error) {
|
||||||
|
return nil, errs.ErrInternalServer.Wrap("not implemented")
|
||||||
|
}
|
||||||
|
|
||||||
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()
|
||||||
|
|||||||
@ -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"`
|
||||||
|
}
|
||||||
|
|||||||
@ -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 {
|
||||||
|
|||||||
@ -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{
|
||||||
|
|||||||
@ -17,6 +17,7 @@ package user
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
|
"math/rand"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@ -56,6 +57,10 @@ type userServer struct {
|
|||||||
RegisterCenter registry.SvcDiscoveryRegistry
|
RegisterCenter registry.SvcDiscoveryRegistry
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *userServer) UpdateUserInfoEx(ctx context.Context, req *pbuser.UpdateUserInfoExReq) (*pbuser.UpdateUserInfoExResp, error) {
|
||||||
|
return nil, errs.ErrInternalServer.Wrap("not implemented")
|
||||||
|
}
|
||||||
|
|
||||||
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 {
|
||||||
@ -72,6 +77,12 @@ func Start(client registry.SvcDiscoveryRegistry, server *grpc.Server) error {
|
|||||||
for k, v := range config.Config.Manager.UserID {
|
for k, v := range config.Config.Manager.UserID {
|
||||||
users = append(users, &tablerelation.UserModel{UserID: v, Nickname: config.Config.Manager.Nickname[k], AppMangerLevel: constant.AppAdmin})
|
users = append(users, &tablerelation.UserModel{UserID: v, Nickname: config.Config.Manager.Nickname[k], AppMangerLevel: constant.AppAdmin})
|
||||||
}
|
}
|
||||||
|
if len(config.Config.IMAdmin.UserID) != len(config.Config.IMAdmin.Nickname) {
|
||||||
|
return errors.New("len(config.Config.AppNotificationAdmin.AppManagerUid) != len(config.Config.AppNotificationAdmin.Nickname)")
|
||||||
|
}
|
||||||
|
for k, v := range config.Config.IMAdmin.UserID {
|
||||||
|
users = append(users, &tablerelation.UserModel{UserID: v, Nickname: config.Config.IMAdmin.Nickname[k], AppMangerLevel: constant.AppNotificationAdmin})
|
||||||
|
}
|
||||||
userDB, err := mgo.NewUserMongo(mongo.GetDatabase())
|
userDB, err := mgo.NewUserMongo(mongo.GetDatabase())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -424,3 +435,119 @@ func (s *userServer) ProcessUserCommandGet(ctx context.Context, req *pbuser.Proc
|
|||||||
// Return the response with the slice
|
// Return the response with the slice
|
||||||
return &pbuser.ProcessUserCommandGetResp{KVArray: commandInfoSlice}, nil
|
return &pbuser.ProcessUserCommandGetResp{KVArray: commandInfoSlice}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *userServer) AddNotificationAccount(ctx context.Context, req *pbuser.AddNotificationAccountReq) (*pbuser.AddNotificationAccountResp, error) {
|
||||||
|
if err := authverify.CheckIMAdmin(ctx); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var userID string
|
||||||
|
for i := 0; i < 20; i++ {
|
||||||
|
userId := s.genUserID()
|
||||||
|
_, err := s.UserDatabase.FindWithError(ctx, []string{userId})
|
||||||
|
if err == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
userID = userId
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if userID == "" {
|
||||||
|
return nil, errs.ErrInternalServer.Wrap("gen user id failed")
|
||||||
|
}
|
||||||
|
|
||||||
|
user := &tablerelation.UserModel{
|
||||||
|
UserID: userID,
|
||||||
|
Nickname: req.NickName,
|
||||||
|
FaceURL: req.FaceURL,
|
||||||
|
CreateTime: time.Now(),
|
||||||
|
AppMangerLevel: constant.AppNotificationAdmin,
|
||||||
|
}
|
||||||
|
if err := s.UserDatabase.Create(ctx, []*tablerelation.UserModel{user}); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &pbuser.AddNotificationAccountResp{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *userServer) UpdateNotificationAccountInfo(ctx context.Context, req *pbuser.UpdateNotificationAccountInfoReq) (*pbuser.UpdateNotificationAccountInfoResp, error) {
|
||||||
|
if err := authverify.CheckIMAdmin(ctx); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := s.UserDatabase.FindWithError(ctx, []string{req.UserID}); err != nil {
|
||||||
|
return nil, errs.ErrArgs.Wrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
user := map[string]interface{}{}
|
||||||
|
|
||||||
|
if req.NickName != "" {
|
||||||
|
user["nickname"] = req.NickName
|
||||||
|
}
|
||||||
|
|
||||||
|
if req.FaceURL != "" {
|
||||||
|
user["face_url"] = req.FaceURL
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := s.UserDatabase.UpdateByMap(ctx, req.UserID, user); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &pbuser.UpdateNotificationAccountInfoResp{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *userServer) SearchNotificationAccount(ctx context.Context, req *pbuser.SearchNotificationAccountReq) (*pbuser.SearchNotificationAccountResp, error) {
|
||||||
|
if err := authverify.CheckIMAdmin(ctx); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, users, err := s.UserDatabase.Page(ctx, req.Pagination)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var total int64
|
||||||
|
accounts := make([]*pbuser.NotificationAccountInfo, 0, len(users))
|
||||||
|
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) GetNotificationAccount(ctx context.Context, req *pbuser.GetNotificationAccountReq) (*pbuser.GetNotificationAccountResp, error) {
|
||||||
|
if req.UserID == "" {
|
||||||
|
return nil, errs.ErrArgs.Wrap("userID is empty")
|
||||||
|
}
|
||||||
|
user, err := s.UserDatabase.GetUserByID(ctx, req.UserID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errs.ErrUserIDNotFound.Wrap()
|
||||||
|
}
|
||||||
|
if user.AppMangerLevel == constant.AppAdmin || user.AppMangerLevel == constant.AppNotificationAdmin {
|
||||||
|
return &pbuser.GetNotificationAccountResp{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, errs.ErrNoPermission.Wrap("notification messages cannot be sent for this ID")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *userServer) genUserID() string {
|
||||||
|
const l = 10
|
||||||
|
data := make([]byte, l)
|
||||||
|
rand.Read(data)
|
||||||
|
chars := []byte("0123456789")
|
||||||
|
for i := 0; i < len(data); i++ {
|
||||||
|
if i == 0 {
|
||||||
|
data[i] = chars[1:][data[i]%9]
|
||||||
|
} else {
|
||||||
|
data[i] = chars[data[i]%10]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return string(data)
|
||||||
|
}
|
||||||
|
|||||||
@ -87,7 +87,7 @@ type OANotificationElem struct {
|
|||||||
NotificationType int32 `mapstructure:"notificationType" json:"notificationType" validate:"required"`
|
NotificationType int32 `mapstructure:"notificationType" json:"notificationType" validate:"required"`
|
||||||
Text string `mapstructure:"text" json:"text" validate:"required"`
|
Text string `mapstructure:"text" json:"text" validate:"required"`
|
||||||
Url string `mapstructure:"url" json:"url"`
|
Url string `mapstructure:"url" json:"url"`
|
||||||
MixType int32 `mapstructure:"mixType" json:"mixType" validate:"required"`
|
MixType int32 `mapstructure:"mixType" json:"mixType"`
|
||||||
PictureElem *PictureElem `mapstructure:"pictureElem" json:"pictureElem"`
|
PictureElem *PictureElem `mapstructure:"pictureElem" json:"pictureElem"`
|
||||||
SoundElem *SoundElem `mapstructure:"soundElem" json:"soundElem"`
|
SoundElem *SoundElem `mapstructure:"soundElem" json:"soundElem"`
|
||||||
VideoElem *VideoElem `mapstructure:"videoElem" json:"videoElem"`
|
VideoElem *VideoElem `mapstructure:"videoElem" json:"videoElem"`
|
||||||
|
|||||||
@ -54,6 +54,15 @@ func CheckAdmin(ctx context.Context) error {
|
|||||||
}
|
}
|
||||||
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 {
|
||||||
|
if utils.IsContain(mcontext.GetOpUserID(ctx), config.Config.IMAdmin.UserID) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if utils.IsContain(mcontext.GetOpUserID(ctx), config.Config.Manager.UserID) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return errs.ErrNoPermission.Wrap(fmt.Sprintf("user %s is not CheckIMAdmin userID", mcontext.GetOpUserID(ctx)))
|
||||||
|
}
|
||||||
|
|
||||||
func ParseRedisInterfaceToken(redisToken any) (*tokenverify.Claims, error) {
|
func ParseRedisInterfaceToken(redisToken any) (*tokenverify.Claims, error) {
|
||||||
return tokenverify.GetClaimFromToken(string(redisToken.([]uint8)), Secret())
|
return tokenverify.GetClaimFromToken(string(redisToken.([]uint8)), Secret())
|
||||||
|
|||||||
@ -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"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -236,6 +236,11 @@ type configStruct struct {
|
|||||||
Nickname []string `yaml:"nickname"`
|
Nickname []string `yaml:"nickname"`
|
||||||
} `yaml:"manager"`
|
} `yaml:"manager"`
|
||||||
|
|
||||||
|
IMAdmin struct {
|
||||||
|
UserID []string `yaml:"userID"`
|
||||||
|
Nickname []string `yaml:"nickname"`
|
||||||
|
} `yaml:"im-admin"`
|
||||||
|
|
||||||
MultiLoginPolicy int `yaml:"multiLoginPolicy"`
|
MultiLoginPolicy int `yaml:"multiLoginPolicy"`
|
||||||
ChatPersistenceMysql bool `yaml:"chatPersistenceMysql"`
|
ChatPersistenceMysql bool `yaml:"chatPersistenceMysql"`
|
||||||
MsgCacheTimeout int `yaml:"msgCacheTimeout"`
|
MsgCacheTimeout int `yaml:"msgCacheTimeout"`
|
||||||
|
|||||||
@ -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)
|
||||||
|
}
|
||||||
|
|||||||
@ -50,6 +50,8 @@ type UserDatabase interface {
|
|||||||
IsExist(ctx context.Context, userIDs []string) (exist bool, err error)
|
IsExist(ctx context.Context, userIDs []string) (exist bool, err error)
|
||||||
// GetAllUserID Get all user IDs
|
// GetAllUserID Get all user IDs
|
||||||
GetAllUserID(ctx context.Context, pagination pagination.Pagination) (int64, []string, error)
|
GetAllUserID(ctx context.Context, pagination pagination.Pagination) (int64, []string, error)
|
||||||
|
// Get user by userID
|
||||||
|
GetUserByID(ctx context.Context, userID string) (user *relation.UserModel, err error)
|
||||||
// InitOnce Inside the function, first query whether it exists in the db, if it exists, do nothing; if it does not exist, insert it
|
// InitOnce Inside the function, first query whether it exists in the db, if it exists, do nothing; if it does not exist, insert it
|
||||||
InitOnce(ctx context.Context, users []*relation.UserModel) (err error)
|
InitOnce(ctx context.Context, users []*relation.UserModel) (err error)
|
||||||
// CountTotal Get the total number of users
|
// CountTotal Get the total number of users
|
||||||
@ -183,6 +185,10 @@ func (u *userDatabase) GetAllUserID(ctx context.Context, pagination pagination.P
|
|||||||
return u.userDB.GetAllUserID(ctx, pagination)
|
return u.userDB.GetAllUserID(ctx, pagination)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (u *userDatabase) GetUserByID(ctx context.Context, userID string) (user *relation.UserModel, err error) {
|
||||||
|
return u.userDB.Take(ctx, userID)
|
||||||
|
}
|
||||||
|
|
||||||
// CountTotal Get the total number of users.
|
// CountTotal Get the total number of users.
|
||||||
func (u *userDatabase) CountTotal(ctx context.Context, before *time.Time) (count int64, err error) {
|
func (u *userDatabase) CountTotal(ctx context.Context, before *time.Time) (count int64, err error) {
|
||||||
return u.userDB.CountTotal(ctx, before)
|
return u.userDB.CountTotal(ctx, before)
|
||||||
|
|||||||
@ -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 = ","
|
||||||
|
|||||||
@ -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)
|
||||||
|
}
|
||||||
|
|||||||
@ -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))
|
||||||
|
}
|
||||||
|
|||||||
@ -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
|
||||||
|
}
|
||||||
|
|||||||
@ -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
|
||||||
|
}
|
||||||
|
|||||||
@ -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)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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)
|
||||||
|
|||||||
@ -112,7 +112,6 @@ func callBackPostReturn(ctx context.Context, url, command string, input interfac
|
|||||||
//v.Set(constant.CallbackCommand, command)
|
//v.Set(constant.CallbackCommand, command)
|
||||||
//url = url + "/" + v.Encode()
|
//url = url + "/" + v.Encode()
|
||||||
url = url + "/" + command
|
url = url + "/" + command
|
||||||
|
|
||||||
b, err := Post(ctx, url, nil, input, callbackConfig.CallbackTimeOut)
|
b, err := Post(ctx, url, nil, input, callbackConfig.CallbackTimeOut)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if callbackConfig.CallbackFailedContinue != nil && *callbackConfig.CallbackFailedContinue {
|
if callbackConfig.CallbackFailedContinue != nil && *callbackConfig.CallbackFailedContinue {
|
||||||
@ -121,6 +120,7 @@ func callBackPostReturn(ctx context.Context, url, command string, input interfac
|
|||||||
}
|
}
|
||||||
return errs.ErrNetwork.Wrap(err.Error())
|
return errs.ErrNetwork.Wrap(err.Error())
|
||||||
}
|
}
|
||||||
|
defer log.ZDebug(ctx, "callback", "data", string(b))
|
||||||
|
|
||||||
if err = json.Unmarshal(b, output); err != nil {
|
if err = json.Unmarshal(b, output); err != nil {
|
||||||
if callbackConfig.CallbackFailedContinue != nil && *callbackConfig.CallbackFailedContinue {
|
if callbackConfig.CallbackFailedContinue != nil && *callbackConfig.CallbackFailedContinue {
|
||||||
|
|||||||
@ -179,3 +179,10 @@ func (u *UserRpcClient) SetUserStatus(ctx context.Context, userID string, status
|
|||||||
})
|
})
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (u *UserRpcClient) GetNotificationByID(ctx context.Context, userID string) error {
|
||||||
|
_, err := u.Client.GetNotificationAccount(ctx, &user.GetNotificationAccountReq{
|
||||||
|
UserID: userID,
|
||||||
|
})
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|||||||
@ -89,4 +89,4 @@ else
|
|||||||
echo "++++ Check all openim service ports successfully !"
|
echo "++++ Check all openim service ports successfully !"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
set -e
|
set -e
|
||||||
@ -353,6 +353,8 @@ def "MANAGER_USERID_3" "openIMAdmin" # 管理员ID 3
|
|||||||
def "NICKNAME_1" "system1" # 昵称1
|
def "NICKNAME_1" "system1" # 昵称1
|
||||||
def "NICKNAME_2" "system2" # 昵称2
|
def "NICKNAME_2" "system2" # 昵称2
|
||||||
def "NICKNAME_3" "system3" # 昵称3
|
def "NICKNAME_3" "system3" # 昵称3
|
||||||
|
def "IM_ADMIN_USERID" "imAdmin" # IM管理员ID
|
||||||
|
def "IM_ADMIN_NAME" "imAdmin" # IM管理员昵称
|
||||||
def "MULTILOGIN_POLICY" "1" # 多登录策略
|
def "MULTILOGIN_POLICY" "1" # 多登录策略
|
||||||
def "CHAT_PERSISTENCE_MYSQL" "true" # 聊天持久化MySQL
|
def "CHAT_PERSISTENCE_MYSQL" "true" # 聊天持久化MySQL
|
||||||
def "MSG_CACHE_TIMEOUT" "86400" # 消息缓存超时
|
def "MSG_CACHE_TIMEOUT" "86400" # 消息缓存超时
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user