feat: proxy workbench admin through service

This commit is contained in:
richjohnrambo
2026-09-16 14:03:58 +08:00
parent c258e0473b
commit e159f9a88e
8 changed files with 255 additions and 219 deletions
+3
View File
@@ -58,6 +58,9 @@ ENV IM_ADMIN_API_SECRET=
ENV IM_INTERNAL_API_BASE_URL=https://api.118im.com
ENV IM_INTERNAL_API_SERVICE_NAME=hotgo-admin
ENV IM_INTERNAL_API_SECRET=
ENV IM_WORKBENCH_API_BASE_URL=http://im_workbench:6061
ENV IM_WORKBENCH_API_SERVICE_NAME=hotgo-admin
ENV IM_WORKBENCH_API_SECRET=
ENV IM_CONFIG_API_SERVICE_NAME=im-server
ENV IM_CONFIG_API_SECRET=
@@ -0,0 +1,182 @@
package imworkbench
import (
"bytes"
"context"
"crypto/hmac"
cryptorand "crypto/rand"
"crypto/sha256"
"encoding/hex"
"encoding/json"
"io"
"net/http"
"net/url"
"os"
"strconv"
"strings"
"time"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/gctx"
)
const maxResponseBody = 10 << 20
type Config struct {
BaseURL string
ServiceName string
Secret string
Timeout time.Duration
}
type Client struct {
conf Config
http *http.Client
}
type Page[T any] struct {
Items []T `json:"items"`
Total int `json:"total"`
Page int `json:"page"`
PerPage int `json:"per_page"`
}
func DefaultClient(ctx context.Context) *Client {
timeout := g.Cfg().MustGet(ctx, "imWorkbenchApi.timeout", "5s").Duration()
if timeout <= 0 {
timeout = 5 * time.Second
}
secret := g.Cfg().MustGet(ctx, "imWorkbenchApi.serviceSecret").String()
if strings.HasPrefix(secret, "${") && strings.HasSuffix(secret, "}") {
secret = os.Getenv(strings.TrimSuffix(strings.TrimPrefix(secret, "${"), "}"))
}
if secret == "" {
secret = os.Getenv("IM_WORKBENCH_API_SECRET")
}
conf := Config{
BaseURL: g.Cfg().MustGet(ctx, "imWorkbenchApi.baseUrl").String(),
ServiceName: g.Cfg().MustGet(ctx, "imWorkbenchApi.serviceName", "hotgo-admin").String(),
Secret: secret,
Timeout: timeout,
}
return &Client{conf: conf, http: &http.Client{Timeout: timeout}}
}
func (c *Client) Get(ctx context.Context, path string, values url.Values, out any) error {
if values != nil {
if query := values.Encode(); query != "" {
path += "?" + query
}
}
return c.doJSON(ctx, http.MethodGet, path, nil, out)
}
func (c *Client) Post(ctx context.Context, path string, in any, out any) error {
body, err := json.Marshal(in)
if err != nil {
return gerror.Wrap(err, "序列化工作台请求失败")
}
return c.doJSON(ctx, http.MethodPost, path, body, out)
}
func (c *Client) doJSON(ctx context.Context, method string, path string, body []byte, out any) error {
req, err := c.newRequest(ctx, method, path, body)
if err != nil {
return err
}
resp, err := c.http.Do(req)
if err != nil {
g.Log().Warningf(ctx, "IM workbench request failed url:%s err:%+v", req.URL.String(), err)
return gerror.New("IM工作台服务暂不可用")
}
defer resp.Body.Close()
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
return parseError(resp)
}
if out == nil {
return nil
}
responseBody, err := io.ReadAll(io.LimitReader(resp.Body, maxResponseBody))
if err != nil {
return gerror.Wrap(err, "读取IM工作台响应失败")
}
var envelope struct {
Code int `json:"code"`
Message string `json:"message"`
Reason string `json:"reason"`
Data json.RawMessage `json:"data"`
}
if err = json.Unmarshal(responseBody, &envelope); err == nil && len(envelope.Data) > 0 {
if envelope.Code != 0 {
return stableError(envelope.Message, envelope.Reason)
}
return json.Unmarshal(envelope.Data, out)
}
return json.Unmarshal(responseBody, out)
}
func (c *Client) newRequest(ctx context.Context, method string, path string, body []byte) (*http.Request, error) {
baseURL := strings.TrimRight(c.conf.BaseURL, "/")
if baseURL == "" {
return nil, gerror.New("IM工作台API未配置")
}
if c.conf.Secret == "" {
return nil, gerror.New("IM工作台API密钥未配置")
}
req, err := http.NewRequestWithContext(ctx, method, baseURL+path, bytes.NewReader(body))
if err != nil {
return nil, gerror.Wrap(err, "创建IM工作台请求失败")
}
timestamp := strconv.FormatInt(time.Now().UTC().Unix(), 10)
nonce, err := randomNonce()
if err != nil {
return nil, err
}
bodyHash := sha256.Sum256(body)
signingText := strings.Join([]string{
method,
req.URL.RequestURI(),
hex.EncodeToString(bodyHash[:]),
timestamp,
nonce,
}, "\n")
mac := hmac.New(sha256.New, []byte(c.conf.Secret))
_, _ = mac.Write([]byte(signingText))
req.Header.Set("Accept", "application/json")
req.Header.Set("Content-Type", "application/json")
req.Header.Set("X-Service-Name", c.conf.ServiceName)
req.Header.Set("X-Timestamp", timestamp)
req.Header.Set("X-Nonce", nonce)
req.Header.Set("X-Signature", hex.EncodeToString(mac.Sum(nil)))
req.Header.Set("X-Request-ID", gctx.CtxId(ctx))
return req, nil
}
func parseError(resp *http.Response) error {
body, _ := io.ReadAll(io.LimitReader(resp.Body, 64<<10))
var envelope struct {
Message string `json:"message"`
Reason string `json:"reason"`
}
_ = json.Unmarshal(body, &envelope)
return stableError(envelope.Message, envelope.Reason)
}
func stableError(message string, reason string) error {
if message != "" {
return gerror.New(message)
}
if reason != "" {
return gerror.New(reason)
}
return gerror.New("IM工作台服务暂不可用")
}
func randomNonce() (string, error) {
var b [16]byte
if _, err := cryptorand.Read(b[:]); err != nil {
return "", gerror.Wrap(err, "生成IM工作台请求随机数失败")
}
return hex.EncodeToString(b[:]), nil
}
@@ -2,17 +2,13 @@ package sys
import (
"context"
"hotgo/internal/library/imworkbench"
"hotgo/internal/model/input/sysin"
"hotgo/internal/service"
"github.com/gogf/gf/v2/database/gdb"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/gtime"
)
const imFeedbackConfigTable = "im_feedback_config"
type sSysImFeedbackConfig struct{}
func NewSysImFeedbackConfig() *sSysImFeedbackConfig { return &sSysImFeedbackConfig{} }
@@ -25,16 +21,14 @@ func (s *sSysImFeedbackConfig) Get(ctx context.Context, in *sysin.ImFeedbackConf
if err = in.Filter(ctx); err != nil {
return
}
db, tenant, err := resolveImTenantDB(ctx, in.TenantId)
tenant, err := resolveImAdminTenant(ctx, in.TenantId)
if err != nil {
return nil, err
}
if err = s.ensureDefault(ctx, db); err != nil {
res = &sysin.ImFeedbackConfigModel{}
if err = imworkbench.DefaultClient(ctx).Get(ctx, "/internal/admin/workbench/feedback-config", nil, res); err != nil {
return nil, err
}
if err = s.model(ctx, db).OrderAsc("id").Limit(1).Scan(&res); err != nil {
return nil, gerror.Wrap(err, "获取IM意见反馈配置失败,请稍后重试!")
}
if res != nil {
res.TenantId = tenant.Id
res.TenantName = tenant.Name
@@ -46,13 +40,10 @@ func (s *sSysImFeedbackConfig) Update(ctx context.Context, in *sysin.ImFeedbackC
if err = in.Filter(ctx); err != nil {
return
}
db, tenant, err := resolveImTenantDB(ctx, in.TenantId)
tenant, err := resolveImAdminTenant(ctx, in.TenantId)
if err != nil {
return err
}
if err = s.ensureDefault(ctx, db); err != nil {
return err
}
categories := `["bug","suggestion","complaint","other"]`
if in.Categories != nil && in.Categories.String() != "" && in.Categories.String() != "null" {
categories = in.Categories.String()
@@ -61,52 +52,15 @@ func (s *sSysImFeedbackConfig) Update(ctx context.Context, in *sysin.ImFeedbackC
if in.AttachmentPolicy != nil && in.AttachmentPolicy.String() != "" && in.AttachmentPolicy.String() != "null" {
attachmentPolicy = in.AttachmentPolicy.String()
}
if _, err = db.Model(imFeedbackConfigTable).Safe().Ctx(ctx).Where("id", 1).Data(g.Map{
if err = imworkbench.DefaultClient(ctx).Post(ctx, "/internal/admin/workbench/feedback-config", g.Map{
"categories": categories,
"max_length": in.MaxLength,
"attachment_policy": attachmentPolicy,
"allow_contact": in.AllowContact,
"diagnostics_notice": in.DiagnosticsNotice,
"updated_at": gtime.Now(),
}).Update(); err != nil {
return gerror.Wrap(err, "更新IM意见反馈配置失败,请稍后重试!")
}, nil); err != nil {
return err
}
_ = writeImAdminAudit(ctx, tenant.Id, "im_feedback_config_update", "im_feedback_config", 1, "success", g.Map{"allow_contact": in.AllowContact})
return nil
}
func (s *sSysImFeedbackConfig) model(ctx context.Context, db gdb.DB) *gdb.Model {
return db.Model(imFeedbackConfigTable).Safe().Ctx(ctx).Fields(
"id",
"CAST(categories AS CHAR) categories",
"max_length",
"CAST(attachment_policy AS CHAR) attachment_policy",
"allow_contact",
"diagnostics_notice",
"updated_at",
)
}
func (s *sSysImFeedbackConfig) ensureDefault(ctx context.Context, db gdb.DB) (err error) {
var count int
if count, err = db.Model(imFeedbackConfigTable).Safe().Ctx(ctx).Where("id", 1).Count(); err != nil {
return gerror.Wrap(err, "检查IM意见反馈默认配置失败,请稍后重试!")
}
if count > 0 {
return nil
}
_, err = db.Model(imFeedbackConfigTable).Safe().Ctx(ctx).Data(g.Map{
"id": 1,
"categories": `["bug","suggestion","complaint","other"]`,
"max_length": 1000,
"attachment_policy": `{"max_files":5,"purpose":"feedback"}`,
"allow_contact": 1,
"diagnostics_notice": "仅在你同意时上传脱敏诊断信息。",
"created_at": gtime.Now(),
"updated_at": gtime.Now(),
}).Insert()
if err != nil {
return gerror.Wrap(err, "初始化IM意见反馈默认配置失败,请稍后重试!")
}
return nil
}
+8 -61
View File
@@ -2,17 +2,13 @@ package sys
import (
"context"
"hotgo/internal/library/imworkbench"
"hotgo/internal/model/input/sysin"
"hotgo/internal/service"
"github.com/gogf/gf/v2/database/gdb"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/gtime"
)
const imMailboxConfigTable = "im_mailbox_config"
type sSysImMailboxConfig struct{}
func NewSysImMailboxConfig() *sSysImMailboxConfig { return &sSysImMailboxConfig{} }
@@ -25,16 +21,14 @@ func (s *sSysImMailboxConfig) Get(ctx context.Context, in *sysin.ImMailboxConfig
if err = in.Filter(ctx); err != nil {
return
}
db, tenant, err := resolveImTenantDB(ctx, in.TenantId)
tenant, err := resolveImAdminTenant(ctx, in.TenantId)
if err != nil {
return nil, err
}
if err = s.ensureDefault(ctx, db); err != nil {
res = &sysin.ImMailboxConfigModel{}
if err = imworkbench.DefaultClient(ctx).Get(ctx, "/internal/admin/workbench/mailbox-config", nil, res); err != nil {
return nil, err
}
if err = s.model(ctx, db).Where("id", 1).Scan(&res); err != nil {
return nil, gerror.Wrap(err, "获取IM总经理信箱配置失败,请稍后重试!")
}
if res != nil {
res.TenantId = tenant.Id
res.TenantName = tenant.Name
@@ -46,13 +40,10 @@ func (s *sSysImMailboxConfig) Update(ctx context.Context, in *sysin.ImMailboxCon
if err = in.Filter(ctx); err != nil {
return
}
db, tenant, err := resolveImTenantDB(ctx, in.TenantId)
tenant, err := resolveImAdminTenant(ctx, in.TenantId)
if err != nil {
return err
}
if err = s.ensureDefault(ctx, db); err != nil {
return err
}
requiredFields := `["subject","content"]`
if in.RequiredFields != nil && in.RequiredFields.String() != "" && in.RequiredFields.String() != "null" {
requiredFields = in.RequiredFields.String()
@@ -61,7 +52,7 @@ func (s *sSysImMailboxConfig) Update(ctx context.Context, in *sysin.ImMailboxCon
if in.AttachmentPolicy != nil && in.AttachmentPolicy.String() != "" && in.AttachmentPolicy.String() != "null" {
attachmentPolicy = in.AttachmentPolicy.String()
}
if _, err = db.Model(imMailboxConfigTable).Safe().Ctx(ctx).Where("id", 1).Data(g.Map{
if err = imworkbench.DefaultClient(ctx).Post(ctx, "/internal/admin/workbench/mailbox-config", g.Map{
"enabled": in.Enabled,
"allow_anonymous": in.AllowAnonymous,
"required_fields": requiredFields,
@@ -69,53 +60,9 @@ func (s *sSysImMailboxConfig) Update(ctx context.Context, in *sysin.ImMailboxCon
"attachment_policy": attachmentPolicy,
"notice_title": in.NoticeTitle,
"notice_content": in.NoticeContent,
"notice_updated_at": gtime.Now(),
"updated_at": gtime.Now(),
}).Update(); err != nil {
return gerror.Wrap(err, "更新IM总经理信箱配置失败,请稍后重试!")
}, nil); err != nil {
return err
}
_ = writeImAdminAudit(ctx, tenant.Id, "im_mailbox_config_update", "im_mailbox_config", 1, "success", g.Map{"enabled": in.Enabled})
return nil
}
func (s *sSysImMailboxConfig) model(ctx context.Context, db gdb.DB) *gdb.Model {
return db.Model(imMailboxConfigTable).Safe().Ctx(ctx).Fields(
"id",
"enabled",
"allow_anonymous",
"CAST(required_fields AS CHAR) required_fields",
"max_length",
"CAST(attachment_policy AS CHAR) attachment_policy",
"notice_title",
"notice_content",
"notice_updated_at",
"updated_at",
)
}
func (s *sSysImMailboxConfig) ensureDefault(ctx context.Context, db gdb.DB) (err error) {
var count int
if count, err = db.Model(imMailboxConfigTable).Safe().Ctx(ctx).Where("id", 1).Count(); err != nil {
return gerror.Wrap(err, "检查IM总经理信箱默认配置失败,请稍后重试!")
}
if count > 0 {
return nil
}
_, err = db.Model(imMailboxConfigTable).Safe().Ctx(ctx).Data(g.Map{
"id": 1,
"enabled": 1,
"allow_anonymous": 1,
"required_fields": `["subject","content"]`,
"max_length": 5000,
"attachment_policy": `{"max_files":5,"purpose":"executive_mailbox"}`,
"notice_title": "总经理信箱",
"notice_content": "请如实填写诉求,匿名提交将按企业政策保护展示身份。",
"notice_updated_at": gtime.Now(),
"created_at": gtime.Now(),
"updated_at": gtime.Now(),
}).Insert()
if err != nil {
return gerror.Wrap(err, "初始化IM总经理信箱默认配置失败,请稍后重试!")
}
return nil
}
+40 -103
View File
@@ -2,18 +2,15 @@ package sys
import (
"context"
"hotgo/internal/consts"
"hotgo/internal/library/imworkbench"
"hotgo/internal/model/input/sysin"
"hotgo/internal/service"
"net/url"
"github.com/gogf/gf/v2/database/gdb"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/gtime"
)
const imWorkbenchAppsTable = "im_workbench_apps"
type sSysImWorkbench struct{}
func NewSysImWorkbench() *sSysImWorkbench { return &sSysImWorkbench{} }
@@ -26,47 +23,46 @@ func (s *sSysImWorkbench) List(ctx context.Context, in *sysin.ImWorkbenchAppList
if err = in.Filter(ctx); err != nil {
return
}
db, tenant, err := resolveImTenantDB(ctx, in.TenantId)
tenant, err := resolveImAdminTenant(ctx, in.TenantId)
if err != nil {
return
}
if err = s.ensureDefaultApps(ctx, db); err != nil {
return
}
mod := s.model(ctx, db)
values := url.Values{}
values.Set("page", intToString(in.Page))
values.Set("per_page", intToString(in.PerPage))
if in.Code != "" {
mod = mod.WhereLike("code", "%"+in.Code+"%")
values.Set("code", in.Code)
}
if in.Name != "" {
mod = mod.WhereLike("name", "%"+in.Name+"%")
values.Set("name", in.Name)
}
if in.Enabled != nil {
mod = mod.Where("enabled", *in.Enabled)
values.Set("enabled", intToString(*in.Enabled))
}
mod = mod.Page(in.Page, in.PerPage).OrderAsc("sort_order").OrderAsc("id")
if err = mod.ScanAndCount(&list, &totalCount, false); err != nil {
err = gerror.Wrap(err, "获取IM工作台模块失败,请稍后重试!")
return
var page imworkbench.Page[sysin.ImWorkbenchAppModel]
if err = imworkbench.DefaultClient(ctx).Get(ctx, "/internal/admin/workbench/apps", values, &page); err != nil {
return nil, 0, err
}
for _, item := range list {
list = make([]*sysin.ImWorkbenchAppModel, 0, len(page.Items))
for i := range page.Items {
item := page.Items[i]
item.TenantId = tenant.Id
item.TenantName = tenant.Name
list = append(list, &item)
}
return
return list, page.Total, nil
}
func (s *sSysImWorkbench) Edit(ctx context.Context, in *sysin.ImWorkbenchAppEditInp) (err error) {
if err = in.Filter(ctx); err != nil {
return
}
db, tenant, err := resolveImTenantDB(ctx, in.TenantId)
tenant, err := resolveImAdminTenant(ctx, in.TenantId)
if err != nil {
return
}
if err = s.ensureDefaultApps(ctx, db); err != nil {
return
}
data := g.Map{
"id": in.Id,
"code": in.Code,
"name": in.Name,
"icon_file_id": in.IconFileId,
@@ -75,25 +71,18 @@ func (s *sSysImWorkbench) Edit(ctx context.Context, in *sysin.ImWorkbenchAppEdit
"unread_key": in.UnreadKey,
"enabled": in.Enabled,
"permission": in.Permission,
"updated_at": gtime.Now(),
}
action := "im_workbench_app_create"
targetId := in.Id
if in.Id > 0 {
if _, err = db.Model(imWorkbenchAppsTable).Safe().Ctx(ctx).WherePri(in.Id).Data(data).Update(); err != nil {
return gerror.Wrap(err, "更新IM工作台模块失败,请稍后重试!")
}
action = "im_workbench_app_update"
} else {
data["created_at"] = gtime.Now()
if _, err = db.Model(imWorkbenchAppsTable).Safe().Ctx(ctx).Data(data).Insert(); err != nil {
return gerror.Wrap(err, "新增IM工作台模块失败,请稍后重试!")
}
idValue, err := db.Model(imWorkbenchAppsTable).Safe().Ctx(ctx).Fields("id").Where("code", in.Code).Value()
if err != nil {
return gerror.Wrap(err, "获取IM工作台模块ID失败,请稍后重试!")
}
targetId = idValue.Int64()
}
var res sysin.ImWorkbenchAppModel
if err = imworkbench.DefaultClient(ctx).Post(ctx, "/internal/admin/workbench/apps", data, &res); err != nil {
return err
}
if targetId <= 0 {
targetId = res.Id
}
_ = writeImAdminAudit(ctx, tenant.Id, action, "im_workbench_app", targetId, "success", g.Map{
"code": in.Code, "route": in.Route, "enabled": in.Enabled, "permission": in.Permission,
@@ -105,21 +94,12 @@ func (s *sSysImWorkbench) Delete(ctx context.Context, in *sysin.ImWorkbenchAppDe
if err = in.Filter(ctx); err != nil {
return
}
db, tenant, err := resolveImTenantDB(ctx, in.TenantId)
tenant, err := resolveImAdminTenant(ctx, in.TenantId)
if err != nil {
return
}
if err = s.ensureDefaultApps(ctx, db); err != nil {
return
}
mod := db.Model(imWorkbenchAppsTable).Safe().Ctx(ctx)
if in.Id > 0 {
mod = mod.WherePri(in.Id)
} else {
mod = mod.Where("code", in.Code)
}
if _, err = mod.Data(g.Map{"enabled": consts.ImWorkbenchAppDisabled, "updated_at": gtime.Now()}).Update(); err != nil {
return gerror.Wrap(err, "禁用IM工作台模块失败,请稍后重试!")
if err = imworkbench.DefaultClient(ctx).Post(ctx, "/internal/admin/workbench/apps/disable", g.Map{"id": in.Id, "code": in.Code}, nil); err != nil {
return err
}
_ = writeImAdminAudit(ctx, tenant.Id, "im_workbench_app_disable", "im_workbench_app", in.Id, "success", g.Map{"code": in.Code})
return nil
@@ -129,66 +109,23 @@ func (s *sSysImWorkbench) Reorder(ctx context.Context, in *sysin.ImWorkbenchAppR
if err = in.Filter(ctx); err != nil {
return
}
db, tenant, err := resolveImTenantDB(ctx, in.TenantId)
tenant, err := resolveImAdminTenant(ctx, in.TenantId)
if err != nil {
return
}
if err = s.ensureDefaultApps(ctx, db); err != nil {
return
items := make([]g.Map, 0, len(in.Items))
for _, item := range in.Items {
items = append(items, g.Map{
"code": item.Code,
"sort_order": item.SortOrder,
})
}
if err = db.Transaction(ctx, func(ctx context.Context, tx gdb.TX) error {
for _, item := range in.Items {
if _, e := tx.Model(imWorkbenchAppsTable).Safe().Ctx(ctx).Where("code", item.Code).
Data(g.Map{"sort_order": item.SortOrder, "updated_at": gtime.Now()}).Update(); e != nil {
return e
}
if err = imworkbench.DefaultClient(ctx).Post(ctx, "/internal/admin/workbench/apps/reorder", g.Map{"items": items}, nil); err != nil {
if err.Error() == "" {
return gerror.New("调整IM工作台模块排序失败,请稍后重试!")
}
return nil
}); err != nil {
return gerror.Wrap(err, "调整IM工作台模块排序失败,请稍后重试!")
return err
}
_ = writeImAdminAudit(ctx, tenant.Id, "im_workbench_app_reorder", "im_workbench_app", 0, "success", g.Map{"count": len(in.Items)})
return nil
}
func (s *sSysImWorkbench) model(ctx context.Context, db gdb.DB) *gdb.Model {
return db.Model(imWorkbenchAppsTable).Safe().Ctx(ctx).Fields(
"id",
"code",
"name",
"icon_file_id",
"route",
"sort_order",
"unread_key",
"enabled",
"permission",
"created_at",
"updated_at",
)
}
func (s *sSysImWorkbench) ensureDefaultApps(ctx context.Context, db gdb.DB) (err error) {
defaults := []g.Map{
{"code": "organization", "name": "组织架构", "route": consts.ImWorkbenchRouteOrganization, "sort_order": 10, "unread_key": "organization", "enabled": consts.ImWorkbenchAppEnabled, "permission": consts.ImWorkbenchPermissionEnabled},
{"code": "meetings", "name": "会议通知", "route": consts.ImWorkbenchRouteMeetings, "sort_order": 20, "unread_key": "meetings", "enabled": consts.ImWorkbenchAppEnabled, "permission": consts.ImWorkbenchPermissionEnabled},
{"code": "daily_reports", "name": "日报", "route": consts.ImWorkbenchRouteDailyReports, "sort_order": 30, "unread_key": "daily_reports", "enabled": consts.ImWorkbenchAppEnabled, "permission": consts.ImWorkbenchPermissionEnabled},
{"code": "monthly_reports", "name": "月报", "route": consts.ImWorkbenchRouteMonthlyReports, "sort_order": 40, "unread_key": "monthly_reports", "enabled": consts.ImWorkbenchAppEnabled, "permission": consts.ImWorkbenchPermissionEnabled},
{"code": "executive_mailbox", "name": "总经理信箱", "route": consts.ImWorkbenchRouteExecutiveMailbox, "sort_order": 50, "unread_key": "executive_mailbox", "enabled": consts.ImWorkbenchAppEnabled, "permission": consts.ImWorkbenchPermissionEnabled},
}
now := gtime.Now()
for _, item := range defaults {
var count int
if count, err = db.Model(imWorkbenchAppsTable).Safe().Ctx(ctx).Where("code", item["code"]).Count(); err != nil {
return gerror.Wrap(err, "检查IM工作台默认模块失败,请稍后重试!")
}
if count > 0 {
continue
}
item["created_at"] = now
item["updated_at"] = now
if _, err = db.Model(imWorkbenchAppsTable).Safe().Ctx(ctx).Data(item).Insert(); err != nil {
return gerror.Wrap(err, "初始化IM工作台默认模块失败,请稍后重试!")
}
}
return nil
}
@@ -319,6 +319,12 @@ imInternalApi:
serviceName: "hotgo-admin"
serviceSecret: "${IM_INTERNAL_API_SECRET}"
imWorkbenchApi:
baseUrl: "http://im_workbench:6061"
serviceName: "hotgo-admin"
serviceSecret: "${IM_WORKBENCH_API_SECRET}"
timeout: "5s"
imConfigApi:
serviceName: "im-server"
serviceSecret: "${IM_CONFIG_API_SECRET}"
+6
View File
@@ -316,6 +316,12 @@ imInternalApi:
serviceName: "$IM_INTERNAL_API_SERVICE_NAME"
serviceSecret: "$IM_INTERNAL_API_SECRET"
imWorkbenchApi:
baseUrl: "$IM_WORKBENCH_API_BASE_URL"
serviceName: "$IM_WORKBENCH_API_SERVICE_NAME"
serviceSecret: "$IM_WORKBENCH_API_SECRET"
timeout: "5s"
imConfigApi:
serviceName: "$IM_CONFIG_API_SERVICE_NAME"
serviceSecret: "$IM_CONFIG_API_SECRET"
+2 -1
View File
@@ -6,11 +6,12 @@ cd /app
# Render the real config from the template using only the specific env vars we
# expect — avoids accidentally substituting anything else that might contain a
# literal '$' in the YAML.
envsubst '$MYSQL_DSN $REDIS_ADDR $REDIS_DB $REDIS_PASSWORD $TOKEN_SECRET_KEY $QUEUE_DRIVER $KAFKA_ADDRESS $KAFKA_VERSION $KAFKA_USERNAME $KAFKA_PASSWORD $KAFKA_SASL_MECHANISM $IM_ADMIN_API_BASE_URL $IM_ADMIN_API_SERVICE_ID $IM_ADMIN_API_SECRET $IM_INTERNAL_API_BASE_URL $IM_INTERNAL_API_SERVICE_NAME $IM_INTERNAL_API_SECRET $IM_CONFIG_API_SERVICE_NAME $IM_CONFIG_API_SECRET' \
envsubst '$MYSQL_DSN $REDIS_ADDR $REDIS_DB $REDIS_PASSWORD $TOKEN_SECRET_KEY $QUEUE_DRIVER $KAFKA_ADDRESS $KAFKA_VERSION $KAFKA_USERNAME $KAFKA_PASSWORD $KAFKA_SASL_MECHANISM $IM_ADMIN_API_BASE_URL $IM_ADMIN_API_SERVICE_ID $IM_ADMIN_API_SECRET $IM_INTERNAL_API_BASE_URL $IM_INTERNAL_API_SERVICE_NAME $IM_INTERNAL_API_SECRET $IM_WORKBENCH_API_BASE_URL $IM_WORKBENCH_API_SERVICE_NAME $IM_WORKBENCH_API_SECRET $IM_CONFIG_API_SERVICE_NAME $IM_CONFIG_API_SECRET' \
< manifest/config/config.template > manifest/config/config.yaml
echo "queue.driver=${QUEUE_DRIVER:-}"
echo "queue.kafka.address=${KAFKA_ADDRESS:-}"
echo "imAdminApi.baseUrl=${IM_ADMIN_API_BASE_URL:-}"
echo "imWorkbenchApi.baseUrl=${IM_WORKBENCH_API_BASE_URL:-}"
exec ./hotgo