317 lines
10 KiB
Go
317 lines
10 KiB
Go
/*
|
|
* Copyright 2025 coze-dev Authors
|
|
*
|
|
* 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 plugin
|
|
|
|
import (
|
|
"context"
|
|
"strings"
|
|
"time"
|
|
|
|
productCommon "github.com/coze-dev/coze-studio/backend/api/model/marketplace/product_common"
|
|
productAPI "github.com/coze-dev/coze-studio/backend/api/model/marketplace/product_public_api"
|
|
pluginAPI "github.com/coze-dev/coze-studio/backend/api/model/plugin_develop"
|
|
common "github.com/coze-dev/coze-studio/backend/api/model/plugin_develop/common"
|
|
"github.com/coze-dev/coze-studio/backend/application/base/ctxutil"
|
|
"github.com/coze-dev/coze-studio/backend/crossdomain/contract/plugin/consts"
|
|
"github.com/coze-dev/coze-studio/backend/crossdomain/contract/plugin/convert/api"
|
|
"github.com/coze-dev/coze-studio/backend/domain/plugin/dto"
|
|
"github.com/coze-dev/coze-studio/backend/domain/plugin/entity"
|
|
"github.com/coze-dev/coze-studio/backend/domain/plugin/repository"
|
|
"github.com/coze-dev/coze-studio/backend/domain/plugin/service"
|
|
search "github.com/coze-dev/coze-studio/backend/domain/search/service"
|
|
user "github.com/coze-dev/coze-studio/backend/domain/user/service"
|
|
"github.com/coze-dev/coze-studio/backend/infra/contract/storage"
|
|
"github.com/coze-dev/coze-studio/backend/pkg/errorx"
|
|
"github.com/coze-dev/coze-studio/backend/pkg/lang/ptr"
|
|
"github.com/coze-dev/coze-studio/backend/pkg/logs"
|
|
"github.com/coze-dev/coze-studio/backend/types/errno"
|
|
)
|
|
|
|
var PluginApplicationSVC = &PluginApplicationService{}
|
|
|
|
type PluginApplicationService struct {
|
|
DomainSVC service.PluginService
|
|
eventbus search.ResourceEventBus
|
|
oss storage.Storage
|
|
userSVC user.User
|
|
|
|
toolRepo repository.ToolRepository
|
|
pluginRepo repository.PluginRepository
|
|
}
|
|
|
|
func (p *PluginApplicationService) CheckAndLockPluginEdit(ctx context.Context, req *pluginAPI.CheckAndLockPluginEditRequest) (resp *pluginAPI.CheckAndLockPluginEditResponse, err error) {
|
|
resp = &pluginAPI.CheckAndLockPluginEditResponse{
|
|
Data: &common.CheckAndLockPluginEditData{
|
|
Seized: true,
|
|
},
|
|
}
|
|
|
|
return resp, nil
|
|
}
|
|
|
|
func (p *PluginApplicationService) GetBotDefaultParams(ctx context.Context, req *pluginAPI.GetBotDefaultParamsRequest) (resp *pluginAPI.GetBotDefaultParamsResponse, err error) {
|
|
_, exist, err := p.pluginRepo.GetOnlinePlugin(ctx, req.PluginID, repository.WithPluginID())
|
|
if err != nil {
|
|
return nil, errorx.Wrapf(err, "GetOnlinePlugin failed, pluginID=%d", req.PluginID)
|
|
}
|
|
if !exist {
|
|
return nil, errorx.New(errno.ErrPluginRecordNotFound)
|
|
}
|
|
|
|
draftAgentTool, err := p.DomainSVC.GetDraftAgentToolByName(ctx, req.BotID, req.APIName)
|
|
if err != nil {
|
|
return nil, errorx.Wrapf(err, "GetDraftAgentToolByName failed, agentID=%d, toolName=%s", req.BotID, req.APIName)
|
|
}
|
|
|
|
reqAPIParams, err := draftAgentTool.ToReqAPIParameter()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
respAPIParams, err := draftAgentTool.ToRespAPIParameter()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
resp = &pluginAPI.GetBotDefaultParamsResponse{
|
|
RequestParams: reqAPIParams,
|
|
ResponseParams: respAPIParams,
|
|
}
|
|
|
|
return resp, nil
|
|
}
|
|
|
|
func (p *PluginApplicationService) UpdateBotDefaultParams(ctx context.Context, req *pluginAPI.UpdateBotDefaultParamsRequest) (resp *pluginAPI.UpdateBotDefaultParamsResponse, err error) {
|
|
op, err := api.APIParamsToOpenapiOperation(req.RequestParams, req.ResponseParams)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
err = p.DomainSVC.UpdateBotDefaultParams(ctx, &dto.UpdateBotDefaultParamsRequest{
|
|
PluginID: req.PluginID,
|
|
ToolName: req.APIName,
|
|
AgentID: req.BotID,
|
|
Parameters: op.Parameters,
|
|
RequestBody: op.RequestBody,
|
|
Responses: op.Responses,
|
|
})
|
|
if err != nil {
|
|
return nil, errorx.Wrapf(err, "UpdateBotDefaultParams failed, agentID=%d, toolName=%s", req.BotID, req.APIName)
|
|
}
|
|
|
|
resp = &pluginAPI.UpdateBotDefaultParamsResponse{}
|
|
|
|
return resp, nil
|
|
}
|
|
|
|
func (p *PluginApplicationService) UnlockPluginEdit(ctx context.Context, req *pluginAPI.UnlockPluginEditRequest) (resp *pluginAPI.UnlockPluginEditResponse, err error) {
|
|
resp = &pluginAPI.UnlockPluginEditResponse{
|
|
Released: true,
|
|
}
|
|
return resp, nil
|
|
}
|
|
|
|
func (p *PluginApplicationService) PublicGetProductList(ctx context.Context, req *productAPI.GetProductListRequest) (resp *productAPI.GetProductListResponse, err error) {
|
|
res, err := p.DomainSVC.ListPluginProducts(ctx, &dto.ListPluginProductsRequest{})
|
|
if err != nil {
|
|
return nil, errorx.Wrapf(err, "ListPluginProducts failed")
|
|
}
|
|
|
|
products := make([]*productAPI.ProductInfo, 0, len(res.Plugins))
|
|
for _, pl := range res.Plugins {
|
|
tls, err := p.toolRepo.GetPluginAllOnlineTools(ctx, pl.ID)
|
|
if err != nil {
|
|
return nil, errorx.Wrapf(err, "GetPluginAllOnlineTools failed, pluginID=%d", pl.ID)
|
|
}
|
|
|
|
pi, err := p.buildProductInfo(ctx, pl, tls)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
products = append(products, pi)
|
|
}
|
|
|
|
if req.GetKeyword() != "" {
|
|
filterProducts := make([]*productAPI.ProductInfo, 0, len(products))
|
|
for _, _p := range products {
|
|
if strings.Contains(strings.ToLower(_p.MetaInfo.Name), strings.ToLower(req.GetKeyword())) {
|
|
filterProducts = append(filterProducts, _p)
|
|
}
|
|
}
|
|
products = filterProducts
|
|
}
|
|
|
|
resp = &productAPI.GetProductListResponse{
|
|
Data: &productAPI.GetProductListData{
|
|
Products: products,
|
|
HasMore: false, // Finish at one time
|
|
Total: int32(res.Total),
|
|
},
|
|
}
|
|
|
|
return resp, nil
|
|
}
|
|
|
|
func (p *PluginApplicationService) buildProductInfo(ctx context.Context, plugin *entity.PluginInfo, tools []*entity.ToolInfo) (*productAPI.ProductInfo, error) {
|
|
metaInfo, err := p.buildProductMetaInfo(ctx, plugin)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
extraInfo, err := p.buildPluginProductExtraInfo(ctx, plugin, tools)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
pi := &productAPI.ProductInfo{
|
|
CommercialSetting: &productCommon.CommercialSetting{
|
|
CommercialType: productCommon.ProductPaidType_Free,
|
|
},
|
|
MetaInfo: metaInfo,
|
|
PluginExtra: extraInfo,
|
|
}
|
|
|
|
return pi, nil
|
|
}
|
|
|
|
func (p *PluginApplicationService) buildProductMetaInfo(ctx context.Context, plugin *entity.PluginInfo) (*productAPI.ProductMetaInfo, error) {
|
|
iconURL, err := p.oss.GetObjectUrl(ctx, plugin.GetIconURI())
|
|
if err != nil {
|
|
logs.CtxWarnf(ctx, "get icon url failed with '%s', err=%v", plugin.GetIconURI(), err)
|
|
}
|
|
|
|
return &productAPI.ProductMetaInfo{
|
|
ID: plugin.GetRefProductID(),
|
|
EntityID: plugin.ID,
|
|
EntityType: productCommon.ProductEntityType_Plugin,
|
|
IconURL: iconURL,
|
|
Name: plugin.GetName(),
|
|
Description: plugin.GetDesc(),
|
|
IsFree: true,
|
|
IsOfficial: true,
|
|
Status: productCommon.ProductStatus_Listed,
|
|
ListedAt: time.Now().Unix(),
|
|
UserInfo: &productCommon.UserInfo{
|
|
Name: "Coze Official",
|
|
},
|
|
}, nil
|
|
}
|
|
|
|
func (p *PluginApplicationService) buildPluginProductExtraInfo(ctx context.Context, plugin *entity.PluginInfo, tools []*entity.ToolInfo) (*productAPI.PluginExtraInfo, error) {
|
|
ei := &productAPI.PluginExtraInfo{
|
|
IsOfficial: true,
|
|
PluginType: func() *productCommon.PluginType {
|
|
if plugin.PluginType == common.PluginType_LOCAL {
|
|
return ptr.Of(productCommon.PluginType_LocalPlugin)
|
|
}
|
|
return ptr.Of(productCommon.PluginType_CLoudPlugin)
|
|
}(),
|
|
}
|
|
|
|
toolInfos := make([]*productAPI.PluginToolInfo, 0, len(tools))
|
|
for _, tl := range tools {
|
|
params, err := tl.ToToolParameters()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
toolInfo := &productAPI.PluginToolInfo{
|
|
ID: tl.ID,
|
|
Name: tl.GetName(),
|
|
Description: tl.GetDesc(),
|
|
Parameters: params,
|
|
}
|
|
|
|
example := plugin.GetToolExample(ctx, tl.GetName())
|
|
if example != nil {
|
|
toolInfo.Example = &productAPI.PluginToolExample{
|
|
ReqExample: example.RequestExample,
|
|
RespExample: example.ResponseExample,
|
|
}
|
|
}
|
|
|
|
toolInfos = append(toolInfos, toolInfo)
|
|
}
|
|
|
|
ei.Tools = toolInfos
|
|
|
|
authInfo := plugin.GetAuthInfo()
|
|
|
|
authMode := ptr.Of(productAPI.PluginAuthMode_NoAuth)
|
|
if authInfo != nil {
|
|
if authInfo.Type == consts.AuthzTypeOfService || authInfo.Type == consts.AuthzTypeOfOAuth {
|
|
authMode = ptr.Of(productAPI.PluginAuthMode_Required)
|
|
err := plugin.Manifest.Validate(false)
|
|
if err != nil {
|
|
logs.CtxWarnf(ctx, "validate plugin manifest failed, err=%v", err)
|
|
} else {
|
|
authMode = ptr.Of(productAPI.PluginAuthMode_Configured)
|
|
}
|
|
}
|
|
}
|
|
|
|
ei.AuthMode = authMode
|
|
|
|
return ei, nil
|
|
}
|
|
|
|
func (p *PluginApplicationService) PublicGetProductDetail(ctx context.Context, req *productAPI.GetProductDetailRequest) (resp *productAPI.GetProductDetailResponse, err error) {
|
|
plugin, exist, err := p.pluginRepo.GetOnlinePlugin(ctx, req.GetEntityID())
|
|
if err != nil {
|
|
return nil, errorx.Wrapf(err, "GetOnlinePlugin failed, pluginID=%d", req.GetEntityID())
|
|
}
|
|
if !exist {
|
|
return nil, errorx.New(errno.ErrPluginRecordNotFound)
|
|
}
|
|
|
|
tools, err := p.toolRepo.GetPluginAllOnlineTools(ctx, plugin.ID)
|
|
if err != nil {
|
|
return nil, errorx.Wrapf(err, "GetPluginAllOnlineTools failed, pluginID=%d", plugin.ID)
|
|
}
|
|
pi, err := p.buildProductInfo(ctx, plugin, tools)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
resp = &productAPI.GetProductDetailResponse{
|
|
Data: &productAPI.GetProductDetailData{
|
|
MetaInfo: pi.MetaInfo,
|
|
PluginExtra: pi.PluginExtra,
|
|
},
|
|
}
|
|
|
|
return resp, nil
|
|
}
|
|
|
|
func (p *PluginApplicationService) validateDraftPluginAccess(ctx context.Context, pluginID int64) (plugin *entity.PluginInfo, err error) {
|
|
uid := ctxutil.GetUIDFromCtx(ctx)
|
|
if uid == nil {
|
|
return nil, errorx.New(errno.ErrPluginPermissionCode, errorx.KV(errno.PluginMsgKey, "session is required"))
|
|
}
|
|
|
|
plugin, err = p.DomainSVC.GetDraftPlugin(ctx, pluginID)
|
|
if err != nil {
|
|
return nil, errorx.Wrapf(err, "GetDraftPlugin failed, pluginID=%d", pluginID)
|
|
}
|
|
|
|
if plugin.DeveloperID != *uid {
|
|
return nil, errorx.New(errno.ErrPluginPermissionCode, errorx.KV(errno.PluginMsgKey, "you are not the plugin owner"))
|
|
}
|
|
|
|
return plugin, nil
|
|
}
|