复制项目

This commit is contained in:
kim.dev.6789
2026-01-14 22:16:44 +08:00
parent e2577b8cee
commit e50142a3b9
691 changed files with 97009 additions and 1 deletions

View File

@@ -0,0 +1,14 @@
package webhook
import (
"context"
"git.imall.cloud/openim/open-im-server-deploy/pkg/common/config"
)
func WithCondition(ctx context.Context, before *config.BeforeConfig, callback func(context.Context) error) error {
if !before.Enable {
return nil
}
return callback(ctx)
}

View File

@@ -0,0 +1,433 @@
// Copyright © 2023 OpenIM. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package webhook
import (
"context"
"encoding/json"
"sync"
"time"
"git.imall.cloud/openim/open-im-server-deploy/pkg/common/config"
"git.imall.cloud/openim/open-im-server-deploy/pkg/common/storage/model"
"git.imall.cloud/openim/open-im-server-deploy/pkg/common/storage/database"
"github.com/openimsdk/tools/errs"
"github.com/openimsdk/tools/log"
"github.com/openimsdk/tools/utils/datautil"
)
const (
// WebhookConfigKey 数据库中的webhook配置键
WebhookConfigKey = "webhook_config"
// DefaultRefreshInterval 默认刷新间隔30秒方便调试
DefaultRefreshInterval = 30 * time.Second
)
// ConfigManager webhook配置管理器支持从数据库读取和定时刷新
type ConfigManager struct {
db database.SystemConfig
defaultConfig *config.Webhooks
mu sync.RWMutex
cachedConfig *config.Webhooks
lastUpdate time.Time
refreshInterval time.Duration
stopCh chan struct{}
}
// NewConfigManager 创建webhook配置管理器
func NewConfigManager(db database.SystemConfig, defaultConfig *config.Webhooks) *ConfigManager {
cm := &ConfigManager{
db: db,
defaultConfig: defaultConfig,
cachedConfig: defaultConfig,
refreshInterval: DefaultRefreshInterval,
stopCh: make(chan struct{}),
}
return cm
}
// Start 启动配置管理器,开始定时刷新
func (cm *ConfigManager) Start(ctx context.Context) error {
// 立即加载一次配置
log.ZInfo(ctx, "webhook config manager starting, initial refresh...")
if err := cm.Refresh(ctx); err != nil {
log.ZWarn(ctx, "initial webhook config refresh failed, using default config", err)
} else {
currentConfig := cm.GetConfig()
log.ZInfo(ctx, "webhook config manager started successfully", "url", currentConfig.URL, "refresh_interval", cm.refreshInterval)
}
// 启动定时刷新goroutine
go cm.refreshLoop(ctx)
return nil
}
// Stop 停止配置管理器
func (cm *ConfigManager) Stop() {
close(cm.stopCh)
}
// refreshLoop 定时刷新循环
func (cm *ConfigManager) refreshLoop(ctx context.Context) {
ticker := time.NewTicker(cm.refreshInterval)
defer ticker.Stop()
log.ZInfo(ctx, "webhook config refresh loop started", "interval", cm.refreshInterval)
for {
select {
case <-ticker.C:
log.ZDebug(ctx, "webhook config scheduled refresh triggered")
if err := cm.Refresh(ctx); err != nil {
log.ZWarn(ctx, "webhook config refresh failed", err)
}
case <-cm.stopCh:
log.ZInfo(ctx, "webhook config refresh loop stopped")
return
}
}
}
// Refresh 从数据库刷新配置
func (cm *ConfigManager) Refresh(ctx context.Context) error {
// 从数据库读取配置
sysConfig, err := cm.db.FindByKey(ctx, WebhookConfigKey)
if err != nil {
// 如果查询出错,使用默认配置
log.ZWarn(ctx, "failed to get webhook config from database, using default config", err)
cm.mu.Lock()
cm.cachedConfig = cm.defaultConfig
cm.lastUpdate = time.Now()
cm.mu.Unlock()
return nil
}
// 如果数据库中没有配置,使用默认配置
if sysConfig == nil {
cm.mu.Lock()
cm.cachedConfig = cm.defaultConfig
cm.lastUpdate = time.Now()
cm.mu.Unlock()
log.ZInfo(ctx, "webhook config not found in database, using default config", "default_url", cm.defaultConfig.URL)
return nil
}
// 检查值类型
if sysConfig.ValueType != model.SystemConfigValueTypeJSON {
cm.mu.Lock()
cm.cachedConfig = cm.defaultConfig
cm.lastUpdate = time.Now()
cm.mu.Unlock()
log.ZWarn(ctx, "webhook config valueType is not json, using default config", nil, "value_type", sysConfig.ValueType)
return nil
}
// 如果配置被禁用,使用默认配置
if !sysConfig.Enabled {
cm.mu.Lock()
cm.cachedConfig = cm.defaultConfig
cm.lastUpdate = time.Now()
cm.mu.Unlock()
log.ZInfo(ctx, "webhook config is disabled, using default config", "default_url", cm.defaultConfig.URL)
return nil
}
// 如果配置值为空,使用默认配置
if sysConfig.Value == "" {
cm.mu.Lock()
cm.cachedConfig = cm.defaultConfig
cm.lastUpdate = time.Now()
cm.mu.Unlock()
log.ZInfo(ctx, "webhook config value is empty, using default config", "default_url", cm.defaultConfig.URL)
return nil
}
valuePreview := sysConfig.Value
if len(valuePreview) > 100 {
valuePreview = valuePreview[:100] + "..."
}
log.ZDebug(ctx, "webhook config value found", "value_length", len(sysConfig.Value), "value_preview", valuePreview)
// 解析配置
var webhookConfig config.Webhooks
if err := json.Unmarshal([]byte(sysConfig.Value), &webhookConfig); err != nil {
// 如果解析失败,使用默认配置
log.ZWarn(ctx, "failed to unmarshal webhook config, using default config", err)
cm.mu.Lock()
cm.cachedConfig = cm.defaultConfig
cm.lastUpdate = time.Now()
cm.mu.Unlock()
return nil
}
// 验证解析后的配置确保AttentionIds不为nil
if webhookConfig.AfterSendGroupMsg.AttentionIds == nil {
webhookConfig.AfterSendGroupMsg.AttentionIds = []string{}
}
normalized, ok := normalizeWebhookConfig(cm.defaultConfig, &webhookConfig)
if !ok {
log.ZWarn(ctx, "webhook config URL is empty, using default config", nil)
cm.mu.Lock()
cm.cachedConfig = cm.defaultConfig
cm.lastUpdate = time.Now()
cm.mu.Unlock()
return nil
}
// 更新缓存
cm.mu.Lock()
oldURL := cm.cachedConfig.URL
oldAttentionIdsCount := len(cm.cachedConfig.AfterSendGroupMsg.AttentionIds)
cm.cachedConfig = normalized
cm.lastUpdate = time.Now()
newAttentionIdsCount := len(normalized.AfterSendGroupMsg.AttentionIds)
cm.mu.Unlock()
// 如果URL或AttentionIds发生变化记录日志
urlChanged := oldURL != webhookConfig.URL
attentionIdsChanged := oldAttentionIdsCount != newAttentionIdsCount
if urlChanged || attentionIdsChanged {
log.ZInfo(ctx, "webhook config updated from database",
"old_url", oldURL, "new_url", webhookConfig.URL,
"old_attention_ids_count", oldAttentionIdsCount,
"new_attention_ids_count", newAttentionIdsCount,
"url_changed", urlChanged,
"attention_ids_changed", attentionIdsChanged)
} else {
log.ZDebug(ctx, "webhook config refreshed (no change)", "url", webhookConfig.URL, "attention_ids_count", newAttentionIdsCount)
}
return nil
}
// GetConfig 获取当前缓存的配置
func (cm *ConfigManager) GetConfig() *config.Webhooks {
cm.mu.RLock()
defer cm.mu.RUnlock()
return cm.cachedConfig
}
// normalizeWebhookConfig 校验并填充缺省值,返回是否有效
func normalizeWebhookConfig(defaultCfg *config.Webhooks, cfg *config.Webhooks) (*config.Webhooks, bool) {
if cfg == nil {
return nil, false
}
if cfg.URL == "" {
return nil, false
}
normalized := *cfg // 浅拷贝
// 填充默认值,避免数据库配置错误导致回调不可用
if normalized.AfterSendGroupMsg.AttentionIds == nil {
normalized.AfterSendGroupMsg.AttentionIds = []string{}
}
applyBeforeDefaults := func(dst *config.BeforeConfig, fallback config.BeforeConfig) {
if dst.Timeout <= 0 {
dst.Timeout = fallback.Timeout
}
}
applyAfterDefaults := func(dst *config.AfterConfig, fallback config.AfterConfig) {
if dst.Timeout <= 0 {
dst.Timeout = fallback.Timeout
}
if dst.AttentionIds == nil {
dst.AttentionIds = []string{}
}
}
if defaultCfg != nil {
applyBeforeDefaults(&normalized.BeforeSendSingleMsg, defaultCfg.BeforeSendSingleMsg)
applyBeforeDefaults(&normalized.BeforeUpdateUserInfoEx, defaultCfg.BeforeUpdateUserInfoEx)
applyAfterDefaults(&normalized.AfterUpdateUserInfoEx, defaultCfg.AfterUpdateUserInfoEx)
applyAfterDefaults(&normalized.AfterSendSingleMsg, defaultCfg.AfterSendSingleMsg)
applyBeforeDefaults(&normalized.BeforeSendGroupMsg, defaultCfg.BeforeSendGroupMsg)
applyBeforeDefaults(&normalized.BeforeMsgModify, defaultCfg.BeforeMsgModify)
applyAfterDefaults(&normalized.AfterSendGroupMsg, defaultCfg.AfterSendGroupMsg)
applyAfterDefaults(&normalized.AfterMsgSaveDB, defaultCfg.AfterMsgSaveDB)
applyAfterDefaults(&normalized.AfterUserOnline, defaultCfg.AfterUserOnline)
applyAfterDefaults(&normalized.AfterUserOffline, defaultCfg.AfterUserOffline)
applyAfterDefaults(&normalized.AfterUserKickOff, defaultCfg.AfterUserKickOff)
applyBeforeDefaults(&normalized.BeforeOfflinePush, defaultCfg.BeforeOfflinePush)
applyBeforeDefaults(&normalized.BeforeOnlinePush, defaultCfg.BeforeOnlinePush)
applyBeforeDefaults(&normalized.BeforeGroupOnlinePush, defaultCfg.BeforeGroupOnlinePush)
applyBeforeDefaults(&normalized.BeforeAddFriend, defaultCfg.BeforeAddFriend)
applyBeforeDefaults(&normalized.BeforeUpdateUserInfo, defaultCfg.BeforeUpdateUserInfo)
applyAfterDefaults(&normalized.AfterUpdateUserInfo, defaultCfg.AfterUpdateUserInfo)
applyBeforeDefaults(&normalized.BeforeCreateGroup, defaultCfg.BeforeCreateGroup)
applyAfterDefaults(&normalized.AfterCreateGroup, defaultCfg.AfterCreateGroup)
applyBeforeDefaults(&normalized.BeforeMemberJoinGroup, defaultCfg.BeforeMemberJoinGroup)
applyBeforeDefaults(&normalized.BeforeSetGroupMemberInfo, defaultCfg.BeforeSetGroupMemberInfo)
applyAfterDefaults(&normalized.AfterSetGroupMemberInfo, defaultCfg.AfterSetGroupMemberInfo)
applyAfterDefaults(&normalized.AfterQuitGroup, defaultCfg.AfterQuitGroup)
applyAfterDefaults(&normalized.AfterKickGroupMember, defaultCfg.AfterKickGroupMember)
applyAfterDefaults(&normalized.AfterDismissGroup, defaultCfg.AfterDismissGroup)
}
return &normalized, true
}
// GetURL 获取当前webhook URL
func (cm *ConfigManager) GetURL() string {
cm.mu.RLock()
defer cm.mu.RUnlock()
return cm.cachedConfig.URL
}
// SetRefreshInterval 设置刷新间隔
func (cm *ConfigManager) SetRefreshInterval(interval time.Duration) {
cm.mu.Lock()
defer cm.mu.Unlock()
cm.refreshInterval = interval
}
// GetLastUpdate 获取最后更新时间
func (cm *ConfigManager) GetLastUpdate() time.Time {
cm.mu.RLock()
defer cm.mu.RUnlock()
return cm.lastUpdate
}
// UpdateAttentionIds 更新webhook配置中的attentionIds添加或移除群ID
// add: true表示添加groupIDfalse表示移除groupID
func UpdateAttentionIds(ctx context.Context, db database.SystemConfig, groupID string, add bool) error {
if groupID == "" {
return nil
}
if db == nil {
return nil
}
// 获取当前配置
sysConfig, err := db.FindByKey(ctx, WebhookConfigKey)
if err != nil {
log.ZWarn(ctx, "UpdateAttentionIds: failed to get webhook config from database", err, "groupID", groupID, "add", add)
return errs.WrapMsg(err, "failed to get webhook config from database")
}
if sysConfig == nil {
log.ZDebug(ctx, "UpdateAttentionIds: webhook config not found in database, skipping", "groupID", groupID, "add", add)
return nil
}
if !sysConfig.Enabled {
log.ZDebug(ctx, "UpdateAttentionIds: webhook config is disabled, skipping", "groupID", groupID, "add", add)
return nil
}
if sysConfig.Value == "" {
log.ZDebug(ctx, "UpdateAttentionIds: webhook config value is empty, skipping", "groupID", groupID, "add", add)
return nil
}
// 检查值类型
if sysConfig.ValueType != model.SystemConfigValueTypeJSON {
log.ZWarn(ctx, "UpdateAttentionIds: webhook config valueType is not json, skipping", nil, "groupID", groupID, "add", add, "value_type", sysConfig.ValueType)
return nil
}
// 解析当前配置
var webhookConfig config.Webhooks
if err := json.Unmarshal([]byte(sysConfig.Value), &webhookConfig); err != nil {
log.ZWarn(ctx, "UpdateAttentionIds: failed to unmarshal webhook config", err, "groupID", groupID, "add", add)
return errs.WrapMsg(err, "failed to unmarshal webhook config")
}
// 记录更新前的Enable状态
enableBefore := webhookConfig.AfterSendGroupMsg.Enable
// 验证解析后的配置是否有效
if webhookConfig.AfterSendGroupMsg.AttentionIds == nil {
webhookConfig.AfterSendGroupMsg.AttentionIds = []string{}
}
// 更新afterSendGroupMsg的attentionIds
attentionIds := webhookConfig.AfterSendGroupMsg.AttentionIds
oldCount := len(attentionIds)
var updated bool
if add {
// 添加groupID如果不存在
if !datautil.Contain(groupID, attentionIds...) {
attentionIds = append(attentionIds, groupID)
webhookConfig.AfterSendGroupMsg.AttentionIds = attentionIds
updated = true
log.ZInfo(ctx, "UpdateAttentionIds: adding groupID to attentionIds", "groupID", groupID, "old_count", oldCount, "new_count", len(attentionIds), "enable", enableBefore)
} else {
log.ZDebug(ctx, "UpdateAttentionIds: groupID already exists in attentionIds, skipping", "groupID", groupID, "count", oldCount)
return nil
}
} else {
// 移除groupID
newAttentionIds := make([]string, 0, len(attentionIds))
for _, id := range attentionIds {
if id != groupID {
newAttentionIds = append(newAttentionIds, id)
}
}
if len(newAttentionIds) != len(attentionIds) {
webhookConfig.AfterSendGroupMsg.AttentionIds = newAttentionIds
updated = true
log.ZInfo(ctx, "UpdateAttentionIds: removing groupID from attentionIds", "groupID", groupID, "old_count", oldCount, "new_count", len(newAttentionIds), "enable", enableBefore)
} else {
log.ZDebug(ctx, "UpdateAttentionIds: groupID not found in attentionIds, skipping", "groupID", groupID, "count", oldCount)
return nil
}
}
if !updated {
return nil
}
// 验证配置URL是否存在必要的验证
if webhookConfig.URL == "" {
log.ZWarn(ctx, "UpdateAttentionIds: webhook config URL is empty, skipping update", nil, "groupID", groupID, "add", add)
return errs.ErrArgs.WrapMsg("webhook config URL is empty")
}
// 记录更新后的Enable状态确保没有被修改
enableAfter := webhookConfig.AfterSendGroupMsg.Enable
if enableBefore != enableAfter {
log.ZWarn(ctx, "UpdateAttentionIds: Enable field changed unexpectedly", nil,
"groupID", groupID, "add", add,
"enable_before", enableBefore, "enable_after", enableAfter)
// 恢复原始值确保不会修改Enable字段
webhookConfig.AfterSendGroupMsg.Enable = enableBefore
}
// 序列化更新后的配置只更新attentionIds不改变其他配置
updatedValue, err := json.Marshal(webhookConfig)
if err != nil {
log.ZWarn(ctx, "UpdateAttentionIds: failed to marshal updated webhook config", err, "groupID", groupID, "add", add)
return errs.WrapMsg(err, "failed to marshal updated webhook config")
}
// 更新数据库
if err := db.Update(ctx, WebhookConfigKey, map[string]any{
"value": string(updatedValue),
}); err != nil {
log.ZWarn(ctx, "UpdateAttentionIds: failed to update webhook config in database", err, "groupID", groupID, "add", add)
return errs.WrapMsg(err, "failed to update webhook config in database")
}
log.ZInfo(ctx, "UpdateAttentionIds: successfully updated attentionIds in database",
"groupID", groupID, "add", add,
"attention_ids_count", len(webhookConfig.AfterSendGroupMsg.AttentionIds),
"enable", webhookConfig.AfterSendGroupMsg.Enable)
return nil
}

15
pkg/common/webhook/doc.go Normal file
View File

@@ -0,0 +1,15 @@
// Copyright © 2024 OpenIM. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package webhook // import "git.imall.cloud/openim/open-im-server-deploy/pkg/common/webhook"

View File

@@ -0,0 +1,191 @@
// Copyright © 2023 OpenIM. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package webhook
import (
"context"
"encoding/json"
"net/http"
"net/url"
"sync"
"git.imall.cloud/openim/open-im-server-deploy/pkg/callbackstruct"
"git.imall.cloud/openim/open-im-server-deploy/pkg/common/config"
"git.imall.cloud/openim/open-im-server-deploy/pkg/common/servererrs"
"git.imall.cloud/openim/protocol/constant"
"github.com/openimsdk/tools/log"
"github.com/openimsdk/tools/mcontext"
"github.com/openimsdk/tools/mq/memamq"
"github.com/openimsdk/tools/utils/httputil"
)
type Client struct {
client *httputil.HTTPClient
url string
queue *memamq.MemoryQueue
configManager *ConfigManager
mu sync.RWMutex
}
const (
webhookWorkerCount = 2
webhookBufferSize = 100
Key = "key"
)
func NewWebhookClient(url string, options ...*memamq.MemoryQueue) *Client {
var queue *memamq.MemoryQueue
if len(options) > 0 && options[0] != nil {
queue = options[0]
} else {
queue = memamq.NewMemoryQueue(webhookWorkerCount, webhookBufferSize)
}
http.DefaultTransport.(*http.Transport).MaxConnsPerHost = 100 // Enhance the default number of max connections per host
return &Client{
client: httputil.NewHTTPClient(httputil.NewClientConfig()),
url: url,
queue: queue,
}
}
// NewWebhookClientWithManager 创建支持动态配置的webhook client
func NewWebhookClientWithManager(configManager *ConfigManager, options ...*memamq.MemoryQueue) *Client {
var queue *memamq.MemoryQueue
if len(options) > 0 && options[0] != nil {
queue = options[0]
} else {
queue = memamq.NewMemoryQueue(webhookWorkerCount, webhookBufferSize)
}
http.DefaultTransport.(*http.Transport).MaxConnsPerHost = 100 // Enhance the default number of max connections per host
return &Client{
client: httputil.NewHTTPClient(httputil.NewClientConfig()),
url: configManager.GetURL(),
queue: queue,
configManager: configManager,
}
}
// getURL 获取当前webhook URL支持动态配置
func (c *Client) getURL() string {
if c.configManager != nil {
url := c.configManager.GetURL()
log.ZDebug(context.Background(), "webhook getURL from config manager", "url", url)
return url
}
c.mu.RLock()
defer c.mu.RUnlock()
log.ZDebug(context.Background(), "webhook getURL from static config", "url", c.url)
return c.url
}
// GetConfig returns the latest webhook config from the manager when available,
// falling back to the provided default configuration.
func (c *Client) GetConfig(defaultConfig *config.Webhooks) *config.Webhooks {
if c == nil {
return defaultConfig
}
if c.configManager != nil {
if cfg := c.configManager.GetConfig(); cfg != nil {
return cfg
}
}
return defaultConfig
}
func (c *Client) SyncPost(ctx context.Context, command string, req callbackstruct.CallbackReq, resp callbackstruct.CallbackResp, before *config.BeforeConfig) error {
return c.post(ctx, command, req, resp, before.Timeout)
}
func (c *Client) AsyncPost(ctx context.Context, command string, req callbackstruct.CallbackReq, resp callbackstruct.CallbackResp, after *config.AfterConfig) {
log.ZDebug(ctx, "webhook AsyncPost called", "command", command, "enable", after.Enable)
if after.Enable {
log.ZInfo(ctx, "webhook AsyncPost queued", "command", command, "timeout", after.Timeout)
c.queue.Push(func() { c.post(ctx, command, req, resp, after.Timeout) })
} else {
log.ZDebug(ctx, "webhook AsyncPost skipped (disabled)", "command", command)
}
}
func (c *Client) AsyncPostWithQuery(ctx context.Context, command string, req callbackstruct.CallbackReq, resp callbackstruct.CallbackResp, after *config.AfterConfig, queryParams map[string]string) {
log.ZDebug(ctx, "webhook AsyncPostWithQuery called", "command", command, "enable", after.Enable)
if after.Enable {
log.ZInfo(ctx, "webhook AsyncPostWithQuery queued", "command", command, "timeout", after.Timeout)
c.queue.Push(func() { c.postWithQuery(ctx, command, req, resp, after.Timeout, queryParams) })
} else {
log.ZDebug(ctx, "webhook AsyncPostWithQuery skipped (disabled)", "command", command)
}
}
func (c *Client) post(ctx context.Context, command string, input interface{}, output callbackstruct.CallbackResp, timeout int) error {
ctx = mcontext.WithMustInfoCtx([]string{mcontext.GetOperationID(ctx), mcontext.GetOpUserID(ctx), mcontext.GetOpUserPlatform(ctx), mcontext.GetConnID(ctx)})
fullURL := c.getURL() + "/" + command
log.ZInfo(ctx, "webhook", "url", fullURL, "input", input, "config", timeout)
operationID, _ := ctx.Value(constant.OperationID).(string)
b, err := c.client.Post(ctx, fullURL, map[string]string{constant.OperationID: operationID}, input, timeout)
if err != nil {
return servererrs.ErrNetwork.WrapMsg(err.Error(), "post url", fullURL)
}
if err = json.Unmarshal(b, output); err != nil {
return servererrs.ErrData.WithDetail(err.Error() + " response format error")
}
if err := output.Parse(); err != nil {
return err
}
log.ZInfo(ctx, "webhook success", "url", fullURL, "input", input, "response", string(b))
return nil
}
func (c *Client) postWithQuery(ctx context.Context, command string, input interface{}, output callbackstruct.CallbackResp, timeout int, queryParams map[string]string) error {
ctx = mcontext.WithMustInfoCtx([]string{mcontext.GetOperationID(ctx), mcontext.GetOpUserID(ctx), mcontext.GetOpUserPlatform(ctx), mcontext.GetConnID(ctx)})
fullURL := c.getURL() + "/" + command
parsedURL, err := url.Parse(fullURL)
if err != nil {
return servererrs.ErrNetwork.WrapMsg(err.Error(), "failed to parse URL", fullURL)
}
query := parsedURL.Query()
operationID, _ := ctx.Value(constant.OperationID).(string)
for key, value := range queryParams {
query.Set(key, value)
}
parsedURL.RawQuery = query.Encode()
fullURL = parsedURL.String()
log.ZInfo(ctx, "webhook", "url", fullURL, "input", input, "config", timeout)
b, err := c.client.Post(ctx, fullURL, map[string]string{constant.OperationID: operationID}, input, timeout)
if err != nil {
return servererrs.ErrNetwork.WrapMsg(err.Error(), "post url", fullURL)
}
if err = json.Unmarshal(b, output); err != nil {
return servererrs.ErrData.WithDetail(err.Error() + " response format error")
}
if err := output.Parse(); err != nil {
return err
}
log.ZInfo(ctx, "webhook success", "url", fullURL, "input", input, "response", string(b))
return nil
}

View File

@@ -0,0 +1,15 @@
// Copyright © 2023 OpenIM. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package webhook