feat: manually mirror opencoze's code from bytedance
Change-Id: I09a73aadda978ad9511264a756b2ce51f5761adf
This commit is contained in:
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
import { useRef, useState } from 'react';
|
||||
|
||||
import { type FormApi } from '@coze-arch/bot-semi/Form';
|
||||
import {
|
||||
type AuthorizationType,
|
||||
type RegisterPluginMetaRequest,
|
||||
type commonParamSchema,
|
||||
} from '@coze-arch/bot-api/plugin_develop';
|
||||
import { type UploadValue } from '@coze-common/biz-components';
|
||||
import { type OauthTccOpt } from '@coze-studio/plugin-shared';
|
||||
|
||||
import { type UsePluginSchameReturnValue, usePluginSchame } from './utils';
|
||||
|
||||
export type FormState = RegisterPluginMetaRequest & {
|
||||
plugin_uri: UploadValue;
|
||||
auth_type: AuthorizationType[];
|
||||
} & Record<string, string>;
|
||||
|
||||
export interface UsePluginFormStateReturn extends UsePluginSchameReturnValue {
|
||||
formApi: React.MutableRefObject<FormApi<FormState> | undefined>;
|
||||
extItems: OauthTccOpt[];
|
||||
setExtItems: React.Dispatch<React.SetStateAction<OauthTccOpt[]>>;
|
||||
headerList: commonParamSchema[];
|
||||
setHeaderList: React.Dispatch<React.SetStateAction<commonParamSchema[]>>;
|
||||
isValidCheckResult: boolean;
|
||||
setIsValidCheckResult: React.Dispatch<React.SetStateAction<boolean>>;
|
||||
pluginTypeCreationMethod?: string;
|
||||
setPluginTypeCreationMethod: React.Dispatch<
|
||||
React.SetStateAction<string | undefined>
|
||||
>;
|
||||
}
|
||||
|
||||
export const usePluginFormState = (): UsePluginFormStateReturn => {
|
||||
const formApi = useRef<
|
||||
FormApi<
|
||||
RegisterPluginMetaRequest & {
|
||||
plugin_uri: UploadValue;
|
||||
auth_type: Array<AuthorizationType>;
|
||||
} & Record<string, string>
|
||||
>
|
||||
>();
|
||||
const { authOption, runtimeOptions, defaultRuntime } = usePluginSchame();
|
||||
const [extItems, setExtItems] = useState<OauthTccOpt[]>([]);
|
||||
const [headerList, setHeaderList] = useState<commonParamSchema[]>([
|
||||
{ name: 'User-Agent', value: 'Coze/1.0' },
|
||||
]);
|
||||
// 合规审核结果
|
||||
const [isValidCheckResult, setIsValidCheckResult] = useState(true);
|
||||
const [pluginTypeCreationMethod, setPluginTypeCreationMethod] =
|
||||
useState<string>();
|
||||
|
||||
return {
|
||||
formApi,
|
||||
extItems,
|
||||
setExtItems,
|
||||
headerList,
|
||||
setHeaderList,
|
||||
isValidCheckResult,
|
||||
setIsValidCheckResult,
|
||||
pluginTypeCreationMethod,
|
||||
setPluginTypeCreationMethod,
|
||||
authOption,
|
||||
runtimeOptions,
|
||||
defaultRuntime,
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,371 @@
|
||||
/* stylelint-disable declaration-no-important */
|
||||
/* stylelint-disable no-descending-specificity */
|
||||
.card {
|
||||
cursor: pointer;
|
||||
|
||||
position: relative;
|
||||
|
||||
min-width: 248px;
|
||||
height: 172px;
|
||||
|
||||
background-color: white !important;
|
||||
border-radius: 8px;
|
||||
|
||||
transition: box-shadow 0.4s;
|
||||
|
||||
&:hover {
|
||||
box-shadow: 0 4px 20px 0 rgb(31 35 41 / 4%),
|
||||
0 4px 10px 0 rgb(31 35 41 / 4%),
|
||||
0 2px 5px 0 rgb(31 35 41 / 4%);
|
||||
}
|
||||
}
|
||||
|
||||
.card-favorite-not-publish {
|
||||
cursor: not-allowed;
|
||||
background-color: var(--light-usage-fill-color-fill-0,
|
||||
rgb(46 50 56 / 5%)) !important;
|
||||
}
|
||||
|
||||
.add-card {
|
||||
background-color: white;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.add-card-inner {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.name-wrap {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
width: 100%;
|
||||
height: 40px;
|
||||
padding: 16px 16px 0;
|
||||
}
|
||||
|
||||
.avatar {
|
||||
flex-shrink: 0;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
border-radius: 50%px;
|
||||
}
|
||||
|
||||
.name {
|
||||
position: absolute;
|
||||
top: 16px;
|
||||
left: 48px;
|
||||
|
||||
overflow: hidden;
|
||||
|
||||
max-width: calc(100% - 156px);
|
||||
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
line-height: 24px;
|
||||
color: #000;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.extra {
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
.card-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: calc(100% - 40px);
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
.description {
|
||||
overflow: hidden;
|
||||
display: -webkit-box;
|
||||
|
||||
font-size: 12px;
|
||||
line-height: 18px;
|
||||
color: #494c4f;
|
||||
|
||||
-webkit-box-orient: vertical;
|
||||
-webkit-line-clamp: 2;
|
||||
}
|
||||
|
||||
.recent-modify {
|
||||
margin-top: 8px;
|
||||
font-size: 12px;
|
||||
line-height: 16px;
|
||||
color: rgb(28 31 35 / 60%);
|
||||
}
|
||||
|
||||
.creator {
|
||||
width: fit-content;
|
||||
padding: 4px;
|
||||
|
||||
font-size: 12px;
|
||||
line-height: 16px;
|
||||
color: #346ef8;
|
||||
|
||||
background: rgb(51 112 255 / 10%);
|
||||
border: none !important;
|
||||
border-radius: 3px !important;
|
||||
}
|
||||
|
||||
.upload-form {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
|
||||
.upload-field {
|
||||
padding-top: 0;
|
||||
|
||||
:global {
|
||||
.semi-form-field-help-text {
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.textarea-single-line {
|
||||
:global {
|
||||
.semi-input-textarea-counter {
|
||||
position: absolute;
|
||||
top: 6px;
|
||||
right: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.textarea-multi-line {
|
||||
margin-bottom: 16px;
|
||||
|
||||
:global {
|
||||
.semi-input-textarea-counter {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
bottom: -20px;
|
||||
|
||||
min-height: 0;
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.footer-draft {
|
||||
align-items: flex-start;
|
||||
|
||||
padding-top: 16px;
|
||||
|
||||
font-size: 12px;
|
||||
line-height: 16px;
|
||||
color: var(--coz-fg-secondary);
|
||||
|
||||
.link {
|
||||
font-weight: 400;
|
||||
color: var(--coz-fg-hglt);
|
||||
}
|
||||
|
||||
:global {
|
||||
.semi-icon {
|
||||
margin-top: 2px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
:global {
|
||||
.semi-form-field {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
input::-webkit-contacts-auto-fill-button {
|
||||
pointer-events: none;
|
||||
|
||||
position: absolute;
|
||||
right: 0;
|
||||
|
||||
display: none !important;
|
||||
|
||||
visibility: hidden;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.upload-form-item {
|
||||
:global {
|
||||
.semi-form-field-label-text {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.collect-num {
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
margin-right: 4px;
|
||||
|
||||
svg {
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
.user-info {
|
||||
margin-top: auto;
|
||||
}
|
||||
|
||||
.extinfo {
|
||||
max-width: 338px;
|
||||
font-size: 12px;
|
||||
|
||||
.extinfo-title {
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.extinfo-text {
|
||||
color: rgb(28 31 35 / 60%);
|
||||
}
|
||||
|
||||
.extinfo-ex {
|
||||
margin-top: 4px;
|
||||
padding: 6px 10px;
|
||||
color: rgb(28 31 35 / 60%);
|
||||
border: 1px solid rgb(28 31 35 / 8%);
|
||||
}
|
||||
}
|
||||
|
||||
.upload-avatar {
|
||||
flex-shrink: 0;
|
||||
|
||||
width: 80px !important;
|
||||
height: 80px !important;
|
||||
|
||||
background: #fff !important;
|
||||
border-radius: var(--spacing-tight, 8px) !important;
|
||||
}
|
||||
|
||||
.header-list {
|
||||
:global {
|
||||
.semi-form-field-label-with-extra {
|
||||
padding-right: 0;
|
||||
}
|
||||
|
||||
.semi-form-field-label-extra {
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.header-list-extra {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.header-list-box {
|
||||
overflow: auto;
|
||||
max-height: 348px;
|
||||
border: 1px solid var(--coz-stroke-primary);
|
||||
border-radius: 8px;
|
||||
|
||||
.header-row {
|
||||
border-bottom: 1px solid var(--coz-stroke-primary);
|
||||
}
|
||||
|
||||
.header-col-content {
|
||||
padding: 6px 8px;
|
||||
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
line-height: 16px;
|
||||
color: var(--coz-fg-secondary);
|
||||
}
|
||||
|
||||
.col-content {
|
||||
padding: 12px 8px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.error-msg-box {
|
||||
position: relative;
|
||||
top: -24px;
|
||||
|
||||
.error-msg {
|
||||
display: block;
|
||||
|
||||
padding: 8px 16px;
|
||||
|
||||
line-height: 16px;
|
||||
color: #F93920;
|
||||
text-align: left;
|
||||
|
||||
.link {
|
||||
font-weight: 400;
|
||||
color: #4D53E8;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.creation-method {
|
||||
display: flex !important;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
justify-content: space-between;
|
||||
|
||||
padding: 0 !important;
|
||||
|
||||
:global {
|
||||
.semi-radio {
|
||||
padding: 8px 12px;
|
||||
background-color: var(--coz-mg-card);
|
||||
border: solid 1px var(--coz-stroke-plus);
|
||||
border-radius: 8px;
|
||||
|
||||
&:hover {
|
||||
background-color: var(--coz-mg-secondary-hovered);
|
||||
}
|
||||
|
||||
&:active {
|
||||
background-color: var(--coz-mg-secondary-pressed);
|
||||
}
|
||||
}
|
||||
|
||||
.semi-radio-inner {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.semi-radio-addon {
|
||||
line-height: 20px;
|
||||
}
|
||||
|
||||
.semi-radio-checked {
|
||||
background: var(--coz-mg-hglt);
|
||||
border: 1px solid var(--coz-stroke-hglt);
|
||||
|
||||
&:hover {
|
||||
background-color: var(--coz-mg-hglt-hovered);
|
||||
}
|
||||
|
||||
&:active {
|
||||
background-color: var(--coz-mg-hglt-pressed);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.code-runtime-list {
|
||||
:global {
|
||||
.semi-select-option-selected .semi-select-option-icon {
|
||||
color: #4d53e8;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.bot-code-edit-title-action {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
width: 100%;
|
||||
}
|
||||
@@ -0,0 +1,714 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
/* eslint-disable max-lines */
|
||||
/* eslint-disable max-lines-per-function */
|
||||
/* eslint-disable complexity */
|
||||
|
||||
/* eslint-disable @coze-arch/max-line-per-function */
|
||||
import { type FC, useEffect, useState, Fragment } from 'react';
|
||||
|
||||
import { useRequest } from 'ahooks';
|
||||
import { useBenefitBasic, UserLevel } from '@coze-studio/premium-store-adapter';
|
||||
import {
|
||||
CLOUD_PLUGIN_COZE,
|
||||
doGetCreationMethodTips,
|
||||
extInfoText,
|
||||
locationOption,
|
||||
authOptionsPlaceholder,
|
||||
grantTypeOptions,
|
||||
type PluginInfoProps,
|
||||
} from '@coze-studio/plugin-shared';
|
||||
import { useCurrentEnterpriseInfo } from '@coze-foundation/enterprise-store-adapter';
|
||||
import { PictureUpload } from '@coze-common/biz-components/picture-upload';
|
||||
import { logger } from '@coze-arch/logger';
|
||||
import { I18n } from '@coze-arch/i18n';
|
||||
import { safeJSONParse } from '@coze-arch/bot-utils';
|
||||
import { useFlags } from '@coze-arch/bot-flags';
|
||||
import {
|
||||
type PrivateLink,
|
||||
type commonParamSchema,
|
||||
} from '@coze-arch/bot-api/plugin_develop';
|
||||
import { FileBizType, IconType } from '@coze-arch/bot-api/developer_api';
|
||||
import { DeveloperApi, PluginDevelopApi } from '@coze-arch/bot-api';
|
||||
import { InfoPopover } from '@coze-agent-ide/bot-plugin-tools/infoPopover';
|
||||
import { IconCozPlus, IconCozTrashCan } from '@coze-arch/coze-design/icons';
|
||||
import {
|
||||
Cascader,
|
||||
Col,
|
||||
Form,
|
||||
FormInput,
|
||||
FormSelect,
|
||||
FormTextArea,
|
||||
IconButton,
|
||||
Input,
|
||||
Row,
|
||||
Typography,
|
||||
withField,
|
||||
} from '@coze-arch/coze-design';
|
||||
|
||||
import {
|
||||
findAuthTypeItemV2,
|
||||
formRuleList,
|
||||
getPictureUploadInitValue,
|
||||
} from './utils';
|
||||
import { type UsePluginFormStateReturn, type FormState } from './hooks';
|
||||
|
||||
import s from './index.module.less';
|
||||
|
||||
const HEADER_LIST_LENGTH_MAX = 20;
|
||||
|
||||
const doFormatTypeAndCreation = (info?: PluginInfoProps) =>
|
||||
info ? `${info?.plugin_type}-${info?.creation_method}` : '';
|
||||
|
||||
const FormCascader = withField(Cascader);
|
||||
|
||||
const getOptionList = () => [
|
||||
{
|
||||
label: I18n.t('plugin_creation_method_cloud_plugin_use_existing_services'),
|
||||
value: CLOUD_PLUGIN_COZE,
|
||||
},
|
||||
];
|
||||
|
||||
export const PluginForm: FC<{
|
||||
pluginState: UsePluginFormStateReturn;
|
||||
visible: boolean;
|
||||
isCreate?: boolean;
|
||||
disabled?: boolean;
|
||||
editInfo?: PluginInfoProps;
|
||||
}> = ({ pluginState, disabled, editInfo, isCreate, visible }) => {
|
||||
const {
|
||||
formApi,
|
||||
extItems,
|
||||
setExtItems,
|
||||
headerList,
|
||||
setHeaderList,
|
||||
isValidCheckResult,
|
||||
setIsValidCheckResult,
|
||||
pluginTypeCreationMethod,
|
||||
setPluginTypeCreationMethod,
|
||||
authOption,
|
||||
} = pluginState;
|
||||
|
||||
const [FLAGS] = useFlags();
|
||||
const { compareLevel } = useBenefitBasic();
|
||||
const enterpriseInfo = useCurrentEnterpriseInfo();
|
||||
const [mainAuthType, setMainAuthType] = useState<number>(0);
|
||||
const [authType, setAuthType] = useState<number>(0);
|
||||
const [disableEditUrl, setDisableEditUrl] = useState<boolean>(false);
|
||||
// 合规审核结果
|
||||
const changeVal = () => {
|
||||
if (!isValidCheckResult) {
|
||||
setIsValidCheckResult(true);
|
||||
}
|
||||
};
|
||||
const creationMethodOption = getOptionList();
|
||||
const creationMethodTip = doGetCreationMethodTips();
|
||||
|
||||
const [privateLinkMap, setPrivateLinkMap] = useState<Array<PrivateLink>>();
|
||||
const { data: privateNetworkListOptions } = useRequest(
|
||||
async () => {
|
||||
const { data } = await PluginDevelopApi.PrivateLinkList({
|
||||
enterprise_id: enterpriseInfo?.enterprise_id,
|
||||
});
|
||||
setPrivateLinkMap(data?.private_links);
|
||||
const list = data?.private_links?.map(item => ({
|
||||
label: item.name,
|
||||
value: item.id,
|
||||
}));
|
||||
|
||||
return [
|
||||
{
|
||||
label: I18n.t('vpc_plugin_create_plugin_2'),
|
||||
value: '0',
|
||||
},
|
||||
...(list ?? []),
|
||||
];
|
||||
},
|
||||
{
|
||||
ready:
|
||||
compareLevel === UserLevel.Enterprise &&
|
||||
// 社区版暂不支持该功能
|
||||
FLAGS['bot.studio.plugin_vpc'] &&
|
||||
!IS_OPEN_SOURCE,
|
||||
},
|
||||
);
|
||||
/**
|
||||
* 获取默认icon, 并设置到form中
|
||||
*/
|
||||
const getIcon = async () => {
|
||||
try {
|
||||
const res = await DeveloperApi.GetIcon({
|
||||
icon_type: IconType.Plugin,
|
||||
});
|
||||
const iconData = res.data?.icon_list?.[0];
|
||||
if (!iconData) {
|
||||
return;
|
||||
}
|
||||
const { url = '', uri = '' } = iconData;
|
||||
formApi.current?.setValue('plugin_uri', [
|
||||
{
|
||||
url,
|
||||
uid: uri,
|
||||
},
|
||||
]);
|
||||
} catch (e) {
|
||||
logger.info(`getIcon error: ${e}`);
|
||||
}
|
||||
};
|
||||
useEffect(() => {
|
||||
if (!visible) {
|
||||
return;
|
||||
}
|
||||
if (!isCreate && editInfo) {
|
||||
/**
|
||||
* 以下的 useState 都是更新插件
|
||||
*/
|
||||
/**
|
||||
* service在本次需求中被拓展了二级菜单,service的一级菜单为1,二级菜单为0,1,2
|
||||
* 后端为了不改动历史逻辑,新增一个sub_auth_type的字段,去记录service下的二级菜单的值,但是authType依旧是一个length为1的数组,并且值为1
|
||||
* 因此前端需要手动判断auth_type为1的时候,把sub_auth_type塞进去,组成一个length为2的数组,才能让casdar菜单回填editInfo的内容
|
||||
* 5,6,7是给前端用的,用来判断选中的是哪个。5是apiKey、6是zero、7是oicd
|
||||
* 好问题来了。authType是一个数组,但是长度不固定:
|
||||
* 1. 当auth_type长度为1时,代表是service,内容有且只有1,此时sub_auth_type为0,1,2中的一个
|
||||
* 2. 当auth_type长度为2时,代表是oAuth,内容为[3,4]此时没有sub_auth_type
|
||||
*/
|
||||
if (editInfo.meta_info?.auth_type?.at(0) === 1) {
|
||||
switch (editInfo.meta_info?.sub_auth_type) {
|
||||
case 0:
|
||||
setAuthType(5);
|
||||
break;
|
||||
case 1:
|
||||
setAuthType(6);
|
||||
break;
|
||||
case 2:
|
||||
setAuthType(7);
|
||||
break;
|
||||
default:
|
||||
setAuthType(0);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
setAuthType(editInfo.meta_info?.auth_type?.at(-1) ?? 0);
|
||||
}
|
||||
setMainAuthType(editInfo.meta_info?.auth_type?.at(0) ?? 0);
|
||||
setExtItems(
|
||||
findAuthTypeItemV2(
|
||||
authOption,
|
||||
editInfo.meta_info?.auth_type,
|
||||
editInfo.meta_info?.auth_type?.[1] ??
|
||||
editInfo.meta_info?.sub_auth_type,
|
||||
)?.items || [],
|
||||
);
|
||||
|
||||
const header = editInfo.meta_info?.common_params?.[4] || [];
|
||||
setHeaderList([...header]);
|
||||
setPluginTypeCreationMethod(
|
||||
`${editInfo.plugin_type}-${editInfo.creation_method}`,
|
||||
);
|
||||
|
||||
// 如果存在私网连接,则禁用编辑URL
|
||||
if (
|
||||
editInfo?.meta_info?.private_link_id &&
|
||||
compareLevel === UserLevel.Enterprise &&
|
||||
// 社区版暂不支持该功能
|
||||
FLAGS['bot.studio.plugin_vpc']
|
||||
) {
|
||||
setDisableEditUrl(true);
|
||||
}
|
||||
} else {
|
||||
reset();
|
||||
}
|
||||
}, [visible]);
|
||||
|
||||
const reset = () => {
|
||||
//重置插件
|
||||
getIcon();
|
||||
setAuthType(0);
|
||||
setAuthType(0);
|
||||
setExtItems([]);
|
||||
setHeaderList([{ name: 'User-Agent', value: 'Coze/1.0' }]);
|
||||
setIsValidCheckResult(true);
|
||||
setPluginTypeCreationMethod(undefined);
|
||||
};
|
||||
|
||||
/** 添加header */
|
||||
const addHeader = () => {
|
||||
setHeaderList(list => [...list, { name: '', value: '' }]);
|
||||
};
|
||||
/** 删除header */
|
||||
const deleteHeader = (index: number) => {
|
||||
// 若为最后一个header,则只清空内容,不删除
|
||||
setHeaderList(list =>
|
||||
list.length <= 1
|
||||
? [{ name: '', value: '' }]
|
||||
: list.filter((_, i) => i !== index),
|
||||
);
|
||||
};
|
||||
/** 编辑header */
|
||||
const editHeader = (index: number, header: commonParamSchema) => {
|
||||
setHeaderList(list => list.map((item, i) => (i === index ? header : item)));
|
||||
};
|
||||
|
||||
const renderPluginCoze = () => {
|
||||
let authTypeInitValue = [0];
|
||||
if (!editInfo) {
|
||||
authTypeInitValue = [0];
|
||||
}
|
||||
// 是 OAuth 的情况
|
||||
if (editInfo?.meta_info?.auth_type?.length === 2) {
|
||||
authTypeInitValue = editInfo?.meta_info?.auth_type;
|
||||
}
|
||||
// service & no auth
|
||||
else {
|
||||
// 不需要授权,自然没有sub_auth_type
|
||||
if (editInfo?.meta_info?.auth_type?.at(0) === 0) {
|
||||
authTypeInitValue = editInfo?.meta_info?.auth_type;
|
||||
}
|
||||
// service, 有sub_auth_type
|
||||
else {
|
||||
if (typeof editInfo?.meta_info?.sub_auth_type !== 'undefined') {
|
||||
authTypeInitValue = [
|
||||
...(editInfo?.meta_info?.auth_type || []),
|
||||
editInfo?.meta_info?.sub_auth_type,
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
return (
|
||||
<>
|
||||
{compareLevel === UserLevel.Enterprise &&
|
||||
// 社区版暂不支持该功能
|
||||
FLAGS['bot.studio.plugin_vpc'] ? (
|
||||
<FormSelect
|
||||
label={{
|
||||
text: I18n.t('vpc_plugin_create_plugin_1'),
|
||||
required: true,
|
||||
extra: <InfoPopover data={extInfoText.private_link_id} />,
|
||||
}}
|
||||
field="private_link_id"
|
||||
style={{ width: '100%' }}
|
||||
initValue={editInfo?.meta_info?.private_link_id || '0'}
|
||||
onChange={value => {
|
||||
setDisableEditUrl(value !== '0');
|
||||
if (value === '0') {
|
||||
formApi.current?.setValue('url', '');
|
||||
} else {
|
||||
formApi.current?.setValue(
|
||||
'url',
|
||||
privateLinkMap?.find(item => item.id === value)
|
||||
?.plugin_access_url,
|
||||
);
|
||||
}
|
||||
}}
|
||||
optionList={privateNetworkListOptions}
|
||||
/>
|
||||
) : null}
|
||||
|
||||
{/* 插件URL */}
|
||||
{!disabled ? (
|
||||
<FormInput
|
||||
disabled={disableEditUrl}
|
||||
className={s['textarea-single-line']}
|
||||
initValue={editInfo?.meta_info?.url}
|
||||
trigger={['blur', 'change']}
|
||||
field="url"
|
||||
label={I18n.t('create_plugin_modal_url1')}
|
||||
placeholder={I18n.t('create_plugin_modal_url2')}
|
||||
onBlur={() => {
|
||||
formApi.current?.setValue(
|
||||
'url',
|
||||
formApi.current?.getValue('url')?.trim(),
|
||||
);
|
||||
}}
|
||||
rules={disableEditUrl ? [] : formRuleList.url}
|
||||
/>
|
||||
) : null}
|
||||
{/* 插件Header */}
|
||||
<Form.Slot
|
||||
className={s['header-list']}
|
||||
label={{
|
||||
text: I18n.t('plugin_create_header_list_title'),
|
||||
align: 'right',
|
||||
extra: (
|
||||
<div className={s['header-list-extra']}>
|
||||
<InfoPopover data={extInfoText.header_list} />
|
||||
{headerList.length < HEADER_LIST_LENGTH_MAX && !disabled && (
|
||||
<IconButton
|
||||
size="small"
|
||||
color="secondary"
|
||||
icon={<IconCozPlus className="coz-fg-hglt text-[16px]" />}
|
||||
onClick={addHeader}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
),
|
||||
}}
|
||||
>
|
||||
<div className={s['header-list-box']}>
|
||||
<Row className={s['header-row']}>
|
||||
<Col span={9}>
|
||||
<div className={s['header-col-content']}>Key</div>
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
<div className={s['header-col-content']}>Value</div>
|
||||
</Col>
|
||||
<Col span={3}>
|
||||
<div className={s['header-col-content']}>
|
||||
{I18n.t('plugin_create_action_btn')}
|
||||
</div>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
<div>
|
||||
{headerList?.map((item, index) => (
|
||||
<Row
|
||||
type="flex"
|
||||
justify="space-between"
|
||||
align="middle"
|
||||
key={index}
|
||||
>
|
||||
<Col span={9}>
|
||||
<div className={s['col-content']}>
|
||||
<Input
|
||||
placeholder={'Name'}
|
||||
value={item.name}
|
||||
onChange={name => {
|
||||
editHeader(index, { ...item, name });
|
||||
}}
|
||||
maxLength={100}
|
||||
disabled={disabled}
|
||||
/>
|
||||
</div>
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
<div className={s['col-content']}>
|
||||
<Input
|
||||
placeholder={'Value'}
|
||||
value={item.value}
|
||||
onChange={value => {
|
||||
editHeader(index, { ...item, value });
|
||||
}}
|
||||
maxLength={2000}
|
||||
disabled={disabled}
|
||||
/>
|
||||
</div>
|
||||
</Col>
|
||||
<Col span={3}>
|
||||
<div className={s['col-content']}>
|
||||
<IconButton
|
||||
size="small"
|
||||
color="secondary"
|
||||
icon={
|
||||
<IconCozTrashCan className="coz-fg-secondary text-[14px]" />
|
||||
}
|
||||
disabled={disabled}
|
||||
onClick={() => deleteHeader(index)}
|
||||
/>
|
||||
</div>
|
||||
</Col>
|
||||
</Row>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</Form.Slot>
|
||||
|
||||
{/* 授权方式 */}
|
||||
|
||||
<FormCascader
|
||||
disabled={disabled}
|
||||
rules={[{ required: true }]}
|
||||
style={{ width: '100%' }}
|
||||
initValue={authTypeInitValue}
|
||||
field="auth_type"
|
||||
label={{
|
||||
text: I18n.t('create_plugin_modal_auth1'),
|
||||
extra: <InfoPopover data={extInfoText.auth} />,
|
||||
}}
|
||||
placeholder={I18n.t('please_select_an_authorization_method')}
|
||||
treeData={authOption}
|
||||
displayRender={(list: any) => {
|
||||
if (IS_RELEASE_VERSION) {
|
||||
const value = formApi.current?.getValue('auth_type');
|
||||
if (value?.[0] === 1 && value?.[1] === 1) {
|
||||
return I18n.t('plugin_auth_method_service_zti');
|
||||
}
|
||||
}
|
||||
return `${list.at(-1)}`;
|
||||
}}
|
||||
onChange={(value: any) => {
|
||||
setExtItems(
|
||||
findAuthTypeItemV2(authOption, [value.at(0)], value.at(-1))
|
||||
?.items || [],
|
||||
);
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* 授权方式 - Service - Service Token / API Key */}
|
||||
{mainAuthType === 1 && authType === 5 && (
|
||||
<>
|
||||
<Form.RadioGroup
|
||||
disabled={disabled}
|
||||
rules={[{ required: true }]}
|
||||
field="location"
|
||||
label={{
|
||||
text: I18n.t('create_plugin_modal_location'),
|
||||
extra: <InfoPopover data={extInfoText.location} />,
|
||||
}}
|
||||
options={locationOption}
|
||||
initValue={editInfo?.meta_info?.location || 1}
|
||||
/>
|
||||
|
||||
<FormInput
|
||||
disabled={disabled}
|
||||
initValue={editInfo?.meta_info?.key}
|
||||
trigger={['blur', 'change']}
|
||||
field="key"
|
||||
label={{
|
||||
text: I18n.t('create_plugin_modal_Parameter'),
|
||||
extra: <InfoPopover data={extInfoText.key} />,
|
||||
}}
|
||||
placeholder={I18n.t('create_plugin_modal_Parameter_empty')}
|
||||
maxLength={100}
|
||||
rules={formRuleList.key}
|
||||
/>
|
||||
|
||||
<FormInput
|
||||
disabled={disabled}
|
||||
initValue={editInfo?.meta_info?.service_token}
|
||||
trigger={['blur', 'change']}
|
||||
field="service_token"
|
||||
label={{
|
||||
text: I18n.t('create_plugin_modal_Servicetoken'),
|
||||
extra: <InfoPopover data={extInfoText.service_token} />,
|
||||
}}
|
||||
placeholder={I18n.t('create_plugin_modal_Servicetoken_empty')}
|
||||
maxLength={2000}
|
||||
rules={formRuleList.service_token}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
|
||||
{/* 服务端动态返回授权项 */}
|
||||
{/* Service - OIDC & OAuth - Standard Mode */}
|
||||
{extItems?.map((item, index) => {
|
||||
let formInfo: Record<string, any> = {};
|
||||
// Service - OIDC
|
||||
if (editInfo?.meta_info?.auth_type?.at(0) === 1) {
|
||||
formInfo = safeJSONParse(editInfo.meta_info.auth_payload);
|
||||
}
|
||||
// OAuth - Standard Mode
|
||||
if (editInfo?.meta_info?.auth_type?.at(0) === 3) {
|
||||
formInfo = safeJSONParse(editInfo.meta_info.oauth_info);
|
||||
}
|
||||
|
||||
if (item.type === 'select') {
|
||||
return (
|
||||
<FormSelect
|
||||
disabled={disabled}
|
||||
key={item.key + index}
|
||||
label={item?.label || item.key}
|
||||
field={item.key}
|
||||
optionList={grantTypeOptions}
|
||||
initValue={formInfo?.[item.key] || item.default}
|
||||
style={{ width: '100%' }}
|
||||
rules={[
|
||||
{
|
||||
required: item.required,
|
||||
// @ts-expect-error -- linter-disable-autofix
|
||||
message: authOptionsPlaceholder[item.key],
|
||||
},
|
||||
]}
|
||||
/>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<Fragment key={item.key + index}>
|
||||
<FormInput
|
||||
disabled={disabled}
|
||||
key={item.key}
|
||||
trigger={['blur', 'change']}
|
||||
field={item.key}
|
||||
label={{
|
||||
text: item?.label || item.key,
|
||||
extra: extInfoText[item.key] && (
|
||||
<InfoPopover data={extInfoText[item.key]} />
|
||||
),
|
||||
}}
|
||||
// @ts-expect-error -- linter-disable-autofix
|
||||
placeholder={authOptionsPlaceholder[item.key]}
|
||||
initValue={formInfo?.[item.key] || item.default}
|
||||
maxLength={item.max_len}
|
||||
rules={[
|
||||
{
|
||||
required: item.required,
|
||||
// @ts-expect-error -- linter-disable-autofix
|
||||
message: authOptionsPlaceholder[item.key],
|
||||
},
|
||||
item.type === 'url'
|
||||
? {
|
||||
pattern: /^(http|https):\/\/.+$/,
|
||||
message: I18n.t('create_plugin_modal_URLerror'),
|
||||
}
|
||||
: {
|
||||
// eslint-disable-next-line no-control-regex -- regex
|
||||
pattern: /^[\x00-\x7F]+$/,
|
||||
message: I18n.t('create_plugin_modal_descrip_error'),
|
||||
},
|
||||
...(item?.ruleList || []),
|
||||
]}
|
||||
/>
|
||||
</Fragment>
|
||||
);
|
||||
})}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<Form<FormState>
|
||||
getFormApi={api => (formApi.current = api)}
|
||||
autoScrollToError
|
||||
showValidateIcon={false}
|
||||
className={s['upload-form']}
|
||||
onValueChange={values => {
|
||||
if ('auth_type' in values) {
|
||||
if (values.auth_type.at(0) === 1) {
|
||||
switch (values.auth_type.at(-1)) {
|
||||
case 0:
|
||||
setAuthType(5);
|
||||
break;
|
||||
case 1:
|
||||
setAuthType(6);
|
||||
break;
|
||||
//@ts-expect-error 授权类型兼容
|
||||
case 2:
|
||||
setAuthType(7);
|
||||
break;
|
||||
default:
|
||||
setAuthType(0);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
setAuthType(values.auth_type.at(-1)!);
|
||||
}
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
setMainAuthType(values.auth_type.at(0)!);
|
||||
}
|
||||
}}
|
||||
>
|
||||
{/* 插件头像 */}
|
||||
<PictureUpload
|
||||
noLabel
|
||||
disabled={disabled}
|
||||
fieldClassName={s['upload-field']}
|
||||
field="plugin_uri"
|
||||
iconType={IconType.Plugin}
|
||||
fileBizType={FileBizType.BIZ_PLUGIN_ICON}
|
||||
initValue={getPictureUploadInitValue(editInfo?.meta_info)}
|
||||
onChange={changeVal}
|
||||
/>
|
||||
|
||||
{/* 插件名称/插件描述/url/插件类型 */}
|
||||
<>
|
||||
<FormTextArea
|
||||
disabled={disabled}
|
||||
initValue={editInfo?.meta_info?.name}
|
||||
field="name"
|
||||
className={s['textarea-single-line']}
|
||||
label={I18n.t('create_plugin_modal_name1')}
|
||||
placeholder={I18n.t('create_plugin_modal_name2')}
|
||||
trigger={['blur', 'change']}
|
||||
maxCount={30}
|
||||
maxLength={30}
|
||||
rows={1}
|
||||
onBlur={() => {
|
||||
formApi.current?.setValue(
|
||||
'name',
|
||||
formApi.current?.getValue('name')?.trim(),
|
||||
);
|
||||
}}
|
||||
onChange={changeVal}
|
||||
rules={formRuleList.name}
|
||||
/>
|
||||
<FormTextArea
|
||||
disabled={disabled}
|
||||
initValue={editInfo?.meta_info?.desc}
|
||||
field="desc"
|
||||
label={I18n.t('create_plugin_modal_descrip1')}
|
||||
trigger={['blur', 'change']}
|
||||
placeholder={I18n.t('create_plugin_modal_descrip2')}
|
||||
rows={2}
|
||||
maxCount={600}
|
||||
maxLength={600}
|
||||
onBlur={() => {
|
||||
formApi.current?.setValue(
|
||||
'desc',
|
||||
formApi.current?.getValue('desc')?.trim(),
|
||||
);
|
||||
}}
|
||||
onChange={changeVal}
|
||||
rules={formRuleList.desc}
|
||||
/>
|
||||
{/* 插件类型 */}
|
||||
<Form.Slot
|
||||
label={{
|
||||
text: I18n.t('plugin_creation_method'),
|
||||
required: true,
|
||||
extra: <InfoPopover data={creationMethodTip} />,
|
||||
}}
|
||||
>
|
||||
{isCreate ? (
|
||||
<Form.RadioGroup
|
||||
noLabel
|
||||
className={s['creation-method']}
|
||||
direction="vertical"
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
message: I18n.t(
|
||||
'plugin_creation_select_creation_method_warning',
|
||||
),
|
||||
},
|
||||
]}
|
||||
field="creation_method"
|
||||
disabled={disabled}
|
||||
options={creationMethodOption}
|
||||
initValue={
|
||||
editInfo ? doFormatTypeAndCreation(editInfo) : undefined
|
||||
}
|
||||
onChange={v => setPluginTypeCreationMethod(v.target.value)}
|
||||
/>
|
||||
) : (
|
||||
<Typography.Text fontSize="14px">
|
||||
{
|
||||
creationMethodOption.find(
|
||||
option => option.value === pluginTypeCreationMethod,
|
||||
)?.label
|
||||
}
|
||||
</Typography.Text>
|
||||
)}
|
||||
</Form.Slot>
|
||||
</>
|
||||
|
||||
{pluginTypeCreationMethod === CLOUD_PLUGIN_COZE
|
||||
? renderPluginCoze()
|
||||
: null}
|
||||
</Form>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,314 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
import { type PluginInfoProps } from '@coze-studio/plugin-shared';
|
||||
import { type UploadValue } from '@coze-common/biz-components';
|
||||
import { I18n } from '@coze-arch/i18n';
|
||||
import { safeJSONParse } from '@coze-arch/bot-utils';
|
||||
import {
|
||||
type commonParamSchema,
|
||||
type CreationMethod,
|
||||
ParameterLocation,
|
||||
type PluginType,
|
||||
type PluginMetaInfo,
|
||||
} from '@coze-arch/bot-api/plugin_develop';
|
||||
import { PluginDevelopApi } from '@coze-arch/bot-api';
|
||||
|
||||
import { type FormState } from './hooks';
|
||||
|
||||
export const formRuleList = {
|
||||
name: [
|
||||
{
|
||||
required: true,
|
||||
message: I18n.t('create_plugin_modal_name1_error'),
|
||||
},
|
||||
IS_OVERSEA || IS_BOE
|
||||
? {
|
||||
pattern: /^[\w\s]+$/,
|
||||
message: I18n.t('create_plugin_modal_nameerror'),
|
||||
}
|
||||
: {
|
||||
pattern: /^[\w\s\u4e00-\u9fa5]+$/u, // 国内增加支持中文
|
||||
message: I18n.t('create_plugin_modal_nameerror_cn'),
|
||||
},
|
||||
],
|
||||
desc: [
|
||||
{
|
||||
required: true,
|
||||
message: I18n.t('create_plugin_modal_descrip1_error'),
|
||||
},
|
||||
// 只有cn 线上才支持中文
|
||||
IS_OVERSEA || IS_BOE
|
||||
? {
|
||||
// eslint-disable-next-line no-control-regex -- regex
|
||||
pattern: /^[\x00-\x7F]+$/,
|
||||
message: I18n.t('create_plugin_modal_descrip_error'),
|
||||
}
|
||||
: {},
|
||||
],
|
||||
url: [
|
||||
{
|
||||
required: true,
|
||||
message: I18n.t('create_plugin_modal_url1_error'),
|
||||
},
|
||||
{
|
||||
pattern: /^(https):\/\/.+$/,
|
||||
message: I18n.t('create_plugin_modal_url_error_https'),
|
||||
},
|
||||
],
|
||||
key: [
|
||||
{
|
||||
required: true,
|
||||
message: I18n.t('create_plugin_modal_Parameter_error'),
|
||||
},
|
||||
{
|
||||
// eslint-disable-next-line no-control-regex -- regex
|
||||
pattern: /^[\x00-\x7F]+$/,
|
||||
message: I18n.t('plugin_Parametename_error'),
|
||||
},
|
||||
],
|
||||
service_token: [
|
||||
{
|
||||
required: true,
|
||||
message: I18n.t('create_plugin_modal_Servicetoken_error'),
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
export const getPictureUploadInitValue = (
|
||||
info?: PluginMetaInfo,
|
||||
): UploadValue | undefined => {
|
||||
if (!info) {
|
||||
return;
|
||||
}
|
||||
return [
|
||||
{
|
||||
url: info.icon?.url || '',
|
||||
uid: info?.icon?.uri || '',
|
||||
},
|
||||
];
|
||||
};
|
||||
|
||||
export interface AuthOption {
|
||||
label: string;
|
||||
value: number;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- any
|
||||
[key: string]: any;
|
||||
}
|
||||
/** 递归寻找auth选项下的输入项 */
|
||||
export const findAuthTypeItem = (data: AuthOption[], targetKey = 0) => {
|
||||
for (const item of data) {
|
||||
if (item.value === targetKey) {
|
||||
return item;
|
||||
} else if (item.children?.length > 0) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- any
|
||||
const res: any = findAuthTypeItem(item.children, targetKey);
|
||||
if (res) {
|
||||
return res;
|
||||
}
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
};
|
||||
|
||||
export const findAuthTypeItemV2 = (
|
||||
opts: AuthOption[],
|
||||
authType?: number[],
|
||||
subAuthType?: number,
|
||||
) => {
|
||||
if (authType?.[0] === 0) {
|
||||
return opts.find(item => item.value === 0);
|
||||
} else if (authType?.[0] === 1) {
|
||||
const optsItem = opts.find(item => item.value === 1);
|
||||
return optsItem?.children.find(
|
||||
(item: AuthOption) => item.value === subAuthType,
|
||||
);
|
||||
} else if (authType?.[0] === 3) {
|
||||
const optsItem = opts.find(item => item.value === 3);
|
||||
|
||||
return optsItem?.children.find(
|
||||
(item: AuthOption) => item.value === subAuthType,
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
interface RuntimeOptionsType {
|
||||
label: string;
|
||||
value: string;
|
||||
}
|
||||
|
||||
interface IdeConfType {
|
||||
key: string;
|
||||
type: string;
|
||||
default: string;
|
||||
options: {
|
||||
value: string;
|
||||
name: string;
|
||||
}[];
|
||||
}
|
||||
|
||||
export interface UsePluginSchameReturnValue {
|
||||
authOption: AuthOption[];
|
||||
runtimeOptions: RuntimeOptionsType[];
|
||||
defaultRuntime: string;
|
||||
}
|
||||
|
||||
// 获取schame 和 runtime options
|
||||
export const usePluginSchame = (): UsePluginSchameReturnValue => {
|
||||
const [authOption, setAuthOption] = useState<AuthOption[]>([]);
|
||||
const [runtimeOptions, setRuntimeOptions] = useState<RuntimeOptionsType[]>(
|
||||
[],
|
||||
);
|
||||
const [defaultRuntime, setDefaultRuntime] = useState('1');
|
||||
|
||||
const getOption = async () => {
|
||||
const res = await PluginDevelopApi.GetOAuthSchema();
|
||||
const authOptions = [
|
||||
{
|
||||
label: I18n.t('create_plugin_modal_Authorization_no'),
|
||||
value: 0,
|
||||
key: 'None',
|
||||
},
|
||||
{
|
||||
label: I18n.t('create_plugin_modal_Authorization_service'),
|
||||
value: 1,
|
||||
key: 'Service',
|
||||
children: [
|
||||
{
|
||||
label: I18n.t('plugin_auth_method_service_api_key'),
|
||||
value: 0,
|
||||
key: 'Service Token / API Key',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: I18n.t('create_plugin_modal_Authorization_oauth'),
|
||||
value: 3,
|
||||
key: 'OAuth',
|
||||
children: safeJSONParse(res.oauth_schema),
|
||||
},
|
||||
];
|
||||
setAuthOption(authOptions);
|
||||
const runtimeInfo = (
|
||||
safeJSONParse(res.ide_conf, []) as IdeConfType[]
|
||||
)?.find?.(item => item.key === 'code_runtime_enum');
|
||||
if (runtimeInfo) {
|
||||
const runtimeList = runtimeInfo.options.map(item => ({
|
||||
value: item.value,
|
||||
label: item.name,
|
||||
}));
|
||||
setRuntimeOptions(runtimeList);
|
||||
setDefaultRuntime(runtimeInfo.default);
|
||||
}
|
||||
};
|
||||
useEffect(() => {
|
||||
getOption();
|
||||
}, []);
|
||||
|
||||
return { authOption, runtimeOptions, defaultRuntime };
|
||||
};
|
||||
|
||||
export const convertPluginMetaParams = ({
|
||||
val,
|
||||
spaceId,
|
||||
headerList,
|
||||
projectId,
|
||||
creationMethod,
|
||||
defaultRuntime,
|
||||
pluginType,
|
||||
extItemsJSON,
|
||||
}: {
|
||||
val: FormState;
|
||||
spaceId: string;
|
||||
headerList: commonParamSchema[];
|
||||
projectId: string | undefined;
|
||||
creationMethod: CreationMethod;
|
||||
defaultRuntime: string;
|
||||
pluginType: PluginType;
|
||||
extItemsJSON: Record<string, string>;
|
||||
}) => {
|
||||
const mainAuthType = val.auth_type?.at(0);
|
||||
const serviceSubAuthType = val.auth_type?.at(-1);
|
||||
const initParams = {
|
||||
...val,
|
||||
icon: { uri: val?.plugin_uri?.[0]?.uid },
|
||||
auth_type: mainAuthType,
|
||||
common_params: {
|
||||
[ParameterLocation.Header]: headerList,
|
||||
[ParameterLocation.Body]: [],
|
||||
[ParameterLocation.Path]: [],
|
||||
[ParameterLocation.Query]: [],
|
||||
},
|
||||
space_id: spaceId,
|
||||
project_id: projectId,
|
||||
creation_method: creationMethod,
|
||||
ide_code_runtime: val.ide_code_runtime ?? defaultRuntime,
|
||||
plugin_type: Number(pluginType) as unknown as PluginType,
|
||||
private_link_id:
|
||||
val.private_link_id === '0' ? undefined : val.private_link_id,
|
||||
};
|
||||
const params =
|
||||
mainAuthType === 1
|
||||
? {
|
||||
...initParams,
|
||||
sub_auth_type: serviceSubAuthType,
|
||||
auth_payload: JSON.stringify(extItemsJSON),
|
||||
}
|
||||
: {
|
||||
...initParams,
|
||||
sub_auth_type: mainAuthType === 3 ? serviceSubAuthType : undefined,
|
||||
oauth_info: JSON.stringify(extItemsJSON),
|
||||
};
|
||||
return params;
|
||||
};
|
||||
|
||||
export const registerPluginMeta = async ({
|
||||
params,
|
||||
}: {
|
||||
params: ReturnType<typeof convertPluginMetaParams>;
|
||||
}) => {
|
||||
const res = await PluginDevelopApi.RegisterPluginMeta(
|
||||
{
|
||||
...params,
|
||||
},
|
||||
{
|
||||
__disableErrorToast: true,
|
||||
},
|
||||
);
|
||||
return res.plugin_id;
|
||||
};
|
||||
|
||||
export const updatePluginMeta = async ({
|
||||
params,
|
||||
editInfo,
|
||||
}: {
|
||||
params: ReturnType<typeof convertPluginMetaParams>;
|
||||
editInfo: PluginInfoProps | undefined;
|
||||
}) => {
|
||||
await PluginDevelopApi.UpdatePluginMeta(
|
||||
{
|
||||
...params,
|
||||
plugin_id: editInfo?.plugin_id || '',
|
||||
edit_version: editInfo?.edit_version,
|
||||
},
|
||||
{
|
||||
__disableErrorToast: true,
|
||||
},
|
||||
);
|
||||
return '';
|
||||
};
|
||||
27
frontend/packages/studio/plugin-form-adapter/src/index.ts
Normal file
27
frontend/packages/studio/plugin-form-adapter/src/index.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
export { PluginForm } from './components/plugin-form-content';
|
||||
export {
|
||||
usePluginFormState,
|
||||
type UsePluginFormStateReturn,
|
||||
} from './components/plugin-form-content/hooks';
|
||||
|
||||
export {
|
||||
convertPluginMetaParams,
|
||||
registerPluginMeta,
|
||||
updatePluginMeta,
|
||||
} from './components/plugin-form-content/utils';
|
||||
17
frontend/packages/studio/plugin-form-adapter/src/typings.d.ts
vendored
Normal file
17
frontend/packages/studio/plugin-form-adapter/src/typings.d.ts
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/// <reference types='@coze-arch/bot-typings' />
|
||||
Reference in New Issue
Block a user