Compare commits
10 Commits
v4.8.3
...
v4.8.4-fix
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c11131d653 | ||
|
|
f7f4a8de4d | ||
|
|
6385794603 | ||
|
|
b8b26ad700 | ||
|
|
05611df056 | ||
|
|
d0085a23e6 | ||
|
|
bc6864c3dc | ||
|
|
b20d075d35 | ||
|
|
19c8a06d51 | ||
|
|
fcb915c988 |
6
.vscode/nextapi.code-snippets
vendored
@@ -35,17 +35,19 @@
|
||||
"scope": "typescriptreact",
|
||||
"prefix": "context",
|
||||
"body": [
|
||||
"import { ReactNode } from 'react';",
|
||||
"import React, { ReactNode } from 'react';",
|
||||
"import { createContext } from 'use-context-selector';",
|
||||
"",
|
||||
"type ContextType = {$1};",
|
||||
"",
|
||||
"export const Context = createContext<ContextType>({});",
|
||||
"",
|
||||
"export const ContextProvider = ({ children }: { children: ReactNode }) => {",
|
||||
"const ContextProvider = ({ children }: { children: ReactNode }) => {",
|
||||
" const contextValue: ContextType = {};",
|
||||
" return <Context.Provider value={contextValue}>{children}</Context.Provider>;",
|
||||
"};",
|
||||
"",
|
||||
"export default ContextProvider"
|
||||
],
|
||||
"description": "FastGPT usecontext template"
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ weight: 708
|
||||
|
||||
**开发环境下**,你需要将示例配置文件 `config.json` 复制成 `config.local.json` 文件才会生效。
|
||||
|
||||
这个配置文件中包含了系统参数和各个模型配置,`使用时务必去掉注释!!!!!!!!!!!!!!`
|
||||
这个配置文件中包含了系统参数和各个模型配置:
|
||||
|
||||
## 4.6.8+ 版本新配置文件
|
||||
|
||||
@@ -158,7 +158,7 @@ llm模型全部合并
|
||||
|
||||
1. [部署 ReRank 模型](/docs/development/custom-models/bge-rerank/)
|
||||
1. 找到 FastGPT 的配置文件中的 `reRankModels`, 4.6.6 以前是 `ReRankModels`。
|
||||
2. 修改对应的值:(记得去掉注释)
|
||||
2. 修改对应的值:
|
||||
|
||||
```json
|
||||
{
|
||||
|
||||
@@ -204,7 +204,7 @@ docker restart oneapi
|
||||
|
||||
### Mongo 副本集自动初始化失败
|
||||
|
||||
最新的 docker-compose 示例优化 Mongo 副本集初始化,实现了全自动。目前在 unbuntu20,22 centos7, wsl2, mac, window 均通过测试。仍无法正常启动,大部分是因为 cpu 不支持 AUX 指令集,可以切换 Mongo4.x 版本。
|
||||
最新的 docker-compose 示例优化 Mongo 副本集初始化,实现了全自动。目前在 unbuntu20,22 centos7, wsl2, mac, window 均通过测试。仍无法正常启动,大部分是因为 cpu 不支持 AVX 指令集,可以切换 Mongo4.x 版本。
|
||||
|
||||
如果是由于,无法自动初始化副本集合,可以手动初始化副本集:
|
||||
|
||||
|
||||
@@ -79,6 +79,7 @@ Mongo 数据库需要注意,需要注意在连接地址中增加 `directConnec
|
||||
# 给自动化脚本代码执行权限(非 linux 系统, 可以手动执行里面的 postinstall.sh 文件内容)
|
||||
chmod -R +x ./scripts/
|
||||
# 代码根目录下执行,会安装根 package、projects 和 packages 内所有依赖
|
||||
# 如果提示 isolate-vm 安装失败,可以参考:https://github.com/laverdet/isolated-vm?tab=readme-ov-file#requirements
|
||||
pnpm i
|
||||
|
||||
# 非 Make 运行
|
||||
@@ -103,6 +104,8 @@ docker build -f ./projects/app/Dockerfile -t registry.cn-hangzhou.aliyuncs.com/f
|
||||
make build name=app image=registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.8.1 proxy=taobao
|
||||
```
|
||||
|
||||
如果不使用 `docker` 打包,需要手动把 `Dockerfile` 里 run 阶段的内容全部手动执行一遍(非常不推荐)。
|
||||
|
||||
## 提交代码至开源仓库
|
||||
|
||||
1. 确保你的代码是 Fork [FastGPT](https://github.com/labring/FastGPT) 仓库
|
||||
|
||||
38
docSite/content/zh-cn/docs/development/upgrading/484.md
Normal file
@@ -0,0 +1,38 @@
|
||||
---
|
||||
title: 'V4.8.4'
|
||||
description: 'FastGPT V4.8.4 更新说明'
|
||||
icon: 'upgrade'
|
||||
draft: false
|
||||
toc: true
|
||||
weight: 820
|
||||
---
|
||||
|
||||
## 升级指南
|
||||
|
||||
### 1. 修改镜像
|
||||
|
||||
- fastgpt 镜像 tag 修改成 v4.8.4
|
||||
- fastgpt-sandbox 镜像 tag 修改成 v4.8.4 (选择性,无变更)
|
||||
- 商业版镜像 tag 修改成 v4.8.4
|
||||
|
||||
### 2. 商业版用户执行初始化
|
||||
|
||||
从任意终端,发起 1 个 HTTP 请求。其中 {{rootkey}} 替换成环境变量里的 `rootkey`;{{host}} 替换成**FastGPT 商业版的域名**。
|
||||
|
||||
```bash
|
||||
curl --location --request POST 'https://{{host}}/api/admin/init/484' \
|
||||
--header 'rootkey: {{rootkey}}' \
|
||||
--header 'Content-Type: application/json'
|
||||
```
|
||||
|
||||
## V4.8.4 更新说明
|
||||
|
||||
1. 新增 - 应用使用新权限系统。
|
||||
2. 新增 - 应用支持文件夹。
|
||||
3. 优化 - 文本分割增加连续换行、制表符清除,避免大文本性能问题。
|
||||
4. 重要修复 - 修复系统插件运行池数据污染问题,由于从内存获取,会导致全局污染。
|
||||
5. 修复 - Debug 模式下,相同 source 和 target 内容,导致连线显示异常。
|
||||
6. 修复 - 定时执行初始化错误。
|
||||
7. 修复 - 应用调用传参异常。
|
||||
8. 修复 - ctrl + cv 复杂节点时,nodeId错误。
|
||||
9. 调整组件库全局theme。
|
||||
@@ -114,15 +114,15 @@ services:
|
||||
# fastgpt
|
||||
sandbox:
|
||||
container_name: sandbox
|
||||
image: ghcr.io/labring/fastgpt-sandbox:v4.8.3 # git
|
||||
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-sandbox:v4.8.3 # 阿里云
|
||||
image: ghcr.io/labring/fastgpt-sandbox:v4.8.4 # git
|
||||
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-sandbox:v4.8.4 # 阿里云
|
||||
networks:
|
||||
- fastgpt
|
||||
restart: always
|
||||
fastgpt:
|
||||
container_name: fastgpt
|
||||
image: ghcr.io/labring/fastgpt:v4.8.3 # git
|
||||
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.8.3 # 阿里云
|
||||
image: ghcr.io/labring/fastgpt:v4.8.4 # git
|
||||
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.8.4 # 阿里云
|
||||
ports:
|
||||
- 3000:3000
|
||||
networks:
|
||||
@@ -154,6 +154,8 @@ services:
|
||||
- MILVUS_TOKEN=none
|
||||
# sandbox 地址
|
||||
- SANDBOX_URL=http://sandbox:3000
|
||||
# 日志等级: debug, info, warn, error
|
||||
- LOG_LEVEL=info
|
||||
volumes:
|
||||
- ./config.json:/app/data/config.json
|
||||
|
||||
|
||||
@@ -72,15 +72,15 @@ services:
|
||||
# fastgpt
|
||||
sandbox:
|
||||
container_name: sandbox
|
||||
image: ghcr.io/labring/fastgpt-sandbox:v4.8.3 # git
|
||||
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-sandbox:v4.8.3 # 阿里云
|
||||
image: ghcr.io/labring/fastgpt-sandbox:v4.8.4 # git
|
||||
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-sandbox:v4.8.4 # 阿里云
|
||||
networks:
|
||||
- fastgpt
|
||||
restart: always
|
||||
fastgpt:
|
||||
container_name: fastgpt
|
||||
image: ghcr.io/labring/fastgpt:v4.8.3 # git
|
||||
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.8.3 # 阿里云
|
||||
image: ghcr.io/labring/fastgpt:v4.8.4 # git
|
||||
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.8.4 # 阿里云
|
||||
ports:
|
||||
- 3000:3000
|
||||
networks:
|
||||
@@ -111,6 +111,8 @@ services:
|
||||
- PG_URL=postgresql://username:password@pg:5432/postgres
|
||||
# sandbox 地址
|
||||
- SANDBOX_URL=http://sandbox:3000
|
||||
# 日志等级: debug, info, warn, error
|
||||
- LOG_LEVEL=info
|
||||
volumes:
|
||||
- ./config.json:/app/data/config.json
|
||||
|
||||
|
||||
@@ -53,15 +53,15 @@ services:
|
||||
wait $$!
|
||||
sandbox:
|
||||
container_name: sandbox
|
||||
image: ghcr.io/labring/fastgpt-sandbox:v4.8.3 # git
|
||||
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-sandbox:v4.8.3 # 阿里云
|
||||
image: ghcr.io/labring/fastgpt-sandbox:v4.8.4 # git
|
||||
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-sandbox:v4.8.4 # 阿里云
|
||||
networks:
|
||||
- fastgpt
|
||||
restart: always
|
||||
fastgpt:
|
||||
container_name: fastgpt
|
||||
image: ghcr.io/labring/fastgpt:v4.8.3 # git
|
||||
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.8.3 # 阿里云
|
||||
image: ghcr.io/labring/fastgpt:v4.8.4 # git
|
||||
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.8.4 # 阿里云
|
||||
ports:
|
||||
- 3000:3000
|
||||
networks:
|
||||
@@ -92,6 +92,8 @@ services:
|
||||
- MILVUS_TOKEN=zilliz_cloud_token
|
||||
# sandbox 地址
|
||||
- SANDBOX_URL=http://sandbox:3000
|
||||
# 日志等级: debug, info, warn, error
|
||||
- LOG_LEVEL=info
|
||||
volumes:
|
||||
- ./config.json:/app/data/config.json
|
||||
|
||||
|
||||
14
packages/global/common/parentFolder/type.d.ts
vendored
@@ -2,3 +2,17 @@ export type ParentTreePathItemType = {
|
||||
parentId: string;
|
||||
parentName: string;
|
||||
};
|
||||
export type ParentIdType = string | null | undefined;
|
||||
|
||||
export type GetResourceFolderListProps = {
|
||||
parentId: ParentIdType;
|
||||
};
|
||||
export type GetResourceFolderListItemResponse = {
|
||||
name: string;
|
||||
id: string;
|
||||
};
|
||||
|
||||
export type GetResourceListItemResponse = GetResourceFolderListItemResponse & {
|
||||
avatar: string;
|
||||
isFolder: boolean;
|
||||
};
|
||||
|
||||
17
packages/global/common/parentFolder/utils.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
import { ParentIdType } from './type';
|
||||
|
||||
export const parseParentIdInMongo = (parentId: ParentIdType) => {
|
||||
if (parentId === undefined) return {};
|
||||
|
||||
if (parentId === null || parentId === '')
|
||||
return {
|
||||
parentId: null
|
||||
};
|
||||
|
||||
const pattern = /^[0-9a-fA-F]{24}$/;
|
||||
if (pattern.test(parentId))
|
||||
return {
|
||||
parentId
|
||||
};
|
||||
return {};
|
||||
};
|
||||
@@ -102,6 +102,8 @@ const commonSplit = (props: SplitProps): SplitResponse => {
|
||||
text = text.replace(/(```[\s\S]*?```|~~~[\s\S]*?~~~)/g, function (match) {
|
||||
return match.replace(/\n/g, codeBlockMarker);
|
||||
});
|
||||
// replace invalid \n
|
||||
text = text.replace(/(\r?\n|\r){3,}/g, '\n\n\n');
|
||||
|
||||
// The larger maxLen is, the next sentence is less likely to trigger splitting
|
||||
const stepReges: { reg: RegExp; maxLen: number }[] = [
|
||||
@@ -338,7 +340,7 @@ const commonSplit = (props: SplitProps): SplitResponse => {
|
||||
*/
|
||||
export const splitText2Chunks = (props: SplitProps): SplitResponse => {
|
||||
let { text = '' } = props;
|
||||
|
||||
const start = Date.now();
|
||||
const splitWithCustomSign = text.split(CUSTOM_SPLIT_SIGN);
|
||||
|
||||
const splitResult = splitWithCustomSign.map((item) => {
|
||||
@@ -348,7 +350,7 @@ export const splitText2Chunks = (props: SplitProps): SplitResponse => {
|
||||
|
||||
return commonSplit(props);
|
||||
});
|
||||
|
||||
console.log(Date.now() - start);
|
||||
return {
|
||||
chunks: splitResult.map((item) => item.chunks).flat(),
|
||||
chars: splitResult.reduce((sum, item) => sum + item.chars, 0)
|
||||
|
||||
@@ -31,7 +31,6 @@ export type FastGPTFeConfigsType = {
|
||||
show_openai_account?: boolean;
|
||||
show_promotion?: boolean;
|
||||
show_team_chat?: boolean;
|
||||
hide_app_flow?: boolean;
|
||||
concatMd?: string;
|
||||
docUrl?: string;
|
||||
chatbotUrl?: string;
|
||||
|
||||
11
packages/global/core/app/collaborator.d.ts
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
import { UpdateClbPermissionProps } from '../../support/permission/collaborator';
|
||||
import { PermissionValueType } from '../../support/permission/type';
|
||||
|
||||
export type UpdateAppCollaboratorBody = UpdateClbPermissionProps & {
|
||||
appId: string;
|
||||
};
|
||||
|
||||
export type AppCollaboratorDeleteParams = {
|
||||
appId: string;
|
||||
tmbId: string;
|
||||
};
|
||||
@@ -1,10 +1,14 @@
|
||||
import { AppTTSConfigType, AppWhisperConfigType } from './type';
|
||||
|
||||
export enum AppTypeEnum {
|
||||
folder = 'folder',
|
||||
simple = 'simple',
|
||||
advanced = 'advanced'
|
||||
}
|
||||
export const AppTypeMap = {
|
||||
[AppTypeEnum.folder]: {
|
||||
label: 'folder'
|
||||
},
|
||||
[AppTypeEnum.simple]: {
|
||||
label: 'simple'
|
||||
},
|
||||
|
||||
20
packages/global/core/app/type.d.ts
vendored
@@ -1,5 +1,4 @@
|
||||
import type { FlowNodeTemplateType, StoreNodeItemType } from '../workflow/type';
|
||||
|
||||
import { AppTypeEnum } from './constants';
|
||||
import { PermissionTypeEnum } from '../../support/permission/constant';
|
||||
import { VariableInputEnum } from '../workflow/constants';
|
||||
@@ -7,14 +6,19 @@ import { SelectedDatasetType } from '../workflow/api';
|
||||
import { DatasetSearchModeEnum } from '../dataset/constants';
|
||||
import { TeamTagSchema as TeamTagsSchemaType } from '@fastgpt/global/support/user/team/type.d';
|
||||
import { StoreEdgeItemType } from '../workflow/type/edge';
|
||||
import { PermissionValueType } from '../../support/permission/type';
|
||||
import { AppPermission } from '../../support/permission/app/controller';
|
||||
import { ParentIdType } from '../../common/parentFolder/type';
|
||||
|
||||
export type AppSchema = {
|
||||
_id: string;
|
||||
parentId?: ParentIdType;
|
||||
teamId: string;
|
||||
tmbId: string;
|
||||
name: string;
|
||||
type: `${AppTypeEnum}`;
|
||||
type: AppTypeEnum;
|
||||
version?: 'v1' | 'v2';
|
||||
|
||||
name: string;
|
||||
avatar: string;
|
||||
intro: string;
|
||||
updateTime: number;
|
||||
@@ -27,9 +31,9 @@ export type AppSchema = {
|
||||
scheduledTriggerConfig?: AppScheduledTriggerConfigType | null;
|
||||
scheduledTriggerNextTime?: Date;
|
||||
|
||||
permission: `${PermissionTypeEnum}`;
|
||||
inited?: boolean;
|
||||
teamTags: string[];
|
||||
defaultPermission: PermissionValueType;
|
||||
};
|
||||
|
||||
export type AppListItemType = {
|
||||
@@ -37,13 +41,13 @@ export type AppListItemType = {
|
||||
name: string;
|
||||
avatar: string;
|
||||
intro: string;
|
||||
isOwner: boolean;
|
||||
permission: `${PermissionTypeEnum}`;
|
||||
type: AppTypeEnum;
|
||||
defaultPermission: PermissionValueType;
|
||||
permission: AppPermission;
|
||||
};
|
||||
|
||||
export type AppDetailType = AppSchema & {
|
||||
isOwner: boolean;
|
||||
canWrite: boolean;
|
||||
permission: AppPermission;
|
||||
};
|
||||
|
||||
export type AppSimpleEditFormType = {
|
||||
|
||||
@@ -21,7 +21,7 @@ export const getDefaultAppForm = (): AppSimpleEditFormType => ({
|
||||
limit: 1500,
|
||||
searchMode: DatasetSearchModeEnum.embedding,
|
||||
usingReRank: false,
|
||||
datasetSearchUsingExtensionQuery: true,
|
||||
datasetSearchUsingExtensionQuery: false,
|
||||
datasetSearchExtensionBg: ''
|
||||
},
|
||||
selectedTools: [],
|
||||
|
||||
@@ -35,7 +35,10 @@ export const RunAppModule: FlowNodeTemplateType = {
|
||||
required: true
|
||||
},
|
||||
Input_Template_History,
|
||||
Input_Template_UserChatInput
|
||||
{
|
||||
...Input_Template_UserChatInput,
|
||||
toolDescription: '用户问题'
|
||||
}
|
||||
],
|
||||
outputs: [
|
||||
{
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { FlowNodeTypeEnum } from '../../node/constant';
|
||||
import { FlowNodeTemplateType } from '../../type/index.d';
|
||||
import { FlowNodeTemplateTypeEnum, WorkflowIOValueTypeEnum } from '../../constants';
|
||||
import { FlowNodeTemplateTypeEnum } from '../../constants';
|
||||
import { getHandleConfig } from '../utils';
|
||||
|
||||
export const SystemConfigNode: FlowNodeTemplateType = {
|
||||
|
||||
@@ -20,6 +20,7 @@ import { RuntimeNodeItemType } from '../runtime/type';
|
||||
import { PluginTypeEnum } from '../../plugin/constants';
|
||||
import { RuntimeEdgeItemType, StoreEdgeItemType } from './edge';
|
||||
import { NextApiResponse } from 'next';
|
||||
import { AppDetailType, AppSchema } from '../../app/type';
|
||||
|
||||
export type FlowNodeCommonType = {
|
||||
flowNodeType: FlowNodeTypeEnum; // render node card
|
||||
@@ -105,8 +106,8 @@ export type NodeSourceNodeItemType = {
|
||||
/* --------------- function type -------------------- */
|
||||
export type SelectAppItemType = {
|
||||
id: string;
|
||||
name: string;
|
||||
logo: string;
|
||||
// name: string;
|
||||
// logo?: string;
|
||||
};
|
||||
|
||||
/* agent */
|
||||
@@ -131,7 +132,7 @@ export type ChatDispatchProps = {
|
||||
teamId: string;
|
||||
tmbId: string;
|
||||
user: UserModelSchema;
|
||||
appId?: string;
|
||||
app: AppDetailType | AppSchema;
|
||||
chatId?: string;
|
||||
responseChatItemId?: string;
|
||||
histories: ChatItemType[];
|
||||
|
||||
20
packages/global/support/permission/app/constant.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
import { NullPermission, PermissionKeyEnum, PermissionList } from '../constant';
|
||||
import { PermissionListType } from '../type';
|
||||
|
||||
export enum AppPermissionKeyEnum {}
|
||||
export const AppPermissionList: PermissionListType = {
|
||||
[PermissionKeyEnum.read]: {
|
||||
...PermissionList[PermissionKeyEnum.read],
|
||||
description: '可使用该应用进行对话'
|
||||
},
|
||||
[PermissionKeyEnum.write]: {
|
||||
...PermissionList[PermissionKeyEnum.write],
|
||||
description: '可查看和编辑应用'
|
||||
},
|
||||
[PermissionKeyEnum.manage]: {
|
||||
...PermissionList[PermissionKeyEnum.manage],
|
||||
description: '写权限基础上,可配置发布渠道、查看对话日志、分配该应用权限'
|
||||
}
|
||||
};
|
||||
|
||||
export const AppDefaultPermissionVal = NullPermission;
|
||||
15
packages/global/support/permission/app/controller.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import { PerConstructPros, Permission } from '../controller';
|
||||
import { AppDefaultPermissionVal } from './constant';
|
||||
|
||||
export class AppPermission extends Permission {
|
||||
constructor(props?: PerConstructPros) {
|
||||
if (!props) {
|
||||
props = {
|
||||
per: AppDefaultPermissionVal
|
||||
};
|
||||
} else if (!props?.per) {
|
||||
props.per = AppDefaultPermissionVal;
|
||||
}
|
||||
super(props);
|
||||
}
|
||||
}
|
||||
0
packages/global/support/permission/app/type.d.ts
vendored
Normal file
15
packages/global/support/permission/collaborator.d.ts
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
import { Permission } from './controller';
|
||||
import { PermissionValueType } from './type';
|
||||
|
||||
export type CollaboratorItemType = {
|
||||
teamId: string;
|
||||
tmbId: string;
|
||||
permission: Permission;
|
||||
name: string;
|
||||
avatar: string;
|
||||
};
|
||||
|
||||
export type UpdateClbPermissionProps = {
|
||||
tmbIds: string[];
|
||||
permission: PermissionValueType;
|
||||
};
|
||||
@@ -1,3 +1,6 @@
|
||||
import { Permission } from './controller';
|
||||
import { PermissionListType } from './type';
|
||||
|
||||
export enum AuthUserTypeEnum {
|
||||
token = 'token',
|
||||
root = 'root',
|
||||
@@ -8,7 +11,10 @@ export enum AuthUserTypeEnum {
|
||||
|
||||
export enum PermissionTypeEnum {
|
||||
'private' = 'private',
|
||||
'public' = 'public'
|
||||
'public' = 'public',
|
||||
clbPrivate = 'clbPrivate',
|
||||
publicRead = 'publicRead',
|
||||
publicWrite = 'publicWrite'
|
||||
}
|
||||
export const PermissionTypeMap = {
|
||||
[PermissionTypeEnum.private]: {
|
||||
@@ -18,11 +24,56 @@ export const PermissionTypeMap = {
|
||||
[PermissionTypeEnum.public]: {
|
||||
iconLight: 'support/permission/publicLight',
|
||||
label: 'permission.Public'
|
||||
},
|
||||
[PermissionTypeEnum.publicRead]: {
|
||||
iconLight: 'support/permission/publicLight',
|
||||
label: '团队可访问'
|
||||
},
|
||||
[PermissionTypeEnum.publicWrite]: {
|
||||
iconLight: 'support/permission/publicLight',
|
||||
label: '团队可编辑'
|
||||
},
|
||||
[PermissionTypeEnum.clbPrivate]: {
|
||||
iconLight: 'support/permission/privateLight',
|
||||
label: '仅协作者'
|
||||
}
|
||||
};
|
||||
|
||||
export enum ResourceTypeEnum {
|
||||
export enum PerResourceTypeEnum {
|
||||
team = 'team',
|
||||
app = 'app',
|
||||
dataset = 'dataset'
|
||||
}
|
||||
|
||||
/* new permission */
|
||||
export enum PermissionKeyEnum {
|
||||
read = 'read',
|
||||
write = 'write',
|
||||
manage = 'manage'
|
||||
}
|
||||
export const PermissionList: PermissionListType = {
|
||||
[PermissionKeyEnum.read]: {
|
||||
name: '读权限',
|
||||
description: '',
|
||||
value: 0b100,
|
||||
checkBoxType: 'single'
|
||||
},
|
||||
[PermissionKeyEnum.write]: {
|
||||
name: '写权限',
|
||||
description: '',
|
||||
value: 0b110, // 如果某个资源有特殊要求,再重写这个值
|
||||
checkBoxType: 'single'
|
||||
},
|
||||
[PermissionKeyEnum.manage]: {
|
||||
name: '管理员',
|
||||
description: '',
|
||||
value: 0b111,
|
||||
checkBoxType: 'single'
|
||||
}
|
||||
};
|
||||
|
||||
export const NullPermission = 0;
|
||||
export const OwnerPermissionVal = ~0 >>> 0;
|
||||
export const ReadPermissionVal = PermissionList['read'].value;
|
||||
export const WritePermissionVal = PermissionList['write'].value;
|
||||
export const ManagePermissionVal = PermissionList['manage'].value;
|
||||
|
||||
71
packages/global/support/permission/controller.ts
Normal file
@@ -0,0 +1,71 @@
|
||||
import { PermissionValueType } from './type';
|
||||
import { PermissionList, NullPermission, OwnerPermissionVal } from './constant';
|
||||
|
||||
export type PerConstructPros = {
|
||||
per?: PermissionValueType;
|
||||
isOwner?: boolean;
|
||||
};
|
||||
|
||||
// the Permission helper class
|
||||
export class Permission {
|
||||
value: PermissionValueType;
|
||||
isOwner: boolean;
|
||||
hasManagePer: boolean;
|
||||
hasWritePer: boolean;
|
||||
hasReadPer: boolean;
|
||||
|
||||
constructor(props?: PerConstructPros) {
|
||||
const { per = NullPermission, isOwner = false } = props || {};
|
||||
if (isOwner) {
|
||||
this.value = OwnerPermissionVal;
|
||||
} else {
|
||||
this.value = per;
|
||||
}
|
||||
|
||||
this.isOwner = isOwner;
|
||||
this.hasManagePer = this.checkPer(PermissionList['manage'].value);
|
||||
this.hasWritePer = this.checkPer(PermissionList['write'].value);
|
||||
this.hasReadPer = this.checkPer(PermissionList['read'].value);
|
||||
}
|
||||
|
||||
// add permission(s)
|
||||
// it can be chaining called.
|
||||
// @example
|
||||
// const perm = new Permission(permission)
|
||||
// perm.add(PermissionList['read'])
|
||||
// perm.add(PermissionList['read'], PermissionList['write'])
|
||||
// perm.add(PermissionList['read']).add(PermissionList['write'])
|
||||
addPer(...perList: PermissionValueType[]) {
|
||||
for (let oer of perList) {
|
||||
this.value = this.value | oer;
|
||||
}
|
||||
this.updatePermissions();
|
||||
return this.value;
|
||||
}
|
||||
|
||||
removePer(...perList: PermissionValueType[]) {
|
||||
for (let per of perList) {
|
||||
this.value = this.value & ~per;
|
||||
}
|
||||
this.updatePermissions();
|
||||
return this.value;
|
||||
}
|
||||
|
||||
checkPer(perm: PermissionValueType): boolean {
|
||||
// if the permission is owner permission, only owner has this permission.
|
||||
if (perm === OwnerPermissionVal) {
|
||||
return this.value === OwnerPermissionVal;
|
||||
} else if (this.hasManagePer) {
|
||||
// The manager has all permissions except the owner permission
|
||||
return true;
|
||||
}
|
||||
return (this.value & perm) === perm;
|
||||
}
|
||||
|
||||
private updatePermissions() {
|
||||
this.isOwner = this.value === OwnerPermissionVal;
|
||||
this.hasManagePer = this.checkPer(PermissionList['manage'].value);
|
||||
this.hasWritePer = this.checkPer(PermissionList['write'].value);
|
||||
this.hasReadPer = this.checkPer(PermissionList['read'].value);
|
||||
}
|
||||
}
|
||||
21
packages/global/support/permission/type.d.ts
vendored
@@ -1,6 +1,20 @@
|
||||
import { AuthUserTypeEnum } from './constant';
|
||||
import { TeamMemberWithUserSchema } from '../user/team/type';
|
||||
import { AuthUserTypeEnum, PermissionKeyEnum } from './constant';
|
||||
|
||||
// PermissionValueType, the type of permission's value is a number, which is a bit field actually.
|
||||
// It is spired by the permission system in Linux.
|
||||
// The lowest 3 bits present the permission of reading, writing and managing.
|
||||
// The higher bits are advanced permissions or extended permissions, which could be customized.
|
||||
export type PermissionValueType = number;
|
||||
export type PermissionListType<T = {}> = Record<
|
||||
T | PermissionKeyEnum,
|
||||
{
|
||||
name: string;
|
||||
description: string;
|
||||
value: PermissionValueType;
|
||||
checkBoxType: 'single' | 'multiple';
|
||||
}
|
||||
>;
|
||||
|
||||
export type AuthResponseType = {
|
||||
teamId: string;
|
||||
@@ -17,4 +31,9 @@ export type ResourcePermissionType = {
|
||||
tmbId: string;
|
||||
resourceType: ResourceType;
|
||||
permission: PermissionValueType;
|
||||
resourceId: string;
|
||||
};
|
||||
|
||||
export type ResourcePerWithTmbWithUser = Omit<ResourcePermissionType, 'tmbId'> & {
|
||||
tmbId: TeamMemberWithUserSchema;
|
||||
};
|
||||
|
||||
19
packages/global/support/permission/user/constant.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
import { PermissionKeyEnum, PermissionList, ReadPermissionVal } from '../constant';
|
||||
import { PermissionListType } from '../type';
|
||||
|
||||
export const TeamPermissionList: PermissionListType = {
|
||||
[PermissionKeyEnum.read]: {
|
||||
...PermissionList[PermissionKeyEnum.read],
|
||||
description: '成员仅可阅读相关资源,无法新建资源'
|
||||
},
|
||||
[PermissionKeyEnum.write]: {
|
||||
...PermissionList[PermissionKeyEnum.write],
|
||||
description: '除了可读资源外,还可以新建新的资源'
|
||||
},
|
||||
[PermissionKeyEnum.manage]: {
|
||||
...PermissionList[PermissionKeyEnum.manage],
|
||||
description: '可创建资源、邀请、删除成员'
|
||||
}
|
||||
};
|
||||
|
||||
export const TeamDefaultPermissionVal = ReadPermissionVal;
|
||||
15
packages/global/support/permission/user/controller.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import { PerConstructPros, Permission } from '../controller';
|
||||
import { TeamDefaultPermissionVal } from './constant';
|
||||
|
||||
export class TeamPermission extends Permission {
|
||||
constructor(props?: PerConstructPros) {
|
||||
if (!props) {
|
||||
props = {
|
||||
per: TeamDefaultPermissionVal
|
||||
};
|
||||
} else if (!props?.per) {
|
||||
props.per = TeamDefaultPermissionVal;
|
||||
}
|
||||
super(props);
|
||||
}
|
||||
}
|
||||
@@ -1,22 +1,25 @@
|
||||
import { TeamMemberRoleEnum } from '../user/team/constant';
|
||||
import { PermissionTypeEnum } from './constant';
|
||||
import { Permission } from './controller';
|
||||
|
||||
/* team public source, or owner source in team */
|
||||
export function mongoRPermission({
|
||||
teamId,
|
||||
tmbId,
|
||||
role
|
||||
permission
|
||||
}: {
|
||||
teamId: string;
|
||||
tmbId: string;
|
||||
role: `${TeamMemberRoleEnum}`;
|
||||
permission: Permission;
|
||||
}) {
|
||||
if (permission.isOwner) {
|
||||
return {
|
||||
teamId
|
||||
};
|
||||
}
|
||||
return {
|
||||
teamId,
|
||||
...(role === TeamMemberRoleEnum.visitor && { permission: PermissionTypeEnum.public }),
|
||||
...(role === TeamMemberRoleEnum.admin && {
|
||||
$or: [{ permission: PermissionTypeEnum.public }, { tmbId }]
|
||||
})
|
||||
$or: [{ permission: PermissionTypeEnum.public }, { tmbId }]
|
||||
};
|
||||
}
|
||||
export function mongoOwnerPermission({ teamId, tmbId }: { teamId: string; tmbId: string }) {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { PermissionValueType } from 'support/permission/type';
|
||||
import { PermissionValueType } from '../../permission/type';
|
||||
import { TeamMemberRoleEnum } from './constant';
|
||||
import { LafAccountType, TeamMemberSchema } from './type';
|
||||
|
||||
@@ -22,8 +22,7 @@ export type UpdateTeamProps = {
|
||||
|
||||
/* ------------- member ----------- */
|
||||
export type DelMemberProps = {
|
||||
teamId: string;
|
||||
memberId: string;
|
||||
tmbId: string;
|
||||
};
|
||||
export type UpdateTeamMemberProps = {
|
||||
teamId: string;
|
||||
@@ -34,7 +33,7 @@ export type UpdateTeamMemberProps = {
|
||||
export type InviteMemberProps = {
|
||||
teamId: string;
|
||||
usernames: string[];
|
||||
role: `${TeamMemberRoleEnum}`;
|
||||
permission: PermissionValueType;
|
||||
};
|
||||
export type UpdateInviteProps = {
|
||||
tmbId: string;
|
||||
@@ -44,9 +43,3 @@ export type InviteMemberResponse = Record<
|
||||
'invite' | 'inValid' | 'inTeam',
|
||||
{ username: string; userId: string }[]
|
||||
>;
|
||||
|
||||
export type UpdateTeamMemberPermissionProps = {
|
||||
teamId: string;
|
||||
memberIds: string[];
|
||||
permission: PermissionValueType;
|
||||
};
|
||||
|
||||
8
packages/global/support/user/team/type.d.ts
vendored
@@ -2,6 +2,7 @@ import type { UserModelSchema } from '../type';
|
||||
import type { TeamMemberRoleEnum, TeamMemberStatusEnum } from './constant';
|
||||
import { LafAccountType } from './type';
|
||||
import { PermissionValueType, ResourcePermissionType } from '../../permission/type';
|
||||
import { TeamPermission } from '../../permission/user/controller';
|
||||
|
||||
export type TeamSchema = {
|
||||
_id: string;
|
||||
@@ -49,7 +50,7 @@ export type TeamMemberWithTeamAndUserSchema = Omit<TeamMemberWithTeamSchema, 'us
|
||||
userId: UserModelSchema;
|
||||
};
|
||||
|
||||
export type TeamItemType = {
|
||||
export type TeamTmbItemType = {
|
||||
userId: string;
|
||||
teamId: string;
|
||||
teamName: string;
|
||||
@@ -61,9 +62,8 @@ export type TeamItemType = {
|
||||
defaultTeam: boolean;
|
||||
role: `${TeamMemberRoleEnum}`;
|
||||
status: `${TeamMemberStatusEnum}`;
|
||||
canWrite: boolean;
|
||||
lafAccount?: LafAccountType;
|
||||
defaultPermission: PermissionValueType;
|
||||
permission: TeamPermission;
|
||||
};
|
||||
|
||||
export type TeamMemberItemType = {
|
||||
@@ -75,7 +75,7 @@ export type TeamMemberItemType = {
|
||||
// TODO: this should be deprecated.
|
||||
role: `${TeamMemberRoleEnum}`;
|
||||
status: `${TeamMemberStatusEnum}`;
|
||||
permission: PermissionValueType;
|
||||
permission: TeamPermission;
|
||||
};
|
||||
|
||||
export type TeamTagItemType = {
|
||||
|
||||
4
packages/global/support/user/type.d.ts
vendored
@@ -1,5 +1,5 @@
|
||||
import { UserStatusEnum } from './constant';
|
||||
import { TeamItemType } from './team/type';
|
||||
import { TeamTmbItemType } from './team/type';
|
||||
|
||||
export type UserModelSchema = {
|
||||
_id: string;
|
||||
@@ -29,6 +29,6 @@ export type UserType = {
|
||||
timezone: string;
|
||||
promotionRate: UserModelSchema['promotionRate'];
|
||||
openaiAccount: UserModelSchema['openaiAccount'];
|
||||
team: TeamItemType;
|
||||
team: TeamTmbItemType;
|
||||
standardInfo?: standardInfoType;
|
||||
};
|
||||
|
||||
@@ -8,7 +8,7 @@ import { detectFileEncoding } from '@fastgpt/global/common/file/tools';
|
||||
import { CommonErrEnum } from '@fastgpt/global/common/error/code/common';
|
||||
import { MongoRawTextBuffer } from '../../buffer/rawText/schema';
|
||||
import { readRawContentByFileBuffer } from '../read/utils';
|
||||
import { PassThrough } from 'stream';
|
||||
import { gridFsStream2Buffer } from './utils';
|
||||
|
||||
export function getGFSCollection(bucket: `${BucketNameEnum}`) {
|
||||
MongoFileSchema;
|
||||
@@ -113,35 +113,16 @@ export async function getDownloadStream({
|
||||
fileId: string;
|
||||
}) {
|
||||
const bucket = getGridBucket(bucketName);
|
||||
const stream = bucket.openDownloadStream(new Types.ObjectId(fileId));
|
||||
const copyStream = stream.pipe(new PassThrough());
|
||||
const encodeStream = bucket.openDownloadStream(new Types.ObjectId(fileId), { end: 100 });
|
||||
const rawStream = bucket.openDownloadStream(new Types.ObjectId(fileId));
|
||||
|
||||
/* get encoding */
|
||||
const buffer = await (() => {
|
||||
return new Promise<Buffer>((resolve, reject) => {
|
||||
let tmpBuffer: Buffer = Buffer.from([]);
|
||||
|
||||
stream.on('data', (chunk) => {
|
||||
if (tmpBuffer.length < 20) {
|
||||
tmpBuffer = Buffer.concat([tmpBuffer, chunk]);
|
||||
}
|
||||
if (tmpBuffer.length >= 20) {
|
||||
resolve(tmpBuffer);
|
||||
}
|
||||
});
|
||||
stream.on('end', () => {
|
||||
resolve(tmpBuffer);
|
||||
});
|
||||
stream.on('error', (err) => {
|
||||
reject(err);
|
||||
});
|
||||
});
|
||||
})();
|
||||
const buffer = await gridFsStream2Buffer(encodeStream);
|
||||
|
||||
const encoding = detectFileEncoding(buffer);
|
||||
|
||||
return {
|
||||
fileStream: copyStream,
|
||||
fileStream: rawStream,
|
||||
encoding
|
||||
// encoding: 'utf-8'
|
||||
};
|
||||
@@ -169,32 +150,21 @@ export const readFileContentFromMongo = async ({
|
||||
filename: fileBuffer.metadata?.filename || ''
|
||||
};
|
||||
}
|
||||
const start = Date.now();
|
||||
|
||||
const [file, { encoding, fileStream }] = await Promise.all([
|
||||
getFileById({ bucketName, fileId }),
|
||||
getDownloadStream({ bucketName, fileId })
|
||||
]);
|
||||
|
||||
// console.log('get file stream', Date.now() - start);
|
||||
if (!file) {
|
||||
return Promise.reject(CommonErrEnum.fileNotFound);
|
||||
}
|
||||
|
||||
const extension = file?.filename?.split('.')?.pop()?.toLowerCase() || '';
|
||||
|
||||
const fileBuffers = await (() => {
|
||||
return new Promise<Buffer>((resolve, reject) => {
|
||||
let buffer = Buffer.from([]);
|
||||
fileStream.on('data', (chunk) => {
|
||||
buffer = Buffer.concat([buffer, chunk]);
|
||||
});
|
||||
fileStream.on('end', () => {
|
||||
resolve(buffer);
|
||||
});
|
||||
fileStream.on('error', (err) => {
|
||||
reject(err);
|
||||
});
|
||||
});
|
||||
})();
|
||||
const fileBuffers = await gridFsStream2Buffer(fileStream);
|
||||
// console.log('get file buffer', Date.now() - start);
|
||||
|
||||
const { rawText } = await readRawContentByFileBuffer({
|
||||
extension,
|
||||
|
||||
15
packages/service/common/file/gridfs/utils.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
export const gridFsStream2Buffer = (stream: NodeJS.ReadableStream) => {
|
||||
return new Promise<Buffer>((resolve, reject) => {
|
||||
let tmpBuffer: Buffer = Buffer.from([]);
|
||||
|
||||
stream.on('data', (chunk) => {
|
||||
tmpBuffer = Buffer.concat([tmpBuffer, chunk]);
|
||||
});
|
||||
stream.on('end', () => {
|
||||
resolve(tmpBuffer);
|
||||
});
|
||||
stream.on('error', (err) => {
|
||||
reject(err);
|
||||
});
|
||||
});
|
||||
};
|
||||
@@ -1,17 +1,24 @@
|
||||
import { addLog } from '../system/log';
|
||||
import { connectionMongo, ClientSession } from './index';
|
||||
|
||||
export const mongoSessionRun = async <T = unknown>(fn: (session: ClientSession) => Promise<T>) => {
|
||||
const session = await connectionMongo.startSession();
|
||||
let committed = false;
|
||||
|
||||
try {
|
||||
session.startTransaction();
|
||||
const result = await fn(session);
|
||||
|
||||
await session.commitTransaction();
|
||||
committed = true;
|
||||
|
||||
return result as T;
|
||||
} catch (error) {
|
||||
await session.abortTransaction();
|
||||
if (!committed) {
|
||||
await session.abortTransaction();
|
||||
} else {
|
||||
addLog.warn('Un catch mongo session error', { error });
|
||||
}
|
||||
return Promise.reject(error);
|
||||
} finally {
|
||||
await session.endSession();
|
||||
|
||||
@@ -70,7 +70,7 @@ export const countGptMessagesTokens = (
|
||||
|
||||
callbackMap[id] = (data) => {
|
||||
// 检测是否有内存泄漏
|
||||
addLog.info(`Count token time: ${Date.now() - start}, token: ${data}`);
|
||||
addLog.debug(`Count token time: ${Date.now() - start}, token: ${data}`);
|
||||
// console.log(process.memoryUsage());
|
||||
|
||||
resolve(data);
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import dayjs from 'dayjs';
|
||||
import chalk from 'chalk';
|
||||
import { isProduction } from './constants';
|
||||
|
||||
enum LogLevelEnum {
|
||||
debug = 'debug',
|
||||
info = 'info',
|
||||
warn = 'warn',
|
||||
error = 'error'
|
||||
debug = 0,
|
||||
info = 1,
|
||||
warn = 2,
|
||||
error = 3
|
||||
}
|
||||
const logMap = {
|
||||
[LogLevelEnum.debug]: {
|
||||
@@ -21,20 +22,35 @@ const logMap = {
|
||||
levelLog: chalk.red('[Error]')
|
||||
}
|
||||
};
|
||||
const envLogLevelMap: Record<string, number> = {
|
||||
debug: 0,
|
||||
info: 1,
|
||||
warn: 2,
|
||||
error: 3
|
||||
};
|
||||
|
||||
const logLevel = (() => {
|
||||
if (!isProduction) return LogLevelEnum.debug;
|
||||
const envLogLevel = (process.env.LOG_LEVEL || 'info').toLocaleLowerCase();
|
||||
if (!envLogLevel || envLogLevelMap[envLogLevel] === undefined) return LogLevelEnum.info;
|
||||
return envLogLevelMap[envLogLevel];
|
||||
})();
|
||||
|
||||
/* add logger */
|
||||
export const addLog = {
|
||||
log(level: LogLevelEnum, msg: string, obj: Record<string, any> = {}) {
|
||||
if (level < logLevel) return;
|
||||
|
||||
const stringifyObj = JSON.stringify(obj);
|
||||
const isEmpty = Object.keys(obj).length === 0;
|
||||
|
||||
console.log(
|
||||
`${logMap[level].levelLog} ${dayjs().format('YYYY-MM-DD HH:mm:ss')} ${msg} ${
|
||||
level !== 'error' && !isEmpty ? stringifyObj : ''
|
||||
level !== LogLevelEnum.error && !isEmpty ? stringifyObj : ''
|
||||
}`
|
||||
);
|
||||
|
||||
level === 'error' && console.error(obj);
|
||||
level === LogLevelEnum.error && console.error(obj);
|
||||
|
||||
const lokiUrl = process.env.LOKI_LOG_URL as string;
|
||||
if (!lokiUrl) return;
|
||||
|
||||
@@ -3,6 +3,7 @@ import { NodeInputKeyEnum } from '@fastgpt/global/core/workflow/constants';
|
||||
import { FlowNodeTypeEnum } from '@fastgpt/global/core/workflow/node/constant';
|
||||
import { getLLMModel } from '../ai/model';
|
||||
import { MongoAppVersion } from './version/schema';
|
||||
import { MongoApp } from './schema';
|
||||
|
||||
export const beforeUpdateAppFormat = <T extends AppSchema['modules'] | undefined>({
|
||||
nodes
|
||||
@@ -65,3 +66,40 @@ export const getAppLatestVersion = async (appId: string, app?: AppSchema) => {
|
||||
chatConfig: app?.chatConfig || {}
|
||||
};
|
||||
};
|
||||
|
||||
/* Get apps */
|
||||
export async function findAppAndAllChildren({
|
||||
teamId,
|
||||
appId,
|
||||
fields
|
||||
}: {
|
||||
teamId: string;
|
||||
appId: string;
|
||||
fields?: string;
|
||||
}): Promise<AppSchema[]> {
|
||||
const find = async (id: string) => {
|
||||
const children = await MongoApp.find(
|
||||
{
|
||||
teamId,
|
||||
parentId: id
|
||||
},
|
||||
fields
|
||||
).lean();
|
||||
|
||||
let apps = children;
|
||||
|
||||
for (const child of children) {
|
||||
const grandChildrenIds = await find(child._id);
|
||||
apps = apps.concat(grandChildrenIds);
|
||||
}
|
||||
|
||||
return apps;
|
||||
};
|
||||
const [app, childDatasets] = await Promise.all([MongoApp.findById(appId, fields), find(appId)]);
|
||||
|
||||
if (!app) {
|
||||
return Promise.reject('Dataset not found');
|
||||
}
|
||||
|
||||
return [app, ...childDatasets];
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { AppTypeMap } from '@fastgpt/global/core/app/constants';
|
||||
import { AppTypeEnum, AppTypeMap } from '@fastgpt/global/core/app/constants';
|
||||
import { connectionMongo, type Model } from '../../common/mongo';
|
||||
const { Schema, model, models } = connectionMongo;
|
||||
import type { AppSchema as AppType } from '@fastgpt/global/core/app/type.d';
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
TeamCollectionName,
|
||||
TeamMemberCollectionName
|
||||
} from '@fastgpt/global/support/user/team/constant';
|
||||
import { AppDefaultPermissionVal } from '@fastgpt/global/support/permission/app/constant';
|
||||
|
||||
export const AppCollectionName = 'apps';
|
||||
|
||||
@@ -21,6 +22,11 @@ export const chatConfigType = {
|
||||
};
|
||||
|
||||
const AppSchema = new Schema({
|
||||
parentId: {
|
||||
type: Schema.Types.ObjectId,
|
||||
ref: AppCollectionName,
|
||||
default: null
|
||||
},
|
||||
teamId: {
|
||||
type: Schema.Types.ObjectId,
|
||||
ref: TeamCollectionName,
|
||||
@@ -37,8 +43,8 @@ const AppSchema = new Schema({
|
||||
},
|
||||
type: {
|
||||
type: String,
|
||||
default: 'advanced',
|
||||
enum: Object.keys(AppTypeMap)
|
||||
default: AppTypeEnum.advanced,
|
||||
enum: Object.values(AppTypeEnum)
|
||||
},
|
||||
version: {
|
||||
type: String,
|
||||
@@ -98,12 +104,18 @@ const AppSchema = new Schema({
|
||||
|
||||
inited: {
|
||||
type: Boolean
|
||||
},
|
||||
|
||||
// the default permission of a app
|
||||
defaultPermission: {
|
||||
type: Number,
|
||||
default: AppDefaultPermissionVal
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
AppSchema.index({ updateTime: -1 });
|
||||
AppSchema.index({ teamId: 1 });
|
||||
AppSchema.index({ teamId: 1, type: 1 });
|
||||
AppSchema.index({ scheduledTriggerConfig: 1, intervalNextTime: -1 });
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
|
||||
@@ -142,17 +142,17 @@ export const delCollectionRelatedSource = async ({
|
||||
.map((item) => item?.metadata?.relatedImgId || '')
|
||||
.filter(Boolean);
|
||||
|
||||
// delete files
|
||||
await delFileByFileIdList({
|
||||
bucketName: BucketNameEnum.dataset,
|
||||
fileIdList
|
||||
});
|
||||
// delete images
|
||||
await delImgByRelatedId({
|
||||
teamId,
|
||||
relateIds: relatedImageIds,
|
||||
session
|
||||
});
|
||||
// delete files
|
||||
await delFileByFileIdList({
|
||||
bucketName: BucketNameEnum.dataset,
|
||||
fileIdList
|
||||
});
|
||||
};
|
||||
/**
|
||||
* delete collection and it related data
|
||||
@@ -182,14 +182,16 @@ export async function delCollectionAndRelatedSources({
|
||||
);
|
||||
const collectionIds = collections.map((item) => String(item._id));
|
||||
|
||||
await delCollectionRelatedSource({ collections, session });
|
||||
|
||||
// delete training data
|
||||
await MongoDatasetTraining.deleteMany({
|
||||
teamId,
|
||||
datasetIds: { $in: datasetIds },
|
||||
collectionId: { $in: collectionIds }
|
||||
});
|
||||
|
||||
/* file and imgs */
|
||||
await delCollectionRelatedSource({ collections, session });
|
||||
|
||||
// delete dataset.datas
|
||||
await MongoDatasetData.deleteMany(
|
||||
{ teamId, datasetIds: { $in: datasetIds }, collectionId: { $in: collectionIds } },
|
||||
@@ -199,6 +201,7 @@ export async function delCollectionAndRelatedSources({
|
||||
// delete collections
|
||||
await MongoDatasetCollection.deleteMany(
|
||||
{
|
||||
teamId,
|
||||
_id: { $in: collectionIds }
|
||||
},
|
||||
{ session }
|
||||
|
||||
@@ -42,7 +42,7 @@ export async function findCollectionAndChild({
|
||||
return collections;
|
||||
}
|
||||
const [collection, childCollections] = await Promise.all([
|
||||
MongoDatasetCollection.findById(collectionId, fields),
|
||||
MongoDatasetCollection.findById(collectionId, fields).lean(),
|
||||
find(collectionId)
|
||||
]);
|
||||
|
||||
|
||||
@@ -82,17 +82,18 @@ export async function delDatasetRelevantData({
|
||||
teamId,
|
||||
datasetId: { $in: datasetIds }
|
||||
},
|
||||
'_id teamId fileId metadata'
|
||||
'_id teamId datasetId fileId metadata'
|
||||
).lean();
|
||||
|
||||
// image and file
|
||||
await delCollectionRelatedSource({ collections, session });
|
||||
|
||||
// delete training data
|
||||
await MongoDatasetTraining.deleteMany({
|
||||
teamId,
|
||||
datasetId: { $in: datasetIds }
|
||||
});
|
||||
|
||||
// image and file
|
||||
await delCollectionRelatedSource({ collections, session });
|
||||
|
||||
// delete dataset.datas
|
||||
await MongoDatasetData.deleteMany({ teamId, datasetId: { $in: datasetIds } }, { session });
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ import type { PluginRuntimeType, PluginTemplateType } from '@fastgpt/global/core
|
||||
import { FlowNodeTemplateTypeEnum } from '@fastgpt/global/core/workflow/constants';
|
||||
import { getHandleConfig } from '../../../global/core/workflow/template/utils';
|
||||
import { getNanoid } from '@fastgpt/global/common/string/tools';
|
||||
import { cloneDeep } from 'lodash';
|
||||
|
||||
/*
|
||||
plugin id rule:
|
||||
@@ -36,7 +37,7 @@ const getPluginTemplateById = async (id: string): Promise<PluginTemplateType> =>
|
||||
const item = global.communityPlugins?.find((plugin) => plugin.id === pluginId);
|
||||
if (!item) return Promise.reject('plugin not found');
|
||||
|
||||
return item;
|
||||
return cloneDeep(item);
|
||||
}
|
||||
if (source === PluginSourceEnum.personal) {
|
||||
const item = await MongoPlugin.findById(id).lean();
|
||||
|
||||
@@ -217,16 +217,16 @@ export async function dispatchWorkFlow(data: Props): Promise<DispatchFlowRespons
|
||||
|
||||
if (res?.closed || props.maxRunTimes <= 0) return;
|
||||
props.maxRunTimes--;
|
||||
console.log(props.maxRunTimes, user._id);
|
||||
addLog.debug(`Run node`, { maxRunTimes: props.maxRunTimes, uid: user._id });
|
||||
|
||||
await surrenderProcess();
|
||||
|
||||
if (status === 'run') {
|
||||
addLog.info(`[dispatchWorkFlow] nodeRunWithActive: ${node.name}`);
|
||||
addLog.debug(`[dispatchWorkFlow] nodeRunWithActive: ${node.name}`);
|
||||
return nodeRunWithActive(node);
|
||||
}
|
||||
if (status === 'skip') {
|
||||
addLog.info(`[dispatchWorkFlow] nodeRunWithSkip: ${node.name}`);
|
||||
addLog.debug(`[dispatchWorkFlow] nodeRunWithSkip: ${node.name}`);
|
||||
return nodeRunWithSkip(node);
|
||||
}
|
||||
|
||||
@@ -270,8 +270,6 @@ export async function dispatchWorkFlow(data: Props): Promise<DispatchFlowRespons
|
||||
nodes: runtimeNodes,
|
||||
variables
|
||||
});
|
||||
// console.log(JSON.stringify(input, null, 2), '=====================');
|
||||
|
||||
// format valueType
|
||||
params[input.key] = valueTypeFormat(value, input.valueType);
|
||||
});
|
||||
@@ -407,13 +405,13 @@ export function responseStatus({
|
||||
/* get system variable */
|
||||
export function getSystemVariable({
|
||||
user,
|
||||
appId,
|
||||
app,
|
||||
chatId,
|
||||
responseChatItemId,
|
||||
histories = []
|
||||
}: Props) {
|
||||
return {
|
||||
appId,
|
||||
appId: String(app._id),
|
||||
chatId,
|
||||
responseChatItemId,
|
||||
histories,
|
||||
|
||||
@@ -43,14 +43,13 @@ export const dispatchHttp468Request = async (props: HttpRequestProps): Promise<H
|
||||
let {
|
||||
res,
|
||||
detail,
|
||||
appId,
|
||||
app: { _id: appId },
|
||||
chatId,
|
||||
stream,
|
||||
responseChatItemId,
|
||||
variables,
|
||||
node: { outputs },
|
||||
histories,
|
||||
isToolCall,
|
||||
params: {
|
||||
system_httpMethod: httpMethod = 'POST',
|
||||
system_httpReqUrl: httpReqUrl,
|
||||
|
||||
@@ -17,6 +17,8 @@ import { DispatchNodeResponseKeyEnum } from '@fastgpt/global/core/workflow/runti
|
||||
import { getHistories } from '../utils';
|
||||
import { chatValue2RuntimePrompt, runtimePrompt2ChatsValue } from '@fastgpt/global/core/chat/adapt';
|
||||
import { DispatchNodeResultType } from '@fastgpt/global/core/workflow/runtime/type';
|
||||
import { authAppByTmbId } from '../../../../support/permission/app/auth';
|
||||
import { ReadPermissionVal } from '@fastgpt/global/support/permission/constant';
|
||||
|
||||
type Props = ModuleDispatchProps<{
|
||||
[NodeInputKeyEnum.userChatInput]: string;
|
||||
@@ -31,28 +33,26 @@ type Response = DispatchNodeResultType<{
|
||||
export const dispatchAppRequest = async (props: Props): Promise<Response> => {
|
||||
const {
|
||||
res,
|
||||
teamId,
|
||||
app: workflowApp,
|
||||
stream,
|
||||
detail,
|
||||
histories,
|
||||
query,
|
||||
params: { userChatInput, history, app }
|
||||
} = props;
|
||||
let start = Date.now();
|
||||
|
||||
if (!userChatInput) {
|
||||
return Promise.reject('Input is empty');
|
||||
}
|
||||
|
||||
const appData = await MongoApp.findOne({
|
||||
_id: app.id,
|
||||
teamId
|
||||
// 检查该工作流的tmb是否有调用该app的权限(不是校验对话的人,是否有权限)
|
||||
const { app: appData } = await authAppByTmbId({
|
||||
appId: app.id,
|
||||
teamId: workflowApp.teamId,
|
||||
tmbId: workflowApp.tmbId,
|
||||
per: ReadPermissionVal
|
||||
});
|
||||
|
||||
if (!appData) {
|
||||
return Promise.reject('App not found');
|
||||
}
|
||||
|
||||
if (res && stream) {
|
||||
responseWrite({
|
||||
res,
|
||||
@@ -64,18 +64,19 @@ export const dispatchAppRequest = async (props: Props): Promise<Response> => {
|
||||
}
|
||||
|
||||
const chatHistories = getHistories(history, histories);
|
||||
const { files } = chatValue2RuntimePrompt(query);
|
||||
|
||||
const { flowResponses, flowUsages, assistantResponses } = await dispatchWorkFlow({
|
||||
...props,
|
||||
appId: app.id,
|
||||
app: appData,
|
||||
runtimeNodes: storeNodes2RuntimeNodes(appData.modules, getDefaultEntryNodeIds(appData.modules)),
|
||||
runtimeEdges: initWorkflowEdgeStatus(appData.edges),
|
||||
histories: chatHistories,
|
||||
query,
|
||||
variables: {
|
||||
...props.variables,
|
||||
userChatInput
|
||||
}
|
||||
query: runtimePrompt2ChatsValue({
|
||||
files,
|
||||
text: userChatInput
|
||||
}),
|
||||
variables: props.variables
|
||||
});
|
||||
|
||||
const completeMessages = chatHistories.concat([
|
||||
|
||||
@@ -21,7 +21,7 @@ const UNDEFINED_SIGN = 'UNDEFINED_SIGN';
|
||||
|
||||
export const dispatchLafRequest = async (props: LafRequestProps): Promise<LafResponse> => {
|
||||
let {
|
||||
appId,
|
||||
app: { _id: appId },
|
||||
chatId,
|
||||
responseChatItemId,
|
||||
variables,
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
"decompress": "^4.2.1",
|
||||
"domino-ext": "^2.1.4",
|
||||
"encoding": "^0.1.13",
|
||||
"lodash": "^4.17.21",
|
||||
"file-type": "^19.0.0",
|
||||
"iconv-lite": "^0.6.3",
|
||||
"joplin-turndown-plugin-gfm": "^1.0.12",
|
||||
@@ -41,6 +42,7 @@
|
||||
"@types/multer": "^1.4.10",
|
||||
"@types/node-cron": "^3.0.11",
|
||||
"@types/papaparse": "5.3.7",
|
||||
"@types/lodash": "^4.14.191",
|
||||
"@types/pg": "^8.6.6",
|
||||
"@types/tunnel": "^0.0.4",
|
||||
"@types/turndown": "^5.0.4"
|
||||
|
||||
85
packages/service/support/permission/app/auth.ts
Normal file
@@ -0,0 +1,85 @@
|
||||
/* Auth app permission */
|
||||
import { MongoApp } from '../../../core/app/schema';
|
||||
import { AppDetailType } from '@fastgpt/global/core/app/type.d';
|
||||
import { AuthPropsType } from '../type/auth.d';
|
||||
import { parseHeaderCert } from '../controller';
|
||||
import { PerResourceTypeEnum } from '@fastgpt/global/support/permission/constant';
|
||||
import { AppErrEnum } from '@fastgpt/global/common/error/code/app';
|
||||
import { getTmbInfoByTmbId } from '../../user/team/controller';
|
||||
import { getResourcePermission } from '../controller';
|
||||
import { AppPermission } from '@fastgpt/global/support/permission/app/controller';
|
||||
import { AuthResponseType } from '../type/auth.d';
|
||||
import { PermissionValueType } from '@fastgpt/global/support/permission/type';
|
||||
|
||||
export const authAppByTmbId = async ({
|
||||
teamId,
|
||||
tmbId,
|
||||
appId,
|
||||
per
|
||||
}: {
|
||||
teamId: string;
|
||||
tmbId: string;
|
||||
appId: string;
|
||||
per: PermissionValueType;
|
||||
}) => {
|
||||
const { permission: tmbPer } = await getTmbInfoByTmbId({ tmbId });
|
||||
|
||||
const app = await (async () => {
|
||||
// get app and per
|
||||
const [app, rp] = await Promise.all([
|
||||
MongoApp.findOne({ _id: appId, teamId }).lean(),
|
||||
getResourcePermission({
|
||||
teamId,
|
||||
tmbId,
|
||||
resourceId: appId,
|
||||
resourceType: PerResourceTypeEnum.app
|
||||
}) // this could be null
|
||||
]);
|
||||
|
||||
if (!app) {
|
||||
return Promise.reject(AppErrEnum.unExist);
|
||||
}
|
||||
|
||||
const isOwner = tmbPer.isOwner || String(app.tmbId) === tmbId;
|
||||
const Per = new AppPermission({ per: rp?.permission ?? app.defaultPermission, isOwner });
|
||||
|
||||
if (!Per.checkPer(per)) {
|
||||
return Promise.reject(AppErrEnum.unAuthApp);
|
||||
}
|
||||
|
||||
return {
|
||||
...app,
|
||||
permission: Per
|
||||
};
|
||||
})();
|
||||
|
||||
return { app };
|
||||
};
|
||||
|
||||
export const authApp = async ({
|
||||
appId,
|
||||
per,
|
||||
...props
|
||||
}: AuthPropsType & {
|
||||
appId: string;
|
||||
}): Promise<
|
||||
AuthResponseType & {
|
||||
app: AppDetailType;
|
||||
}
|
||||
> => {
|
||||
const result = await parseHeaderCert(props);
|
||||
const { teamId, tmbId } = result;
|
||||
|
||||
const { app } = await authAppByTmbId({
|
||||
teamId,
|
||||
tmbId,
|
||||
appId,
|
||||
per
|
||||
});
|
||||
|
||||
return {
|
||||
...result,
|
||||
permission: app.permission,
|
||||
app
|
||||
};
|
||||
};
|
||||
@@ -1,72 +0,0 @@
|
||||
import { MongoApp } from '../../../core/app/schema';
|
||||
import { AppDetailType } from '@fastgpt/global/core/app/type.d';
|
||||
import { AuthModeType } from '../type';
|
||||
import { AuthResponseType } from '@fastgpt/global/support/permission/type';
|
||||
import { TeamMemberRoleEnum } from '@fastgpt/global/support/user/team/constant';
|
||||
import { parseHeaderCert } from '../controller';
|
||||
import { PermissionTypeEnum } from '@fastgpt/global/support/permission/constant';
|
||||
import { AppErrEnum } from '@fastgpt/global/common/error/code/app';
|
||||
import { getTmbInfoByTmbId } from '../../user/team/controller';
|
||||
|
||||
// 模型使用权校验
|
||||
export async function authApp({
|
||||
appId,
|
||||
per = 'owner',
|
||||
...props
|
||||
}: AuthModeType & {
|
||||
appId: string;
|
||||
}): Promise<
|
||||
AuthResponseType & {
|
||||
teamOwner: boolean;
|
||||
app: AppDetailType;
|
||||
role: `${TeamMemberRoleEnum}`;
|
||||
}
|
||||
> {
|
||||
const result = await parseHeaderCert(props);
|
||||
const { teamId, tmbId } = result;
|
||||
const { role } = await getTmbInfoByTmbId({ tmbId });
|
||||
|
||||
const { app, isOwner, canWrite } = await (async () => {
|
||||
// get app
|
||||
const app = await MongoApp.findOne({ _id: appId, teamId }).lean();
|
||||
if (!app) {
|
||||
return Promise.reject(AppErrEnum.unExist);
|
||||
}
|
||||
|
||||
const isOwner = String(app.tmbId) === tmbId || role === TeamMemberRoleEnum.owner;
|
||||
const canWrite =
|
||||
isOwner ||
|
||||
(app.permission === PermissionTypeEnum.public && role !== TeamMemberRoleEnum.visitor);
|
||||
|
||||
if (per === 'r') {
|
||||
if (!isOwner && app.permission !== PermissionTypeEnum.public) {
|
||||
return Promise.reject(AppErrEnum.unAuthApp);
|
||||
}
|
||||
}
|
||||
if (per === 'w' && !canWrite) {
|
||||
return Promise.reject(AppErrEnum.unAuthApp);
|
||||
}
|
||||
if (per === 'owner' && !isOwner) {
|
||||
return Promise.reject(AppErrEnum.unAuthApp);
|
||||
}
|
||||
|
||||
return {
|
||||
app: {
|
||||
...app,
|
||||
isOwner,
|
||||
canWrite
|
||||
},
|
||||
isOwner,
|
||||
canWrite
|
||||
};
|
||||
})();
|
||||
|
||||
return {
|
||||
...result,
|
||||
app,
|
||||
role,
|
||||
isOwner,
|
||||
canWrite,
|
||||
teamOwner: role === TeamMemberRoleEnum.owner
|
||||
};
|
||||
}
|
||||
@@ -198,4 +198,4 @@ export async function authDatasetFile({
|
||||
} catch (error) {
|
||||
return Promise.reject(DatasetErrEnum.unAuthDatasetFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,96 +0,0 @@
|
||||
import { TeamMemberRoleEnum } from '@fastgpt/global/support/user/team/constant';
|
||||
import { AuthModeType } from '../type';
|
||||
import { AuthResponseType } from '@fastgpt/global/support/permission/type';
|
||||
import { AppDetailType } from '@fastgpt/global/core/app/type';
|
||||
import { OutLinkSchema } from '@fastgpt/global/support/outLink/type';
|
||||
import { parseHeaderCert } from '../controller';
|
||||
import { MongoOutLink } from '../../outLink/schema';
|
||||
import { MongoApp } from '../../../core/app/schema';
|
||||
import { OutLinkErrEnum } from '@fastgpt/global/common/error/code/outLink';
|
||||
import { PermissionTypeEnum } from '@fastgpt/global/support/permission/constant';
|
||||
import { AppErrEnum } from '@fastgpt/global/common/error/code/app';
|
||||
import { getTmbInfoByTmbId } from '../../user/team/controller';
|
||||
|
||||
/* crud outlink permission */
|
||||
export async function authOutLinkCrud({
|
||||
outLinkId,
|
||||
per = 'owner',
|
||||
...props
|
||||
}: AuthModeType & {
|
||||
outLinkId: string;
|
||||
}): Promise<
|
||||
AuthResponseType & {
|
||||
app: AppDetailType;
|
||||
outLink: OutLinkSchema;
|
||||
}
|
||||
> {
|
||||
const result = await parseHeaderCert(props);
|
||||
const { tmbId, teamId } = result;
|
||||
|
||||
const { role } = await getTmbInfoByTmbId({ tmbId });
|
||||
|
||||
const { app, outLink, isOwner, canWrite } = await (async () => {
|
||||
const outLink = await MongoOutLink.findOne({ _id: outLinkId, teamId });
|
||||
|
||||
if (!outLink) {
|
||||
throw new Error(OutLinkErrEnum.unExist);
|
||||
}
|
||||
|
||||
const app = await MongoApp.findById(outLink.appId);
|
||||
|
||||
if (!app) {
|
||||
return Promise.reject(AppErrEnum.unExist);
|
||||
}
|
||||
|
||||
const isOwner = String(outLink.tmbId) === tmbId || role === TeamMemberRoleEnum.owner;
|
||||
const canWrite =
|
||||
isOwner ||
|
||||
(app.permission === PermissionTypeEnum.public && role !== TeamMemberRoleEnum.visitor);
|
||||
|
||||
if (per === 'r' && !isOwner && app.permission !== PermissionTypeEnum.public) {
|
||||
return Promise.reject(OutLinkErrEnum.unAuthLink);
|
||||
}
|
||||
if (per === 'w' && !canWrite) {
|
||||
return Promise.reject(OutLinkErrEnum.unAuthLink);
|
||||
}
|
||||
if (per === 'owner' && !isOwner) {
|
||||
return Promise.reject(OutLinkErrEnum.unAuthLink);
|
||||
}
|
||||
|
||||
return {
|
||||
app: {
|
||||
...app,
|
||||
isOwner: String(app.tmbId) === tmbId,
|
||||
canWrite
|
||||
},
|
||||
outLink,
|
||||
isOwner,
|
||||
canWrite
|
||||
};
|
||||
})();
|
||||
|
||||
return {
|
||||
...result,
|
||||
app,
|
||||
outLink,
|
||||
isOwner,
|
||||
canWrite
|
||||
};
|
||||
}
|
||||
|
||||
/* outLink exist and it app exist */
|
||||
export async function authOutLinkValid({ shareId }: { shareId?: string }) {
|
||||
if (!shareId) {
|
||||
return Promise.reject(OutLinkErrEnum.linkUnInvalid);
|
||||
}
|
||||
const shareChat = await MongoOutLink.findOne({ shareId });
|
||||
|
||||
if (!shareChat) {
|
||||
return Promise.reject(OutLinkErrEnum.linkUnInvalid);
|
||||
}
|
||||
|
||||
return {
|
||||
appId: shareChat.appId,
|
||||
shareChat
|
||||
};
|
||||
}
|
||||
@@ -1,66 +0,0 @@
|
||||
import { AuthResponseType } from '@fastgpt/global/support/permission/type';
|
||||
import { AuthModeType } from '../type';
|
||||
import { TeamItemType } from '@fastgpt/global/support/user/team/type';
|
||||
import { TeamMemberRoleEnum } from '@fastgpt/global/support/user/team/constant';
|
||||
import { parseHeaderCert } from '../controller';
|
||||
import { getTmbInfoByTmbId } from '../../user/team/controller';
|
||||
import { UserErrEnum } from '../../../../global/common/error/code/user';
|
||||
import { TeamErrEnum } from '@fastgpt/global/common/error/code/team';
|
||||
|
||||
export async function authUserNotVisitor(props: AuthModeType): Promise<
|
||||
AuthResponseType & {
|
||||
team: TeamItemType;
|
||||
role: `${TeamMemberRoleEnum}`;
|
||||
}
|
||||
> {
|
||||
const { teamId, tmbId } = await parseHeaderCert(props);
|
||||
const team = await getTmbInfoByTmbId({ tmbId });
|
||||
|
||||
if (team.role === TeamMemberRoleEnum.visitor) {
|
||||
return Promise.reject(UserErrEnum.binVisitor);
|
||||
}
|
||||
|
||||
return {
|
||||
teamId,
|
||||
tmbId,
|
||||
team,
|
||||
role: team.role,
|
||||
isOwner: team.role === TeamMemberRoleEnum.owner, // teamOwner
|
||||
canWrite: true
|
||||
};
|
||||
}
|
||||
|
||||
/* auth user role */
|
||||
export async function authUserRole(props: AuthModeType): Promise<
|
||||
AuthResponseType & {
|
||||
role: `${TeamMemberRoleEnum}`;
|
||||
teamOwner: boolean;
|
||||
}
|
||||
> {
|
||||
const result = await parseHeaderCert(props);
|
||||
const { role: userRole, canWrite } = await getTmbInfoByTmbId({ tmbId: result.tmbId });
|
||||
|
||||
return {
|
||||
...result,
|
||||
isOwner: true,
|
||||
role: userRole,
|
||||
teamOwner: userRole === TeamMemberRoleEnum.owner,
|
||||
canWrite
|
||||
};
|
||||
}
|
||||
|
||||
/* auth teamMember in team role */
|
||||
export async function authTeamOwner(props: AuthModeType): Promise<
|
||||
AuthResponseType & {
|
||||
role: `${TeamMemberRoleEnum}`;
|
||||
teamOwner: boolean;
|
||||
}
|
||||
> {
|
||||
const authRes = await authUserRole(props);
|
||||
|
||||
if (authRes.role !== TeamMemberRoleEnum.owner) {
|
||||
return Promise.reject(TeamErrEnum.unAuthTeam);
|
||||
}
|
||||
|
||||
return authRes;
|
||||
}
|
||||
@@ -3,10 +3,39 @@ import { ERROR_ENUM } from '@fastgpt/global/common/error/errorCode';
|
||||
import jwt from 'jsonwebtoken';
|
||||
import { NextApiResponse } from 'next';
|
||||
import type { AuthModeType, ReqHeaderAuthType } from './type.d';
|
||||
import { AuthUserTypeEnum } from '@fastgpt/global/support/permission/constant';
|
||||
import { AuthUserTypeEnum, PerResourceTypeEnum } from '@fastgpt/global/support/permission/constant';
|
||||
import { authOpenApiKey } from '../openapi/auth';
|
||||
import { FileTokenQuery } from '@fastgpt/global/common/file/type';
|
||||
import { MongoResourcePermission } from './schema';
|
||||
|
||||
export const getResourcePermission = async ({
|
||||
resourceType,
|
||||
teamId,
|
||||
tmbId,
|
||||
resourceId
|
||||
}: {
|
||||
resourceType: PerResourceTypeEnum;
|
||||
teamId: string;
|
||||
tmbId: string;
|
||||
resourceId?: string;
|
||||
}) => {
|
||||
const per = await MongoResourcePermission.findOne({
|
||||
tmbId,
|
||||
teamId,
|
||||
resourceType,
|
||||
resourceId
|
||||
});
|
||||
|
||||
if (!per) {
|
||||
return null;
|
||||
}
|
||||
return per;
|
||||
};
|
||||
export const delResourcePermissionById = (id: string) => {
|
||||
return MongoResourcePermission.findByIdAndRemove(id);
|
||||
};
|
||||
|
||||
/* 下面代码等迁移 */
|
||||
/* create token */
|
||||
export function createJWT(user: { _id?: string; team?: { teamId?: string; tmbId: string } }) {
|
||||
const key = process.env.TOKEN_KEY as string;
|
||||
|
||||
69
packages/service/support/permission/publish/authLink.ts
Normal file
@@ -0,0 +1,69 @@
|
||||
import { AppDetailType } from '@fastgpt/global/core/app/type';
|
||||
import { OutLinkSchema } from '@fastgpt/global/support/outLink/type';
|
||||
import { parseHeaderCert } from '../controller';
|
||||
import { MongoOutLink } from '../../outLink/schema';
|
||||
import { OutLinkErrEnum } from '@fastgpt/global/common/error/code/outLink';
|
||||
import { ManagePermissionVal } from '@fastgpt/global/support/permission/constant';
|
||||
import { AuthPropsType } from '../type/auth';
|
||||
import { AuthResponseType } from '../type/auth';
|
||||
import { authAppByTmbId } from '../app/auth';
|
||||
|
||||
/* crud outlink permission */
|
||||
export async function authOutLinkCrud({
|
||||
outLinkId,
|
||||
per,
|
||||
...props
|
||||
}: AuthPropsType & {
|
||||
outLinkId: string;
|
||||
}): Promise<
|
||||
AuthResponseType & {
|
||||
app: AppDetailType;
|
||||
outLink: OutLinkSchema;
|
||||
}
|
||||
> {
|
||||
const result = await parseHeaderCert(props);
|
||||
const { tmbId, teamId } = result;
|
||||
|
||||
const { app, outLink } = await (async () => {
|
||||
const outLink = await MongoOutLink.findOne({ _id: outLinkId, teamId });
|
||||
if (!outLink) {
|
||||
throw new Error(OutLinkErrEnum.unExist);
|
||||
}
|
||||
|
||||
const { app } = await authAppByTmbId({
|
||||
teamId,
|
||||
tmbId,
|
||||
appId: outLink.appId,
|
||||
per: ManagePermissionVal
|
||||
});
|
||||
|
||||
return {
|
||||
outLink,
|
||||
app
|
||||
};
|
||||
})();
|
||||
|
||||
return {
|
||||
...result,
|
||||
permission: app.permission,
|
||||
app,
|
||||
outLink
|
||||
};
|
||||
}
|
||||
|
||||
/* outLink exist and it app exist */
|
||||
export async function authOutLinkValid({ shareId }: { shareId?: string }) {
|
||||
if (!shareId) {
|
||||
return Promise.reject(OutLinkErrEnum.linkUnInvalid);
|
||||
}
|
||||
const shareChat = await MongoOutLink.findOne({ shareId });
|
||||
|
||||
if (!shareChat) {
|
||||
return Promise.reject(OutLinkErrEnum.linkUnInvalid);
|
||||
}
|
||||
|
||||
return {
|
||||
appId: shareChat.appId,
|
||||
shareChat
|
||||
};
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
import { ResourcePermissionType } from '@fastgpt/global/support/permission/type';
|
||||
import { MongoResourcePermission } from './schema';
|
||||
import { ResourceTypeEnum } from '@fastgpt/global/support/permission/constant';
|
||||
|
||||
export async function getResourcePermission({
|
||||
tmbId,
|
||||
resourceType
|
||||
}: {
|
||||
tmbId: string;
|
||||
resourceType: ResourceTypeEnum;
|
||||
}) {
|
||||
return (await MongoResourcePermission.findOne({
|
||||
tmbId,
|
||||
resourceType
|
||||
})) as ResourcePermissionType;
|
||||
}
|
||||
@@ -1,127 +0,0 @@
|
||||
// PermissionValueType, the type of permission's value is a number, which is a bit field actually.
|
||||
// It is spired by the permission system in Linux.
|
||||
// The lowest 3 bits present the permission of reading, writing and managing.
|
||||
// The higher bits are advanced permissions or extended permissions, which could be customized.
|
||||
export type PermissionValueType = number;
|
||||
export type PermissionListType = { [key: string]: PermissionValueType };
|
||||
export const NullPermission: PermissionValueType = 0;
|
||||
|
||||
// the Permission helper class
|
||||
export class Permission {
|
||||
value: PermissionValueType;
|
||||
constructor(value: PermissionValueType) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
// add permission(s)
|
||||
// it can be chaining called.
|
||||
// @example
|
||||
// const perm = new Permission(permission)
|
||||
// perm.add(PermissionList['read'])
|
||||
// perm.add(PermissionList['read'], PermissionList['write'])
|
||||
// perm.add(PermissionList['read']).add(PermissionList['write'])
|
||||
add(...perm: PermissionValueType[]): Permission {
|
||||
for (let p of perm) {
|
||||
this.value = addPermission(this.value, p);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
remove(...perm: PermissionValueType[]): Permission {
|
||||
for (let p of perm) {
|
||||
this.value = removePermission(this.value, p);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
check(perm: PermissionValueType): Permission | boolean {
|
||||
if (checkPermission(this.value, perm)) {
|
||||
return this;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function constructPermission(permList: PermissionValueType[]) {
|
||||
return new Permission(NullPermission).add(...permList);
|
||||
}
|
||||
|
||||
// The base Permissions List
|
||||
// It can be extended, for example:
|
||||
// export const UserPermissionList: PermissionListType = {
|
||||
// ...PermissionList,
|
||||
// 'Invite': 0b1000
|
||||
// }
|
||||
export const PermissionList: PermissionListType = {
|
||||
Read: 0b100,
|
||||
Write: 0b010,
|
||||
Manage: 0b001
|
||||
};
|
||||
|
||||
// list of permissions. could be customized.
|
||||
// ! removal of the basic permissions is not recommended.
|
||||
// const PermList: Array<PermissionType> = [ReadPerm, WritePerm, ManagePerm];
|
||||
|
||||
// return the list of permissions
|
||||
// @param Perm(optional): the list of permissions to be added
|
||||
// export function getPermList(Perm?: PermissionType[]): Array<PermissionType> {
|
||||
// if (Perm === undefined) {
|
||||
// return PermList;
|
||||
// } else {
|
||||
// return PermList.concat(Perm);
|
||||
// }
|
||||
// }
|
||||
|
||||
// check the permission
|
||||
// @param [val]: The permission value to be checked
|
||||
// @parma [perm]: Which Permission value will be checked
|
||||
// @returns [booean]: if the [val] has the [perm]
|
||||
// example:
|
||||
// const perm = user.permission // get this permisiion from db or somewhere else
|
||||
// const ok = checkPermission(perm, PermissionList['Read'])
|
||||
export function checkPermission(val: PermissionValueType, perm: PermissionValueType): boolean {
|
||||
return (val & perm) === perm;
|
||||
}
|
||||
|
||||
// add the permission
|
||||
// it can be chaining called.
|
||||
// return the new permission value based on [val] added with [perm]
|
||||
// @param val: PermissionValueType
|
||||
// @param perm: PermissionValueType
|
||||
// example:
|
||||
// const basePerm = 0b001; // Manage only
|
||||
export function addPermission(
|
||||
val: PermissionValueType,
|
||||
perm: PermissionValueType
|
||||
): PermissionValueType {
|
||||
return val | perm;
|
||||
}
|
||||
|
||||
// remove the permission
|
||||
export function removePermission(
|
||||
val: PermissionValueType,
|
||||
perm: PermissionValueType
|
||||
): PermissionValueType {
|
||||
return val & ~perm;
|
||||
}
|
||||
|
||||
// export function parsePermission(val: PermissionValueType, list: PermissionValueType[]) {
|
||||
// const result: [[string, boolean]] = [] as any;
|
||||
// list.forEach((perm) => {
|
||||
// result.push([perm[0], checkPermission(val, perm)]);
|
||||
// });
|
||||
// return result;
|
||||
// }
|
||||
|
||||
export function hasManage(val: PermissionValueType) {
|
||||
return checkPermission(val, PermissionList['Manage']);
|
||||
}
|
||||
|
||||
export function hasWrite(val: PermissionValueType) {
|
||||
return checkPermission(val, PermissionList['Write']);
|
||||
}
|
||||
|
||||
export function hasRead(val: PermissionValueType) {
|
||||
return checkPermission(val, PermissionList['Read']);
|
||||
}
|
||||
@@ -2,11 +2,13 @@ import {
|
||||
TeamCollectionName,
|
||||
TeamMemberCollectionName
|
||||
} from '@fastgpt/global/support/user/team/constant';
|
||||
import { Model, connectionMongo } from '../../../common/mongo';
|
||||
import { Model, connectionMongo } from '../../common/mongo';
|
||||
import type { ResourcePermissionType } from '@fastgpt/global/support/permission/type';
|
||||
import { ResourceTypeEnum } from '@fastgpt/global/support/permission/constant';
|
||||
import { PerResourceTypeEnum } from '@fastgpt/global/support/permission/constant';
|
||||
const { Schema, model, models } = connectionMongo;
|
||||
|
||||
export const ResourcePermissionCollectionName = 'resource_permission';
|
||||
|
||||
export const ResourcePermissionSchema = new Schema({
|
||||
teamId: {
|
||||
type: Schema.Types.ObjectId,
|
||||
@@ -17,30 +19,41 @@ export const ResourcePermissionSchema = new Schema({
|
||||
ref: TeamMemberCollectionName
|
||||
},
|
||||
resourceType: {
|
||||
type: Object.values(ResourceTypeEnum),
|
||||
type: Object.values(PerResourceTypeEnum),
|
||||
required: true
|
||||
},
|
||||
permission: {
|
||||
type: Number,
|
||||
required: true
|
||||
},
|
||||
// Resrouce ID: App or DataSet or any other resource type.
|
||||
// It is null if the resourceType is team.
|
||||
resourceId: {
|
||||
type: Schema.Types.ObjectId
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
ResourcePermissionSchema.index(
|
||||
{
|
||||
resourceType: 1,
|
||||
teamId: 1,
|
||||
tmbId: 1,
|
||||
resourceId: 1
|
||||
},
|
||||
{
|
||||
unique: true
|
||||
}
|
||||
);
|
||||
ResourcePermissionSchema.index({
|
||||
resourceType: 1,
|
||||
teamId: 1,
|
||||
resourceType: 1
|
||||
});
|
||||
ResourcePermissionSchema.index({
|
||||
tmbId: 1,
|
||||
resourceType: 1
|
||||
resourceId: 1
|
||||
});
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
|
||||
export const ResourcePermissionCollectionName = 'resource_permission';
|
||||
|
||||
export const MongoResourcePermission: Model<ResourcePermissionType> =
|
||||
models[ResourcePermissionCollectionName] ||
|
||||
model(ResourcePermissionCollectionName, ResourcePermissionSchema);
|
||||
@@ -1,4 +1,3 @@
|
||||
import { getVectorCountByTeamId } from '../../common/vectorStore/controller';
|
||||
import { getTeamPlanStatus, getTeamStandPlan } from '../../support/wallet/sub/utils';
|
||||
import { MongoApp } from '../../core/app/schema';
|
||||
import { MongoPlugin } from '../../core/plugin/schema';
|
||||
@@ -6,6 +5,7 @@ import { MongoDataset } from '../../core/dataset/schema';
|
||||
import { DatasetTypeEnum } from '@fastgpt/global/core/dataset/constants';
|
||||
import { TeamErrEnum } from '@fastgpt/global/common/error/code/team';
|
||||
import { SystemErrEnum } from '@fastgpt/global/common/error/code/system';
|
||||
import { AppTypeEnum } from '@fastgpt/global/core/app/constants';
|
||||
|
||||
export const checkDatasetLimit = async ({
|
||||
teamId,
|
||||
@@ -67,7 +67,7 @@ export const checkTeamDatasetLimit = async (teamId: string) => {
|
||||
export const checkTeamAppLimit = async (teamId: string) => {
|
||||
const [{ standardConstants }, appCount] = await Promise.all([
|
||||
getTeamStandPlan({ teamId }),
|
||||
MongoApp.count({ teamId })
|
||||
MongoApp.count({ teamId, type: { $in: [AppTypeEnum.advanced, AppTypeEnum.simple] } })
|
||||
]);
|
||||
|
||||
if (standardConstants && appCount >= standardConstants.maxAppAmount) {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { ApiRequestProps } from '../../type/next';
|
||||
import type { PermissionValueType } from '@fastgpt/global/support/permission/type';
|
||||
|
||||
export type ReqHeaderAuthType = {
|
||||
cookie?: string;
|
||||
@@ -8,10 +9,11 @@ export type ReqHeaderAuthType = {
|
||||
userid?: string;
|
||||
authorization?: string;
|
||||
};
|
||||
|
||||
export type AuthModeType = {
|
||||
req: ApiRequestProps;
|
||||
authToken?: boolean;
|
||||
authRoot?: boolean;
|
||||
authApiKey?: boolean;
|
||||
per?: 'r' | 'w' | 'owner';
|
||||
per?: PermissionValueType | 'r' | 'w' | 'owner'; // this is for compatibility
|
||||
};
|
||||
|
||||
21
packages/service/support/permission/type/auth.d.ts
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
import { AuthUserTypeEnum } from '@fastgpt/global/support/permission/constant';
|
||||
import { Permission } from '@fastgpt/global/support/permission/controller';
|
||||
import { ApiRequestProps } from '../../../type/next';
|
||||
import { PermissionValueType } from '@fastgpt/global/support/permission/type';
|
||||
|
||||
export type AuthPropsType = {
|
||||
req: ApiRequestProps;
|
||||
authToken?: boolean;
|
||||
authRoot?: boolean;
|
||||
authApiKey?: boolean;
|
||||
per: PermissionValueType;
|
||||
};
|
||||
|
||||
export type AuthResponseType = {
|
||||
teamId: string;
|
||||
tmbId: string;
|
||||
authType?: `${AuthUserTypeEnum}`;
|
||||
appId?: string;
|
||||
apikey?: string;
|
||||
permission: Permission;
|
||||
};
|
||||
26
packages/service/support/permission/user/auth.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
import { AuthResponseType } from '../type/auth.d';
|
||||
import { AuthPropsType } from '../type/auth.d';
|
||||
import { TeamTmbItemType } from '@fastgpt/global/support/user/team/type';
|
||||
import { parseHeaderCert } from '../controller';
|
||||
import { getTmbInfoByTmbId } from '../../user/team/controller';
|
||||
import { TeamErrEnum } from '@fastgpt/global/common/error/code/team';
|
||||
|
||||
/* auth user role */
|
||||
export async function authUserPer(props: AuthPropsType): Promise<
|
||||
AuthResponseType & {
|
||||
tmb: TeamTmbItemType;
|
||||
}
|
||||
> {
|
||||
const result = await parseHeaderCert(props);
|
||||
const tmb = await getTmbInfoByTmbId({ tmbId: result.tmbId });
|
||||
|
||||
if (!tmb.permission.checkPer(props.per)) {
|
||||
return Promise.reject(TeamErrEnum.unAuthTeam);
|
||||
}
|
||||
|
||||
return {
|
||||
...result,
|
||||
permission: tmb.permission,
|
||||
tmb
|
||||
};
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import { TeamItemType, TeamMemberWithTeamSchema } from '@fastgpt/global/support/user/team/type';
|
||||
import { TeamTmbItemType, TeamMemberWithTeamSchema } from '@fastgpt/global/support/user/team/type';
|
||||
import { ClientSession, Types } from '../../../common/mongo';
|
||||
import {
|
||||
TeamMemberRoleEnum,
|
||||
@@ -8,13 +8,22 @@ import {
|
||||
import { MongoTeamMember } from './teamMemberSchema';
|
||||
import { MongoTeam } from './teamSchema';
|
||||
import { UpdateTeamProps } from '@fastgpt/global/support/user/team/controller';
|
||||
import { getResourcePermission } from '../../permission/controller';
|
||||
import { PerResourceTypeEnum } from '@fastgpt/global/support/permission/constant';
|
||||
import { TeamPermission } from '@fastgpt/global/support/permission/user/controller';
|
||||
|
||||
async function getTeamMember(match: Record<string, any>): Promise<TeamItemType> {
|
||||
async function getTeamMember(match: Record<string, any>): Promise<TeamTmbItemType> {
|
||||
const tmb = (await MongoTeamMember.findOne(match).populate('teamId')) as TeamMemberWithTeamSchema;
|
||||
if (!tmb) {
|
||||
return Promise.reject('member not exist');
|
||||
}
|
||||
|
||||
const tmbPer = await getResourcePermission({
|
||||
resourceType: PerResourceTypeEnum.team,
|
||||
teamId: tmb.teamId._id,
|
||||
tmbId: tmb._id
|
||||
});
|
||||
|
||||
return {
|
||||
userId: String(tmb.userId),
|
||||
teamId: String(tmb.teamId._id),
|
||||
@@ -27,9 +36,11 @@ async function getTeamMember(match: Record<string, any>): Promise<TeamItemType>
|
||||
role: tmb.role,
|
||||
status: tmb.status,
|
||||
defaultTeam: tmb.defaultTeam,
|
||||
canWrite: tmb.role !== TeamMemberRoleEnum.visitor,
|
||||
lafAccount: tmb.teamId.lafAccount,
|
||||
defaultPermission: tmb.teamId.defaultPermission
|
||||
permission: new TeamPermission({
|
||||
per: tmbPer?.permission ?? tmb.teamId.defaultPermission,
|
||||
isOwner: tmb.role === TeamMemberRoleEnum.owner
|
||||
})
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ const { Schema, model, models } = connectionMongo;
|
||||
import { TeamSchema as TeamType } from '@fastgpt/global/support/user/team/type.d';
|
||||
import { userCollectionName } from '../../user/schema';
|
||||
import { TeamCollectionName } from '@fastgpt/global/support/user/team/constant';
|
||||
import { NullPermission } from '../../permission/resourcePermission/permisson';
|
||||
import { TeamDefaultPermissionVal } from '@fastgpt/global/support/permission/user/constant';
|
||||
|
||||
const TeamSchema = new Schema({
|
||||
name: {
|
||||
@@ -16,7 +16,7 @@ const TeamSchema = new Schema({
|
||||
},
|
||||
defaultPermission: {
|
||||
type: Number,
|
||||
default: NullPermission
|
||||
default: TeamDefaultPermissionVal
|
||||
},
|
||||
avatar: {
|
||||
type: String,
|
||||
|
||||
@@ -49,11 +49,13 @@ const DateRangePicker = ({
|
||||
py={1}
|
||||
borderRadius={'sm'}
|
||||
cursor={'pointer'}
|
||||
bg={'myWhite.600'}
|
||||
bg={'myGray.100'}
|
||||
fontSize={'sm'}
|
||||
onClick={() => setShowSelected(true)}
|
||||
>
|
||||
<Box>{formatSelected}</Box>
|
||||
<Box color={'myGray.600'} fontWeight={'400'}>
|
||||
{formatSelected}
|
||||
</Box>
|
||||
<MyIcon ml={2} name={'date'} w={'16px'} color={'myGray.600'} />
|
||||
</Flex>
|
||||
{showSelected && (
|
||||
|
||||
@@ -5,7 +5,7 @@ import { DraggableProvided } from 'react-beautiful-dnd';
|
||||
|
||||
const DragIcon = ({ provided, ...props }: { provided: DraggableProvided } & BoxProps) => {
|
||||
return (
|
||||
<Box {...provided.dragHandleProps} {...props}>
|
||||
<Box {...provided.dragHandleProps} {...props} lineHeight={1}>
|
||||
<DragHandleIcon color={'myGray.500'} _hover={{ color: 'primary.600' }} />
|
||||
</Box>
|
||||
);
|
||||
|
||||
@@ -12,7 +12,7 @@ const EmptyTip = ({ text, ...props }: Props) => {
|
||||
return (
|
||||
<Flex mt={5} flexDirection={'column'} alignItems={'center'} py={'10vh'} {...props}>
|
||||
<MyIcon name="empty" w={'48px'} h={'48px'} color={'transparent'} />
|
||||
<Box mt={2} color={'myGray.500'}>
|
||||
<Box mt={2} color={'myGray.500'} fontSize={'sm'}>
|
||||
{text || t('common.empty.Common Tip')}
|
||||
</Box>
|
||||
</Flex>
|
||||
|
||||
@@ -46,10 +46,12 @@ export const iconPaths = {
|
||||
'common/refreshLight': () => import('./icons/common/refreshLight.svg'),
|
||||
'common/resultLight': () => import('./icons/common/resultLight.svg'),
|
||||
'common/retryLight': () => import('./icons/common/retryLight.svg'),
|
||||
'common/rightArrowFill': () => import('./icons/common/rightArrowFill.svg'),
|
||||
'common/rightArrowLight': () => import('./icons/common/rightArrowLight.svg'),
|
||||
'common/routePushLight': () => import('./icons/common/routePushLight.svg'),
|
||||
'common/saveFill': () => import('./icons/common/saveFill.svg'),
|
||||
'common/searchLight': () => import('./icons/common/searchLight.svg'),
|
||||
'common/select': () => import('./icons/common/select.svg'),
|
||||
'common/selectLight': () => import('./icons/common/selectLight.svg'),
|
||||
'common/settingLight': () => import('./icons/common/settingLight.svg'),
|
||||
'common/text/t': () => import('./icons/common/text/t.svg'),
|
||||
@@ -71,6 +73,7 @@ export const iconPaths = {
|
||||
'core/app/publish/lark': () => import('./icons/core/app/publish/lark.svg'),
|
||||
'core/app/questionGuide': () => import('./icons/core/app/questionGuide.svg'),
|
||||
'core/app/schedulePlan': () => import('./icons/core/app/schedulePlan.svg'),
|
||||
'core/app/simpleBot': () => import('./icons/core/app/simpleBot.svg'),
|
||||
'core/app/simpleMode/ai': () => import('./icons/core/app/simpleMode/ai.svg'),
|
||||
'core/app/simpleMode/chat': () => import('./icons/core/app/simpleMode/chat.svg'),
|
||||
'core/app/simpleMode/dataset': () => import('./icons/core/app/simpleMode/dataset.svg'),
|
||||
@@ -80,6 +83,8 @@ export const iconPaths = {
|
||||
'core/app/simpleMode/whisper': () => import('./icons/core/app/simpleMode/whisper.svg'),
|
||||
'core/app/toolCall': () => import('./icons/core/app/toolCall.svg'),
|
||||
'core/app/ttsFill': () => import('./icons/core/app/ttsFill.svg'),
|
||||
'core/app/type/simple': () => import('./icons/core/app/type/simple.svg'),
|
||||
'core/app/type/workflow': () => import('./icons/core/app/type/workflow.svg'),
|
||||
'core/app/variable/external': () => import('./icons/core/app/variable/external.svg'),
|
||||
'core/app/variable/input': () => import('./icons/core/app/variable/input.svg'),
|
||||
'core/app/variable/select': () => import('./icons/core/app/variable/select.svg'),
|
||||
@@ -180,6 +185,7 @@ export const iconPaths = {
|
||||
kbTest: () => import('./icons/kbTest.svg'),
|
||||
menu: () => import('./icons/menu.svg'),
|
||||
minus: () => import('./icons/minus.svg'),
|
||||
'modal/AddClb': () => import('./icons/modal/AddClb.svg'),
|
||||
'modal/concat': () => import('./icons/modal/concat.svg'),
|
||||
'modal/confirmPay': () => import('./icons/modal/confirmPay.svg'),
|
||||
'modal/edit': () => import('./icons/modal/edit.svg'),
|
||||
|
||||
@@ -1,52 +1,78 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style=" background: rgb(255, 255, 255); display: block; shape-rendering: auto;" viewBox="0 0 100 100" preserveAspectRatio="xMidYMid">
|
||||
<g transform="rotate(0 50 50)">
|
||||
<rect x="47.5" y="24" rx="2.5" ry="6" width="5" height="12" fill="#3370ff">
|
||||
<animate attributeName="opacity" values="1;0" keyTimes="0;1" dur="1s" begin="-0.9166666666666666s" repeatCount="indefinite"></animate>
|
||||
</rect>
|
||||
</g><g transform="rotate(30 50 50)">
|
||||
<rect x="47.5" y="24" rx="2.5" ry="6" width="5" height="12" fill="#3370ff">
|
||||
<animate attributeName="opacity" values="1;0" keyTimes="0;1" dur="1s" begin="-0.8333333333333334s" repeatCount="indefinite"></animate>
|
||||
</rect>
|
||||
</g><g transform="rotate(60 50 50)">
|
||||
<rect x="47.5" y="24" rx="2.5" ry="6" width="5" height="12" fill="#3370ff">
|
||||
<animate attributeName="opacity" values="1;0" keyTimes="0;1" dur="1s" begin="-0.75s" repeatCount="indefinite"></animate>
|
||||
</rect>
|
||||
</g><g transform="rotate(90 50 50)">
|
||||
<rect x="47.5" y="24" rx="2.5" ry="6" width="5" height="12" fill="#3370ff">
|
||||
<animate attributeName="opacity" values="1;0" keyTimes="0;1" dur="1s" begin="-0.6666666666666666s" repeatCount="indefinite"></animate>
|
||||
</rect>
|
||||
</g><g transform="rotate(120 50 50)">
|
||||
<rect x="47.5" y="24" rx="2.5" ry="6" width="5" height="12" fill="#3370ff">
|
||||
<animate attributeName="opacity" values="1;0" keyTimes="0;1" dur="1s" begin="-0.5833333333333334s" repeatCount="indefinite"></animate>
|
||||
</rect>
|
||||
</g><g transform="rotate(150 50 50)">
|
||||
<rect x="47.5" y="24" rx="2.5" ry="6" width="5" height="12" fill="#3370ff">
|
||||
<animate attributeName="opacity" values="1;0" keyTimes="0;1" dur="1s" begin="-0.5s" repeatCount="indefinite"></animate>
|
||||
</rect>
|
||||
</g><g transform="rotate(180 50 50)">
|
||||
<rect x="47.5" y="24" rx="2.5" ry="6" width="5" height="12" fill="#3370ff">
|
||||
<animate attributeName="opacity" values="1;0" keyTimes="0;1" dur="1s" begin="-0.4166666666666667s" repeatCount="indefinite"></animate>
|
||||
</rect>
|
||||
</g><g transform="rotate(210 50 50)">
|
||||
<rect x="47.5" y="24" rx="2.5" ry="6" width="5" height="12" fill="#3370ff">
|
||||
<animate attributeName="opacity" values="1;0" keyTimes="0;1" dur="1s" begin="-0.3333333333333333s" repeatCount="indefinite"></animate>
|
||||
</rect>
|
||||
</g><g transform="rotate(240 50 50)">
|
||||
<rect x="47.5" y="24" rx="2.5" ry="6" width="5" height="12" fill="#3370ff">
|
||||
<animate attributeName="opacity" values="1;0" keyTimes="0;1" dur="1s" begin="-0.25s" repeatCount="indefinite"></animate>
|
||||
</rect>
|
||||
</g><g transform="rotate(270 50 50)">
|
||||
<rect x="47.5" y="24" rx="2.5" ry="6" width="5" height="12" fill="#3370ff">
|
||||
<animate attributeName="opacity" values="1;0" keyTimes="0;1" dur="1s" begin="-0.16666666666666666s" repeatCount="indefinite"></animate>
|
||||
</rect>
|
||||
</g><g transform="rotate(300 50 50)">
|
||||
<rect x="47.5" y="24" rx="2.5" ry="6" width="5" height="12" fill="#3370ff">
|
||||
<animate attributeName="opacity" values="1;0" keyTimes="0;1" dur="1s" begin="-0.08333333333333333s" repeatCount="indefinite"></animate>
|
||||
</rect>
|
||||
</g><g transform="rotate(330 50 50)">
|
||||
<rect x="47.5" y="24" rx="2.5" ry="6" width="5" height="12" fill="#3370ff">
|
||||
<animate attributeName="opacity" values="1;0" keyTimes="0;1" dur="1s" begin="0s" repeatCount="indefinite"></animate>
|
||||
</rect>
|
||||
</g>
|
||||
<!-- [ldio] generated by https://loading.io/ --></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" preserveAspectRatio="xMidYMid" width="330" height="330"
|
||||
style="shape-rendering: auto; display: block; background: transparent;" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<g>
|
||||
<g transform="rotate(0 50 50)">
|
||||
<rect fill="#3370ff" height="12" width="6" ry="6" rx="3" y="24" x="47">
|
||||
<animate repeatCount="indefinite" begin="-0.9166666666666666s" dur="1s" keyTimes="0;1" values="1;0"
|
||||
attributeName="opacity"></animate>
|
||||
</rect>
|
||||
</g>
|
||||
<g transform="rotate(30 50 50)">
|
||||
<rect fill="#3370ff" height="12" width="6" ry="6" rx="3" y="24" x="47">
|
||||
<animate repeatCount="indefinite" begin="-0.8333333333333334s" dur="1s" keyTimes="0;1" values="1;0"
|
||||
attributeName="opacity"></animate>
|
||||
</rect>
|
||||
</g>
|
||||
<g transform="rotate(60 50 50)">
|
||||
<rect fill="#3370ff" height="12" width="6" ry="6" rx="3" y="24" x="47">
|
||||
<animate repeatCount="indefinite" begin="-0.75s" dur="1s" keyTimes="0;1" values="1;0" attributeName="opacity">
|
||||
</animate>
|
||||
</rect>
|
||||
</g>
|
||||
<g transform="rotate(90 50 50)">
|
||||
<rect fill="#3370ff" height="12" width="6" ry="6" rx="3" y="24" x="47">
|
||||
<animate repeatCount="indefinite" begin="-0.6666666666666666s" dur="1s" keyTimes="0;1" values="1;0"
|
||||
attributeName="opacity"></animate>
|
||||
</rect>
|
||||
</g>
|
||||
<g transform="rotate(120 50 50)">
|
||||
<rect fill="#3370ff" height="12" width="6" ry="6" rx="3" y="24" x="47">
|
||||
<animate repeatCount="indefinite" begin="-0.5833333333333334s" dur="1s" keyTimes="0;1" values="1;0"
|
||||
attributeName="opacity"></animate>
|
||||
</rect>
|
||||
</g>
|
||||
<g transform="rotate(150 50 50)">
|
||||
<rect fill="#3370ff" height="12" width="6" ry="6" rx="3" y="24" x="47">
|
||||
<animate repeatCount="indefinite" begin="-0.5s" dur="1s" keyTimes="0;1" values="1;0" attributeName="opacity">
|
||||
</animate>
|
||||
</rect>
|
||||
</g>
|
||||
<g transform="rotate(180 50 50)">
|
||||
<rect fill="#3370ff" height="12" width="6" ry="6" rx="3" y="24" x="47">
|
||||
<animate repeatCount="indefinite" begin="-0.4166666666666667s" dur="1s" keyTimes="0;1" values="1;0"
|
||||
attributeName="opacity"></animate>
|
||||
</rect>
|
||||
</g>
|
||||
<g transform="rotate(210 50 50)">
|
||||
<rect fill="#3370ff" height="12" width="6" ry="6" rx="3" y="24" x="47">
|
||||
<animate repeatCount="indefinite" begin="-0.3333333333333333s" dur="1s" keyTimes="0;1" values="1;0"
|
||||
attributeName="opacity"></animate>
|
||||
</rect>
|
||||
</g>
|
||||
<g transform="rotate(240 50 50)">
|
||||
<rect fill="#3370ff" height="12" width="6" ry="6" rx="3" y="24" x="47">
|
||||
<animate repeatCount="indefinite" begin="-0.25s" dur="1s" keyTimes="0;1" values="1;0" attributeName="opacity">
|
||||
</animate>
|
||||
</rect>
|
||||
</g>
|
||||
<g transform="rotate(270 50 50)">
|
||||
<rect fill="#3370ff" height="12" width="6" ry="6" rx="3" y="24" x="47">
|
||||
<animate repeatCount="indefinite" begin="-0.16666666666666666s" dur="1s" keyTimes="0;1" values="1;0"
|
||||
attributeName="opacity"></animate>
|
||||
</rect>
|
||||
</g>
|
||||
<g transform="rotate(300 50 50)">
|
||||
<rect fill="#3370ff" height="12" width="6" ry="6" rx="3" y="24" x="47">
|
||||
<animate repeatCount="indefinite" begin="-0.08333333333333333s" dur="1s" keyTimes="0;1" values="1;0"
|
||||
attributeName="opacity"></animate>
|
||||
</rect>
|
||||
</g>
|
||||
<g transform="rotate(330 50 50)">
|
||||
<rect fill="#3370ff" height="12" width="6" ry="6" rx="3" y="24" x="47">
|
||||
<animate repeatCount="indefinite" begin="0s" dur="1s" keyTimes="0;1" values="1;0" attributeName="opacity">
|
||||
</animate>
|
||||
</rect>
|
||||
</g>
|
||||
<g></g>
|
||||
</g><!-- [ldio] generated by https://loading.io -->
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 3.3 KiB After Width: | Height: | Size: 3.7 KiB |
@@ -0,0 +1,4 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="none">
|
||||
<path
|
||||
d="M5.56201 4.59992C5.56201 3.41205 6.9982 2.81716 7.83815 3.65711L11.2382 7.0572C11.7589 7.5779 11.7589 8.42212 11.2382 8.94282L7.83815 12.3429C6.9982 13.1829 5.56201 12.588 5.56201 11.4001V4.59992Z" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 301 B |
@@ -0,0 +1,4 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 17" fill="none">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd"
|
||||
d="M5.42469 4.94872C5.16434 5.20906 5.16434 5.63117 5.42469 5.89152C5.68504 6.15187 6.10715 6.15187 6.3675 5.89152L8.00002 4.25901L9.63253 5.89152C9.89288 6.15187 10.315 6.15187 10.5753 5.89152C10.8357 5.63117 10.8357 5.20906 10.5753 4.94872L8.47142 2.8448C8.21107 2.58445 7.78896 2.58445 7.52861 2.8448L5.42469 4.94872ZM5.42469 10.8375C5.16434 11.0979 5.16434 11.52 5.42469 11.7803L7.52861 13.8843C7.56115 13.9168 7.59623 13.9453 7.63319 13.9697C7.89196 14.1405 8.24361 14.1121 8.47142 13.8843L10.5753 11.7803C10.8357 11.52 10.8357 11.0979 10.5753 10.8375C10.315 10.5772 9.89288 10.5772 9.63253 10.8375L8.00002 12.47L6.3675 10.8375C6.10715 10.5772 5.68504 10.5772 5.42469 10.8375Z" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 823 B |
@@ -0,0 +1,19 @@
|
||||
<svg viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect width="32" height="32" rx="5.9541" fill="url(#paint0_linear_5533_28322)" />
|
||||
<path
|
||||
d="M15.1466 6.22388C15.593 5.96615 16.3168 5.96615 16.7632 6.22388L23.5846 10.1622C24.4774 10.6776 24.4774 11.5134 23.5846 12.0288L16.853 15.9153C16.4066 16.1731 15.6828 16.1731 15.2364 15.9153L8.41507 11.977C7.52225 11.4616 7.52225 10.6258 8.41506 10.1104L15.1466 6.22388Z"
|
||||
fill="white" />
|
||||
<path
|
||||
d="M7.18506 14.3387C7.18506 13.617 7.6917 13.3245 8.31667 13.6853L14.6222 17.3258C14.9793 17.532 15.2688 18.0335 15.2688 18.4458V25.1119C15.2688 25.8336 14.7622 26.1261 14.1372 25.7653L7.83169 22.1248C7.47457 21.9186 7.18506 21.4172 7.18506 21.0048V14.3387Z"
|
||||
fill="white" />
|
||||
<path
|
||||
d="M16.7302 18.4962C16.7302 18.0838 17.0197 17.5823 17.3768 17.3762L23.6833 13.7351C24.3083 13.3742 24.8149 13.6668 24.8149 14.3884V21.0537C24.8149 21.4661 24.5254 21.9675 24.1683 22.1737L17.8618 25.8148C17.2368 26.1756 16.7302 25.8831 16.7302 25.1614V18.4962Z"
|
||||
fill="white" />
|
||||
<defs>
|
||||
<linearGradient id="paint0_linear_5533_28322" x1="16" y1="0" x2="4.88889" y2="29.3333"
|
||||
gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#67BFFF" />
|
||||
<stop offset="1" stop-color="#5BA6FF" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
@@ -0,0 +1,15 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 14 15" fill="none">
|
||||
<g clip-path="url(#clip0_5676_3579)">
|
||||
<path
|
||||
d="M6.46665 1.10308C6.74565 0.941999 7.19801 0.941998 7.47701 1.10308L11.7403 3.56452C12.2984 3.88669 12.2984 4.40902 11.7403 4.73119L7.53312 7.16023C7.25411 7.32132 6.80176 7.32132 6.52275 7.16023L2.25942 4.6988C1.70141 4.37663 1.70141 3.85429 2.25941 3.53213L6.46665 1.10308Z" />
|
||||
<path
|
||||
d="M1.49066 6.17483C1.49066 5.7238 1.80731 5.54098 2.19792 5.7665L6.13886 8.0418C6.36206 8.17067 6.54301 8.48407 6.54301 8.74181V12.9081C6.54301 13.3592 6.22636 13.542 5.83575 13.3165L1.89481 11.0411C1.6716 10.9123 1.49066 10.5989 1.49066 10.3411V6.17483Z" />
|
||||
<path
|
||||
d="M7.45634 8.77325C7.45634 8.51552 7.63729 8.20212 7.86049 8.07325L11.8021 5.79758C12.1927 5.57206 12.5093 5.75488 12.5093 6.20591V10.3717C12.5093 10.6295 12.3284 10.9429 12.1052 11.0717L8.1636 13.3474C7.77299 13.5729 7.45634 13.3901 7.45634 12.9391V8.77325Z" />
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_5676_3579">
|
||||
<rect width="14" height="14" fill="white" transform="translate(0 0.213127)" />
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.2 KiB |
@@ -0,0 +1,18 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 14 15" fill="none">
|
||||
<path
|
||||
d="M4.35632 8.49118C4.35632 8.79915 4.10666 9.04881 3.79868 9.04881C3.49071 9.04881 3.24105 8.79915 3.24105 8.49118L3.24105 6.36137C3.24105 6.05339 3.49071 5.80373 3.79868 5.80373C4.10666 5.80373 4.35632 6.05339 4.35632 6.36137L4.35632 8.49118Z" />
|
||||
<path fill-rule="evenodd" clip-rule="evenodd"
|
||||
d="M3.79868 5.4847C4.49442 5.4847 5.05843 4.92069 5.05843 4.22495C5.05843 3.52922 4.49442 2.96521 3.79868 2.96521C3.10295 2.96521 2.53894 3.52922 2.53894 4.22495C2.53894 4.92069 3.10295 5.4847 3.79868 5.4847ZM3.79868 6.59996C5.11036 6.59996 6.17369 5.53663 6.17369 4.22495C6.17369 2.91327 5.11036 1.84995 3.79868 1.84995C2.487 1.84995 1.42368 2.91327 1.42368 4.22495C1.42368 5.53663 2.487 6.59996 3.79868 6.59996Z" />
|
||||
<path fill-rule="evenodd" clip-rule="evenodd"
|
||||
d="M6.79074 3.68998C6.79074 3.382 7.0404 3.13234 7.34838 3.13234C7.93704 3.13234 8.52062 3.24176 9.06606 3.45504C9.61153 3.66834 10.1092 3.98174 10.5298 4.37882C10.9505 4.77596 11.286 5.2492 11.5156 5.7724C11.7452 6.29569 11.8639 6.85778 11.8639 7.42627C11.8639 7.73424 11.6142 7.9839 11.3062 7.9839C10.9983 7.9839 10.7486 7.73424 10.7486 7.42627C10.7486 7.01345 10.6625 6.60383 10.4943 6.22051C10.3261 5.83709 10.0786 5.48655 9.76421 5.1898C9.4498 4.89299 9.07481 4.65597 8.65991 4.49373C8.24496 4.33147 7.79922 4.24761 7.34838 4.24761C7.0404 4.24761 6.79074 3.99795 6.79074 3.68998Z" />
|
||||
<path fill-rule="evenodd" clip-rule="evenodd"
|
||||
d="M3.79868 11.8873C4.49442 11.8873 5.05843 11.3233 5.05843 10.6276C5.05843 9.93185 4.49442 9.36785 3.79868 9.36785C3.10295 9.36785 2.53894 9.93185 2.53894 10.6276C2.53894 11.3233 3.10295 11.8873 3.79868 11.8873ZM3.79868 13.0026C5.11036 13.0026 6.17369 11.9393 6.17369 10.6276C6.17369 9.31591 5.11036 8.25258 3.79868 8.25258C2.487 8.25258 1.42368 9.31591 1.42368 10.6276C1.42368 11.9393 2.487 13.0026 3.79868 13.0026Z" />
|
||||
<path fill-rule="evenodd" clip-rule="evenodd"
|
||||
d="M10.7808 9.55902H9.81307C9.47678 9.55902 9.29277 9.55992 9.16063 9.57089C9.15546 9.57132 9.15056 9.57175 9.14592 9.57219C9.14549 9.57682 9.14506 9.58172 9.14463 9.58689C9.13365 9.71903 9.13276 9.90305 9.13276 10.2393V11.207C9.13276 11.5433 9.13365 11.7273 9.14463 11.8595C9.14506 11.8646 9.14549 11.8695 9.14592 11.8742C9.15056 11.8746 9.15546 11.875 9.16063 11.8755C9.29277 11.8864 9.47678 11.8873 9.81307 11.8873H10.7808C11.117 11.8873 11.3011 11.8864 11.4332 11.8755C11.4384 11.875 11.4433 11.8746 11.4479 11.8742C11.4483 11.8695 11.4488 11.8646 11.4492 11.8595C11.4602 11.7273 11.4611 11.5433 11.4611 11.207V10.2393C11.4611 9.90305 11.4602 9.71903 11.4492 9.58689C11.4488 9.58172 11.4483 9.57682 11.4479 9.57219C11.4433 9.57175 11.4384 9.57132 11.4332 9.57089C11.3011 9.55992 11.117 9.55902 10.7808 9.55902ZM8.14306 9.04491C8.01749 9.28663 8.01749 9.6042 8.01749 10.2393V11.207C8.01749 11.8421 8.01749 12.1597 8.14306 12.4014C8.24887 12.6051 8.41495 12.7712 8.61865 12.877C8.86037 13.0026 9.17794 13.0026 9.81307 13.0026H10.7808C11.4159 13.0026 11.7335 13.0026 11.9752 12.877C12.1789 12.7712 12.345 12.6051 12.4508 12.4014C12.5763 12.1597 12.5763 11.8421 12.5763 11.207V10.2393C12.5763 9.6042 12.5763 9.28663 12.4508 9.04491C12.345 8.84122 12.1789 8.67513 11.9752 8.56932C11.7335 8.44375 11.4159 8.44375 10.7808 8.44375H9.81307C9.17794 8.44375 8.86037 8.44375 8.61865 8.56932C8.41495 8.67513 8.24887 8.84122 8.14306 9.04491Z" />
|
||||
<path
|
||||
d="M5.05843 4.22495C5.05843 4.92069 4.49442 5.4847 3.79868 5.4847C3.10295 5.4847 2.53894 4.92069 2.53894 4.22495C2.53894 3.52922 3.10295 2.96521 3.79868 2.96521C4.49442 2.96521 5.05843 3.52922 5.05843 4.22495Z" />
|
||||
<path
|
||||
d="M5.05843 10.6276C5.05843 11.3233 4.49442 11.8873 3.79868 11.8873C3.10295 11.8873 2.53894 11.3233 2.53894 10.6276C2.53894 9.93185 3.10295 9.36785 3.79868 9.36785C4.49442 9.36785 5.05843 9.93185 5.05843 10.6276Z" />
|
||||
<path
|
||||
d="M9.81307 9.55902H10.7808C11.117 9.55902 11.3011 9.55992 11.4332 9.57089L11.4479 9.57219L11.4492 9.58689C11.4602 9.71903 11.4611 9.90305 11.4611 10.2393V11.207C11.4611 11.5433 11.4602 11.7273 11.4492 11.8595L11.4479 11.8742L11.4332 11.8755C11.3011 11.8864 11.117 11.8873 10.7808 11.8873H9.81307C9.47678 11.8873 9.29277 11.8864 9.16063 11.8755L9.14592 11.8742L9.14463 11.8595C9.13365 11.7273 9.13276 11.5433 9.13276 11.207V10.2393C9.13276 9.90305 9.13365 9.71903 9.14463 9.58689L9.14592 9.57219L9.16063 9.57089C9.29277 9.55992 9.47678 9.55902 9.81307 9.55902Z" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 4.4 KiB |
@@ -0,0 +1,5 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="none">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd"
|
||||
d="M8.00303 3.64414C6.69861 3.64414 5.64117 4.70158 5.64117 6.006C5.64117 7.31042 6.69861 8.36786 8.00303 8.36786C9.30744 8.36786 10.3649 7.31042 10.3649 6.006C10.3649 4.70158 9.30744 3.64414 8.00303 3.64414ZM3.9745 6.006C3.9745 3.78111 5.77813 1.97748 8.00303 1.97748C10.2279 1.97748 12.0315 3.78111 12.0315 6.006C12.0315 8.23089 10.2279 10.0345 8.00303 10.0345C5.77813 10.0345 3.9745 8.23089 3.9745 6.006ZM12.0234 2.73039C12.1961 2.30378 12.6819 2.09793 13.1085 2.27062C14.5833 2.86762 15.6261 4.31403 15.6261 6.006C15.6261 7.69797 14.5833 9.14439 13.1085 9.74138C12.6819 9.91407 12.1961 9.70823 12.0234 9.28161C11.8507 8.855 12.0565 8.36917 12.4831 8.19649C13.3502 7.84549 13.9595 6.9959 13.9595 6.006C13.9595 5.01611 13.3502 4.16651 12.4831 3.81552C12.0565 3.64283 11.8507 3.157 12.0234 2.73039ZM6.77537 11.563H10C10.4603 11.563 10.8334 11.9361 10.8334 12.3964C10.8334 12.8566 10.4603 13.2297 10 13.2297H6.80483C6.04904 13.2297 5.52394 13.2302 5.11329 13.2582C4.71013 13.2857 4.47852 13.337 4.30339 13.4095C3.72467 13.6492 3.26488 14.109 3.02516 14.6877C2.95262 14.8629 2.90135 15.0945 2.87385 15.4976C2.84583 15.9083 2.84538 16.4334 2.84538 17.1892C2.84538 17.6494 2.47228 18.0225 2.01204 18.0225C1.55181 18.0225 1.17871 17.6494 1.17871 17.1892L1.17871 17.1597C1.17871 16.4403 1.1787 15.8583 1.21105 15.3842C1.24434 14.8962 1.31469 14.462 1.48536 14.0499C1.89424 13.0628 2.67848 12.2786 3.66559 11.8697C4.07764 11.699 4.51182 11.6287 4.99984 11.5954C5.47392 11.563 6.05597 11.563 6.77537 11.563ZM15.5916 11.563C16.0518 11.563 16.4249 11.9361 16.4249 12.3964V13.9594H17.988C18.4482 13.9594 18.8213 14.3325 18.8213 14.7928C18.8213 15.253 18.4482 15.6261 17.988 15.6261H16.4249V17.1892C16.4249 17.6494 16.0518 18.0225 15.5916 18.0225C15.1314 18.0225 14.7583 17.6494 14.7583 17.1892V15.6261H13.1952C12.735 15.6261 12.3619 15.253 12.3619 14.7928C12.3619 14.3325 12.735 13.9594 13.1952 13.9594H14.7583V12.3964C14.7583 11.9361 15.1314 11.563 15.5916 11.563Z"
|
||||
fill="#3370FF" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.1 KiB |
@@ -1,5 +1,4 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 18 18" fill="none">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd"
|
||||
d="M7.00303 2.64414C5.69861 2.64414 4.64117 3.70158 4.64117 5.006C4.64117 6.31042 5.69861 7.36786 7.00303 7.36786C8.30744 7.36786 9.36488 6.31042 9.36488 5.006C9.36488 3.70158 8.30744 2.64414 7.00303 2.64414ZM2.9745 5.006C2.9745 2.78111 4.77813 0.977478 7.00303 0.977478C9.22792 0.977478 11.0315 2.78111 11.0315 5.006C11.0315 7.23089 9.22792 9.03453 7.00303 9.03453C4.77813 9.03453 2.9745 7.23089 2.9745 5.006ZM11.0234 1.73039C11.1961 1.30378 11.6819 1.09793 12.1085 1.27062C13.5833 1.86762 14.6261 3.31403 14.6261 5.006C14.6261 6.69797 13.5833 8.14439 12.1085 8.74138C11.6819 8.91407 11.1961 8.70823 11.0234 8.28161C10.8507 7.855 11.0565 7.36917 11.4831 7.19649C12.3502 6.84549 12.9595 5.9959 12.9595 5.006C12.9595 4.01611 12.3502 3.16651 11.4831 2.81552C11.0565 2.64283 10.8507 2.157 11.0234 1.73039ZM5.77537 10.563H9.00002C9.46026 10.563 9.83335 10.9361 9.83335 11.3964C9.83335 11.8566 9.46026 12.2297 9.00002 12.2297H5.80483C5.04904 12.2297 4.52394 12.2302 4.11329 12.2582C3.71013 12.2857 3.47852 12.337 3.30339 12.4095C2.72467 12.6492 2.26488 13.109 2.02516 13.6877C1.95262 13.8629 1.90135 14.0945 1.87385 14.4976C1.84583 14.9083 1.84538 15.4334 1.84538 16.1892C1.84538 16.6494 1.47228 17.0225 1.01204 17.0225C0.551807 17.0225 0.178711 16.6494 0.178711 16.1892L0.178711 16.1597C0.178705 15.4403 0.1787 14.8583 0.211047 14.3842C0.244344 13.8962 0.314685 13.462 0.485364 13.0499C0.894235 12.0628 1.67848 11.2786 2.66559 10.8697C3.07764 10.699 3.51182 10.6287 3.99984 10.5954C4.47392 10.563 5.05597 10.563 5.77537 10.563ZM14.5916 10.563C15.0518 10.563 15.4249 10.9361 15.4249 11.3964V12.9594H16.988C17.4482 12.9594 17.8213 13.3325 17.8213 13.7928C17.8213 14.253 17.4482 14.6261 16.988 14.6261H15.4249V16.1892C15.4249 16.6494 15.0518 17.0225 14.5916 17.0225C14.1314 17.0225 13.7583 16.6494 13.7583 16.1892V14.6261H12.1952C11.735 14.6261 11.3619 14.253 11.3619 13.7928C11.3619 13.3325 11.735 12.9594 12.1952 12.9594H13.7583V11.3964C13.7583 10.9361 14.1314 10.563 14.5916 10.563Z"
|
||||
fill="#3370FF" />
|
||||
</svg>
|
||||
d="M7.00303 2.64414C5.69861 2.64414 4.64117 3.70158 4.64117 5.006C4.64117 6.31042 5.69861 7.36786 7.00303 7.36786C8.30744 7.36786 9.36488 6.31042 9.36488 5.006C9.36488 3.70158 8.30744 2.64414 7.00303 2.64414ZM2.9745 5.006C2.9745 2.78111 4.77813 0.977478 7.00303 0.977478C9.22792 0.977478 11.0315 2.78111 11.0315 5.006C11.0315 7.23089 9.22792 9.03453 7.00303 9.03453C4.77813 9.03453 2.9745 7.23089 2.9745 5.006ZM11.0234 1.73039C11.1961 1.30378 11.6819 1.09793 12.1085 1.27062C13.5833 1.86762 14.6261 3.31403 14.6261 5.006C14.6261 6.69797 13.5833 8.14439 12.1085 8.74138C11.6819 8.91407 11.1961 8.70823 11.0234 8.28161C10.8507 7.855 11.0565 7.36917 11.4831 7.19649C12.3502 6.84549 12.9595 5.9959 12.9595 5.006C12.9595 4.01611 12.3502 3.16651 11.4831 2.81552C11.0565 2.64283 10.8507 2.157 11.0234 1.73039ZM5.77537 10.563H9.00002C9.46026 10.563 9.83335 10.9361 9.83335 11.3964C9.83335 11.8566 9.46026 12.2297 9.00002 12.2297H5.80483C5.04904 12.2297 4.52394 12.2302 4.11329 12.2582C3.71013 12.2857 3.47852 12.337 3.30339 12.4095C2.72467 12.6492 2.26488 13.109 2.02516 13.6877C1.95262 13.8629 1.90135 14.0945 1.87385 14.4976C1.84583 14.9083 1.84538 15.4334 1.84538 16.1892C1.84538 16.6494 1.47228 17.0225 1.01204 17.0225C0.551807 17.0225 0.178711 16.6494 0.178711 16.1892L0.178711 16.1597C0.178705 15.4403 0.1787 14.8583 0.211047 14.3842C0.244344 13.8962 0.314685 13.462 0.485364 13.0499C0.894235 12.0628 1.67848 11.2786 2.66559 10.8697C3.07764 10.699 3.51182 10.6287 3.99984 10.5954C4.47392 10.563 5.05597 10.563 5.77537 10.563ZM14.5916 10.563C15.0518 10.563 15.4249 10.9361 15.4249 11.3964V12.9594H16.988C17.4482 12.9594 17.8213 13.3325 17.8213 13.7928C17.8213 14.253 17.4482 14.6261 16.988 14.6261H15.4249V16.1892C15.4249 16.6494 15.0518 17.0225 14.5916 17.0225C14.1314 17.0225 13.7583 16.6494 13.7583 16.1892V14.6261H12.1952C11.735 14.6261 11.3619 14.253 11.3619 13.7928C11.3619 13.3325 11.735 12.9594 12.1952 12.9594H13.7583V11.3964C13.7583 10.9361 14.1314 10.563 14.5916 10.563Z"/>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 2.1 KiB |
17
packages/web/components/common/MyBox/FormLabel.tsx
Normal file
@@ -0,0 +1,17 @@
|
||||
import React from 'react';
|
||||
import { Box, BoxProps } from '@chakra-ui/react';
|
||||
|
||||
const FormLabel = ({
|
||||
children,
|
||||
...props
|
||||
}: BoxProps & {
|
||||
children: React.ReactNode;
|
||||
}) => {
|
||||
return (
|
||||
<Box color={'myGray.900'} fontSize={'sm'} {...props}>
|
||||
{children}
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
export default FormLabel;
|
||||
@@ -1,16 +1,17 @@
|
||||
import React, { forwardRef } from 'react';
|
||||
import { Box, BoxProps } from '@chakra-ui/react';
|
||||
import { Box, BoxProps, SpinnerProps } from '@chakra-ui/react';
|
||||
import Loading from '../MyLoading';
|
||||
|
||||
type Props = BoxProps & {
|
||||
isLoading?: boolean;
|
||||
text?: string;
|
||||
size?: SpinnerProps['size'];
|
||||
};
|
||||
|
||||
const MyBox = ({ text, isLoading, children, ...props }: Props, ref: any) => {
|
||||
const MyBox = ({ text, isLoading, children, size, ...props }: Props, ref: any) => {
|
||||
return (
|
||||
<Box ref={ref} position={isLoading ? 'relative' : 'unset'} {...props}>
|
||||
{isLoading && <Loading fixed={false} text={text} />}
|
||||
{isLoading && <Loading fixed={false} text={text} size={size} />}
|
||||
{children}
|
||||
</Box>
|
||||
);
|
||||
|
||||
@@ -3,7 +3,14 @@ import { Divider, type DividerProps } from '@chakra-ui/react';
|
||||
|
||||
const MyDivider = (props: DividerProps) => {
|
||||
const { h } = props;
|
||||
return <Divider my={4} borderBottomWidth={h || '1x'} {...props}></Divider>;
|
||||
return (
|
||||
<Divider
|
||||
my={4}
|
||||
borderBottomWidth={h || '2px'}
|
||||
borderColor={props.color || 'myGray.200'}
|
||||
{...props}
|
||||
></Divider>
|
||||
);
|
||||
};
|
||||
|
||||
export default MyDivider;
|
||||
|
||||
@@ -56,7 +56,7 @@ const CustomRightDrawer = ({
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
<Box flex={'1'} fontSize={'lg'}>
|
||||
<Box flex={'1'} fontSize={'md'}>
|
||||
{title}
|
||||
</Box>
|
||||
<CloseButton position={'relative'} fontSize={'sm'} top={0} right={0} onClick={onClose} />
|
||||
|
||||
@@ -61,7 +61,7 @@ const MyRightDrawer = ({
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
<Box flex={'1'} fontSize={'lg'}>
|
||||
<Box flex={'1'} fontSize={'md'}>
|
||||
{title}
|
||||
</Box>
|
||||
<DrawerCloseButton position={'relative'} fontSize={'sm'} top={0} right={0} />
|
||||
|
||||
@@ -1,21 +1,23 @@
|
||||
import React from 'react';
|
||||
import { Spinner, Flex, Box } from '@chakra-ui/react';
|
||||
import { Spinner, Flex, Box, SpinnerProps } from '@chakra-ui/react';
|
||||
|
||||
const Loading = ({
|
||||
fixed = true,
|
||||
text = '',
|
||||
bg = 'rgba(255,255,255,0.5)',
|
||||
zIndex = 1000
|
||||
zIndex = 1000,
|
||||
size = 'lg'
|
||||
}: {
|
||||
fixed?: boolean;
|
||||
text?: string;
|
||||
bg?: string;
|
||||
zIndex?: number;
|
||||
size?: SpinnerProps['size'];
|
||||
}) => {
|
||||
return (
|
||||
<Flex
|
||||
position={fixed ? 'fixed' : 'absolute'}
|
||||
zIndex={zIndex}
|
||||
zIndex={fixed ? zIndex : 10}
|
||||
bg={bg}
|
||||
borderRadius={'md'}
|
||||
top={0}
|
||||
@@ -31,7 +33,7 @@ const Loading = ({
|
||||
speed="0.65s"
|
||||
emptyColor="myGray.100"
|
||||
color="primary.500"
|
||||
size="xl"
|
||||
size={size}
|
||||
/>
|
||||
{text && (
|
||||
<Box mt={2} color="primary.600" fontWeight={'bold'}>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { useRef, useState } from 'react';
|
||||
import React, { useMemo, useRef, useState } from 'react';
|
||||
import {
|
||||
Menu,
|
||||
MenuList,
|
||||
@@ -9,27 +9,35 @@ import {
|
||||
MenuItemProps
|
||||
} from '@chakra-ui/react';
|
||||
import MyIcon from '../Icon';
|
||||
import MyDivider from '../MyDivider';
|
||||
import type { IconNameType } from '../Icon/type';
|
||||
|
||||
type MenuItemType = 'primary' | 'danger';
|
||||
export type MenuItemType = 'primary' | 'danger';
|
||||
|
||||
export type Props = {
|
||||
width?: number | string;
|
||||
offset?: [number, number];
|
||||
Button: React.ReactNode;
|
||||
trigger?: 'hover' | 'click';
|
||||
iconSize?: string;
|
||||
menuList: {
|
||||
isActive?: boolean;
|
||||
label: string | React.ReactNode;
|
||||
icon?: string;
|
||||
type?: MenuItemType;
|
||||
onClick: () => any;
|
||||
label?: string;
|
||||
children: {
|
||||
isActive?: boolean;
|
||||
type?: MenuItemType;
|
||||
icon?: IconNameType | string;
|
||||
label: string | React.ReactNode;
|
||||
description?: string;
|
||||
onClick: () => any;
|
||||
}[];
|
||||
}[];
|
||||
};
|
||||
|
||||
const MyMenu = ({
|
||||
width = 'auto',
|
||||
trigger = 'hover',
|
||||
offset = [0, 5],
|
||||
offset,
|
||||
iconSize = '1rem',
|
||||
Button,
|
||||
menuList
|
||||
}: Props) => {
|
||||
@@ -41,17 +49,19 @@ const MyMenu = ({
|
||||
}
|
||||
},
|
||||
danger: {
|
||||
color: 'red.600',
|
||||
_hover: {
|
||||
color: 'red.600',
|
||||
background: 'red.1'
|
||||
}
|
||||
}
|
||||
};
|
||||
const menuItemStyles: MenuItemProps = {
|
||||
borderRadius: 'sm',
|
||||
py: 3,
|
||||
py: 2,
|
||||
px: 3,
|
||||
display: 'flex',
|
||||
alignItems: 'center'
|
||||
alignItems: 'center',
|
||||
fontSize: 'sm'
|
||||
};
|
||||
const ref = useRef<HTMLDivElement>(null);
|
||||
const closeTimer = useRef<any>();
|
||||
@@ -64,8 +74,21 @@ const MyMenu = ({
|
||||
}
|
||||
});
|
||||
|
||||
const computeOffset = useMemo<[number, number]>(() => {
|
||||
if (offset) return offset;
|
||||
if (typeof width === 'number') return [-width / 2, 5];
|
||||
return [0, 5];
|
||||
}, [offset]);
|
||||
|
||||
return (
|
||||
<Menu offset={offset} isOpen={isOpen} autoSelect={false} direction={'ltr'} isLazy>
|
||||
<Menu
|
||||
offset={computeOffset}
|
||||
isOpen={isOpen}
|
||||
autoSelect={false}
|
||||
direction={'ltr'}
|
||||
isLazy
|
||||
lazyBehavior={'keepMounted'}
|
||||
>
|
||||
<Box
|
||||
ref={ref}
|
||||
onMouseEnter={() => {
|
||||
@@ -84,7 +107,8 @@ const MyMenu = ({
|
||||
>
|
||||
<Box
|
||||
position={'relative'}
|
||||
onClickCapture={() => {
|
||||
onClickCapture={(e) => {
|
||||
e.stopPropagation();
|
||||
if (trigger === 'click') {
|
||||
setIsOpen(!isOpen);
|
||||
}
|
||||
@@ -109,23 +133,41 @@ const MyMenu = ({
|
||||
'0px 2px 4px rgba(161, 167, 179, 0.25), 0px 0px 1px rgba(121, 141, 159, 0.25);'
|
||||
}
|
||||
>
|
||||
{menuList.map((item, i) => (
|
||||
<MenuItem
|
||||
key={i}
|
||||
{...menuItemStyles}
|
||||
{...typeMapStyle[item.type || 'primary']}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
setIsOpen(false);
|
||||
item.onClick && item.onClick();
|
||||
}}
|
||||
color={item.isActive ? 'primary.700' : 'myGray.600'}
|
||||
whiteSpace={'pre-wrap'}
|
||||
>
|
||||
{!!item.icon && <MyIcon name={item.icon as any} w={'16px'} mr={2} />}
|
||||
{item.label}
|
||||
</MenuItem>
|
||||
))}
|
||||
{menuList.map((item, i) => {
|
||||
return (
|
||||
<Box key={i}>
|
||||
{item.label && <Box fontSize={'sm'}>{item.label}</Box>}
|
||||
{i !== 0 && <MyDivider h={'1.5px'} my={1} />}
|
||||
{item.children.map((child, index) => (
|
||||
<MenuItem
|
||||
key={index}
|
||||
{...menuItemStyles}
|
||||
onClickCapture={(e) => {
|
||||
e.stopPropagation();
|
||||
setIsOpen(false);
|
||||
child.onClick && child.onClick();
|
||||
}}
|
||||
color={child.isActive ? 'primary.700' : 'myGray.600'}
|
||||
whiteSpace={'pre-wrap'}
|
||||
_notLast={{ mb: 0.5 }}
|
||||
{...typeMapStyle[child.type || 'primary']}
|
||||
>
|
||||
{!!child.icon && <MyIcon name={child.icon as any} w={iconSize} mr={3} />}
|
||||
<Box>
|
||||
<Box color={child.description ? 'myGray.900' : 'inherit'} fontSize={'sm'}>
|
||||
{child.label}
|
||||
</Box>
|
||||
{child.description && (
|
||||
<Box color={'myGray.500'} fontSize={'mini'}>
|
||||
{child.description}
|
||||
</Box>
|
||||
)}
|
||||
</Box>
|
||||
</MenuItem>
|
||||
))}
|
||||
</Box>
|
||||
);
|
||||
})}
|
||||
</MenuList>
|
||||
</Box>
|
||||
</Menu>
|
||||
|
||||
92
packages/web/components/common/MyModal/EditFolderModal.tsx
Normal file
@@ -0,0 +1,92 @@
|
||||
import React, { useMemo } from 'react';
|
||||
import { ModalFooter, ModalBody, Input, Button, Box, Textarea } from '@chakra-ui/react';
|
||||
import MyModal from './index';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { useRequest2 } from '../../../hooks/useRequest';
|
||||
import FormLabel from '../MyBox/FormLabel';
|
||||
import { useForm } from 'react-hook-form';
|
||||
|
||||
export type EditFolderFormType = {
|
||||
id?: string;
|
||||
name?: string;
|
||||
intro?: string;
|
||||
};
|
||||
type CommitType = {
|
||||
name: string;
|
||||
intro?: string;
|
||||
};
|
||||
|
||||
const EditFolderModal = ({
|
||||
onClose,
|
||||
onCreate,
|
||||
onEdit,
|
||||
id,
|
||||
name,
|
||||
intro
|
||||
}: EditFolderFormType & {
|
||||
onClose: () => void;
|
||||
onCreate: (data: CommitType) => any;
|
||||
onEdit: (data: CommitType & { id: string }) => any;
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
const isEdit = !!id;
|
||||
const { register, handleSubmit } = useForm<EditFolderFormType>({
|
||||
defaultValues: {
|
||||
name,
|
||||
intro
|
||||
}
|
||||
});
|
||||
|
||||
const typeMap = useMemo(
|
||||
() =>
|
||||
isEdit
|
||||
? {
|
||||
title: t('dataset.Edit Folder')
|
||||
}
|
||||
: {
|
||||
title: t('dataset.Create Folder')
|
||||
},
|
||||
[isEdit, t]
|
||||
);
|
||||
|
||||
const { run: onSave, loading } = useRequest2(
|
||||
({ name = '', intro }: EditFolderFormType) => {
|
||||
if (!name) return;
|
||||
|
||||
if (isEdit) return onEdit({ id, name, intro });
|
||||
return onCreate({ name, intro });
|
||||
},
|
||||
{
|
||||
onSuccess: (res) => {
|
||||
onClose();
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
return (
|
||||
<MyModal isOpen onClose={onClose} iconSrc="common/folderFill" title={typeMap.title}>
|
||||
<ModalBody>
|
||||
<Box>
|
||||
<FormLabel mb={1}>{t('common.Input name')}</FormLabel>
|
||||
<Input
|
||||
{...register('name', { required: true })}
|
||||
bg={'myGray.50'}
|
||||
autoFocus
|
||||
maxLength={20}
|
||||
/>
|
||||
</Box>
|
||||
<Box mt={4}>
|
||||
<FormLabel mb={1}>{t('common.Input folder description')}</FormLabel>
|
||||
<Textarea {...register('intro')} bg={'myGray.50'} maxLength={200} />
|
||||
</Box>
|
||||
</ModalBody>
|
||||
<ModalFooter>
|
||||
<Button isLoading={loading} onClick={handleSubmit(onSave)} px={6}>
|
||||
{t('common.Confirm')}
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
</MyModal>
|
||||
);
|
||||
};
|
||||
|
||||
export default EditFolderModal;
|
||||
@@ -64,6 +64,7 @@ const MyModal = ({
|
||||
borderBottom={'1px solid #F4F6F8'}
|
||||
roundedTop={'lg'}
|
||||
py={'10px'}
|
||||
fontSize={'md'}
|
||||
>
|
||||
{iconSrc && (
|
||||
<>
|
||||
@@ -77,7 +78,7 @@ const MyModal = ({
|
||||
{title}
|
||||
<Box flex={1} />
|
||||
{onClose && (
|
||||
<ModalCloseButton position={'relative'} fontSize={'sm'} top={0} right={0} />
|
||||
<ModalCloseButton position={'relative'} fontSize={'xs'} top={0} right={0} />
|
||||
)}
|
||||
</ModalHeader>
|
||||
)}
|
||||
|
||||
48
packages/web/components/common/MyPopover/index.tsx
Normal file
@@ -0,0 +1,48 @@
|
||||
import React from 'react';
|
||||
import {
|
||||
Popover,
|
||||
PopoverTrigger,
|
||||
PopoverContent,
|
||||
useDisclosure,
|
||||
PlacementWithLogical
|
||||
} from '@chakra-ui/react';
|
||||
|
||||
const MyPopover = ({
|
||||
Trigger,
|
||||
placement,
|
||||
offset,
|
||||
trigger,
|
||||
children
|
||||
}: {
|
||||
Trigger: React.ReactNode;
|
||||
placement?: PlacementWithLogical;
|
||||
offset?: [number, number];
|
||||
trigger?: 'hover' | 'click';
|
||||
children: (e: { onClose: () => void }) => React.ReactNode;
|
||||
}) => {
|
||||
const firstFieldRef = React.useRef(null);
|
||||
|
||||
const { onOpen, onClose, isOpen } = useDisclosure();
|
||||
|
||||
return (
|
||||
<Popover
|
||||
isOpen={isOpen}
|
||||
initialFocusRef={firstFieldRef}
|
||||
onOpen={onOpen}
|
||||
onClose={onClose}
|
||||
placement={placement}
|
||||
offset={offset}
|
||||
closeOnBlur={false}
|
||||
trigger={trigger}
|
||||
openDelay={100}
|
||||
closeDelay={100}
|
||||
isLazy
|
||||
lazyBehavior="keepMounted"
|
||||
>
|
||||
<PopoverTrigger>{Trigger}</PopoverTrigger>
|
||||
<PopoverContent p={4}>{children({ onClose })}</PopoverContent>
|
||||
</Popover>
|
||||
);
|
||||
};
|
||||
|
||||
export default MyPopover;
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Box, Flex, useDisclosure, useOutsideClick } from '@chakra-ui/react';
|
||||
import React, { useRef } from 'react';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import FillTag from '../Tag/index';
|
||||
import MyTag from '../Tag/index';
|
||||
import MyIcon from '../Icon';
|
||||
|
||||
export type SelectProps = {
|
||||
@@ -51,7 +51,7 @@ const MultipleSelect = ({
|
||||
if (!listItem) return null;
|
||||
|
||||
return (
|
||||
<FillTag colorSchema="blue" p={2} cursor={'default'}>
|
||||
<MyTag colorSchema="blue" p={2} cursor={'default'}>
|
||||
{listItem.alias || listItem.label}
|
||||
<MyIcon
|
||||
name={'common/closeLight'}
|
||||
@@ -63,7 +63,7 @@ const MultipleSelect = ({
|
||||
onSelect(value.filter((i) => i !== item));
|
||||
}}
|
||||
/>
|
||||
</FillTag>
|
||||
</MyTag>
|
||||
);
|
||||
})}
|
||||
{value.length === 0 && placeholder && (
|
||||
|
||||
@@ -16,12 +16,13 @@ import { useLoading } from '../../../hooks/useLoading';
|
||||
import MyIcon from '../Icon';
|
||||
|
||||
export type SelectProps = ButtonProps & {
|
||||
value?: string;
|
||||
value?: string | number;
|
||||
placeholder?: string;
|
||||
list: {
|
||||
alias?: string;
|
||||
label: string | React.ReactNode;
|
||||
value: string;
|
||||
description?: string;
|
||||
value: string | number;
|
||||
}[];
|
||||
isLoading?: boolean;
|
||||
onchange?: (val: any) => void;
|
||||
@@ -125,18 +126,27 @@ const MySelect = (
|
||||
{...menuItemStyles}
|
||||
{...(value === item.value
|
||||
? {
|
||||
color: 'primary.500',
|
||||
bg: 'myWhite.300'
|
||||
color: 'primary.600',
|
||||
bg: 'myGray.100'
|
||||
}
|
||||
: {})}
|
||||
: {
|
||||
color: 'myGray.900'
|
||||
})}
|
||||
onClick={() => {
|
||||
if (onchange && value !== item.value) {
|
||||
onchange(item.value);
|
||||
}
|
||||
}}
|
||||
whiteSpace={'pre-wrap'}
|
||||
fontSize={'sm'}
|
||||
display={'block'}
|
||||
>
|
||||
{item.label}
|
||||
<Box>{item.label}</Box>
|
||||
{item.description && (
|
||||
<Box color={'myGray.500'} fontSize={'xs'}>
|
||||
{item.description}
|
||||
</Box>
|
||||
)}
|
||||
</MenuItem>
|
||||
))}
|
||||
</MenuList>
|
||||
|
||||
@@ -9,7 +9,7 @@ type Props = IconProps & {
|
||||
const QuestionTip = ({ label, maxW, ...props }: Props) => {
|
||||
return (
|
||||
<MyTooltip label={label} maxW={maxW}>
|
||||
<QuestionOutlineIcon {...props} />
|
||||
<QuestionOutlineIcon w={'0.9rem'} {...props} />
|
||||
</MyTooltip>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -9,32 +9,24 @@ const MyTooltip = ({ children, forceShow = false, shouldWrapChildren = true, ...
|
||||
const [isPc] = useMediaQuery('(min-width: 900px)');
|
||||
|
||||
return isPc || forceShow ? (
|
||||
<Box
|
||||
css={css({
|
||||
'& span': {
|
||||
display: 'block'
|
||||
}
|
||||
})}
|
||||
<Tooltip
|
||||
className="chakra-tooltip"
|
||||
bg={'white'}
|
||||
arrowShadowColor={'rgba(0,0,0,0.05)'}
|
||||
hasArrow
|
||||
arrowSize={12}
|
||||
offset={[-15, 15]}
|
||||
color={'myGray.800'}
|
||||
px={4}
|
||||
py={2}
|
||||
borderRadius={'8px'}
|
||||
whiteSpace={'pre-wrap'}
|
||||
boxShadow={'1px 1px 10px rgba(0,0,0,0.2)'}
|
||||
shouldWrapChildren={shouldWrapChildren}
|
||||
{...props}
|
||||
>
|
||||
<Tooltip
|
||||
className="tooltip"
|
||||
bg={'white'}
|
||||
arrowShadowColor={'rgba(0,0,0,0.05)'}
|
||||
hasArrow
|
||||
arrowSize={12}
|
||||
offset={[-15, 15]}
|
||||
color={'myGray.800'}
|
||||
px={4}
|
||||
py={2}
|
||||
borderRadius={'8px'}
|
||||
whiteSpace={'pre-wrap'}
|
||||
boxShadow={'1px 1px 10px rgba(0,0,0,0.2)'}
|
||||
shouldWrapChildren={shouldWrapChildren}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
</Tooltip>
|
||||
</Box>
|
||||
{children}
|
||||
</Tooltip>
|
||||
) : (
|
||||
<>{children}</>
|
||||
);
|
||||
|
||||
@@ -2,7 +2,6 @@ import React from 'react';
|
||||
import { Box, Flex, useTheme, Grid, type GridProps } from '@chakra-ui/react';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import MyTooltip from '../MyTooltip';
|
||||
import { QuestionOutlineIcon } from '@chakra-ui/icons';
|
||||
import QuestionTip from '../MyTooltip/QuestionTip';
|
||||
|
||||
// @ts-ignore
|
||||
@@ -95,6 +94,7 @@ const LeftRadio = ({
|
||||
color={'myGray.900'}
|
||||
fontWeight={item.desc ? '500' : 'normal'}
|
||||
whiteSpace={'nowrap'}
|
||||
fontSize={'sm'}
|
||||
>
|
||||
{typeof item.title === 'string' ? t(item.title) : item.title}
|
||||
</Box>
|
||||
|
||||
@@ -23,6 +23,7 @@ const RowTabs = ({ list, value, onChange, py = '7px', px = '12px', ...props }: P
|
||||
borderColor={'borderColor.base'}
|
||||
bg={'myGray.50'}
|
||||
gap={'4px'}
|
||||
fontSize={'sm'}
|
||||
{...props}
|
||||
>
|
||||
{list.map((item) => (
|
||||
|
||||
@@ -1,69 +1,83 @@
|
||||
import React, { useMemo } from 'react';
|
||||
import { Flex, type FlexProps } from '@chakra-ui/react';
|
||||
|
||||
interface Props extends FlexProps {
|
||||
children: React.ReactNode | React.ReactNode[];
|
||||
colorSchema?: 'blue' | 'green' | 'gray' | 'purple';
|
||||
type?: 'fill' | 'solid';
|
||||
}
|
||||
type ColorSchemaType = 'white' | 'blue' | 'green' | 'red' | 'yellow' | 'gray' | 'purple' | 'adora';
|
||||
|
||||
const MyTag = ({ children, colorSchema = 'blue', type = 'fill', ...props }: Props) => {
|
||||
export type TagProps = FlexProps & {
|
||||
children: React.ReactNode | React.ReactNode[];
|
||||
colorSchema?: ColorSchemaType;
|
||||
type?: 'fill' | 'borderFill' | 'borderSolid';
|
||||
};
|
||||
|
||||
const colorMap: Record<
|
||||
ColorSchemaType,
|
||||
{
|
||||
borderColor: string;
|
||||
bg: string;
|
||||
color: string;
|
||||
}
|
||||
> = {
|
||||
white: {
|
||||
borderColor: 'myGray.400',
|
||||
bg: 'white',
|
||||
color: 'myGray.700'
|
||||
},
|
||||
yellow: {
|
||||
borderColor: 'yellow.200',
|
||||
bg: 'yellow.50',
|
||||
color: 'yellow.600'
|
||||
},
|
||||
green: {
|
||||
borderColor: 'green.200',
|
||||
bg: 'green.50',
|
||||
color: 'green.600'
|
||||
},
|
||||
red: {
|
||||
borderColor: 'red.200',
|
||||
bg: 'red.50',
|
||||
color: 'red.600'
|
||||
},
|
||||
gray: {
|
||||
borderColor: 'myGray.200',
|
||||
bg: 'myGray.50',
|
||||
color: 'myGray.700'
|
||||
},
|
||||
blue: {
|
||||
borderColor: 'primary.200',
|
||||
bg: 'primary.50',
|
||||
color: 'primary.600'
|
||||
},
|
||||
purple: {
|
||||
borderColor: '#ECF',
|
||||
bg: '#F6EEFA',
|
||||
color: '#A558C9'
|
||||
},
|
||||
adora: {
|
||||
borderColor: '#D3CAFF',
|
||||
bg: '#F0EEFF',
|
||||
color: '#6F5DD7'
|
||||
}
|
||||
};
|
||||
|
||||
const MyTag = ({ children, colorSchema = 'blue', type = 'fill', ...props }: TagProps) => {
|
||||
const theme = useMemo(() => {
|
||||
const fillMap = {
|
||||
blue: {
|
||||
borderColor: 'primary.200',
|
||||
bg: 'primary.50',
|
||||
color: 'primary.700'
|
||||
},
|
||||
green: {
|
||||
borderColor: 'green.200',
|
||||
bg: 'green.50',
|
||||
color: 'green.600'
|
||||
},
|
||||
purple: {
|
||||
borderColor: '#ECF',
|
||||
bg: '#F6EEFA',
|
||||
color: '#A558C9'
|
||||
},
|
||||
gray: {
|
||||
borderColor: 'myGray.200',
|
||||
bg: 'myGray.50',
|
||||
color: 'myGray.700'
|
||||
}
|
||||
};
|
||||
const solidMap = {
|
||||
blue: {
|
||||
borderColor: 'primary.200',
|
||||
color: 'primary.600'
|
||||
},
|
||||
green: {
|
||||
borderColor: 'green.200',
|
||||
color: 'green.600'
|
||||
},
|
||||
purple: {
|
||||
borderColor: '#ECF',
|
||||
color: '#9E53C1'
|
||||
},
|
||||
gray: {
|
||||
borderColor: 'myGray.200',
|
||||
color: 'myGray.700'
|
||||
}
|
||||
};
|
||||
return type === 'fill' ? fillMap[colorSchema] : solidMap[colorSchema];
|
||||
return colorMap[colorSchema];
|
||||
}, [colorSchema]);
|
||||
|
||||
return (
|
||||
<Flex
|
||||
px={2}
|
||||
px={2.5}
|
||||
lineHeight={1}
|
||||
py={1}
|
||||
borderRadius={'sm'}
|
||||
fontSize={'sm'}
|
||||
fontSize={'xs'}
|
||||
alignItems={'center'}
|
||||
whiteSpace={'nowrap'}
|
||||
borderWidth={'1px'}
|
||||
{...props}
|
||||
{...theme}
|
||||
{...props}
|
||||
borderColor={type !== 'fill' ? theme.borderColor : 'transparent'}
|
||||
bg={type !== 'borderSolid' ? theme.bg : 'transparent'}
|
||||
>
|
||||
{children}
|
||||
</Flex>
|
||||
|
||||
@@ -24,7 +24,7 @@ const CodeEditor = (props: Props) => {
|
||||
<MyEditor {...props} bg={'myGray.50'} defaultHeight={600} />
|
||||
</ModalBody>
|
||||
<ModalFooter>
|
||||
<Button mr={2} onClick={onClose}>
|
||||
<Button mr={2} onClick={onClose} px={6}>
|
||||
{t('common.Confirm')}
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
|
||||
@@ -105,7 +105,7 @@ export default function Editor({
|
||||
>
|
||||
<Box
|
||||
color={'myGray.400'}
|
||||
fontSize={'11px'}
|
||||
fontSize={'mini'}
|
||||
userSelect={'none'}
|
||||
whiteSpace={'pre-wrap'}
|
||||
wordBreak={'break-all'}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
border-radius: var(--chakra-radii-md);
|
||||
padding: 8px 12px;
|
||||
background: var(--chakra-colors-gray-50);
|
||||
font-size: 14px;
|
||||
font-size: var(--chakra-fontSizes-sm);
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
border-radius: var(--chakra-radii-sm);
|
||||
padding: 6px 8px;
|
||||
background: #fff;
|
||||
font-size: 14px;
|
||||
font-size: var(--chakra-fontSizes-sm);
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
|
||||
@@ -78,7 +78,7 @@ const PromptEditor = ({
|
||||
/>
|
||||
</ModalBody>
|
||||
<ModalFooter>
|
||||
<Button mr={2} onClick={onClose}>
|
||||
<Button mr={2} onClick={onClose} px={6}>
|
||||
{t('common.Confirm')}
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
|
||||
@@ -142,7 +142,7 @@ const NodeInputSelect = ({
|
||||
|
||||
return (
|
||||
<MyMenu
|
||||
offset={[0, -1]}
|
||||
offset={[-0.5, -0.5]}
|
||||
Button={
|
||||
<Button
|
||||
size={'xs'}
|
||||
@@ -154,7 +154,7 @@ const NodeInputSelect = ({
|
||||
<Box fontWeight={'medium'}>{renderTypeData.title}</Box>
|
||||
</Button>
|
||||
}
|
||||
menuList={filterMenuList}
|
||||
menuList={[{ children: filterMenuList }]}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -72,6 +72,7 @@ export const useConfirm = (props?: {
|
||||
}) => {
|
||||
const timer = useRef<any>();
|
||||
const [countDownAmount, setCountDownAmount] = useState(countDown);
|
||||
const [requesting, setRequesting] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
timer.current = setInterval(() => {
|
||||
@@ -85,32 +86,40 @@ export const useConfirm = (props?: {
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<MyModal isOpen={isOpen} iconSrc={iconSrc} title={title} maxW={['90vw', '500px']}>
|
||||
<ModalBody pt={5} whiteSpace={'pre-wrap'}>
|
||||
<MyModal isOpen={isOpen} iconSrc={iconSrc} title={title} maxW={['90vw', '400px']}>
|
||||
<ModalBody pt={5} whiteSpace={'pre-wrap'} fontSize={'sm'}>
|
||||
{customContent}
|
||||
</ModalBody>
|
||||
{!hideFooter && (
|
||||
<ModalFooter>
|
||||
{showCancel && (
|
||||
<Button
|
||||
size={'sm'}
|
||||
variant={'whiteBase'}
|
||||
onClick={() => {
|
||||
onClose();
|
||||
typeof cancelCb.current === 'function' && cancelCb.current();
|
||||
}}
|
||||
px={5}
|
||||
>
|
||||
{closeText}
|
||||
</Button>
|
||||
)}
|
||||
|
||||
<Button
|
||||
size={'sm'}
|
||||
bg={bg ? bg : map.bg}
|
||||
isDisabled={countDownAmount > 0}
|
||||
ml={4}
|
||||
isLoading={isLoading}
|
||||
onClick={() => {
|
||||
onClose();
|
||||
typeof confirmCb.current === 'function' && confirmCb.current();
|
||||
ml={3}
|
||||
isLoading={isLoading || requesting}
|
||||
px={5}
|
||||
onClick={async () => {
|
||||
setRequesting(true);
|
||||
try {
|
||||
typeof confirmCb.current === 'function' && (await confirmCb.current());
|
||||
onClose();
|
||||
} catch (error) {}
|
||||
setRequesting(false);
|
||||
}}
|
||||
>
|
||||
{countDownAmount > 0 ? `${countDownAmount}s` : confirmText}
|
||||
|
||||
@@ -115,7 +115,9 @@ export const useEditTextarea = ({
|
||||
{closeBtnText}
|
||||
</Button>
|
||||
)}
|
||||
<Button onClick={onclickConfirm}>{t('common.Confirm')}</Button>
|
||||
<Button onClick={onclickConfirm} px={6}>
|
||||
{t('common.Confirm')}
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
</MyModal>
|
||||
),
|
||||
|
||||