Compare commits
22 Commits
v4.8.3
...
v4.8.5-alp
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c8bc9838e3 | ||
|
|
5c8c7fb9f2 | ||
|
|
d902d29c71 | ||
|
|
9d29b471bc | ||
|
|
4a33e04a08 | ||
|
|
a9ab9ebe8e | ||
|
|
24596a6e21 | ||
|
|
5cc01b8509 | ||
|
|
980b4d3db5 | ||
|
|
2b25e3cc2d | ||
|
|
565bfc8486 | ||
|
|
b17d14bb7d | ||
|
|
c11131d653 | ||
|
|
f7f4a8de4d | ||
|
|
6385794603 | ||
|
|
b8b26ad700 | ||
|
|
05611df056 | ||
|
|
d0085a23e6 | ||
|
|
bc6864c3dc | ||
|
|
b20d075d35 | ||
|
|
19c8a06d51 | ||
|
|
fcb915c988 |
96
.github/workflows/build-sandbox-image.yml
vendored
96
.github/workflows/build-sandbox-image.yml
vendored
@@ -10,6 +10,7 @@ jobs:
|
|||||||
build-fastgpt-sandbox-images:
|
build-fastgpt-sandbox-images:
|
||||||
runs-on: ubuntu-20.04
|
runs-on: ubuntu-20.04
|
||||||
steps:
|
steps:
|
||||||
|
# install env
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
with:
|
with:
|
||||||
@@ -30,22 +31,53 @@ jobs:
|
|||||||
key: ${{ runner.os }}-buildx-${{ github.sha }}
|
key: ${{ runner.os }}-buildx-${{ github.sha }}
|
||||||
restore-keys: |
|
restore-keys: |
|
||||||
${{ runner.os }}-buildx-
|
${{ runner.os }}-buildx-
|
||||||
|
|
||||||
|
# login docker
|
||||||
- name: Login to GitHub Container Registry
|
- name: Login to GitHub Container Registry
|
||||||
uses: docker/login-action@v2
|
uses: docker/login-action@v2
|
||||||
with:
|
with:
|
||||||
registry: ghcr.io
|
registry: ghcr.io
|
||||||
username: ${{ github.repository_owner }}
|
username: ${{ github.repository_owner }}
|
||||||
password: ${{ secrets.GH_PAT }}
|
password: ${{ secrets.GH_PAT }}
|
||||||
- name: Set DOCKER_REPO_TAGGED based on branch or tag
|
- name: Login to Ali Hub
|
||||||
|
uses: docker/login-action@v2
|
||||||
|
with:
|
||||||
|
registry: registry.cn-hangzhou.aliyuncs.com
|
||||||
|
username: ${{ secrets.ALI_HUB_USERNAME }}
|
||||||
|
password: ${{ secrets.ALI_HUB_PASSWORD }}
|
||||||
|
- name: Login to Docker Hub
|
||||||
|
uses: docker/login-action@v2
|
||||||
|
with:
|
||||||
|
username: ${{ secrets.DOCKER_HUB_NAME }}
|
||||||
|
password: ${{ secrets.DOCKER_HUB_PASSWORD }}
|
||||||
|
|
||||||
|
# Set tag
|
||||||
|
- name: Set image name and tag
|
||||||
run: |
|
run: |
|
||||||
if [[ "${{ github.ref_name }}" == "main" ]]; then
|
if [[ "${{ github.ref_name }}" == "main" ]]; then
|
||||||
echo "DOCKER_REPO_TAGGED=ghcr.io/${{ github.repository_owner }}/fastgpt-sandbox:latest" >> $GITHUB_ENV
|
echo "Git_Tag=ghcr.io/${{ github.repository_owner }}/fastgpt-sandbox:latest" >> $GITHUB_ENV
|
||||||
|
echo "Git_Latest=ghcr.io/${{ github.repository_owner }}/fastgpt-sandbox:latest" >> $GITHUB_ENV
|
||||||
|
echo "Ali_Tag=${{ secrets.ALI_IMAGE_NAME }}/fastgpt-sandbox:latest" >> $GITHUB_ENV
|
||||||
|
echo "Ali_Latest=${{ secrets.ALI_IMAGE_NAME }}/fastgpt-sandbox:latest" >> $GITHUB_ENV
|
||||||
|
echo "Docker_Hub_Tag=${{ secrets.DOCKER_IMAGE_NAME }}/fastgpt-sandbox:latest" >> $GITHUB_ENV
|
||||||
|
echo "Docker_Hub_Latest=${{ secrets.DOCKER_IMAGE_NAME }}/fastgpt-sandbox:latest" >> $GITHUB_ENV
|
||||||
else
|
else
|
||||||
echo "DOCKER_REPO_TAGGED=ghcr.io/${{ github.repository_owner }}/fastgpt-sandbox:${{ github.ref_name }}" >> $GITHUB_ENV
|
echo "Git_Tag=ghcr.io/${{ github.repository_owner }}/fastgpt-sandbox:${{ github.ref_name }}" >> $GITHUB_ENV
|
||||||
|
echo "Git_Latest=ghcr.io/${{ github.repository_owner }}/fastgpt-sandbox:latest" >> $GITHUB_ENV
|
||||||
|
echo "Ali_Tag=${{ secrets.ALI_IMAGE_NAME }}/fastgpt-sandbox:${{ github.ref_name }}" >> $GITHUB_ENV
|
||||||
|
echo "Ali_Latest=${{ secrets.ALI_IMAGE_NAME }}/fastgpt-sandbox:latest" >> $GITHUB_ENV
|
||||||
|
echo "Docker_Hub_Tag=${{ secrets.DOCKER_IMAGE_NAME }}/fastgpt-sandbox:${{ github.ref_name }}" >> $GITHUB_ENV
|
||||||
|
echo "Docker_Hub_Latest=${{ secrets.DOCKER_IMAGE_NAME }}/fastgpt-sandbox:latest" >> $GITHUB_ENV
|
||||||
fi
|
fi
|
||||||
|
|
||||||
- name: Build and publish image for main branch or tag push event
|
- name: Build and publish image for main branch or tag push event
|
||||||
env:
|
env:
|
||||||
DOCKER_REPO_TAGGED: ${{ env.DOCKER_REPO_TAGGED }}
|
Git_Tag: ${{ env.Git_Tag }}
|
||||||
|
Git_Latest: ${{ env.Git_Latest }}
|
||||||
|
Ali_Tag: ${{ env.Ali_Tag }}
|
||||||
|
Ali_Latest: ${{ env.Ali_Latest }}
|
||||||
|
Docker_Hub_Tag: ${{ env.Docker_Hub_Tag }}
|
||||||
|
Docker_Hub_Latest: ${{ env.Docker_Hub_Latest }}
|
||||||
run: |
|
run: |
|
||||||
docker buildx build \
|
docker buildx build \
|
||||||
-f projects/sandbox/Dockerfile \
|
-f projects/sandbox/Dockerfile \
|
||||||
@@ -55,54 +87,10 @@ jobs:
|
|||||||
--push \
|
--push \
|
||||||
--cache-from=type=local,src=/tmp/.buildx-cache \
|
--cache-from=type=local,src=/tmp/.buildx-cache \
|
||||||
--cache-to=type=local,dest=/tmp/.buildx-cache \
|
--cache-to=type=local,dest=/tmp/.buildx-cache \
|
||||||
-t ${DOCKER_REPO_TAGGED} \
|
-t ${Git_Tag} \
|
||||||
|
-t ${Git_Latest} \
|
||||||
|
-t ${Ali_Tag} \
|
||||||
|
-t ${Ali_Latest} \
|
||||||
|
-t ${Docker_Hub_Tag} \
|
||||||
|
-t ${Docker_Hub_Latest} \
|
||||||
.
|
.
|
||||||
push-to-ali-hub:
|
|
||||||
needs: build-fastgpt-sandbox-images
|
|
||||||
runs-on: ubuntu-20.04
|
|
||||||
steps:
|
|
||||||
- name: Checkout code
|
|
||||||
uses: actions/checkout@v3
|
|
||||||
- name: Login to Ali Hub
|
|
||||||
uses: docker/login-action@v2
|
|
||||||
with:
|
|
||||||
registry: registry.cn-hangzhou.aliyuncs.com
|
|
||||||
username: ${{ secrets.ALI_HUB_USERNAME }}
|
|
||||||
password: ${{ secrets.ALI_HUB_PASSWORD }}
|
|
||||||
- name: Set DOCKER_REPO_TAGGED based on branch or tag
|
|
||||||
run: |
|
|
||||||
if [[ "${{ github.ref_name }}" == "main" ]]; then
|
|
||||||
echo "IMAGE_TAG=latest" >> $GITHUB_ENV
|
|
||||||
else
|
|
||||||
echo "IMAGE_TAG=${{ github.ref_name }}" >> $GITHUB_ENV
|
|
||||||
fi
|
|
||||||
- name: Pull image from GitHub Container Registry
|
|
||||||
run: docker pull ghcr.io/${{ github.repository_owner }}/fastgpt-sandbox:${{env.IMAGE_TAG}}
|
|
||||||
- name: Tag image with Docker Hub repository name and version tag
|
|
||||||
run: docker tag ghcr.io/${{ github.repository_owner }}/fastgpt-sandbox:${{env.IMAGE_TAG}} ${{ secrets.ALI_IMAGE_NAME }}/fastgpt-sandbox:${{env.IMAGE_TAG}}
|
|
||||||
- name: Push image to Docker Hub
|
|
||||||
run: docker push ${{ secrets.ALI_IMAGE_NAME }}/fastgpt-sandbox:${{env.IMAGE_TAG}}
|
|
||||||
push-to-docker-hub:
|
|
||||||
needs: build-fastgpt-sandbox-images
|
|
||||||
runs-on: ubuntu-20.04
|
|
||||||
steps:
|
|
||||||
- name: Checkout code
|
|
||||||
uses: actions/checkout@v3
|
|
||||||
- name: Login to Docker Hub
|
|
||||||
uses: docker/login-action@v2
|
|
||||||
with:
|
|
||||||
username: ${{ secrets.DOCKER_HUB_NAME }}
|
|
||||||
password: ${{ secrets.DOCKER_HUB_PASSWORD }}
|
|
||||||
- name: Set DOCKER_REPO_TAGGED based on branch or tag
|
|
||||||
run: |
|
|
||||||
if [[ "${{ github.ref_name }}" == "main" ]]; then
|
|
||||||
echo "IMAGE_TAG=latest" >> $GITHUB_ENV
|
|
||||||
else
|
|
||||||
echo "IMAGE_TAG=${{ github.ref_name }}" >> $GITHUB_ENV
|
|
||||||
fi
|
|
||||||
- name: Pull image from GitHub Container Registry
|
|
||||||
run: docker pull ghcr.io/${{ github.repository_owner }}/fastgpt-sandbox:${{env.IMAGE_TAG}}
|
|
||||||
- name: Tag image with Docker Hub repository name and version tag
|
|
||||||
run: docker tag ghcr.io/${{ github.repository_owner }}/fastgpt-sandbox:${{env.IMAGE_TAG}} ${{ secrets.DOCKER_IMAGE_NAME }}/fastgpt-sandbox:${{env.IMAGE_TAG}}
|
|
||||||
- name: Push image to Docker Hub
|
|
||||||
run: docker push ${{ secrets.DOCKER_IMAGE_NAME }}/fastgpt-sandbox:${{env.IMAGE_TAG}}
|
|
||||||
|
|||||||
91
.github/workflows/fastgpt-image.yml
vendored
91
.github/workflows/fastgpt-image.yml
vendored
@@ -11,6 +11,7 @@ jobs:
|
|||||||
build-fastgpt-images:
|
build-fastgpt-images:
|
||||||
runs-on: ubuntu-20.04
|
runs-on: ubuntu-20.04
|
||||||
steps:
|
steps:
|
||||||
|
# install env
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
with:
|
with:
|
||||||
@@ -31,19 +32,45 @@ jobs:
|
|||||||
key: ${{ runner.os }}-buildx-${{ github.sha }}
|
key: ${{ runner.os }}-buildx-${{ github.sha }}
|
||||||
restore-keys: |
|
restore-keys: |
|
||||||
${{ runner.os }}-buildx-
|
${{ runner.os }}-buildx-
|
||||||
|
|
||||||
|
# login docker
|
||||||
- name: Login to GitHub Container Registry
|
- name: Login to GitHub Container Registry
|
||||||
uses: docker/login-action@v2
|
uses: docker/login-action@v2
|
||||||
with:
|
with:
|
||||||
registry: ghcr.io
|
registry: ghcr.io
|
||||||
username: ${{ github.repository_owner }}
|
username: ${{ github.repository_owner }}
|
||||||
password: ${{ secrets.GH_PAT }}
|
password: ${{ secrets.GH_PAT }}
|
||||||
- name: Set DOCKER_REPO_TAGGED based on branch or tag
|
- name: Login to Ali Hub
|
||||||
|
uses: docker/login-action@v2
|
||||||
|
with:
|
||||||
|
registry: registry.cn-hangzhou.aliyuncs.com
|
||||||
|
username: ${{ secrets.ALI_HUB_USERNAME }}
|
||||||
|
password: ${{ secrets.ALI_HUB_PASSWORD }}
|
||||||
|
- name: Login to Docker Hub
|
||||||
|
uses: docker/login-action@v2
|
||||||
|
with:
|
||||||
|
username: ${{ secrets.DOCKER_HUB_NAME }}
|
||||||
|
password: ${{ secrets.DOCKER_HUB_PASSWORD }}
|
||||||
|
|
||||||
|
# Set tag
|
||||||
|
- name: Set image name and tag
|
||||||
run: |
|
run: |
|
||||||
if [[ "${{ github.ref_name }}" == "main" ]]; then
|
if [[ "${{ github.ref_name }}" == "main" ]]; then
|
||||||
echo "DOCKER_REPO_TAGGED=ghcr.io/${{ github.repository_owner }}/fastgpt:latest" >> $GITHUB_ENV
|
echo "Git_Tag=ghcr.io/${{ github.repository_owner }}/fastgpt:latest" >> $GITHUB_ENV
|
||||||
|
echo "Git_Latest=ghcr.io/${{ github.repository_owner }}/fastgpt:latest" >> $GITHUB_ENV
|
||||||
|
echo "Ali_Tag=${{ secrets.ALI_IMAGE_NAME }}/fastgpt:latest" >> $GITHUB_ENV
|
||||||
|
echo "Ali_Latest=${{ secrets.ALI_IMAGE_NAME }}/fastgpt:latest" >> $GITHUB_ENV
|
||||||
|
echo "Docker_Hub_Tag=${{ secrets.DOCKER_IMAGE_NAME }}/fastgpt:latest" >> $GITHUB_ENV
|
||||||
|
echo "Docker_Hub_Latest=${{ secrets.DOCKER_IMAGE_NAME }}/fastgpt:latest" >> $GITHUB_ENV
|
||||||
else
|
else
|
||||||
echo "DOCKER_REPO_TAGGED=ghcr.io/${{ github.repository_owner }}/fastgpt:${{ github.ref_name }}" >> $GITHUB_ENV
|
echo "Git_Tag=ghcr.io/${{ github.repository_owner }}/fastgpt:${{ github.ref_name }}" >> $GITHUB_ENV
|
||||||
|
echo "Git_Latest=ghcr.io/${{ github.repository_owner }}/fastgpt:latest" >> $GITHUB_ENV
|
||||||
|
echo "Ali_Tag=${{ secrets.ALI_IMAGE_NAME }}/fastgpt:${{ github.ref_name }}" >> $GITHUB_ENV
|
||||||
|
echo "Ali_Latest=${{ secrets.ALI_IMAGE_NAME }}/fastgpt:latest" >> $GITHUB_ENV
|
||||||
|
echo "Docker_Hub_Tag=${{ secrets.DOCKER_IMAGE_NAME }}/fastgpt:${{ github.ref_name }}" >> $GITHUB_ENV
|
||||||
|
echo "Docker_Hub_Latest=${{ secrets.DOCKER_IMAGE_NAME }}/fastgpt:latest" >> $GITHUB_ENV
|
||||||
fi
|
fi
|
||||||
|
|
||||||
- name: Build and publish image for main branch or tag push event
|
- name: Build and publish image for main branch or tag push event
|
||||||
env:
|
env:
|
||||||
DOCKER_REPO_TAGGED: ${{ env.DOCKER_REPO_TAGGED }}
|
DOCKER_REPO_TAGGED: ${{ env.DOCKER_REPO_TAGGED }}
|
||||||
@@ -56,56 +83,10 @@ jobs:
|
|||||||
--push \
|
--push \
|
||||||
--cache-from=type=local,src=/tmp/.buildx-cache \
|
--cache-from=type=local,src=/tmp/.buildx-cache \
|
||||||
--cache-to=type=local,dest=/tmp/.buildx-cache \
|
--cache-to=type=local,dest=/tmp/.buildx-cache \
|
||||||
-t ${DOCKER_REPO_TAGGED} \
|
-t ${Git_Tag} \
|
||||||
|
-t ${Git_Latest} \
|
||||||
|
-t ${Ali_Tag} \
|
||||||
|
-t ${Ali_Latest} \
|
||||||
|
-t ${Docker_Hub_Tag} \
|
||||||
|
-t ${Docker_Hub_Latest} \
|
||||||
.
|
.
|
||||||
push-to-docker-hub:
|
|
||||||
needs: build-fastgpt-images
|
|
||||||
runs-on: ubuntu-20.04
|
|
||||||
if: github.repository == 'labring/FastGPT'
|
|
||||||
steps:
|
|
||||||
- name: Checkout code
|
|
||||||
uses: actions/checkout@v3
|
|
||||||
- name: Login to Docker Hub
|
|
||||||
uses: docker/login-action@v2
|
|
||||||
with:
|
|
||||||
username: ${{ secrets.DOCKER_HUB_NAME }}
|
|
||||||
password: ${{ secrets.DOCKER_HUB_PASSWORD }}
|
|
||||||
- name: Set DOCKER_REPO_TAGGED based on branch or tag
|
|
||||||
run: |
|
|
||||||
if [[ "${{ github.ref_name }}" == "main" ]]; then
|
|
||||||
echo "IMAGE_TAG=latest" >> $GITHUB_ENV
|
|
||||||
else
|
|
||||||
echo "IMAGE_TAG=${{ github.ref_name }}" >> $GITHUB_ENV
|
|
||||||
fi
|
|
||||||
- name: Pull image from GitHub Container Registry
|
|
||||||
run: docker pull ghcr.io/${{ github.repository_owner }}/fastgpt:${{env.IMAGE_TAG}}
|
|
||||||
- name: Tag image with Docker Hub repository name and version tag
|
|
||||||
run: docker tag ghcr.io/${{ github.repository_owner }}/fastgpt:${{env.IMAGE_TAG}} ${{ secrets.DOCKER_IMAGE_NAME }}/fastgpt:${{env.IMAGE_TAG}}
|
|
||||||
- name: Push image to Docker Hub
|
|
||||||
run: docker push ${{ secrets.DOCKER_IMAGE_NAME }}/fastgpt:${{env.IMAGE_TAG}}
|
|
||||||
push-to-ali-hub:
|
|
||||||
needs: build-fastgpt-images
|
|
||||||
if: github.repository == 'labring/FastGPT'
|
|
||||||
runs-on: ubuntu-20.04
|
|
||||||
steps:
|
|
||||||
- name: Checkout code
|
|
||||||
uses: actions/checkout@v3
|
|
||||||
- name: Login to Ali Hub
|
|
||||||
uses: docker/login-action@v2
|
|
||||||
with:
|
|
||||||
registry: registry.cn-hangzhou.aliyuncs.com
|
|
||||||
username: ${{ secrets.ALI_HUB_USERNAME }}
|
|
||||||
password: ${{ secrets.ALI_HUB_PASSWORD }}
|
|
||||||
- name: Set DOCKER_REPO_TAGGED based on branch or tag
|
|
||||||
run: |
|
|
||||||
if [[ "${{ github.ref_name }}" == "main" ]]; then
|
|
||||||
echo "IMAGE_TAG=latest" >> $GITHUB_ENV
|
|
||||||
else
|
|
||||||
echo "IMAGE_TAG=${{ github.ref_name }}" >> $GITHUB_ENV
|
|
||||||
fi
|
|
||||||
- name: Pull image from GitHub Container Registry
|
|
||||||
run: docker pull ghcr.io/${{ github.repository_owner }}/fastgpt:${{env.IMAGE_TAG}}
|
|
||||||
- name: Tag image with Docker Hub repository name and version tag
|
|
||||||
run: docker tag ghcr.io/${{ github.repository_owner }}/fastgpt:${{env.IMAGE_TAG}} ${{ secrets.ALI_IMAGE_NAME }}/fastgpt:${{env.IMAGE_TAG}}
|
|
||||||
- name: Push image to Docker Hub
|
|
||||||
run: docker push ${{ secrets.ALI_IMAGE_NAME }}/fastgpt:${{env.IMAGE_TAG}}
|
|
||||||
|
|||||||
6
.vscode/nextapi.code-snippets
vendored
6
.vscode/nextapi.code-snippets
vendored
@@ -35,17 +35,19 @@
|
|||||||
"scope": "typescriptreact",
|
"scope": "typescriptreact",
|
||||||
"prefix": "context",
|
"prefix": "context",
|
||||||
"body": [
|
"body": [
|
||||||
"import { ReactNode } from 'react';",
|
"import React, { ReactNode } from 'react';",
|
||||||
"import { createContext } from 'use-context-selector';",
|
"import { createContext } from 'use-context-selector';",
|
||||||
"",
|
"",
|
||||||
"type ContextType = {$1};",
|
"type ContextType = {$1};",
|
||||||
"",
|
"",
|
||||||
"export const Context = createContext<ContextType>({});",
|
"export const Context = createContext<ContextType>({});",
|
||||||
"",
|
"",
|
||||||
"export const ContextProvider = ({ children }: { children: ReactNode }) => {",
|
"const ContextProvider = ({ children }: { children: ReactNode }) => {",
|
||||||
" const contextValue: ContextType = {};",
|
" const contextValue: ContextType = {};",
|
||||||
" return <Context.Provider value={contextValue}>{children}</Context.Provider>;",
|
" return <Context.Provider value={contextValue}>{children}</Context.Provider>;",
|
||||||
"};",
|
"};",
|
||||||
|
"",
|
||||||
|
"export default ContextProvider"
|
||||||
],
|
],
|
||||||
"description": "FastGPT usecontext template"
|
"description": "FastGPT usecontext template"
|
||||||
}
|
}
|
||||||
|
|||||||
5
.vscode/settings.json
vendored
5
.vscode/settings.json
vendored
@@ -1,10 +1,11 @@
|
|||||||
{
|
{
|
||||||
"editor.formatOnSave": true,
|
"editor.formatOnSave": true,
|
||||||
"editor.mouseWheelZoom": true,
|
"editor.mouseWheelZoom": true,
|
||||||
|
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||||
|
"prettier.prettierPath": "../node_modules/prettier",
|
||||||
"typescript.tsdk": "node_modules/typescript/lib",
|
"typescript.tsdk": "node_modules/typescript/lib",
|
||||||
"prettier.prettierPath": "",
|
|
||||||
"i18n-ally.localesPaths": [
|
"i18n-ally.localesPaths": [
|
||||||
"projects/app/i18n",
|
"packages/web/i18n",
|
||||||
],
|
],
|
||||||
"i18n-ally.enabledParsers": ["json", "yaml", "js", "ts"],
|
"i18n-ally.enabledParsers": ["json", "yaml", "js", "ts"],
|
||||||
"i18n-ally.keystyle": "nested",
|
"i18n-ally.keystyle": "nested",
|
||||||
|
|||||||
2
LICENSE
2
LICENSE
@@ -5,7 +5,7 @@ The FastGPT is licensed under the Apache License 2.0, with the following additio
|
|||||||
1. FastGPT is permitted to be used for commercialization. You can use FastGPT as a "backend-as-a-service" for your other applications, or delivering it to enterprises as an application development platform. However, when the following conditions are met, you must contact the producer to obtain a commercial license:
|
1. FastGPT is permitted to be used for commercialization. You can use FastGPT as a "backend-as-a-service" for your other applications, or delivering it to enterprises as an application development platform. However, when the following conditions are met, you must contact the producer to obtain a commercial license:
|
||||||
|
|
||||||
a. Multi-tenant SaaS service: Unless explicitly authorized by FastGPT in writing, you may not use the FastGPT.AI source code to operate a multi-tenant SaaS service that is similar to the FastGPT.
|
a. Multi-tenant SaaS service: Unless explicitly authorized by FastGPT in writing, you may not use the FastGPT.AI source code to operate a multi-tenant SaaS service that is similar to the FastGPT.
|
||||||
b. LOGO and copyright information: In the process of using FastGPT, you may not remove or moFastGPT the LOGO or copyright information in the FastGPT console.
|
b. LOGO and copyright information: In the process of using FastGPT, you may not remove or modify the LOGO or copyright information in the FastGPT console.
|
||||||
|
|
||||||
Please contact yujinlong@sealos.io by email to inquire about licensing matters.
|
Please contact yujinlong@sealos.io by email to inquire about licensing matters.
|
||||||
|
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 67 KiB After Width: | Height: | Size: 209 KiB |
@@ -11,7 +11,7 @@ weight: 708
|
|||||||
|
|
||||||
**开发环境下**,你需要将示例配置文件 `config.json` 复制成 `config.local.json` 文件才会生效。
|
**开发环境下**,你需要将示例配置文件 `config.json` 复制成 `config.local.json` 文件才会生效。
|
||||||
|
|
||||||
这个配置文件中包含了系统参数和各个模型配置,`使用时务必去掉注释!!!!!!!!!!!!!!`
|
这个配置文件中包含了系统参数和各个模型配置:
|
||||||
|
|
||||||
## 4.6.8+ 版本新配置文件
|
## 4.6.8+ 版本新配置文件
|
||||||
|
|
||||||
@@ -148,6 +148,9 @@ llm模型全部合并
|
|||||||
- /imgs/model/openai.svg - OpenAI GPT
|
- /imgs/model/openai.svg - OpenAI GPT
|
||||||
- /imgs/model/qwen.svg - 通义千问
|
- /imgs/model/qwen.svg - 通义千问
|
||||||
- /imgs/model/yi.svg - 零一万物
|
- /imgs/model/yi.svg - 零一万物
|
||||||
|
- /imgs/model/gemini.svg - gemini
|
||||||
|
- /imgs/model/deepseek.svg - deepseek
|
||||||
|
- /imgs/model/minimax.svg - minimax
|
||||||
-
|
-
|
||||||
|
|
||||||
## 特殊模型
|
## 特殊模型
|
||||||
@@ -158,7 +161,7 @@ llm模型全部合并
|
|||||||
|
|
||||||
1. [部署 ReRank 模型](/docs/development/custom-models/bge-rerank/)
|
1. [部署 ReRank 模型](/docs/development/custom-models/bge-rerank/)
|
||||||
1. 找到 FastGPT 的配置文件中的 `reRankModels`, 4.6.6 以前是 `ReRankModels`。
|
1. 找到 FastGPT 的配置文件中的 `reRankModels`, 4.6.6 以前是 `ReRankModels`。
|
||||||
2. 修改对应的值:(记得去掉注释)
|
2. 修改对应的值:
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -204,7 +204,7 @@ docker restart oneapi
|
|||||||
|
|
||||||
### Mongo 副本集自动初始化失败
|
### 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 版本。
|
||||||
|
|
||||||
如果是由于,无法自动初始化副本集合,可以手动初始化副本集:
|
如果是由于,无法自动初始化副本集合,可以手动初始化副本集:
|
||||||
|
|
||||||
@@ -363,3 +363,14 @@ mongo连接失败,查看mongo的运行状态**对应日志**。
|
|||||||
### 无法导出知识库、无法使用语音输入/播报
|
### 无法导出知识库、无法使用语音输入/播报
|
||||||
|
|
||||||
没配置 SSL 证书,无权使用部分功能。
|
没配置 SSL 证书,无权使用部分功能。
|
||||||
|
|
||||||
|
### 登录提示 Network Error
|
||||||
|
|
||||||
|
由于服务初始化错误,系统重启导致。
|
||||||
|
|
||||||
|
* 90%是由于配置文件写不对,导致 JSON 解析报错
|
||||||
|
* 剩下的基本是因为向量数据库连不上
|
||||||
|
|
||||||
|
### 如何修改密码
|
||||||
|
|
||||||
|
修改`docker-compose.yml`文件中`DEFAULT_ROOT_PSW`并重启即可,密码会自动更新。
|
||||||
@@ -31,7 +31,9 @@ images: []
|
|||||||
### 页面崩溃
|
### 页面崩溃
|
||||||
|
|
||||||
1. 关闭翻译
|
1. 关闭翻译
|
||||||
2. 检查配置文件是否正常加载,如果没有正常加载会导致缺失系统信息,在某些操作下会导致空指针。(95%情况是配置文件不对,可以F12打开控制台,看具体的空指针情况)
|
2. 检查配置文件是否正常加载,如果没有正常加载会导致缺失系统信息,在某些操作下会导致空指针。
|
||||||
|
* 95%情况是配置文件不对。会提示 xxx undefined
|
||||||
|
* 提示`URI malformed`,请 Issue 反馈具体操作和页面,这是由于特殊字符串编码解析报错。
|
||||||
3. 某些api不兼容问题(较少)
|
3. 某些api不兼容问题(较少)
|
||||||
|
|
||||||
### 开启内容补全后,响应速度变慢
|
### 开启内容补全后,响应速度变慢
|
||||||
|
|||||||
@@ -79,6 +79,7 @@ Mongo 数据库需要注意,需要注意在连接地址中增加 `directConnec
|
|||||||
# 给自动化脚本代码执行权限(非 linux 系统, 可以手动执行里面的 postinstall.sh 文件内容)
|
# 给自动化脚本代码执行权限(非 linux 系统, 可以手动执行里面的 postinstall.sh 文件内容)
|
||||||
chmod -R +x ./scripts/
|
chmod -R +x ./scripts/
|
||||||
# 代码根目录下执行,会安装根 package、projects 和 packages 内所有依赖
|
# 代码根目录下执行,会安装根 package、projects 和 packages 内所有依赖
|
||||||
|
# 如果提示 isolate-vm 安装失败,可以参考:https://github.com/laverdet/isolated-vm?tab=readme-ov-file#requirements
|
||||||
pnpm i
|
pnpm i
|
||||||
|
|
||||||
# 非 Make 运行
|
# 非 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
|
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) 仓库
|
1. 确保你的代码是 Fork [FastGPT](https://github.com/labring/FastGPT) 仓库
|
||||||
|
|||||||
38
docSite/content/zh-cn/docs/development/upgrading/484.md
Normal file
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。
|
||||||
59
docSite/content/zh-cn/docs/development/upgrading/485.md
Normal file
59
docSite/content/zh-cn/docs/development/upgrading/485.md
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
---
|
||||||
|
title: 'V4.8.5(进行中)'
|
||||||
|
description: 'FastGPT V4.8.5 更新说明'
|
||||||
|
icon: 'upgrade'
|
||||||
|
draft: false
|
||||||
|
toc: true
|
||||||
|
weight: 819
|
||||||
|
---
|
||||||
|
|
||||||
|
## 升级指南
|
||||||
|
|
||||||
|
### 1. 做好数据库备份
|
||||||
|
|
||||||
|
### 2. 修改镜像
|
||||||
|
|
||||||
|
- fastgpt 镜像 tag 修改成 v4.8.5-alpha
|
||||||
|
- 商业版镜像 tag 修改成 v4.8.5 -alpha
|
||||||
|
|
||||||
|
### 3. 执行初始化
|
||||||
|
|
||||||
|
从任意终端,发起 1 个 HTTP 请求。其中 {{rootkey}} 替换成环境变量里的 `rootkey`;{{host}} 替换成**FastGPT 域名**。
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl --location --request POST 'https://{{host}}/api/admin/initv485' \
|
||||||
|
--header 'rootkey: {{rootkey}}' \
|
||||||
|
--header 'Content-Type: application/json'
|
||||||
|
```
|
||||||
|
|
||||||
|
会把插件的数据表合并到应用中,插件表不会删除。
|
||||||
|
|
||||||
|
------
|
||||||
|
|
||||||
|
**商业版用户执行额外的初始化**
|
||||||
|
|
||||||
|
从任意终端,发起 1 个 HTTP 请求。其中 {{rootkey}} 替换成环境变量里的 `rootkey`;{{host}} 替换成**FastGPT 商业版的域名**:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl --location --request POST 'https://{{host}}/api/admin/init/485' \
|
||||||
|
--header 'rootkey: {{rootkey}}' \
|
||||||
|
--header 'Content-Type: application/json'
|
||||||
|
```
|
||||||
|
|
||||||
|
会重置知识库权限系统。
|
||||||
|
|
||||||
|
## V4.8.5 更新说明
|
||||||
|
|
||||||
|
1. 新增 - 合并插件和应用,统一成工作台
|
||||||
|
2. 新增 - 应用创建副本功能
|
||||||
|
3. 新增 - 应用创建模板
|
||||||
|
4. 新增 - 支持代码运行结果作为工具输出。
|
||||||
|
5. 优化 - 原文件编码存取
|
||||||
|
6. 优化 - 文件夹读取,支持单个文件夹超出 100 个文件
|
||||||
|
7. 优化 - 问答拆分/手动录入,当有`a`字段时,自动将`q`作为补充索引。
|
||||||
|
8. 优化 - 对话框页面代码
|
||||||
|
9. 修复 - SSR渲染
|
||||||
|
10. 修复 - 定时任务无法实际关闭
|
||||||
|
11. 修复 - 输入引导特殊字符导致正则报错
|
||||||
|
12. 修复 - 文件包含特殊字符`%`,且为转义时会导致页面崩溃
|
||||||
|
13. 修复 - 自定义输入选择知识库引用时页面崩溃
|
||||||
@@ -9,7 +9,7 @@ weight: 506
|
|||||||
|
|
||||||
# FastGPT 三分钟接入微信/企业微信
|
# FastGPT 三分钟接入微信/企业微信
|
||||||
私人微信和企业微信接入的方式基本一样,不同的地方会刻意指出。
|
私人微信和企业微信接入的方式基本一样,不同的地方会刻意指出。
|
||||||
[查看视频教程](https://www.bilibili.com/video/BV1cu411F7FN/?spm_id_from=333.1007.top_right_bar_window_history.content.click&vd_source=903c2b09b7412037c2eddc6a8fb9828b)
|
[查看视频教程](https://www.bilibili.com/video/BV1rJ4m1w7xk/)
|
||||||
## 创建APIKey
|
## 创建APIKey
|
||||||
首先找到我们需要接入的应用,然后点击「外部使用」->「API访问」创建一个APIKey并保存。
|
首先找到我们需要接入的应用,然后点击「外部使用」->「API访问」创建一个APIKey并保存。
|
||||||
|
|
||||||
@@ -27,7 +27,7 @@ weight: 506
|
|||||||
|
|
||||||
## sealos部署服务
|
## sealos部署服务
|
||||||
|
|
||||||
[访问sealos](https://cloud.sealos.io/) 登陆进来之后打开「应用管理」-> 「新建应用」。
|
[访问sealos](https://cloud.sealos.run/) 登陆进来之后打开「应用管理」-> 「新建应用」。
|
||||||
- 应用名:称随便填写
|
- 应用名:称随便填写
|
||||||
- 镜像名:私人微信填写 aibotk/wechat-assistant 企业微信填写 aibotk/worker-assistant
|
- 镜像名:私人微信填写 aibotk/wechat-assistant 企业微信填写 aibotk/worker-assistant
|
||||||
- cpu和内存建议 1c1g
|
- cpu和内存建议 1c1g
|
||||||
@@ -38,14 +38,14 @@ weight: 506
|
|||||||
|
|
||||||

|

|
||||||
|
|
||||||
这里需要填写四个环境变量:
|
这里需要填写三个环境变量:
|
||||||
```
|
```
|
||||||
AIBOTK_KEY=微秘书 APIKEY
|
AIBOTK_KEY=微秘书 APIKEY
|
||||||
AIBOTK_SECRET=微秘书 APISECRET
|
AIBOTK_SECRET=微秘书 APISECRET
|
||||||
WORK_PRO_TOKEN=你申请的企微 token (企业微信需要填写,私人微信不需要)
|
WORK_PRO_TOKEN=你申请的企微 token (企业微信需要填写,私人微信不需要)
|
||||||
```
|
```
|
||||||
|
|
||||||
这里最后两个变量只有部署企业微信才需要,私人微信只需要填写前两个即可。
|
这里最后的企业微信 Token 在微秘书的->会员开通栏目中自行购买。
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
@@ -55,9 +55,7 @@ WORK_PRO_TOKEN=你申请的企微 token (企业微信需要填写,私人
|
|||||||
|
|
||||||

|

|
||||||
|
|
||||||
`WORK_PRO_TOKEN` [点击这里](https://tss.rpachat.com/?aff=aibotk)申请 token 然后填入即可。
|
`WORK_PRO_TOKEN` 微秘书的会员中心中自行购买即可。
|
||||||
|
|
||||||
`WECHATY_PUPPET_SERVICE_AUTHORITY`的值复制过去就可以。
|
|
||||||
|
|
||||||
填写完毕后点右上角「部署」,等待应用状态变为运行中。
|
填写完毕后点右上角「部署」,等待应用状态变为运行中。
|
||||||
|
|
||||||
|
|||||||
@@ -272,7 +272,7 @@ Response:
|
|||||||
{
|
{
|
||||||
"key": "Authorization",
|
"key": "Authorization",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"value": "Bearer sk-zsfBsxEU3ApSFGYxF4CdB97e9556412588421823371b9f7b"
|
"value": "Bearer "
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"label": "",
|
"label": "",
|
||||||
|
|||||||
@@ -26,456 +26,378 @@ weight: 404
|
|||||||
{{% details title="编排配置" closed="true" %}}
|
{{% details title="编排配置" closed="true" %}}
|
||||||
|
|
||||||
```json
|
```json
|
||||||
[
|
{
|
||||||
{
|
"nodes": [
|
||||||
"nodeId": "userGuide",
|
{
|
||||||
"name": "core.module.template.App system setting",
|
"nodeId": "userGuide",
|
||||||
"intro": "core.app.tip.userGuideTip",
|
"name": "系统配置",
|
||||||
"avatar": "/imgs/workflow/userGuide.png",
|
"intro": "可以配置应用的系统参数",
|
||||||
"flowNodeType": "userGuide",
|
"avatar": "/imgs/workflow/userGuide.png",
|
||||||
"position": {
|
"flowNodeType": "userGuide",
|
||||||
"x": -92.26884681344463,
|
"position": {
|
||||||
"y": 710.9354029649536
|
"x": 303.41163758039283,
|
||||||
|
"y": -552.297639861266
|
||||||
|
},
|
||||||
|
"version": "481",
|
||||||
|
"inputs": [],
|
||||||
|
"outputs": []
|
||||||
},
|
},
|
||||||
"inputs": [
|
{
|
||||||
{
|
"nodeId": "workflowStartNodeId",
|
||||||
"key": "welcomeText",
|
"name": "流程开始",
|
||||||
"type": "hidden",
|
"intro": "",
|
||||||
"valueType": "string",
|
"avatar": "/imgs/workflow/userChatInput.svg",
|
||||||
"label": "core.app.Welcome Text",
|
"flowNodeType": "workflowStart",
|
||||||
"showTargetInApp": false,
|
"position": {
|
||||||
"showTargetInPlugin": false,
|
"x": 529.3935295017156,
|
||||||
"value": "",
|
"y": 197.114018410347
|
||||||
"connected": false
|
|
||||||
},
|
},
|
||||||
{
|
"version": "481",
|
||||||
"key": "variables",
|
"inputs": [
|
||||||
"type": "hidden",
|
{
|
||||||
"valueType": "any",
|
"key": "userChatInput",
|
||||||
"label": "core.module.Variable",
|
"renderTypeList": [
|
||||||
"value": [],
|
"reference",
|
||||||
"showTargetInApp": false,
|
"textarea"
|
||||||
"showTargetInPlugin": false,
|
],
|
||||||
"connected": false
|
"valueType": "string",
|
||||||
},
|
"label": "用户问题",
|
||||||
{
|
"required": true,
|
||||||
"key": "questionGuide",
|
"toolDescription": "用户问题"
|
||||||
"valueType": "boolean",
|
|
||||||
"type": "switch",
|
|
||||||
"label": "",
|
|
||||||
"showTargetInApp": false,
|
|
||||||
"showTargetInPlugin": false,
|
|
||||||
"value": false,
|
|
||||||
"connected": false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"key": "tts",
|
|
||||||
"type": "hidden",
|
|
||||||
"valueType": "any",
|
|
||||||
"label": "",
|
|
||||||
"showTargetInApp": false,
|
|
||||||
"showTargetInPlugin": false,
|
|
||||||
"value": {
|
|
||||||
"type": "web"
|
|
||||||
},
|
|
||||||
"connected": false
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"outputs": []
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"nodeId": "userChatInput",
|
|
||||||
"name": "core.module.template.Chat entrance",
|
|
||||||
"intro": "当用户发送一个内容后,流程将会从这个模块开始执行。",
|
|
||||||
"avatar": "/imgs/workflow/userChatInput.svg",
|
|
||||||
"flowNodeType": "questionInput",
|
|
||||||
"position": {
|
|
||||||
"x": 241.60980819261408,
|
|
||||||
"y": 1330.9528898009685
|
|
||||||
},
|
|
||||||
"inputs": [
|
|
||||||
{
|
|
||||||
"key": "userChatInput",
|
|
||||||
"type": "systemInput",
|
|
||||||
"valueType": "string",
|
|
||||||
"label": "core.module.input.label.user question",
|
|
||||||
"showTargetInApp": false,
|
|
||||||
"showTargetInPlugin": false,
|
|
||||||
"connected": false
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"outputs": [
|
|
||||||
{
|
|
||||||
"key": "userChatInput",
|
|
||||||
"label": "core.module.input.label.user question",
|
|
||||||
"type": "source",
|
|
||||||
"valueType": "string",
|
|
||||||
"targets": [
|
|
||||||
{
|
|
||||||
"nodeId": "n84rvg",
|
|
||||||
"key": "userChatInput"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"nodeId": "n84rvg",
|
|
||||||
"name": "工具调用(实验)",
|
|
||||||
"intro": "通过AI模型自动选择一个或多个功能块进行调用,也可以对插件进行调用。",
|
|
||||||
"avatar": "/imgs/workflow/tool.svg",
|
|
||||||
"flowNodeType": "tools",
|
|
||||||
"showStatus": true,
|
|
||||||
"position": {
|
|
||||||
"x": 809.4264785615641,
|
|
||||||
"y": 873.3971746859133
|
|
||||||
},
|
|
||||||
"inputs": [
|
|
||||||
{
|
|
||||||
"key": "model",
|
|
||||||
"type": "settingLLMModel",
|
|
||||||
"label": "core.module.input.label.aiModel",
|
|
||||||
"required": true,
|
|
||||||
"valueType": "string",
|
|
||||||
"showTargetInApp": false,
|
|
||||||
"showTargetInPlugin": false,
|
|
||||||
"llmModelType": "all",
|
|
||||||
"value": "gpt-3.5-turbo",
|
|
||||||
"connected": false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"key": "temperature",
|
|
||||||
"type": "hidden",
|
|
||||||
"label": "",
|
|
||||||
"value": 0,
|
|
||||||
"valueType": "number",
|
|
||||||
"min": 0,
|
|
||||||
"max": 10,
|
|
||||||
"step": 1,
|
|
||||||
"showTargetInApp": false,
|
|
||||||
"showTargetInPlugin": false,
|
|
||||||
"connected": false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"key": "maxToken",
|
|
||||||
"type": "hidden",
|
|
||||||
"label": "",
|
|
||||||
"value": 2000,
|
|
||||||
"valueType": "number",
|
|
||||||
"min": 100,
|
|
||||||
"max": 4000,
|
|
||||||
"step": 50,
|
|
||||||
"showTargetInApp": false,
|
|
||||||
"showTargetInPlugin": false,
|
|
||||||
"connected": false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"key": "systemPrompt",
|
|
||||||
"type": "textarea",
|
|
||||||
"max": 3000,
|
|
||||||
"valueType": "string",
|
|
||||||
"label": "core.ai.Prompt",
|
|
||||||
"description": "core.app.tip.chatNodeSystemPromptTip",
|
|
||||||
"placeholder": "core.app.tip.chatNodeSystemPromptTip",
|
|
||||||
"showTargetInApp": true,
|
|
||||||
"showTargetInPlugin": true,
|
|
||||||
"connected": false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"key": "history",
|
|
||||||
"type": "numberInput",
|
|
||||||
"label": "core.module.input.label.chat history",
|
|
||||||
"required": true,
|
|
||||||
"min": 0,
|
|
||||||
"max": 30,
|
|
||||||
"valueType": "chatHistory",
|
|
||||||
"value": 6,
|
|
||||||
"showTargetInApp": true,
|
|
||||||
"showTargetInPlugin": true,
|
|
||||||
"connected": false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"key": "userChatInput",
|
|
||||||
"type": "custom",
|
|
||||||
"label": "",
|
|
||||||
"required": true,
|
|
||||||
"valueType": "string",
|
|
||||||
"showTargetInApp": true,
|
|
||||||
"showTargetInPlugin": true,
|
|
||||||
"connected": true
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"outputs": [
|
|
||||||
{
|
|
||||||
"key": "userChatInput",
|
|
||||||
"label": "core.module.input.label.user question",
|
|
||||||
"type": "hidden",
|
|
||||||
"valueType": "string",
|
|
||||||
"targets": []
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"key": "selectedTools",
|
|
||||||
"valueType": "tools",
|
|
||||||
"type": "hidden",
|
|
||||||
"targets": [
|
|
||||||
{
|
|
||||||
"nodeId": "3mbu91",
|
|
||||||
"key": "selectedTools"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"key": "finish",
|
|
||||||
"label": "",
|
|
||||||
"description": "",
|
|
||||||
"valueType": "boolean",
|
|
||||||
"type": "hidden",
|
|
||||||
"targets": []
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"nodeId": "3mbu91",
|
|
||||||
"name": "HTTP 请求",
|
|
||||||
"intro": "调用飞书webhook,发送一个通知",
|
|
||||||
"avatar": "/imgs/workflow/http.png",
|
|
||||||
"flowNodeType": "httpRequest468",
|
|
||||||
"showStatus": true,
|
|
||||||
"position": {
|
|
||||||
"x": 1483.6437630977423,
|
|
||||||
"y": 798.9716928475544
|
|
||||||
},
|
|
||||||
"inputs": [
|
|
||||||
|
|
||||||
{
|
|
||||||
"key": "system_httpMethod",
|
|
||||||
"type": "custom",
|
|
||||||
"valueType": "string",
|
|
||||||
"label": "",
|
|
||||||
"value": "POST",
|
|
||||||
"required": true,
|
|
||||||
"showTargetInApp": false,
|
|
||||||
"showTargetInPlugin": false,
|
|
||||||
"connected": false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"key": "system_httpReqUrl",
|
|
||||||
"type": "hidden",
|
|
||||||
"valueType": "string",
|
|
||||||
"label": "",
|
|
||||||
"description": "core.module.input.description.Http Request Url",
|
|
||||||
"placeholder": "https://api.ai.com/getInventory",
|
|
||||||
"required": false,
|
|
||||||
"showTargetInApp": false,
|
|
||||||
"showTargetInPlugin": false,
|
|
||||||
"value": "这里填写你的飞书机器人地址",
|
|
||||||
"connected": false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"key": "system_httpHeader",
|
|
||||||
"type": "custom",
|
|
||||||
"valueType": "any",
|
|
||||||
"value": [],
|
|
||||||
"label": "",
|
|
||||||
"description": "core.module.input.description.Http Request Header",
|
|
||||||
"placeholder": "core.module.input.description.Http Request Header",
|
|
||||||
"required": false,
|
|
||||||
"showTargetInApp": false,
|
|
||||||
"showTargetInPlugin": false,
|
|
||||||
"connected": false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"key": "system_httpParams",
|
|
||||||
"type": "hidden",
|
|
||||||
"valueType": "any",
|
|
||||||
"value": [],
|
|
||||||
"label": "",
|
|
||||||
"required": false,
|
|
||||||
"showTargetInApp": false,
|
|
||||||
"showTargetInPlugin": false,
|
|
||||||
"connected": false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"key": "system_httpJsonBody",
|
|
||||||
"type": "hidden",
|
|
||||||
"valueType": "any",
|
|
||||||
"value": "{\r\n \"msg_type\": \"text\",\r\n \"content\": {\r\n \"text\": \"{{text}}\"\r\n }\r\n}",
|
|
||||||
"label": "",
|
|
||||||
"required": false,
|
|
||||||
"showTargetInApp": false,
|
|
||||||
"showTargetInPlugin": false,
|
|
||||||
"connected": false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"key": "DYNAMIC_INPUT_KEY",
|
|
||||||
"type": "target",
|
|
||||||
"valueType": "any",
|
|
||||||
"label": "core.workflow.inputType.dynamicTargetInput",
|
|
||||||
"description": "core.module.input.description.dynamic input",
|
|
||||||
"required": false,
|
|
||||||
"showTargetInApp": false,
|
|
||||||
"showTargetInPlugin": true,
|
|
||||||
"hideInApp": true,
|
|
||||||
"connected": false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"key": "system_addInputParam",
|
|
||||||
"type": "addInputParam",
|
|
||||||
"valueType": "any",
|
|
||||||
"label": "",
|
|
||||||
"required": false,
|
|
||||||
"showTargetInApp": false,
|
|
||||||
"showTargetInPlugin": false,
|
|
||||||
"editField": {
|
|
||||||
"key": true,
|
|
||||||
"description": true,
|
|
||||||
"dataType": true
|
|
||||||
},
|
|
||||||
"defaultEditField": {
|
|
||||||
"label": "",
|
|
||||||
"key": "",
|
|
||||||
"description": "",
|
|
||||||
"inputType": "target",
|
|
||||||
"valueType": "string"
|
|
||||||
},
|
|
||||||
"connected": false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"valueType": "string",
|
|
||||||
"type": "hidden",
|
|
||||||
"key": "text",
|
|
||||||
"label": "text",
|
|
||||||
"toolDescription": "需要发送的通知内容",
|
|
||||||
"required": true,
|
|
||||||
"connected": false
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"outputs": [
|
|
||||||
{
|
|
||||||
"key": "httpRawResponse",
|
|
||||||
"label": "原始响应",
|
|
||||||
"description": "HTTP请求的原始响应。只能接受字符串或JSON类型响应数据。",
|
|
||||||
"valueType": "any",
|
|
||||||
"type": "source",
|
|
||||||
"targets": [
|
|
||||||
{
|
|
||||||
"nodeId": "rzx4mj",
|
|
||||||
"key": "switch"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"nodeId": "psdhs1",
|
|
||||||
"key": "switch"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"key": "system_addOutputParam",
|
|
||||||
"type": "addOutputParam",
|
|
||||||
"valueType": "any",
|
|
||||||
"label": "",
|
|
||||||
"targets": [],
|
|
||||||
"editField": {
|
|
||||||
"key": true,
|
|
||||||
"description": true,
|
|
||||||
"dataType": true,
|
|
||||||
"defaultValue": true
|
|
||||||
},
|
|
||||||
"defaultEditField": {
|
|
||||||
"label": "",
|
|
||||||
"key": "",
|
|
||||||
"description": "",
|
|
||||||
"outputType": "source",
|
|
||||||
"valueType": "string"
|
|
||||||
}
|
}
|
||||||
|
],
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"id": "userChatInput",
|
||||||
|
"key": "userChatInput",
|
||||||
|
"label": "core.module.input.label.user question",
|
||||||
|
"valueType": "string",
|
||||||
|
"type": "static"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"nodeId": "u6IAOEssxoZT",
|
||||||
|
"name": "工具调用(实验)",
|
||||||
|
"intro": "通过AI模型自动选择一个或多个功能块进行调用,也可以对插件进行调用。",
|
||||||
|
"avatar": "/imgs/workflow/tool.svg",
|
||||||
|
"flowNodeType": "tools",
|
||||||
|
"showStatus": true,
|
||||||
|
"position": {
|
||||||
|
"x": 1003.146243538873,
|
||||||
|
"y": 48.52327869406625
|
||||||
},
|
},
|
||||||
{
|
"version": "481",
|
||||||
"type": "source",
|
"inputs": [
|
||||||
"valueType": "string",
|
{
|
||||||
"key": "prompt",
|
"key": "model",
|
||||||
"label": "prompt",
|
"renderTypeList": [
|
||||||
"description": "",
|
"settingLLMModel",
|
||||||
"required": false,
|
"reference"
|
||||||
"edit": true,
|
],
|
||||||
"editField": {
|
"label": "core.module.input.label.aiModel",
|
||||||
"key": true,
|
"valueType": "string",
|
||||||
"description": true,
|
"llmModelType": "all",
|
||||||
"dataType": true,
|
"value": "gpt-3.5-turbo"
|
||||||
"defaultValue": true
|
|
||||||
},
|
},
|
||||||
"targets": []
|
{
|
||||||
}
|
"key": "temperature",
|
||||||
]
|
"renderTypeList": [
|
||||||
},
|
"hidden"
|
||||||
{
|
],
|
||||||
"nodeId": "rzx4mj",
|
"label": "",
|
||||||
"name": "工具调用终止",
|
"value": 0,
|
||||||
"intro": "该模块需配置工具调用使用。当该模块被执行时,本次工具调用将会强制结束,并且不再调用AI针对工具调用结果回答问题。",
|
"valueType": "number",
|
||||||
"avatar": "/imgs/workflow/toolStop.svg",
|
"min": 0,
|
||||||
"flowNodeType": "stopTool",
|
"max": 10,
|
||||||
"position": {
|
"step": 1
|
||||||
"x": 2145.5070710160267,
|
},
|
||||||
"y": 1306.3581817783079
|
{
|
||||||
|
"key": "maxToken",
|
||||||
|
"renderTypeList": [
|
||||||
|
"hidden"
|
||||||
|
],
|
||||||
|
"label": "",
|
||||||
|
"value": 2000,
|
||||||
|
"valueType": "number",
|
||||||
|
"min": 100,
|
||||||
|
"max": 4000,
|
||||||
|
"step": 50
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "systemPrompt",
|
||||||
|
"renderTypeList": [
|
||||||
|
"textarea",
|
||||||
|
"reference"
|
||||||
|
],
|
||||||
|
"max": 3000,
|
||||||
|
"valueType": "string",
|
||||||
|
"label": "core.ai.Prompt",
|
||||||
|
"description": "core.app.tip.chatNodeSystemPromptTip",
|
||||||
|
"placeholder": "core.app.tip.chatNodeSystemPromptTip"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "history",
|
||||||
|
"renderTypeList": [
|
||||||
|
"numberInput",
|
||||||
|
"reference"
|
||||||
|
],
|
||||||
|
"valueType": "chatHistory",
|
||||||
|
"label": "core.module.input.label.chat history",
|
||||||
|
"description": "最多携带多少轮对话记录",
|
||||||
|
"required": true,
|
||||||
|
"min": 0,
|
||||||
|
"max": 50,
|
||||||
|
"value": 6
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "userChatInput",
|
||||||
|
"renderTypeList": [
|
||||||
|
"reference",
|
||||||
|
"textarea"
|
||||||
|
],
|
||||||
|
"valueType": "string",
|
||||||
|
"label": "用户问题",
|
||||||
|
"required": true,
|
||||||
|
"value": [
|
||||||
|
"workflowStartNodeId",
|
||||||
|
"userChatInput"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"id": "answerText",
|
||||||
|
"key": "answerText",
|
||||||
|
"label": "core.module.output.label.Ai response content",
|
||||||
|
"description": "core.module.output.description.Ai response content",
|
||||||
|
"valueType": "string",
|
||||||
|
"type": "static"
|
||||||
|
}
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"inputs": [
|
{
|
||||||
{
|
"nodeId": "fvY5hb0K646V",
|
||||||
"key": "switch",
|
"name": "工具调用终止",
|
||||||
"type": "triggerAndFinish",
|
"intro": "该模块需配置工具调用使用。当该模块被执行时,本次工具调用将会强制结束,并且不再调用AI针对工具调用结果回答问题。",
|
||||||
"label": "",
|
"avatar": "/imgs/workflow/toolStop.svg",
|
||||||
"description": "core.module.input.description.Trigger",
|
"flowNodeType": "stopTool",
|
||||||
"valueType": "any",
|
"position": {
|
||||||
"showTargetInApp": true,
|
"x": 2367.838362362707,
|
||||||
"showTargetInPlugin": true,
|
"y": 732.355988936165
|
||||||
"connected": true
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"outputs": []
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"nodeId": "psdhs1",
|
|
||||||
"name": "指定回复",
|
|
||||||
"intro": "该模块可以直接回复一段指定的内容。常用于引导、提示。非字符串内容传入时,会转成字符串进行输出。",
|
|
||||||
"avatar": "/imgs/workflow/reply.png",
|
|
||||||
"flowNodeType": "answerNode",
|
|
||||||
"position": {
|
|
||||||
"x": 2117.0429459850598,
|
|
||||||
"y": 1658.4125434513746
|
|
||||||
},
|
|
||||||
"inputs": [
|
|
||||||
{
|
|
||||||
"key": "switch",
|
|
||||||
"type": "triggerAndFinish",
|
|
||||||
"label": "",
|
|
||||||
"description": "core.module.input.description.Trigger",
|
|
||||||
"valueType": "any",
|
|
||||||
"showTargetInApp": true,
|
|
||||||
"showTargetInPlugin": true,
|
|
||||||
"connected": true
|
|
||||||
},
|
},
|
||||||
|
"version": "481",
|
||||||
|
"inputs": [],
|
||||||
|
"outputs": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"nodeId": "x9rN2a4WnZmt",
|
||||||
|
"name": "HTTP 请求",
|
||||||
|
"intro": "向飞书发送一个webhooks通知信息。",
|
||||||
|
"avatar": "/imgs/workflow/http.png",
|
||||||
|
"flowNodeType": "httpRequest468",
|
||||||
|
"showStatus": true,
|
||||||
|
"position": {
|
||||||
|
"x": 1623.9214305901633,
|
||||||
|
"y": 22.777089001645862
|
||||||
|
},
|
||||||
|
"version": "481",
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"key": "system_addInputParam",
|
||||||
|
"renderTypeList": [
|
||||||
|
"addInputParam"
|
||||||
|
],
|
||||||
|
"valueType": "dynamic",
|
||||||
|
"label": "",
|
||||||
|
"required": false,
|
||||||
|
"description": "core.module.input.description.HTTP Dynamic Input",
|
||||||
|
"editField": {
|
||||||
|
"key": true,
|
||||||
|
"valueType": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"valueType": "string",
|
||||||
|
"renderTypeList": [
|
||||||
|
"reference"
|
||||||
|
],
|
||||||
|
"key": "text",
|
||||||
|
"label": "text",
|
||||||
|
"toolDescription": "发送的消息",
|
||||||
|
"required": true,
|
||||||
|
"canEdit": true,
|
||||||
|
"editField": {
|
||||||
|
"key": true,
|
||||||
|
"description": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "system_httpMethod",
|
||||||
|
"renderTypeList": [
|
||||||
|
"custom"
|
||||||
|
],
|
||||||
|
"valueType": "string",
|
||||||
|
"label": "",
|
||||||
|
"value": "POST",
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "system_httpReqUrl",
|
||||||
|
"renderTypeList": [
|
||||||
|
"hidden"
|
||||||
|
],
|
||||||
|
"valueType": "string",
|
||||||
|
"label": "",
|
||||||
|
"description": "core.module.input.description.Http Request Url",
|
||||||
|
"placeholder": "https://api.ai.com/getInventory",
|
||||||
|
"required": false,
|
||||||
|
"value": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "system_httpHeader",
|
||||||
|
"renderTypeList": [
|
||||||
|
"custom"
|
||||||
|
],
|
||||||
|
"valueType": "any",
|
||||||
|
"value": [],
|
||||||
|
"label": "",
|
||||||
|
"description": "core.module.input.description.Http Request Header",
|
||||||
|
"placeholder": "core.module.input.description.Http Request Header",
|
||||||
|
"required": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "system_httpParams",
|
||||||
|
"renderTypeList": [
|
||||||
|
"hidden"
|
||||||
|
],
|
||||||
|
"valueType": "any",
|
||||||
|
"value": [],
|
||||||
|
"label": "",
|
||||||
|
"required": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "system_httpJsonBody",
|
||||||
|
"renderTypeList": [
|
||||||
|
"hidden"
|
||||||
|
],
|
||||||
|
"valueType": "any",
|
||||||
|
"value": "{\r\n \"msg_type\": \"text\",\r\n \"content\": {\r\n \"text\": \"{{text}}\"\r\n }\r\n}",
|
||||||
|
"label": "",
|
||||||
|
"required": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"id": "system_addOutputParam",
|
||||||
|
"key": "system_addOutputParam",
|
||||||
|
"type": "dynamic",
|
||||||
|
"valueType": "dynamic",
|
||||||
|
"label": "",
|
||||||
|
"editField": {
|
||||||
|
"key": true,
|
||||||
|
"valueType": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "error",
|
||||||
|
"key": "error",
|
||||||
|
"label": "请求错误",
|
||||||
|
"description": "HTTP请求错误信息,成功时返回空",
|
||||||
|
"valueType": "object",
|
||||||
|
"type": "static"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "httpRawResponse",
|
||||||
|
"key": "httpRawResponse",
|
||||||
|
"label": "原始响应",
|
||||||
|
"required": true,
|
||||||
|
"description": "HTTP请求的原始响应。只能接受字符串或JSON类型响应数据。",
|
||||||
|
"valueType": "any",
|
||||||
|
"type": "static"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"nodeId": "aGHGqH2oUupj",
|
||||||
|
"name": "指定回复",
|
||||||
|
"intro": "该模块可以直接回复一段指定的内容。常用于引导、提示。非字符串内容传入时,会转成字符串进行输出。",
|
||||||
|
"avatar": "/imgs/workflow/reply.png",
|
||||||
|
"flowNodeType": "answerNode",
|
||||||
|
"position": {
|
||||||
|
"x": 2350.7077940158674,
|
||||||
|
"y": 107.32448732713493
|
||||||
|
},
|
||||||
|
"version": "481",
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"key": "text",
|
||||||
|
"renderTypeList": [
|
||||||
|
"textarea",
|
||||||
|
"reference"
|
||||||
|
],
|
||||||
|
"valueType": "any",
|
||||||
|
"required": true,
|
||||||
|
"label": "core.module.input.label.Response content",
|
||||||
|
"description": "core.module.input.description.Response content",
|
||||||
|
"placeholder": "core.module.input.description.Response content",
|
||||||
|
"value": "嘻嘻,发送成功"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"outputs": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"edges": [
|
||||||
|
{
|
||||||
|
"source": "workflowStartNodeId",
|
||||||
|
"target": "u6IAOEssxoZT",
|
||||||
|
"sourceHandle": "workflowStartNodeId-source-right",
|
||||||
|
"targetHandle": "u6IAOEssxoZT-target-left"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"source": "u6IAOEssxoZT",
|
||||||
|
"target": "x9rN2a4WnZmt",
|
||||||
|
"sourceHandle": "selectedTools",
|
||||||
|
"targetHandle": "selectedTools"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"source": "x9rN2a4WnZmt",
|
||||||
|
"target": "fvY5hb0K646V",
|
||||||
|
"sourceHandle": "x9rN2a4WnZmt-source-right",
|
||||||
|
"targetHandle": "fvY5hb0K646V-target-left"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"source": "x9rN2a4WnZmt",
|
||||||
|
"target": "aGHGqH2oUupj",
|
||||||
|
"sourceHandle": "x9rN2a4WnZmt-source-right",
|
||||||
|
"targetHandle": "aGHGqH2oUupj-target-left"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"chatConfig": {
|
||||||
|
"variables": [
|
||||||
{
|
{
|
||||||
"key": "text",
|
"id": "txq1ca",
|
||||||
"type": "textarea",
|
"key": "test",
|
||||||
"valueType": "any",
|
"label": "测试",
|
||||||
"label": "core.module.input.label.Response content",
|
"type": "custom",
|
||||||
"description": "core.module.input.description.Response content",
|
"required": true,
|
||||||
"placeholder": "core.module.input.description.Response content",
|
"maxLen": 50,
|
||||||
"showTargetInApp": true,
|
"enums": [
|
||||||
"showTargetInPlugin": true,
|
{
|
||||||
"value": "笑死发送成功啦",
|
"value": ""
|
||||||
"connected": false
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"outputs": [
|
"questionGuide": false,
|
||||||
{
|
"scheduledTriggerConfig": {
|
||||||
"key": "finish",
|
"cronString": "",
|
||||||
"label": "",
|
"timezone": "Asia/Shanghai",
|
||||||
"description": "",
|
"defaultPrompt": ""
|
||||||
"valueType": "boolean",
|
},
|
||||||
"type": "hidden",
|
"_id": "66715d4bf577287d39e35ecf"
|
||||||
"targets": []
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
]
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
{{% /details %}}
|
{{% /details %}}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ weight: 301
|
|||||||
|
|
||||||
FastGPT 从 V4 版本开始采用新的交互方式来构建 AI 应用。使用了 Flow 节点编排(工作流)的方式来实现复杂工作流,提高可玩性和扩展性。但同时也提高了上手的门槛,有一定开发背景的用户使用起来会比较容易。
|
FastGPT 从 V4 版本开始采用新的交互方式来构建 AI 应用。使用了 Flow 节点编排(工作流)的方式来实现复杂工作流,提高可玩性和扩展性。但同时也提高了上手的门槛,有一定开发背景的用户使用起来会比较容易。
|
||||||
|
|
||||||
[查看视频教程](https://www.bilibili.com/video/BV1aB4y1Z7Hy/?spm_id_from=333.999.list.card_archive.click&vd_source=903c2b09b7412037c2eddc6a8fb9828b)
|
[查看视频教程](https://www.bilibili.com/video/BV1is421u7bQ/)
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
|||||||
@@ -114,15 +114,15 @@ services:
|
|||||||
# fastgpt
|
# fastgpt
|
||||||
sandbox:
|
sandbox:
|
||||||
container_name: sandbox
|
container_name: sandbox
|
||||||
image: ghcr.io/labring/fastgpt-sandbox:v4.8.3 # git
|
image: ghcr.io/labring/fastgpt-sandbox:v4.8.4 # git
|
||||||
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-sandbox:v4.8.3 # 阿里云
|
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-sandbox:v4.8.4 # 阿里云
|
||||||
networks:
|
networks:
|
||||||
- fastgpt
|
- fastgpt
|
||||||
restart: always
|
restart: always
|
||||||
fastgpt:
|
fastgpt:
|
||||||
container_name: fastgpt
|
container_name: fastgpt
|
||||||
image: ghcr.io/labring/fastgpt:v4.8.3 # git
|
image: ghcr.io/labring/fastgpt:v4.8.4 # git
|
||||||
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.8.3 # 阿里云
|
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.8.4 # 阿里云
|
||||||
ports:
|
ports:
|
||||||
- 3000:3000
|
- 3000:3000
|
||||||
networks:
|
networks:
|
||||||
@@ -154,12 +154,15 @@ services:
|
|||||||
- MILVUS_TOKEN=none
|
- MILVUS_TOKEN=none
|
||||||
# sandbox 地址
|
# sandbox 地址
|
||||||
- SANDBOX_URL=http://sandbox:3000
|
- SANDBOX_URL=http://sandbox:3000
|
||||||
|
# 日志等级: debug, info, warn, error
|
||||||
|
- LOG_LEVEL=info
|
||||||
volumes:
|
volumes:
|
||||||
- ./config.json:/app/data/config.json
|
- ./config.json:/app/data/config.json
|
||||||
|
|
||||||
# oneapi
|
# oneapi
|
||||||
mysql:
|
mysql:
|
||||||
image: mysql:8.0.36
|
image: registry.cn-hangzhou.aliyuncs.com/fastgpt/mysql:8.0.36 # 阿里云
|
||||||
|
# image: mysql:8.0.36
|
||||||
container_name: mysql
|
container_name: mysql
|
||||||
restart: always
|
restart: always
|
||||||
ports:
|
ports:
|
||||||
|
|||||||
@@ -72,15 +72,15 @@ services:
|
|||||||
# fastgpt
|
# fastgpt
|
||||||
sandbox:
|
sandbox:
|
||||||
container_name: sandbox
|
container_name: sandbox
|
||||||
image: ghcr.io/labring/fastgpt-sandbox:v4.8.3 # git
|
image: ghcr.io/labring/fastgpt-sandbox:v4.8.4 # git
|
||||||
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-sandbox:v4.8.3 # 阿里云
|
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-sandbox:v4.8.4 # 阿里云
|
||||||
networks:
|
networks:
|
||||||
- fastgpt
|
- fastgpt
|
||||||
restart: always
|
restart: always
|
||||||
fastgpt:
|
fastgpt:
|
||||||
container_name: fastgpt
|
container_name: fastgpt
|
||||||
image: ghcr.io/labring/fastgpt:v4.8.3 # git
|
image: ghcr.io/labring/fastgpt:v4.8.4 # git
|
||||||
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.8.3 # 阿里云
|
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.8.4 # 阿里云
|
||||||
ports:
|
ports:
|
||||||
- 3000:3000
|
- 3000:3000
|
||||||
networks:
|
networks:
|
||||||
@@ -111,12 +111,15 @@ services:
|
|||||||
- PG_URL=postgresql://username:password@pg:5432/postgres
|
- PG_URL=postgresql://username:password@pg:5432/postgres
|
||||||
# sandbox 地址
|
# sandbox 地址
|
||||||
- SANDBOX_URL=http://sandbox:3000
|
- SANDBOX_URL=http://sandbox:3000
|
||||||
|
# 日志等级: debug, info, warn, error
|
||||||
|
- LOG_LEVEL=info
|
||||||
volumes:
|
volumes:
|
||||||
- ./config.json:/app/data/config.json
|
- ./config.json:/app/data/config.json
|
||||||
|
|
||||||
# oneapi
|
# oneapi
|
||||||
mysql:
|
mysql:
|
||||||
image: mysql:8.0.36
|
image: registry.cn-hangzhou.aliyuncs.com/fastgpt/mysql:8.0.36 # 阿里云
|
||||||
|
# image: mysql:8.0.36
|
||||||
container_name: mysql
|
container_name: mysql
|
||||||
restart: always
|
restart: always
|
||||||
ports:
|
ports:
|
||||||
|
|||||||
@@ -53,15 +53,15 @@ services:
|
|||||||
wait $$!
|
wait $$!
|
||||||
sandbox:
|
sandbox:
|
||||||
container_name: sandbox
|
container_name: sandbox
|
||||||
image: ghcr.io/labring/fastgpt-sandbox:v4.8.3 # git
|
image: ghcr.io/labring/fastgpt-sandbox:v4.8.4 # git
|
||||||
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-sandbox:v4.8.3 # 阿里云
|
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-sandbox:v4.8.4 # 阿里云
|
||||||
networks:
|
networks:
|
||||||
- fastgpt
|
- fastgpt
|
||||||
restart: always
|
restart: always
|
||||||
fastgpt:
|
fastgpt:
|
||||||
container_name: fastgpt
|
container_name: fastgpt
|
||||||
image: ghcr.io/labring/fastgpt:v4.8.3 # git
|
image: ghcr.io/labring/fastgpt:v4.8.4 # git
|
||||||
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.8.3 # 阿里云
|
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.8.4 # 阿里云
|
||||||
ports:
|
ports:
|
||||||
- 3000:3000
|
- 3000:3000
|
||||||
networks:
|
networks:
|
||||||
@@ -92,12 +92,15 @@ services:
|
|||||||
- MILVUS_TOKEN=zilliz_cloud_token
|
- MILVUS_TOKEN=zilliz_cloud_token
|
||||||
# sandbox 地址
|
# sandbox 地址
|
||||||
- SANDBOX_URL=http://sandbox:3000
|
- SANDBOX_URL=http://sandbox:3000
|
||||||
|
# 日志等级: debug, info, warn, error
|
||||||
|
- LOG_LEVEL=info
|
||||||
volumes:
|
volumes:
|
||||||
- ./config.json:/app/data/config.json
|
- ./config.json:/app/data/config.json
|
||||||
|
|
||||||
# oneapi
|
# oneapi
|
||||||
mysql:
|
mysql:
|
||||||
image: mysql:8.0.36
|
image: registry.cn-hangzhou.aliyuncs.com/fastgpt/mysql:8.0.36 # 阿里云
|
||||||
|
# image: mysql:8.0.36
|
||||||
container_name: mysql
|
container_name: mysql
|
||||||
restart: always
|
restart: always
|
||||||
ports:
|
ports:
|
||||||
|
|||||||
10
package.json
10
package.json
@@ -15,17 +15,11 @@
|
|||||||
"@chakra-ui/cli": "^2.4.1",
|
"@chakra-ui/cli": "^2.4.1",
|
||||||
"husky": "^8.0.3",
|
"husky": "^8.0.3",
|
||||||
"i18next": "23.10.0",
|
"i18next": "23.10.0",
|
||||||
"lint-staged": "^13.2.1",
|
"lint-staged": "^13.3.0",
|
||||||
"next-i18next": "15.2.0",
|
"next-i18next": "15.2.0",
|
||||||
"prettier": "3.2.4",
|
"prettier": "3.2.4",
|
||||||
"react-i18next": "13.5.0",
|
"react-i18next": "13.5.0",
|
||||||
"zhlint": "^0.7.1"
|
"zhlint": "^0.7.4"
|
||||||
},
|
|
||||||
"resolutions": {
|
|
||||||
"react": "18.3.1",
|
|
||||||
"react-dom": "18.3.1",
|
|
||||||
"@types/react": "18.3.0",
|
|
||||||
"@types/react-dom": "18.3.0"
|
|
||||||
},
|
},
|
||||||
"lint-staged": {
|
"lint-staged": {
|
||||||
"./**/**/*.{ts,tsx,scss}": "npm run format-code",
|
"./**/**/*.{ts,tsx,scss}": "npm run format-code",
|
||||||
|
|||||||
@@ -4,7 +4,8 @@ import { ErrType } from '../errorCode';
|
|||||||
const startCode = 507000;
|
const startCode = 507000;
|
||||||
export enum CommonErrEnum {
|
export enum CommonErrEnum {
|
||||||
fileNotFound = 'fileNotFound',
|
fileNotFound = 'fileNotFound',
|
||||||
unAuthFile = 'unAuthFile'
|
unAuthFile = 'unAuthFile',
|
||||||
|
missingParams = 'missingParams'
|
||||||
}
|
}
|
||||||
const datasetErr = [
|
const datasetErr = [
|
||||||
{
|
{
|
||||||
@@ -14,6 +15,10 @@ const datasetErr = [
|
|||||||
{
|
{
|
||||||
statusText: CommonErrEnum.unAuthFile,
|
statusText: CommonErrEnum.unAuthFile,
|
||||||
message: 'error.unAuthFile'
|
message: 'error.unAuthFile'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
statusText: CommonErrEnum.missingParams,
|
||||||
|
message: 'error.missingParams'
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
export default datasetErr.reduce((acc, cur, index) => {
|
export default datasetErr.reduce((acc, cur, index) => {
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { ErrType } from '../errorCode';
|
|||||||
|
|
||||||
/* dataset: 501000 */
|
/* dataset: 501000 */
|
||||||
export enum DatasetErrEnum {
|
export enum DatasetErrEnum {
|
||||||
|
unExist = 'unExistDataset',
|
||||||
unAuthDataset = 'unAuthDataset',
|
unAuthDataset = 'unAuthDataset',
|
||||||
unCreateCollection = 'unCreateCollection',
|
unCreateCollection = 'unCreateCollection',
|
||||||
unAuthDatasetCollection = 'unAuthDatasetCollection',
|
unAuthDatasetCollection = 'unAuthDatasetCollection',
|
||||||
@@ -11,6 +12,10 @@ export enum DatasetErrEnum {
|
|||||||
unLinkCollection = 'unLinkCollection'
|
unLinkCollection = 'unLinkCollection'
|
||||||
}
|
}
|
||||||
const datasetErr = [
|
const datasetErr = [
|
||||||
|
{
|
||||||
|
statusText: DatasetErrEnum.unExist,
|
||||||
|
message: 'core.dataset.error.unExistDataset'
|
||||||
|
},
|
||||||
{
|
{
|
||||||
statusText: DatasetErrEnum.unAuthDataset,
|
statusText: DatasetErrEnum.unAuthDataset,
|
||||||
message: 'core.dataset.error.unAuthDataset'
|
message: 'core.dataset.error.unAuthDataset'
|
||||||
|
|||||||
@@ -53,4 +53,5 @@ export const uniqueImageTypeList = Object.entries(mongoImageTypeMap)
|
|||||||
|
|
||||||
export const FolderIcon = 'file/fill/folder';
|
export const FolderIcon = 'file/fill/folder';
|
||||||
export const FolderImgUrl = '/imgs/files/folder.svg';
|
export const FolderImgUrl = '/imgs/files/folder.svg';
|
||||||
|
export const HttpPluginImgUrl = '/imgs/app/httpPluginFill.svg';
|
||||||
export const HttpImgUrl = '/imgs/workflow/http.png';
|
export const HttpImgUrl = '/imgs/workflow/http.png';
|
||||||
|
|||||||
14
packages/global/common/parentFolder/type.d.ts
vendored
14
packages/global/common/parentFolder/type.d.ts
vendored
@@ -2,3 +2,17 @@ export type ParentTreePathItemType = {
|
|||||||
parentId: string;
|
parentId: string;
|
||||||
parentName: 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
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) {
|
text = text.replace(/(```[\s\S]*?```|~~~[\s\S]*?~~~)/g, function (match) {
|
||||||
return match.replace(/\n/g, codeBlockMarker);
|
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
|
// The larger maxLen is, the next sentence is less likely to trigger splitting
|
||||||
const stepReges: { reg: RegExp; maxLen: number }[] = [
|
const stepReges: { reg: RegExp; maxLen: number }[] = [
|
||||||
@@ -338,7 +340,7 @@ const commonSplit = (props: SplitProps): SplitResponse => {
|
|||||||
*/
|
*/
|
||||||
export const splitText2Chunks = (props: SplitProps): SplitResponse => {
|
export const splitText2Chunks = (props: SplitProps): SplitResponse => {
|
||||||
let { text = '' } = props;
|
let { text = '' } = props;
|
||||||
|
const start = Date.now();
|
||||||
const splitWithCustomSign = text.split(CUSTOM_SPLIT_SIGN);
|
const splitWithCustomSign = text.split(CUSTOM_SPLIT_SIGN);
|
||||||
|
|
||||||
const splitResult = splitWithCustomSign.map((item) => {
|
const splitResult = splitWithCustomSign.map((item) => {
|
||||||
|
|||||||
@@ -67,6 +67,14 @@ export const getNanoid = (size = 12) => {
|
|||||||
/* Custom text to reg, need to replace special chats */
|
/* Custom text to reg, need to replace special chats */
|
||||||
export const replaceRegChars = (text: string) => text.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
export const replaceRegChars = (text: string) => text.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
||||||
|
|
||||||
|
export const getRegQueryStr = (text: string, flags = 'i') => {
|
||||||
|
const formatText = replaceRegChars(text);
|
||||||
|
const chars = formatText.split('');
|
||||||
|
const regexPattern = chars.join('.*');
|
||||||
|
|
||||||
|
return new RegExp(regexPattern, flags);
|
||||||
|
};
|
||||||
|
|
||||||
/* slice json str */
|
/* slice json str */
|
||||||
export const sliceJsonStr = (str: string) => {
|
export const sliceJsonStr = (str: string) => {
|
||||||
str = str.replace(/(\\n|\\)/g, '').replace(/ /g, '');
|
str = str.replace(/(\\n|\\)/g, '').replace(/ /g, '');
|
||||||
|
|||||||
@@ -31,7 +31,6 @@ export type FastGPTFeConfigsType = {
|
|||||||
show_openai_account?: boolean;
|
show_openai_account?: boolean;
|
||||||
show_promotion?: boolean;
|
show_promotion?: boolean;
|
||||||
show_team_chat?: boolean;
|
show_team_chat?: boolean;
|
||||||
hide_app_flow?: boolean;
|
|
||||||
concatMd?: string;
|
concatMd?: string;
|
||||||
docUrl?: string;
|
docUrl?: string;
|
||||||
chatbotUrl?: string;
|
chatbotUrl?: string;
|
||||||
|
|||||||
11
packages/global/core/app/collaborator.d.ts
vendored
Normal file
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,17 +1,12 @@
|
|||||||
import { AppTTSConfigType, AppWhisperConfigType } from './type';
|
import { AppTTSConfigType, AppWhisperConfigType } from './type';
|
||||||
|
|
||||||
export enum AppTypeEnum {
|
export enum AppTypeEnum {
|
||||||
|
folder = 'folder',
|
||||||
simple = 'simple',
|
simple = 'simple',
|
||||||
advanced = 'advanced'
|
workflow = 'advanced',
|
||||||
|
plugin = 'plugin',
|
||||||
|
httpPlugin = 'httpPlugin'
|
||||||
}
|
}
|
||||||
export const AppTypeMap = {
|
|
||||||
[AppTypeEnum.simple]: {
|
|
||||||
label: 'simple'
|
|
||||||
},
|
|
||||||
[AppTypeEnum.advanced]: {
|
|
||||||
label: 'advanced'
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export const defaultTTSConfig: AppTTSConfigType = { type: 'web' };
|
export const defaultTTSConfig: AppTTSConfigType = { type: 'web' };
|
||||||
|
|
||||||
|
|||||||
24
packages/global/core/app/controller.d.ts
vendored
Normal file
24
packages/global/core/app/controller.d.ts
vendored
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
import { ParentIdType } from 'common/parentFolder/type';
|
||||||
|
import { AppSchema } from './type';
|
||||||
|
import { AppTypeEnum } from './constants';
|
||||||
|
|
||||||
|
export type CreateAppProps = {
|
||||||
|
parentId?: ParentIdType;
|
||||||
|
name?: string;
|
||||||
|
avatar?: string;
|
||||||
|
intro?: string;
|
||||||
|
type?: AppTypeEnum;
|
||||||
|
modules: AppSchema['modules'];
|
||||||
|
edges?: AppSchema['edges'];
|
||||||
|
};
|
||||||
|
export type CreateHttpPluginChildrenPros = Omit<CreateAppProps, 'type'> & {
|
||||||
|
parentId: ParentIdType;
|
||||||
|
name: string;
|
||||||
|
intro: string;
|
||||||
|
avatar: string;
|
||||||
|
modules: AppSchema['modules'];
|
||||||
|
edges: AppSchema['edges'];
|
||||||
|
pluginData: {
|
||||||
|
pluginUniId: string;
|
||||||
|
};
|
||||||
|
};
|
||||||
@@ -2,19 +2,20 @@ import { getNanoid } from '../../../common/string/tools';
|
|||||||
import { OpenApiJsonSchema } from './type';
|
import { OpenApiJsonSchema } from './type';
|
||||||
import yaml from 'js-yaml';
|
import yaml from 'js-yaml';
|
||||||
import { OpenAPIV3 } from 'openapi-types';
|
import { OpenAPIV3 } from 'openapi-types';
|
||||||
import { PluginTypeEnum } from '../constants';
|
import { FlowNodeInputItemType, FlowNodeOutputItemType } from '../../workflow/type/io';
|
||||||
import { FlowNodeInputItemType, FlowNodeOutputItemType } from '../../workflow/type/io.d';
|
|
||||||
import { FlowNodeInputTypeEnum, FlowNodeOutputTypeEnum } from '../../workflow/node/constant';
|
import { FlowNodeInputTypeEnum, FlowNodeOutputTypeEnum } from '../../workflow/node/constant';
|
||||||
import { NodeInputKeyEnum, WorkflowIOValueTypeEnum } from '../../workflow/constants';
|
import { NodeInputKeyEnum, WorkflowIOValueTypeEnum } from '../../workflow/constants';
|
||||||
import { PluginInputModule } from '../../workflow/template/system/pluginInput';
|
import { PluginInputModule } from '../../workflow/template/system/pluginInput';
|
||||||
import { PluginOutputModule } from '../../workflow/template/system/pluginOutput';
|
import { PluginOutputModule } from '../../workflow/template/system/pluginOutput';
|
||||||
import { HttpModule468 } from '../../workflow/template/system/http468';
|
import { HttpModule468 } from '../../workflow/template/system/http468';
|
||||||
import { HttpParamAndHeaderItemType } from '../../workflow/api';
|
import { HttpParamAndHeaderItemType } from '../../workflow/api';
|
||||||
import { CreateOnePluginParams } from '../controller';
|
|
||||||
import { StoreNodeItemType } from '../../workflow/type';
|
import { StoreNodeItemType } from '../../workflow/type';
|
||||||
import { HttpImgUrl } from '../../../common/file/image/constants';
|
import { HttpImgUrl } from '../../../common/file/image/constants';
|
||||||
import SwaggerParser from '@apidevtools/swagger-parser';
|
import SwaggerParser from '@apidevtools/swagger-parser';
|
||||||
import { getHandleId } from '../../../core/workflow/utils';
|
import { getHandleId } from '../../workflow/utils';
|
||||||
|
import { CreateHttpPluginChildrenPros } from '../controller';
|
||||||
|
import { AppTypeEnum } from '../constants';
|
||||||
|
import type { StoreEdgeItemType } from '../../workflow/type/edge';
|
||||||
|
|
||||||
export const str2OpenApiSchema = async (yamlStr = ''): Promise<OpenApiJsonSchema> => {
|
export const str2OpenApiSchema = async (yamlStr = ''): Promise<OpenApiJsonSchema> => {
|
||||||
try {
|
try {
|
||||||
@@ -89,7 +90,7 @@ export const httpApiSchema2Plugins = async ({
|
|||||||
parentId: string;
|
parentId: string;
|
||||||
apiSchemaStr?: string;
|
apiSchemaStr?: string;
|
||||||
customHeader?: string;
|
customHeader?: string;
|
||||||
}): Promise<CreateOnePluginParams[]> => {
|
}): Promise<CreateHttpPluginChildrenPros[]> => {
|
||||||
const jsonSchema = await str2OpenApiSchema(apiSchemaStr);
|
const jsonSchema = await str2OpenApiSchema(apiSchemaStr);
|
||||||
|
|
||||||
const baseUrl = jsonSchema.serverPath;
|
const baseUrl = jsonSchema.serverPath;
|
||||||
@@ -400,7 +401,7 @@ export const httpApiSchema2Plugins = async ({
|
|||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
const edges = [
|
const edges: StoreEdgeItemType[] = [
|
||||||
{
|
{
|
||||||
source: pluginInputId,
|
source: pluginInputId,
|
||||||
target: httpId,
|
target: httpId,
|
||||||
@@ -420,9 +421,12 @@ export const httpApiSchema2Plugins = async ({
|
|||||||
avatar: HttpImgUrl,
|
avatar: HttpImgUrl,
|
||||||
intro: item.description,
|
intro: item.description,
|
||||||
parentId,
|
parentId,
|
||||||
type: PluginTypeEnum.http,
|
type: AppTypeEnum.plugin,
|
||||||
modules,
|
modules,
|
||||||
edges
|
edges,
|
||||||
|
pluginData: {
|
||||||
|
pluginUniId: item.name
|
||||||
|
}
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
31
packages/global/core/app/type.d.ts
vendored
31
packages/global/core/app/type.d.ts
vendored
@@ -1,5 +1,4 @@
|
|||||||
import type { FlowNodeTemplateType, StoreNodeItemType } from '../workflow/type';
|
import type { FlowNodeTemplateType, StoreNodeItemType } from '../workflow/type';
|
||||||
|
|
||||||
import { AppTypeEnum } from './constants';
|
import { AppTypeEnum } from './constants';
|
||||||
import { PermissionTypeEnum } from '../../support/permission/constant';
|
import { PermissionTypeEnum } from '../../support/permission/constant';
|
||||||
import { VariableInputEnum } from '../workflow/constants';
|
import { VariableInputEnum } from '../workflow/constants';
|
||||||
@@ -7,43 +6,57 @@ import { SelectedDatasetType } from '../workflow/api';
|
|||||||
import { DatasetSearchModeEnum } from '../dataset/constants';
|
import { DatasetSearchModeEnum } from '../dataset/constants';
|
||||||
import { TeamTagSchema as TeamTagsSchemaType } from '@fastgpt/global/support/user/team/type.d';
|
import { TeamTagSchema as TeamTagsSchemaType } from '@fastgpt/global/support/user/team/type.d';
|
||||||
import { StoreEdgeItemType } from '../workflow/type/edge';
|
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 = {
|
export type AppSchema = {
|
||||||
_id: string;
|
_id: string;
|
||||||
|
parentId?: ParentIdType;
|
||||||
teamId: string;
|
teamId: string;
|
||||||
tmbId: string;
|
tmbId: string;
|
||||||
name: string;
|
type: AppTypeEnum;
|
||||||
type: `${AppTypeEnum}`;
|
|
||||||
version?: 'v1' | 'v2';
|
version?: 'v1' | 'v2';
|
||||||
|
|
||||||
|
name: string;
|
||||||
avatar: string;
|
avatar: string;
|
||||||
intro: string;
|
intro: string;
|
||||||
updateTime: number;
|
updateTime: Date;
|
||||||
|
|
||||||
modules: StoreNodeItemType[];
|
modules: StoreNodeItemType[];
|
||||||
edges: StoreEdgeItemType[];
|
edges: StoreEdgeItemType[];
|
||||||
|
pluginData?: {
|
||||||
|
nodeVersion?: string;
|
||||||
|
pluginUniId?: string; // plugin unique id(plugin name)
|
||||||
|
apiSchemaStr?: string; // api schema string
|
||||||
|
customHeaders?: string;
|
||||||
|
};
|
||||||
|
|
||||||
// App system config
|
// App system config
|
||||||
chatConfig: AppChatConfigType;
|
chatConfig: AppChatConfigType;
|
||||||
scheduledTriggerConfig?: AppScheduledTriggerConfigType | null;
|
scheduledTriggerConfig?: AppScheduledTriggerConfigType | null;
|
||||||
scheduledTriggerNextTime?: Date;
|
scheduledTriggerNextTime?: Date;
|
||||||
|
|
||||||
permission: `${PermissionTypeEnum}`;
|
|
||||||
inited?: boolean;
|
inited?: boolean;
|
||||||
teamTags: string[];
|
teamTags: string[];
|
||||||
|
defaultPermission: PermissionValueType;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type AppListItemType = {
|
export type AppListItemType = {
|
||||||
_id: string;
|
_id: string;
|
||||||
|
tmbId: string;
|
||||||
name: string;
|
name: string;
|
||||||
avatar: string;
|
avatar: string;
|
||||||
intro: string;
|
intro: string;
|
||||||
isOwner: boolean;
|
type: AppTypeEnum;
|
||||||
permission: `${PermissionTypeEnum}`;
|
updateTime: Date;
|
||||||
|
defaultPermission: PermissionValueType;
|
||||||
|
permission: AppPermission;
|
||||||
|
pluginData?: AppSchema['pluginData'];
|
||||||
};
|
};
|
||||||
|
|
||||||
export type AppDetailType = AppSchema & {
|
export type AppDetailType = AppSchema & {
|
||||||
isOwner: boolean;
|
permission: AppPermission;
|
||||||
canWrite: boolean;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export type AppSimpleEditFormType = {
|
export type AppSimpleEditFormType = {
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ export const getDefaultAppForm = (): AppSimpleEditFormType => ({
|
|||||||
limit: 1500,
|
limit: 1500,
|
||||||
searchMode: DatasetSearchModeEnum.embedding,
|
searchMode: DatasetSearchModeEnum.embedding,
|
||||||
usingReRank: false,
|
usingReRank: false,
|
||||||
datasetSearchUsingExtensionQuery: true,
|
datasetSearchUsingExtensionQuery: false,
|
||||||
datasetSearchExtensionBg: ''
|
datasetSearchExtensionBg: ''
|
||||||
},
|
},
|
||||||
selectedTools: [],
|
selectedTools: [],
|
||||||
@@ -99,7 +99,7 @@ export const appWorkflow2Form = ({
|
|||||||
if (!node.pluginId) return;
|
if (!node.pluginId) return;
|
||||||
|
|
||||||
defaultAppForm.selectedTools.push({
|
defaultAppForm.selectedTools.push({
|
||||||
id: node.pluginId,
|
id: node.nodeId,
|
||||||
pluginId: node.pluginId,
|
pluginId: node.pluginId,
|
||||||
name: node.name,
|
name: node.name,
|
||||||
avatar: node.avatar,
|
avatar: node.avatar,
|
||||||
|
|||||||
8
packages/global/core/app/version.d.ts
vendored
8
packages/global/core/app/version.d.ts
vendored
@@ -1,12 +1,12 @@
|
|||||||
import { StoreNodeItemType } from '../workflow/type';
|
import { StoreNodeItemType } from '../workflow/type';
|
||||||
import { StoreEdgeItemType } from '../workflow/type/edge';
|
import { StoreEdgeItemType } from '../workflow/type/edge';
|
||||||
import { AppChatConfigType } from './type';
|
import { AppChatConfigType, AppSchema } from './type';
|
||||||
|
|
||||||
export type AppVersionSchemaType = {
|
export type AppVersionSchemaType = {
|
||||||
_id: string;
|
_id: string;
|
||||||
appId: string;
|
appId: string;
|
||||||
time: Date;
|
time: Date;
|
||||||
nodes: StoreNodeItemType[];
|
nodes: AppSchema['modules'];
|
||||||
edges: StoreEdgeItemType[];
|
edges: AppSchema['edges'];
|
||||||
chatConfig: AppChatConfigType;
|
chatConfig: AppSchema['chatConfig'];
|
||||||
};
|
};
|
||||||
|
|||||||
6
packages/global/core/dataset/api.d.ts
vendored
6
packages/global/core/dataset/api.d.ts
vendored
@@ -1,20 +1,22 @@
|
|||||||
import { DatasetDataIndexItemType, DatasetSchemaType } from './type';
|
import { DatasetDataIndexItemType, DatasetSchemaType } from './type';
|
||||||
import { TrainingModeEnum, DatasetCollectionTypeEnum } from './constants';
|
import { TrainingModeEnum, DatasetCollectionTypeEnum } from './constants';
|
||||||
import type { LLMModelItemType } from '../ai/model.d';
|
import type { LLMModelItemType } from '../ai/model.d';
|
||||||
|
import { ParentIdType } from 'common/parentFolder/type';
|
||||||
|
|
||||||
/* ================= dataset ===================== */
|
/* ================= dataset ===================== */
|
||||||
export type DatasetUpdateBody = {
|
export type DatasetUpdateBody = {
|
||||||
id: string;
|
id: string;
|
||||||
parentId?: string;
|
parentId?: ParentIdType;
|
||||||
name?: string;
|
name?: string;
|
||||||
avatar?: string;
|
avatar?: string;
|
||||||
intro?: string;
|
intro?: string;
|
||||||
permission?: DatasetSchemaType['permission'];
|
permission?: DatasetSchemaType['permission']; // TODO: Should be deleted.
|
||||||
agentModel?: LLMModelItemType;
|
agentModel?: LLMModelItemType;
|
||||||
status?: DatasetSchemaType['status'];
|
status?: DatasetSchemaType['status'];
|
||||||
|
|
||||||
websiteConfig?: DatasetSchemaType['websiteConfig'];
|
websiteConfig?: DatasetSchemaType['websiteConfig'];
|
||||||
externalReadUrl?: DatasetSchemaType['externalReadUrl'];
|
externalReadUrl?: DatasetSchemaType['externalReadUrl'];
|
||||||
|
defaultPermission?: DatasetSchemaType['defaultPermission'];
|
||||||
};
|
};
|
||||||
|
|
||||||
/* ================= collection ===================== */
|
/* ================= collection ===================== */
|
||||||
|
|||||||
11
packages/global/core/dataset/collaborator.d.ts
vendored
Normal file
11
packages/global/core/dataset/collaborator.d.ts
vendored
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
import { UpdateClbPermissionProps } from '../../support/permission/collaborator';
|
||||||
|
import { PermissionValueType } from '../../support/permission/type';
|
||||||
|
|
||||||
|
export type UpdateDatasetCollaboratorBody = UpdateClbPermissionProps & {
|
||||||
|
datasetId: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type DatasetCollaboratorDeleteParams = {
|
||||||
|
datasetId: string;
|
||||||
|
tmbId: string;
|
||||||
|
};
|
||||||
19
packages/global/core/dataset/type.d.ts
vendored
19
packages/global/core/dataset/type.d.ts
vendored
@@ -1,3 +1,4 @@
|
|||||||
|
import { PermissionValueType } from 'support/permission/type';
|
||||||
import type { LLMModelItemType, VectorModelItemType } from '../../core/ai/model.d';
|
import type { LLMModelItemType, VectorModelItemType } from '../../core/ai/model.d';
|
||||||
import { PermissionTypeEnum } from '../../support/permission/constant';
|
import { PermissionTypeEnum } from '../../support/permission/constant';
|
||||||
import { PushDatasetDataChunkProps } from './api';
|
import { PushDatasetDataChunkProps } from './api';
|
||||||
@@ -8,6 +9,8 @@ import {
|
|||||||
SearchScoreTypeEnum,
|
SearchScoreTypeEnum,
|
||||||
TrainingModeEnum
|
TrainingModeEnum
|
||||||
} from './constants';
|
} from './constants';
|
||||||
|
import { DatasetPermission } from '../../support/permission/dataset/controller';
|
||||||
|
import { Permission } from '../../support/permission/controller';
|
||||||
|
|
||||||
/* schema */
|
/* schema */
|
||||||
export type DatasetSchemaType = {
|
export type DatasetSchemaType = {
|
||||||
@@ -24,7 +27,7 @@ export type DatasetSchemaType = {
|
|||||||
intro: string;
|
intro: string;
|
||||||
type: DatasetTypeEnum;
|
type: DatasetTypeEnum;
|
||||||
status: `${DatasetStatusEnum}`;
|
status: `${DatasetStatusEnum}`;
|
||||||
permission: `${PermissionTypeEnum}`;
|
permission: DatasetPermission;
|
||||||
|
|
||||||
// metadata
|
// metadata
|
||||||
websiteConfig?: {
|
websiteConfig?: {
|
||||||
@@ -32,6 +35,7 @@ export type DatasetSchemaType = {
|
|||||||
selector: string;
|
selector: string;
|
||||||
};
|
};
|
||||||
externalReadUrl?: string;
|
externalReadUrl?: string;
|
||||||
|
defaultPermission: PermissionValueType;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type DatasetCollectionSchemaType = {
|
export type DatasetCollectionSchemaType = {
|
||||||
@@ -132,24 +136,22 @@ export type DatasetListItemType = {
|
|||||||
name: string;
|
name: string;
|
||||||
intro: string;
|
intro: string;
|
||||||
type: DatasetTypeEnum;
|
type: DatasetTypeEnum;
|
||||||
isOwner: boolean;
|
permission: DatasetPermission;
|
||||||
canWrite: boolean;
|
|
||||||
permission: `${PermissionTypeEnum}`;
|
|
||||||
vectorModel: VectorModelItemType;
|
vectorModel: VectorModelItemType;
|
||||||
|
defaultPermission: PermissionValueType;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type DatasetItemType = Omit<DatasetSchemaType, 'vectorModel' | 'agentModel'> & {
|
export type DatasetItemType = Omit<DatasetSchemaType, 'vectorModel' | 'agentModel'> & {
|
||||||
vectorModel: VectorModelItemType;
|
vectorModel: VectorModelItemType;
|
||||||
agentModel: LLMModelItemType;
|
agentModel: LLMModelItemType;
|
||||||
isOwner: boolean;
|
|
||||||
canWrite: boolean;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/* ================= collection ===================== */
|
/* ================= collection ===================== */
|
||||||
export type DatasetCollectionItemType = CollectionWithDatasetType & {
|
export type DatasetCollectionItemType = CollectionWithDatasetType & {
|
||||||
canWrite: boolean;
|
|
||||||
sourceName: string;
|
sourceName: string;
|
||||||
sourceId?: string;
|
sourceId?: string;
|
||||||
file?: DatasetFileSchema;
|
file?: DatasetFileSchema;
|
||||||
|
permission: DatasetPermission;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* ================= data ===================== */
|
/* ================= data ===================== */
|
||||||
@@ -177,10 +179,9 @@ export type DatasetFileSchema = {
|
|||||||
filename: string;
|
filename: string;
|
||||||
contentType: string;
|
contentType: string;
|
||||||
metadata: {
|
metadata: {
|
||||||
contentType: string;
|
|
||||||
datasetId: string;
|
|
||||||
teamId: string;
|
teamId: string;
|
||||||
tmbId: string;
|
tmbId: string;
|
||||||
|
encoding?: string;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -24,13 +24,15 @@ export function getSourceNameIcon({
|
|||||||
sourceName: string;
|
sourceName: string;
|
||||||
sourceId?: string;
|
sourceId?: string;
|
||||||
}) {
|
}) {
|
||||||
const fileIcon = getFileIcon(decodeURIComponent(sourceName), '');
|
try {
|
||||||
if (fileIcon) {
|
const fileIcon = getFileIcon(decodeURIComponent(sourceName.replace(/%/g, '%25')), '');
|
||||||
return fileIcon;
|
if (fileIcon) {
|
||||||
}
|
return fileIcon;
|
||||||
if (strIsLink(sourceId)) {
|
}
|
||||||
return 'common/linkBlue';
|
if (strIsLink(sourceId)) {
|
||||||
}
|
return 'common/linkBlue';
|
||||||
|
}
|
||||||
|
} catch (error) {}
|
||||||
|
|
||||||
return 'file/fill/manual';
|
return 'file/fill/manual';
|
||||||
}
|
}
|
||||||
|
|||||||
2
packages/global/core/plugin/controller.d.ts
vendored
2
packages/global/core/plugin/controller.d.ts
vendored
@@ -1,7 +1,7 @@
|
|||||||
import { StoreEdgeItemType } from 'core/workflow/type/edge';
|
import { StoreEdgeItemType } from 'core/workflow/type/edge';
|
||||||
import type { StoreNodeItemType } from '../workflow/type';
|
import type { StoreNodeItemType } from '../workflow/type';
|
||||||
import { PluginTypeEnum } from './constants';
|
import { PluginTypeEnum } from './constants';
|
||||||
import { HttpAuthMethodType } from './httpPlugin/type';
|
import { HttpAuthMethodType } from '../app/httpPlugin/type';
|
||||||
|
|
||||||
export type CreateOnePluginParams = {
|
export type CreateOnePluginParams = {
|
||||||
name: string;
|
name: string;
|
||||||
|
|||||||
3
packages/global/core/plugin/type.d.ts
vendored
3
packages/global/core/plugin/type.d.ts
vendored
@@ -24,6 +24,7 @@ export type PluginItemSchema = {
|
|||||||
};
|
};
|
||||||
version?: 'v1' | 'v2';
|
version?: 'v1' | 'v2';
|
||||||
nodeVersion?: string;
|
nodeVersion?: string;
|
||||||
|
inited?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* plugin template */
|
/* plugin template */
|
||||||
@@ -33,7 +34,7 @@ export type PluginTemplateType = PluginRuntimeType & {
|
|||||||
source: `${PluginSourceEnum}`;
|
source: `${PluginSourceEnum}`;
|
||||||
templateType: FlowNodeTemplateType['templateType'];
|
templateType: FlowNodeTemplateType['templateType'];
|
||||||
intro: string;
|
intro: string;
|
||||||
nodeVersion: string;
|
version: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type PluginRuntimeType = {
|
export type PluginRuntimeType = {
|
||||||
|
|||||||
@@ -118,3 +118,4 @@ export enum FlowNodeTypeEnum {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const EDGE_TYPE = 'default';
|
export const EDGE_TYPE = 'default';
|
||||||
|
export const defaultNodeVersion = '481';
|
||||||
|
|||||||
@@ -24,10 +24,7 @@ import { IfElseNode } from './system/ifElse/index';
|
|||||||
import { VariableUpdateNode } from './system/variableUpdate';
|
import { VariableUpdateNode } from './system/variableUpdate';
|
||||||
import { CodeNode } from './system/sandbox';
|
import { CodeNode } from './system/sandbox';
|
||||||
|
|
||||||
/* app flow module templates */
|
const systemNodes: FlowNodeTemplateType[] = [
|
||||||
export const appSystemModuleTemplates: FlowNodeTemplateType[] = [
|
|
||||||
SystemConfigNode,
|
|
||||||
WorkflowStart,
|
|
||||||
AiChatModule,
|
AiChatModule,
|
||||||
AssignedAnswerModule,
|
AssignedAnswerModule,
|
||||||
DatasetSearchModule,
|
DatasetSearchModule,
|
||||||
@@ -44,49 +41,26 @@ export const appSystemModuleTemplates: FlowNodeTemplateType[] = [
|
|||||||
VariableUpdateNode,
|
VariableUpdateNode,
|
||||||
CodeNode
|
CodeNode
|
||||||
];
|
];
|
||||||
|
/* app flow module templates */
|
||||||
|
export const appSystemModuleTemplates: FlowNodeTemplateType[] = [
|
||||||
|
SystemConfigNode,
|
||||||
|
WorkflowStart,
|
||||||
|
...systemNodes
|
||||||
|
];
|
||||||
/* plugin flow module templates */
|
/* plugin flow module templates */
|
||||||
export const pluginSystemModuleTemplates: FlowNodeTemplateType[] = [
|
export const pluginSystemModuleTemplates: FlowNodeTemplateType[] = [
|
||||||
PluginInputModule,
|
PluginInputModule,
|
||||||
PluginOutputModule,
|
PluginOutputModule,
|
||||||
AiChatModule,
|
...systemNodes
|
||||||
AssignedAnswerModule,
|
|
||||||
DatasetSearchModule,
|
|
||||||
DatasetConcatModule,
|
|
||||||
RunAppModule,
|
|
||||||
ToolModule,
|
|
||||||
StopToolNode,
|
|
||||||
ClassifyQuestionModule,
|
|
||||||
ContextExtractModule,
|
|
||||||
HttpModule468,
|
|
||||||
AiQueryExtension,
|
|
||||||
LafModule,
|
|
||||||
IfElseNode,
|
|
||||||
VariableUpdateNode,
|
|
||||||
CodeNode
|
|
||||||
];
|
];
|
||||||
|
|
||||||
/* all module */
|
/* all module */
|
||||||
export const moduleTemplatesFlat: FlowNodeTemplateType[] = [
|
export const moduleTemplatesFlat: FlowNodeTemplateType[] = [
|
||||||
|
...systemNodes,
|
||||||
EmptyNode,
|
EmptyNode,
|
||||||
SystemConfigNode,
|
SystemConfigNode,
|
||||||
WorkflowStart,
|
WorkflowStart,
|
||||||
AiChatModule,
|
|
||||||
DatasetSearchModule,
|
|
||||||
DatasetConcatModule,
|
|
||||||
AssignedAnswerModule,
|
|
||||||
ClassifyQuestionModule,
|
|
||||||
ContextExtractModule,
|
|
||||||
HttpModule468,
|
|
||||||
ToolModule,
|
|
||||||
StopToolNode,
|
|
||||||
AiChatModule,
|
|
||||||
RunAppModule,
|
|
||||||
PluginInputModule,
|
PluginInputModule,
|
||||||
PluginOutputModule,
|
PluginOutputModule,
|
||||||
RunPluginModule,
|
RunPluginModule
|
||||||
AiQueryExtension,
|
|
||||||
LafModule,
|
|
||||||
IfElseNode,
|
|
||||||
VariableUpdateNode,
|
|
||||||
CodeNode
|
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -35,7 +35,10 @@ export const RunAppModule: FlowNodeTemplateType = {
|
|||||||
required: true
|
required: true
|
||||||
},
|
},
|
||||||
Input_Template_History,
|
Input_Template_History,
|
||||||
Input_Template_UserChatInput
|
{
|
||||||
|
...Input_Template_UserChatInput,
|
||||||
|
toolDescription: '用户问题'
|
||||||
|
}
|
||||||
],
|
],
|
||||||
outputs: [
|
outputs: [
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { FlowNodeTypeEnum } from '../../node/constant';
|
import { FlowNodeTypeEnum } from '../../node/constant';
|
||||||
import { FlowNodeTemplateType } from '../../type/index.d';
|
import { FlowNodeTemplateType } from '../../type/index.d';
|
||||||
import { FlowNodeTemplateTypeEnum, WorkflowIOValueTypeEnum } from '../../constants';
|
import { FlowNodeTemplateTypeEnum } from '../../constants';
|
||||||
import { getHandleConfig } from '../utils';
|
import { getHandleConfig } from '../utils';
|
||||||
|
|
||||||
export const SystemConfigNode: FlowNodeTemplateType = {
|
export const SystemConfigNode: FlowNodeTemplateType = {
|
||||||
|
|||||||
14
packages/global/core/workflow/type/index.d.ts
vendored
14
packages/global/core/workflow/type/index.d.ts
vendored
@@ -20,6 +20,9 @@ import { RuntimeNodeItemType } from '../runtime/type';
|
|||||||
import { PluginTypeEnum } from '../../plugin/constants';
|
import { PluginTypeEnum } from '../../plugin/constants';
|
||||||
import { RuntimeEdgeItemType, StoreEdgeItemType } from './edge';
|
import { RuntimeEdgeItemType, StoreEdgeItemType } from './edge';
|
||||||
import { NextApiResponse } from 'next';
|
import { NextApiResponse } from 'next';
|
||||||
|
import { AppDetailType, AppSchema } from '../../app/type';
|
||||||
|
import { ParentIdType } from 'common/parentFolder/type';
|
||||||
|
import { AppTypeEnum } from 'core/app/constants';
|
||||||
|
|
||||||
export type FlowNodeCommonType = {
|
export type FlowNodeCommonType = {
|
||||||
flowNodeType: FlowNodeTypeEnum; // render node card
|
flowNodeType: FlowNodeTypeEnum; // render node card
|
||||||
@@ -36,8 +39,8 @@ export type FlowNodeCommonType = {
|
|||||||
|
|
||||||
// plugin data
|
// plugin data
|
||||||
pluginId?: string;
|
pluginId?: string;
|
||||||
pluginType?: `${PluginTypeEnum}`;
|
pluginType?: AppTypeEnum;
|
||||||
parentId?: string;
|
// parentId: ParentIdType;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type FlowNodeTemplateType = FlowNodeCommonType & {
|
export type FlowNodeTemplateType = FlowNodeCommonType & {
|
||||||
@@ -64,7 +67,6 @@ export type FlowNodeTemplateType = FlowNodeCommonType & {
|
|||||||
// action
|
// action
|
||||||
forbidDelete?: boolean; // forbid delete
|
forbidDelete?: boolean; // forbid delete
|
||||||
unique?: boolean;
|
unique?: boolean;
|
||||||
nodeVersion?: string;
|
|
||||||
};
|
};
|
||||||
export type FlowNodeItemType = FlowNodeTemplateType & {
|
export type FlowNodeItemType = FlowNodeTemplateType & {
|
||||||
nodeId: string;
|
nodeId: string;
|
||||||
@@ -105,8 +107,8 @@ export type NodeSourceNodeItemType = {
|
|||||||
/* --------------- function type -------------------- */
|
/* --------------- function type -------------------- */
|
||||||
export type SelectAppItemType = {
|
export type SelectAppItemType = {
|
||||||
id: string;
|
id: string;
|
||||||
name: string;
|
// name: string;
|
||||||
logo: string;
|
// logo?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* agent */
|
/* agent */
|
||||||
@@ -131,7 +133,7 @@ export type ChatDispatchProps = {
|
|||||||
teamId: string;
|
teamId: string;
|
||||||
tmbId: string;
|
tmbId: string;
|
||||||
user: UserModelSchema;
|
user: UserModelSchema;
|
||||||
appId?: string;
|
app: AppDetailType | AppSchema;
|
||||||
chatId?: string;
|
chatId?: string;
|
||||||
responseChatItemId?: string;
|
responseChatItemId?: string;
|
||||||
histories: ChatItemType[];
|
histories: ChatItemType[];
|
||||||
|
|||||||
@@ -17,6 +17,6 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/js-yaml": "^4.0.9",
|
"@types/js-yaml": "^4.0.9",
|
||||||
"@types/node": "^20.8.5"
|
"@types/node": "^20.14.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
20
packages/global/support/permission/app/constant.ts
Normal file
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
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
0
packages/global/support/permission/app/type.d.ts
vendored
Normal file
15
packages/global/support/permission/collaborator.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 {
|
export enum AuthUserTypeEnum {
|
||||||
token = 'token',
|
token = 'token',
|
||||||
root = 'root',
|
root = 'root',
|
||||||
@@ -8,7 +11,10 @@ export enum AuthUserTypeEnum {
|
|||||||
|
|
||||||
export enum PermissionTypeEnum {
|
export enum PermissionTypeEnum {
|
||||||
'private' = 'private',
|
'private' = 'private',
|
||||||
'public' = 'public'
|
'public' = 'public',
|
||||||
|
clbPrivate = 'clbPrivate',
|
||||||
|
publicRead = 'publicRead',
|
||||||
|
publicWrite = 'publicWrite'
|
||||||
}
|
}
|
||||||
export const PermissionTypeMap = {
|
export const PermissionTypeMap = {
|
||||||
[PermissionTypeEnum.private]: {
|
[PermissionTypeEnum.private]: {
|
||||||
@@ -18,11 +24,56 @@ export const PermissionTypeMap = {
|
|||||||
[PermissionTypeEnum.public]: {
|
[PermissionTypeEnum.public]: {
|
||||||
iconLight: 'support/permission/publicLight',
|
iconLight: 'support/permission/publicLight',
|
||||||
label: 'permission.Public'
|
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',
|
team = 'team',
|
||||||
app = 'app',
|
app = 'app',
|
||||||
dataset = 'dataset'
|
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
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
20
packages/global/support/permission/dataset/constant.ts
Normal file
20
packages/global/support/permission/dataset/constant.ts
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
import { NullPermission, PermissionKeyEnum, PermissionList } from '../constant';
|
||||||
|
|
||||||
|
export enum DatasetPermissionKeyEnum {}
|
||||||
|
|
||||||
|
export const DatasetPermissionList = {
|
||||||
|
[PermissionKeyEnum.read]: {
|
||||||
|
...PermissionList[PermissionKeyEnum.read],
|
||||||
|
description: '可查看知识库内容'
|
||||||
|
},
|
||||||
|
[PermissionKeyEnum.write]: {
|
||||||
|
...PermissionList[PermissionKeyEnum.write],
|
||||||
|
description: '可增加和变更知识库内容'
|
||||||
|
},
|
||||||
|
[PermissionKeyEnum.manage]: {
|
||||||
|
...PermissionList[PermissionKeyEnum.manage],
|
||||||
|
description: '可管理整个知识库数据和信息'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const DatasetDefaultPermissionVal = NullPermission;
|
||||||
14
packages/global/support/permission/dataset/controller.ts
Normal file
14
packages/global/support/permission/dataset/controller.ts
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
import { NullPermission } from '../constant';
|
||||||
|
import { PerConstructPros, Permission } from '../controller';
|
||||||
|
export class DatasetPermission extends Permission {
|
||||||
|
constructor(props?: PerConstructPros) {
|
||||||
|
if (!props) {
|
||||||
|
props = {
|
||||||
|
per: NullPermission
|
||||||
|
};
|
||||||
|
} else if (!props?.per) {
|
||||||
|
props.per = NullPermission;
|
||||||
|
}
|
||||||
|
super(props);
|
||||||
|
}
|
||||||
|
}
|
||||||
21
packages/global/support/permission/type.d.ts
vendored
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 PermissionValueType = number;
|
||||||
|
export type PermissionListType<T = {}> = Record<
|
||||||
|
T | PermissionKeyEnum,
|
||||||
|
{
|
||||||
|
name: string;
|
||||||
|
description: string;
|
||||||
|
value: PermissionValueType;
|
||||||
|
checkBoxType: 'single' | 'multiple';
|
||||||
|
}
|
||||||
|
>;
|
||||||
|
|
||||||
export type AuthResponseType = {
|
export type AuthResponseType = {
|
||||||
teamId: string;
|
teamId: string;
|
||||||
@@ -17,4 +31,9 @@ export type ResourcePermissionType = {
|
|||||||
tmbId: string;
|
tmbId: string;
|
||||||
resourceType: ResourceType;
|
resourceType: ResourceType;
|
||||||
permission: PermissionValueType;
|
permission: PermissionValueType;
|
||||||
|
resourceId: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type ResourcePerWithTmbWithUser = Omit<ResourcePermissionType, 'tmbId'> & {
|
||||||
|
tmbId: TeamMemberWithUserSchema;
|
||||||
};
|
};
|
||||||
|
|||||||
19
packages/global/support/permission/user/constant.ts
Normal file
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
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 { TeamMemberRoleEnum } from '../user/team/constant';
|
||||||
import { PermissionTypeEnum } from './constant';
|
import { PermissionTypeEnum } from './constant';
|
||||||
|
import { Permission } from './controller';
|
||||||
|
|
||||||
/* team public source, or owner source in team */
|
/* team public source, or owner source in team */
|
||||||
export function mongoRPermission({
|
export function mongoRPermission({
|
||||||
teamId,
|
teamId,
|
||||||
tmbId,
|
tmbId,
|
||||||
role
|
permission
|
||||||
}: {
|
}: {
|
||||||
teamId: string;
|
teamId: string;
|
||||||
tmbId: string;
|
tmbId: string;
|
||||||
role: `${TeamMemberRoleEnum}`;
|
permission: Permission;
|
||||||
}) {
|
}) {
|
||||||
|
if (permission.isOwner) {
|
||||||
|
return {
|
||||||
|
teamId
|
||||||
|
};
|
||||||
|
}
|
||||||
return {
|
return {
|
||||||
teamId,
|
teamId,
|
||||||
...(role === TeamMemberRoleEnum.visitor && { permission: PermissionTypeEnum.public }),
|
$or: [{ permission: PermissionTypeEnum.public }, { tmbId }]
|
||||||
...(role === TeamMemberRoleEnum.admin && {
|
|
||||||
$or: [{ permission: PermissionTypeEnum.public }, { tmbId }]
|
|
||||||
})
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
export function mongoOwnerPermission({ teamId, tmbId }: { teamId: string; tmbId: string }) {
|
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 { TeamMemberRoleEnum } from './constant';
|
||||||
import { LafAccountType, TeamMemberSchema } from './type';
|
import { LafAccountType, TeamMemberSchema } from './type';
|
||||||
|
|
||||||
@@ -22,8 +22,7 @@ export type UpdateTeamProps = {
|
|||||||
|
|
||||||
/* ------------- member ----------- */
|
/* ------------- member ----------- */
|
||||||
export type DelMemberProps = {
|
export type DelMemberProps = {
|
||||||
teamId: string;
|
tmbId: string;
|
||||||
memberId: string;
|
|
||||||
};
|
};
|
||||||
export type UpdateTeamMemberProps = {
|
export type UpdateTeamMemberProps = {
|
||||||
teamId: string;
|
teamId: string;
|
||||||
@@ -34,7 +33,7 @@ export type UpdateTeamMemberProps = {
|
|||||||
export type InviteMemberProps = {
|
export type InviteMemberProps = {
|
||||||
teamId: string;
|
teamId: string;
|
||||||
usernames: string[];
|
usernames: string[];
|
||||||
role: `${TeamMemberRoleEnum}`;
|
permission: PermissionValueType;
|
||||||
};
|
};
|
||||||
export type UpdateInviteProps = {
|
export type UpdateInviteProps = {
|
||||||
tmbId: string;
|
tmbId: string;
|
||||||
@@ -44,9 +43,3 @@ export type InviteMemberResponse = Record<
|
|||||||
'invite' | 'inValid' | 'inTeam',
|
'invite' | 'inValid' | 'inTeam',
|
||||||
{ username: string; userId: string }[]
|
{ username: string; userId: string }[]
|
||||||
>;
|
>;
|
||||||
|
|
||||||
export type UpdateTeamMemberPermissionProps = {
|
|
||||||
teamId: string;
|
|
||||||
memberIds: string[];
|
|
||||||
permission: PermissionValueType;
|
|
||||||
};
|
|
||||||
|
|||||||
8
packages/global/support/user/team/type.d.ts
vendored
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 type { TeamMemberRoleEnum, TeamMemberStatusEnum } from './constant';
|
||||||
import { LafAccountType } from './type';
|
import { LafAccountType } from './type';
|
||||||
import { PermissionValueType, ResourcePermissionType } from '../../permission/type';
|
import { PermissionValueType, ResourcePermissionType } from '../../permission/type';
|
||||||
|
import { TeamPermission } from '../../permission/user/controller';
|
||||||
|
|
||||||
export type TeamSchema = {
|
export type TeamSchema = {
|
||||||
_id: string;
|
_id: string;
|
||||||
@@ -49,7 +50,7 @@ export type TeamMemberWithTeamAndUserSchema = Omit<TeamMemberWithTeamSchema, 'us
|
|||||||
userId: UserModelSchema;
|
userId: UserModelSchema;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TeamItemType = {
|
export type TeamTmbItemType = {
|
||||||
userId: string;
|
userId: string;
|
||||||
teamId: string;
|
teamId: string;
|
||||||
teamName: string;
|
teamName: string;
|
||||||
@@ -61,9 +62,8 @@ export type TeamItemType = {
|
|||||||
defaultTeam: boolean;
|
defaultTeam: boolean;
|
||||||
role: `${TeamMemberRoleEnum}`;
|
role: `${TeamMemberRoleEnum}`;
|
||||||
status: `${TeamMemberStatusEnum}`;
|
status: `${TeamMemberStatusEnum}`;
|
||||||
canWrite: boolean;
|
|
||||||
lafAccount?: LafAccountType;
|
lafAccount?: LafAccountType;
|
||||||
defaultPermission: PermissionValueType;
|
permission: TeamPermission;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TeamMemberItemType = {
|
export type TeamMemberItemType = {
|
||||||
@@ -75,7 +75,7 @@ export type TeamMemberItemType = {
|
|||||||
// TODO: this should be deprecated.
|
// TODO: this should be deprecated.
|
||||||
role: `${TeamMemberRoleEnum}`;
|
role: `${TeamMemberRoleEnum}`;
|
||||||
status: `${TeamMemberStatusEnum}`;
|
status: `${TeamMemberStatusEnum}`;
|
||||||
permission: PermissionValueType;
|
permission: TeamPermission;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TeamTagItemType = {
|
export type TeamTagItemType = {
|
||||||
|
|||||||
4
packages/global/support/user/type.d.ts
vendored
4
packages/global/support/user/type.d.ts
vendored
@@ -1,5 +1,5 @@
|
|||||||
import { UserStatusEnum } from './constant';
|
import { UserStatusEnum } from './constant';
|
||||||
import { TeamItemType } from './team/type';
|
import { TeamTmbItemType } from './team/type';
|
||||||
|
|
||||||
export type UserModelSchema = {
|
export type UserModelSchema = {
|
||||||
_id: string;
|
_id: string;
|
||||||
@@ -29,6 +29,6 @@ export type UserType = {
|
|||||||
timezone: string;
|
timezone: string;
|
||||||
promotionRate: UserModelSchema['promotionRate'];
|
promotionRate: UserModelSchema['promotionRate'];
|
||||||
openaiAccount: UserModelSchema['openaiAccount'];
|
openaiAccount: UserModelSchema['openaiAccount'];
|
||||||
team: TeamItemType;
|
team: TeamTmbItemType;
|
||||||
standardInfo?: standardInfoType;
|
standardInfo?: standardInfoType;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,21 +1,7 @@
|
|||||||
{
|
{
|
||||||
|
"extends":"../../tsconfig.json",
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"target": "es2015",
|
|
||||||
"lib": ["dom", "dom.iterable", "esnext"],
|
|
||||||
"allowJs": true,
|
|
||||||
"skipLibCheck": true,
|
|
||||||
"strict": true,
|
|
||||||
"forceConsistentCasingInFileNames": true,
|
|
||||||
"noEmit": true,
|
|
||||||
"esModuleInterop": true,
|
|
||||||
"module": "esnext",
|
|
||||||
"moduleResolution": "node",
|
|
||||||
"resolveJsonModule": true,
|
|
||||||
"isolatedModules": true,
|
|
||||||
"jsx": "preserve",
|
|
||||||
"incremental": true,
|
|
||||||
"baseUrl": "."
|
"baseUrl": "."
|
||||||
},
|
},
|
||||||
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", "**/*.d.ts"],
|
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", "**/*.d.ts"]
|
||||||
"exclude": ["node_modules"]
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"dependencies": {},
|
"dependencies": {},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "^20.8.5",
|
"@types/node": "^20.14.2",
|
||||||
"@fastgpt/global": "workspace:*",
|
"@fastgpt/global": "workspace:*",
|
||||||
"@fastgpt/service": "workspace:*"
|
"@fastgpt/service": "workspace:*"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,8 @@ import { detectFileEncoding } from '@fastgpt/global/common/file/tools';
|
|||||||
import { CommonErrEnum } from '@fastgpt/global/common/error/code/common';
|
import { CommonErrEnum } from '@fastgpt/global/common/error/code/common';
|
||||||
import { MongoRawTextBuffer } from '../../buffer/rawText/schema';
|
import { MongoRawTextBuffer } from '../../buffer/rawText/schema';
|
||||||
import { readRawContentByFileBuffer } from '../read/utils';
|
import { readRawContentByFileBuffer } from '../read/utils';
|
||||||
import { PassThrough } from 'stream';
|
import { gridFsStream2Buffer, stream2Encoding } from './utils';
|
||||||
|
import { addLog } from '../../system/log';
|
||||||
|
|
||||||
export function getGFSCollection(bucket: `${BucketNameEnum}`) {
|
export function getGFSCollection(bucket: `${BucketNameEnum}`) {
|
||||||
MongoFileSchema;
|
MongoFileSchema;
|
||||||
@@ -44,8 +45,11 @@ export async function uploadFile({
|
|||||||
const stats = await fsp.stat(path);
|
const stats = await fsp.stat(path);
|
||||||
if (!stats.isFile()) return Promise.reject(`${path} is not a file`);
|
if (!stats.isFile()) return Promise.reject(`${path} is not a file`);
|
||||||
|
|
||||||
|
const { stream: readStream, encoding } = await stream2Encoding(fs.createReadStream(path));
|
||||||
|
|
||||||
metadata.teamId = teamId;
|
metadata.teamId = teamId;
|
||||||
metadata.tmbId = tmbId;
|
metadata.tmbId = tmbId;
|
||||||
|
metadata.encoding = encoding;
|
||||||
|
|
||||||
// create a gridfs bucket
|
// create a gridfs bucket
|
||||||
const bucket = getGridBucket(bucketName);
|
const bucket = getGridBucket(bucketName);
|
||||||
@@ -57,7 +61,7 @@ export async function uploadFile({
|
|||||||
|
|
||||||
// save to gridfs
|
// save to gridfs
|
||||||
await new Promise((resolve, reject) => {
|
await new Promise((resolve, reject) => {
|
||||||
fs.createReadStream(path)
|
readStream
|
||||||
.pipe(stream as any)
|
.pipe(stream as any)
|
||||||
.on('finish', resolve)
|
.on('finish', resolve)
|
||||||
.on('error', reject);
|
.on('error', reject);
|
||||||
@@ -113,38 +117,8 @@ export async function getDownloadStream({
|
|||||||
fileId: string;
|
fileId: string;
|
||||||
}) {
|
}) {
|
||||||
const bucket = getGridBucket(bucketName);
|
const bucket = getGridBucket(bucketName);
|
||||||
const stream = bucket.openDownloadStream(new Types.ObjectId(fileId));
|
|
||||||
const copyStream = stream.pipe(new PassThrough());
|
|
||||||
|
|
||||||
/* get encoding */
|
return bucket.openDownloadStream(new Types.ObjectId(fileId));
|
||||||
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 encoding = detectFileEncoding(buffer);
|
|
||||||
|
|
||||||
return {
|
|
||||||
fileStream: copyStream,
|
|
||||||
encoding
|
|
||||||
// encoding: 'utf-8'
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const readFileContentFromMongo = async ({
|
export const readFileContentFromMongo = async ({
|
||||||
@@ -170,31 +144,22 @@ export const readFileContentFromMongo = async ({
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const [file, { encoding, fileStream }] = await Promise.all([
|
const [file, fileStream] = await Promise.all([
|
||||||
getFileById({ bucketName, fileId }),
|
getFileById({ bucketName, fileId }),
|
||||||
getDownloadStream({ bucketName, fileId })
|
getDownloadStream({ bucketName, fileId })
|
||||||
]);
|
]);
|
||||||
|
// console.log('get file stream', Date.now() - start);
|
||||||
if (!file) {
|
if (!file) {
|
||||||
return Promise.reject(CommonErrEnum.fileNotFound);
|
return Promise.reject(CommonErrEnum.fileNotFound);
|
||||||
}
|
}
|
||||||
|
|
||||||
const extension = file?.filename?.split('.')?.pop()?.toLowerCase() || '';
|
const extension = file?.filename?.split('.')?.pop()?.toLowerCase() || '';
|
||||||
|
|
||||||
const fileBuffers = await (() => {
|
const start = Date.now();
|
||||||
return new Promise<Buffer>((resolve, reject) => {
|
const fileBuffers = await gridFsStream2Buffer(fileStream);
|
||||||
let buffer = Buffer.from([]);
|
addLog.debug('get file buffer', { time: Date.now() - start });
|
||||||
fileStream.on('data', (chunk) => {
|
|
||||||
buffer = Buffer.concat([buffer, chunk]);
|
const encoding = file?.metadata?.encoding || detectFileEncoding(fileBuffers);
|
||||||
});
|
|
||||||
fileStream.on('end', () => {
|
|
||||||
resolve(buffer);
|
|
||||||
});
|
|
||||||
fileStream.on('error', (err) => {
|
|
||||||
reject(err);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
})();
|
|
||||||
|
|
||||||
const { rawText } = await readRawContentByFileBuffer({
|
const { rawText } = await readRawContentByFileBuffer({
|
||||||
extension,
|
extension,
|
||||||
@@ -207,7 +172,8 @@ export const readFileContentFromMongo = async ({
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (rawText.trim()) {
|
// < 14M
|
||||||
|
if (fileBuffers.length < 14 * 1024 * 1024 && rawText.trim()) {
|
||||||
MongoRawTextBuffer.create({
|
MongoRawTextBuffer.create({
|
||||||
sourceId: fileId,
|
sourceId: fileId,
|
||||||
rawText,
|
rawText,
|
||||||
|
|||||||
53
packages/service/common/file/gridfs/utils.ts
Normal file
53
packages/service/common/file/gridfs/utils.ts
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
import { detectFileEncoding } from '@fastgpt/global/common/file/tools';
|
||||||
|
import { PassThrough } from 'stream';
|
||||||
|
|
||||||
|
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);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const stream2Encoding = async (stream: NodeJS.ReadableStream) => {
|
||||||
|
const start = Date.now();
|
||||||
|
const copyStream = stream.pipe(new PassThrough());
|
||||||
|
|
||||||
|
/* get encoding */
|
||||||
|
const buffer = await (() => {
|
||||||
|
return new Promise<Buffer>((resolve, reject) => {
|
||||||
|
let tmpBuffer: Buffer = Buffer.from([]);
|
||||||
|
|
||||||
|
stream.on('data', (chunk) => {
|
||||||
|
if (tmpBuffer.length < 200) {
|
||||||
|
tmpBuffer = Buffer.concat([tmpBuffer, chunk]);
|
||||||
|
|
||||||
|
if (tmpBuffer.length >= 200) {
|
||||||
|
resolve(tmpBuffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
stream.on('end', () => {
|
||||||
|
resolve(tmpBuffer);
|
||||||
|
});
|
||||||
|
stream.on('error', (err) => {
|
||||||
|
reject(err);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
})();
|
||||||
|
|
||||||
|
const enc = detectFileEncoding(buffer);
|
||||||
|
console.log('Get encoding time', Date.now() - start, enc);
|
||||||
|
return {
|
||||||
|
encoding: enc,
|
||||||
|
stream: copyStream
|
||||||
|
};
|
||||||
|
};
|
||||||
@@ -13,7 +13,7 @@ export const NextEntry = ({ beforeCallback = [] }: { beforeCallback?: Promise<an
|
|||||||
return (...args: NextApiHandler[]): NextApiHandler => {
|
return (...args: NextApiHandler[]): NextApiHandler => {
|
||||||
return async function api(req: ApiRequestProps, res: NextApiResponse) {
|
return async function api(req: ApiRequestProps, res: NextApiResponse) {
|
||||||
const start = Date.now();
|
const start = Date.now();
|
||||||
addLog.info(`Request start ${req.url}`);
|
addLog.debug(`Request start ${req.url}`);
|
||||||
try {
|
try {
|
||||||
await Promise.all([withNextCors(req, res), ...beforeCallback]);
|
await Promise.all([withNextCors(req, res), ...beforeCallback]);
|
||||||
|
|
||||||
@@ -24,7 +24,7 @@ export const NextEntry = ({ beforeCallback = [] }: { beforeCallback?: Promise<an
|
|||||||
|
|
||||||
const contentType = res.getHeader('Content-Type');
|
const contentType = res.getHeader('Content-Type');
|
||||||
|
|
||||||
addLog.info(`Request finish ${req.url}, time: ${Date.now() - start}ms`);
|
addLog.debug(`Request finish ${req.url}, time: ${Date.now() - start}ms`);
|
||||||
|
|
||||||
if ((!contentType || contentType === 'application/json') && !res.writableFinished) {
|
if ((!contentType || contentType === 'application/json') && !res.writableFinished) {
|
||||||
return jsonRes(res, {
|
return jsonRes(res, {
|
||||||
|
|||||||
@@ -1,17 +1,24 @@
|
|||||||
|
import { addLog } from '../system/log';
|
||||||
import { connectionMongo, ClientSession } from './index';
|
import { connectionMongo, ClientSession } from './index';
|
||||||
|
|
||||||
export const mongoSessionRun = async <T = unknown>(fn: (session: ClientSession) => Promise<T>) => {
|
export const mongoSessionRun = async <T = unknown>(fn: (session: ClientSession) => Promise<T>) => {
|
||||||
const session = await connectionMongo.startSession();
|
const session = await connectionMongo.startSession();
|
||||||
|
let committed = false;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
session.startTransaction();
|
session.startTransaction();
|
||||||
const result = await fn(session);
|
const result = await fn(session);
|
||||||
|
|
||||||
await session.commitTransaction();
|
await session.commitTransaction();
|
||||||
|
committed = true;
|
||||||
|
|
||||||
return result as T;
|
return result as T;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
await session.abortTransaction();
|
if (!committed) {
|
||||||
|
await session.abortTransaction();
|
||||||
|
} else {
|
||||||
|
addLog.warn('Un catch mongo session error', { error });
|
||||||
|
}
|
||||||
return Promise.reject(error);
|
return Promise.reject(error);
|
||||||
} finally {
|
} finally {
|
||||||
await session.endSession();
|
await session.endSession();
|
||||||
|
|||||||
@@ -70,7 +70,7 @@ export const countGptMessagesTokens = (
|
|||||||
|
|
||||||
callbackMap[id] = (data) => {
|
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());
|
// console.log(process.memoryUsage());
|
||||||
|
|
||||||
resolve(data);
|
resolve(data);
|
||||||
@@ -85,7 +85,14 @@ export const countGptMessagesTokens = (
|
|||||||
functionCall
|
functionCall
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
resolve(0);
|
addLog.error('Count token error', error);
|
||||||
|
const total = messages.reduce((sum, item) => {
|
||||||
|
if (item.content) {
|
||||||
|
return sum + item.content.length;
|
||||||
|
}
|
||||||
|
return sum;
|
||||||
|
}, 0);
|
||||||
|
resolve(total);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
import chalk from 'chalk';
|
import chalk from 'chalk';
|
||||||
|
import { isProduction } from './constants';
|
||||||
|
|
||||||
enum LogLevelEnum {
|
enum LogLevelEnum {
|
||||||
debug = 'debug',
|
debug = 0,
|
||||||
info = 'info',
|
info = 1,
|
||||||
warn = 'warn',
|
warn = 2,
|
||||||
error = 'error'
|
error = 3
|
||||||
}
|
}
|
||||||
const logMap = {
|
const logMap = {
|
||||||
[LogLevelEnum.debug]: {
|
[LogLevelEnum.debug]: {
|
||||||
@@ -21,20 +22,35 @@ const logMap = {
|
|||||||
levelLog: chalk.red('[Error]')
|
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 */
|
/* add logger */
|
||||||
export const addLog = {
|
export const addLog = {
|
||||||
log(level: LogLevelEnum, msg: string, obj: Record<string, any> = {}) {
|
log(level: LogLevelEnum, msg: string, obj: Record<string, any> = {}) {
|
||||||
|
if (level < logLevel) return;
|
||||||
|
|
||||||
const stringifyObj = JSON.stringify(obj);
|
const stringifyObj = JSON.stringify(obj);
|
||||||
const isEmpty = Object.keys(obj).length === 0;
|
const isEmpty = Object.keys(obj).length === 0;
|
||||||
|
|
||||||
console.log(
|
console.log(
|
||||||
`${logMap[level].levelLog} ${dayjs().format('YYYY-MM-DD HH:mm:ss')} ${msg} ${
|
`${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;
|
const lokiUrl = process.env.LOKI_LOG_URL as string;
|
||||||
if (!lokiUrl) return;
|
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 { FlowNodeTypeEnum } from '@fastgpt/global/core/workflow/node/constant';
|
||||||
import { getLLMModel } from '../ai/model';
|
import { getLLMModel } from '../ai/model';
|
||||||
import { MongoAppVersion } from './version/schema';
|
import { MongoAppVersion } from './version/schema';
|
||||||
|
import { MongoApp } from './schema';
|
||||||
|
|
||||||
export const beforeUpdateAppFormat = <T extends AppSchema['modules'] | undefined>({
|
export const beforeUpdateAppFormat = <T extends AppSchema['modules'] | undefined>({
|
||||||
nodes
|
nodes
|
||||||
@@ -65,3 +66,40 @@ export const getAppLatestVersion = async (appId: string, app?: AppSchema) => {
|
|||||||
chatConfig: app?.chatConfig || {}
|
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,12 +1,13 @@
|
|||||||
import { MongoPlugin } from './schema';
|
|
||||||
import { FlowNodeTemplateType } from '@fastgpt/global/core/workflow/type/index.d';
|
import { FlowNodeTemplateType } from '@fastgpt/global/core/workflow/type/index.d';
|
||||||
import { FlowNodeTypeEnum } from '@fastgpt/global/core/workflow/node/constant';
|
import { FlowNodeTypeEnum, defaultNodeVersion } from '@fastgpt/global/core/workflow/node/constant';
|
||||||
import { pluginData2FlowNodeIO } from '@fastgpt/global/core/workflow/utils';
|
import { pluginData2FlowNodeIO } from '@fastgpt/global/core/workflow/utils';
|
||||||
import { PluginSourceEnum } from '@fastgpt/global/core/plugin/constants';
|
import { PluginSourceEnum } from '@fastgpt/global/core/plugin/constants';
|
||||||
import type { PluginRuntimeType, PluginTemplateType } from '@fastgpt/global/core/plugin/type.d';
|
import type { PluginRuntimeType, PluginTemplateType } from '@fastgpt/global/core/plugin/type.d';
|
||||||
import { FlowNodeTemplateTypeEnum } from '@fastgpt/global/core/workflow/constants';
|
import { FlowNodeTemplateTypeEnum } from '@fastgpt/global/core/workflow/constants';
|
||||||
import { getHandleConfig } from '../../../global/core/workflow/template/utils';
|
import { getHandleConfig } from '@fastgpt/global/core/workflow/template/utils';
|
||||||
import { getNanoid } from '@fastgpt/global/common/string/tools';
|
import { getNanoid } from '@fastgpt/global/common/string/tools';
|
||||||
|
import { cloneDeep } from 'lodash';
|
||||||
|
import { MongoApp } from '../schema';
|
||||||
|
|
||||||
/*
|
/*
|
||||||
plugin id rule:
|
plugin id rule:
|
||||||
@@ -18,6 +19,7 @@ import { getNanoid } from '@fastgpt/global/common/string/tools';
|
|||||||
export async function splitCombinePluginId(id: string) {
|
export async function splitCombinePluginId(id: string) {
|
||||||
const splitRes = id.split('-');
|
const splitRes = id.split('-');
|
||||||
if (splitRes.length === 1) {
|
if (splitRes.length === 1) {
|
||||||
|
// app id
|
||||||
return {
|
return {
|
||||||
source: PluginSourceEnum.personal,
|
source: PluginSourceEnum.personal,
|
||||||
pluginId: id
|
pluginId: id
|
||||||
@@ -32,15 +34,17 @@ export async function splitCombinePluginId(id: string) {
|
|||||||
|
|
||||||
const getPluginTemplateById = async (id: string): Promise<PluginTemplateType> => {
|
const getPluginTemplateById = async (id: string): Promise<PluginTemplateType> => {
|
||||||
const { source, pluginId } = await splitCombinePluginId(id);
|
const { source, pluginId } = await splitCombinePluginId(id);
|
||||||
|
|
||||||
if (source === PluginSourceEnum.community) {
|
if (source === PluginSourceEnum.community) {
|
||||||
const item = global.communityPlugins?.find((plugin) => plugin.id === pluginId);
|
const item = global.communityPlugins?.find((plugin) => plugin.id === pluginId);
|
||||||
if (!item) return Promise.reject('plugin not found');
|
if (!item) return Promise.reject('plugin not found');
|
||||||
|
|
||||||
return item;
|
return cloneDeep(item);
|
||||||
}
|
}
|
||||||
if (source === PluginSourceEnum.personal) {
|
if (source === PluginSourceEnum.personal) {
|
||||||
const item = await MongoPlugin.findById(id).lean();
|
const item = await MongoApp.findById(id).lean();
|
||||||
if (!item) return Promise.reject('plugin not found');
|
if (!item) return Promise.reject('plugin not found');
|
||||||
|
|
||||||
return {
|
return {
|
||||||
id: String(item._id),
|
id: String(item._id),
|
||||||
teamId: String(item.teamId),
|
teamId: String(item.teamId),
|
||||||
@@ -53,7 +57,7 @@ const getPluginTemplateById = async (id: string): Promise<PluginTemplateType> =>
|
|||||||
edges: item.edges,
|
edges: item.edges,
|
||||||
templateType: FlowNodeTemplateTypeEnum.personalPlugin,
|
templateType: FlowNodeTemplateTypeEnum.personalPlugin,
|
||||||
isTool: true,
|
isTool: true,
|
||||||
nodeVersion: item?.nodeVersion || ''
|
version: item?.pluginData?.nodeVersion || defaultNodeVersion
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
return Promise.reject('plugin not found');
|
return Promise.reject('plugin not found');
|
||||||
@@ -73,8 +77,7 @@ export async function getPluginPreviewNode({ id }: { id: string }): Promise<Flow
|
|||||||
intro: plugin.intro,
|
intro: plugin.intro,
|
||||||
showStatus: plugin.showStatus,
|
showStatus: plugin.showStatus,
|
||||||
isTool: plugin.isTool,
|
isTool: plugin.isTool,
|
||||||
nodeVersion: plugin.nodeVersion,
|
version: plugin.version,
|
||||||
version: '481',
|
|
||||||
sourceHandle: getHandleConfig(true, true, true, true),
|
sourceHandle: getHandleConfig(true, true, true, true),
|
||||||
targetHandle: getHandleConfig(true, true, true, true),
|
targetHandle: getHandleConfig(true, true, true, true),
|
||||||
...pluginData2FlowNodeIO(plugin.nodes)
|
...pluginData2FlowNodeIO(plugin.nodes)
|
||||||
@@ -1,12 +1,12 @@
|
|||||||
import { AppTypeMap } from '@fastgpt/global/core/app/constants';
|
import { AppTypeEnum } from '@fastgpt/global/core/app/constants';
|
||||||
import { connectionMongo, type Model } from '../../common/mongo';
|
import { connectionMongo, type Model } from '../../common/mongo';
|
||||||
const { Schema, model, models } = connectionMongo;
|
const { Schema, model, models } = connectionMongo;
|
||||||
import type { AppSchema as AppType } from '@fastgpt/global/core/app/type.d';
|
import type { AppSchema as AppType } from '@fastgpt/global/core/app/type.d';
|
||||||
import { PermissionTypeEnum, PermissionTypeMap } from '@fastgpt/global/support/permission/constant';
|
|
||||||
import {
|
import {
|
||||||
TeamCollectionName,
|
TeamCollectionName,
|
||||||
TeamMemberCollectionName
|
TeamMemberCollectionName
|
||||||
} from '@fastgpt/global/support/user/team/constant';
|
} from '@fastgpt/global/support/user/team/constant';
|
||||||
|
import { AppDefaultPermissionVal } from '@fastgpt/global/support/permission/app/constant';
|
||||||
|
|
||||||
export const AppCollectionName = 'apps';
|
export const AppCollectionName = 'apps';
|
||||||
|
|
||||||
@@ -21,6 +21,11 @@ export const chatConfigType = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const AppSchema = new Schema({
|
const AppSchema = new Schema({
|
||||||
|
parentId: {
|
||||||
|
type: Schema.Types.ObjectId,
|
||||||
|
ref: AppCollectionName,
|
||||||
|
default: null
|
||||||
|
},
|
||||||
teamId: {
|
teamId: {
|
||||||
type: Schema.Types.ObjectId,
|
type: Schema.Types.ObjectId,
|
||||||
ref: TeamCollectionName,
|
ref: TeamCollectionName,
|
||||||
@@ -37,8 +42,8 @@ const AppSchema = new Schema({
|
|||||||
},
|
},
|
||||||
type: {
|
type: {
|
||||||
type: String,
|
type: String,
|
||||||
default: 'advanced',
|
default: AppTypeEnum.workflow,
|
||||||
enum: Object.keys(AppTypeMap)
|
enum: Object.values(AppTypeEnum)
|
||||||
},
|
},
|
||||||
version: {
|
version: {
|
||||||
type: String,
|
type: String,
|
||||||
@@ -58,16 +63,11 @@ const AppSchema = new Schema({
|
|||||||
},
|
},
|
||||||
|
|
||||||
// role and auth
|
// role and auth
|
||||||
permission: {
|
|
||||||
type: String,
|
|
||||||
enum: Object.keys(PermissionTypeMap),
|
|
||||||
default: PermissionTypeEnum.private
|
|
||||||
},
|
|
||||||
teamTags: {
|
teamTags: {
|
||||||
type: [String]
|
type: [String]
|
||||||
},
|
},
|
||||||
|
|
||||||
// tmp store
|
// save app(Not publish)
|
||||||
modules: {
|
modules: {
|
||||||
type: Array,
|
type: Array,
|
||||||
default: []
|
default: []
|
||||||
@@ -77,8 +77,16 @@ const AppSchema = new Schema({
|
|||||||
default: []
|
default: []
|
||||||
},
|
},
|
||||||
chatConfig: {
|
chatConfig: {
|
||||||
type: chatConfigType,
|
type: chatConfigType
|
||||||
default: {}
|
},
|
||||||
|
// plugin config
|
||||||
|
pluginData: {
|
||||||
|
type: {
|
||||||
|
nodeVersion: String,
|
||||||
|
pluginUniId: String,
|
||||||
|
apiSchemaStr: String, // http plugin
|
||||||
|
customHeaders: String // http plugin
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
scheduledTriggerConfig: {
|
scheduledTriggerConfig: {
|
||||||
@@ -98,12 +106,18 @@ const AppSchema = new Schema({
|
|||||||
|
|
||||||
inited: {
|
inited: {
|
||||||
type: Boolean
|
type: Boolean
|
||||||
|
},
|
||||||
|
|
||||||
|
// the default permission of a app
|
||||||
|
defaultPermission: {
|
||||||
|
type: Number,
|
||||||
|
default: AppDefaultPermissionVal
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
AppSchema.index({ updateTime: -1 });
|
AppSchema.index({ updateTime: -1 });
|
||||||
AppSchema.index({ teamId: 1 });
|
AppSchema.index({ teamId: 1, type: 1 });
|
||||||
AppSchema.index({ scheduledTriggerConfig: 1, intervalNextTime: -1 });
|
AppSchema.index({ scheduledTriggerConfig: 1, intervalNextTime: -1 });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error);
|
console.log(error);
|
||||||
|
|||||||
@@ -24,8 +24,7 @@ const AppVersionSchema = new Schema({
|
|||||||
default: []
|
default: []
|
||||||
},
|
},
|
||||||
chatConfig: {
|
chatConfig: {
|
||||||
type: chatConfigType,
|
type: chatConfigType
|
||||||
default: {}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -142,17 +142,17 @@ export const delCollectionRelatedSource = async ({
|
|||||||
.map((item) => item?.metadata?.relatedImgId || '')
|
.map((item) => item?.metadata?.relatedImgId || '')
|
||||||
.filter(Boolean);
|
.filter(Boolean);
|
||||||
|
|
||||||
|
// delete files
|
||||||
|
await delFileByFileIdList({
|
||||||
|
bucketName: BucketNameEnum.dataset,
|
||||||
|
fileIdList
|
||||||
|
});
|
||||||
// delete images
|
// delete images
|
||||||
await delImgByRelatedId({
|
await delImgByRelatedId({
|
||||||
teamId,
|
teamId,
|
||||||
relateIds: relatedImageIds,
|
relateIds: relatedImageIds,
|
||||||
session
|
session
|
||||||
});
|
});
|
||||||
// delete files
|
|
||||||
await delFileByFileIdList({
|
|
||||||
bucketName: BucketNameEnum.dataset,
|
|
||||||
fileIdList
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
/**
|
/**
|
||||||
* delete collection and it related data
|
* delete collection and it related data
|
||||||
@@ -182,14 +182,16 @@ export async function delCollectionAndRelatedSources({
|
|||||||
);
|
);
|
||||||
const collectionIds = collections.map((item) => String(item._id));
|
const collectionIds = collections.map((item) => String(item._id));
|
||||||
|
|
||||||
await delCollectionRelatedSource({ collections, session });
|
|
||||||
|
|
||||||
// delete training data
|
// delete training data
|
||||||
await MongoDatasetTraining.deleteMany({
|
await MongoDatasetTraining.deleteMany({
|
||||||
teamId,
|
teamId,
|
||||||
datasetIds: { $in: datasetIds },
|
datasetIds: { $in: datasetIds },
|
||||||
collectionId: { $in: collectionIds }
|
collectionId: { $in: collectionIds }
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/* file and imgs */
|
||||||
|
await delCollectionRelatedSource({ collections, session });
|
||||||
|
|
||||||
// delete dataset.datas
|
// delete dataset.datas
|
||||||
await MongoDatasetData.deleteMany(
|
await MongoDatasetData.deleteMany(
|
||||||
{ teamId, datasetIds: { $in: datasetIds }, collectionId: { $in: collectionIds } },
|
{ teamId, datasetIds: { $in: datasetIds }, collectionId: { $in: collectionIds } },
|
||||||
@@ -199,6 +201,7 @@ export async function delCollectionAndRelatedSources({
|
|||||||
// delete collections
|
// delete collections
|
||||||
await MongoDatasetCollection.deleteMany(
|
await MongoDatasetCollection.deleteMany(
|
||||||
{
|
{
|
||||||
|
teamId,
|
||||||
_id: { $in: collectionIds }
|
_id: { $in: collectionIds }
|
||||||
},
|
},
|
||||||
{ session }
|
{ session }
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ export async function findCollectionAndChild({
|
|||||||
return collections;
|
return collections;
|
||||||
}
|
}
|
||||||
const [collection, childCollections] = await Promise.all([
|
const [collection, childCollections] = await Promise.all([
|
||||||
MongoDatasetCollection.findById(collectionId, fields),
|
MongoDatasetCollection.findById(collectionId, fields).lean(),
|
||||||
find(collectionId)
|
find(collectionId)
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
|||||||
@@ -82,17 +82,18 @@ export async function delDatasetRelevantData({
|
|||||||
teamId,
|
teamId,
|
||||||
datasetId: { $in: datasetIds }
|
datasetId: { $in: datasetIds }
|
||||||
},
|
},
|
||||||
'_id teamId fileId metadata'
|
'_id teamId datasetId fileId metadata'
|
||||||
).lean();
|
).lean();
|
||||||
|
|
||||||
// image and file
|
|
||||||
await delCollectionRelatedSource({ collections, session });
|
|
||||||
|
|
||||||
// delete training data
|
// delete training data
|
||||||
await MongoDatasetTraining.deleteMany({
|
await MongoDatasetTraining.deleteMany({
|
||||||
teamId,
|
teamId,
|
||||||
datasetId: { $in: datasetIds }
|
datasetId: { $in: datasetIds }
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// image and file
|
||||||
|
await delCollectionRelatedSource({ collections, session });
|
||||||
|
|
||||||
// delete dataset.datas
|
// delete dataset.datas
|
||||||
await MongoDatasetData.deleteMany({ teamId, datasetId: { $in: datasetIds } }, { session });
|
await MongoDatasetData.deleteMany({ teamId, datasetId: { $in: datasetIds } }, { session });
|
||||||
|
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import {
|
|||||||
TeamMemberCollectionName
|
TeamMemberCollectionName
|
||||||
} from '@fastgpt/global/support/user/team/constant';
|
} from '@fastgpt/global/support/user/team/constant';
|
||||||
import { PermissionTypeEnum, PermissionTypeMap } from '@fastgpt/global/support/permission/constant';
|
import { PermissionTypeEnum, PermissionTypeMap } from '@fastgpt/global/support/permission/constant';
|
||||||
|
import { DatasetDefaultPermissionVal } from '@fastgpt/global/support/permission/dataset/constant';
|
||||||
|
|
||||||
export const DatasetCollectionName = 'datasets';
|
export const DatasetCollectionName = 'datasets';
|
||||||
|
|
||||||
@@ -90,7 +91,11 @@ const DatasetSchema = new Schema({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
externalReadUrl: String
|
externalReadUrl: String,
|
||||||
|
defaultPermission: {
|
||||||
|
type: Number,
|
||||||
|
default: DatasetDefaultPermissionVal
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -68,10 +68,13 @@ const PluginSchema = new Schema({
|
|||||||
nodeVersion: {
|
nodeVersion: {
|
||||||
type: String,
|
type: String,
|
||||||
default: ''
|
default: ''
|
||||||
}
|
},
|
||||||
|
|
||||||
|
inited: Boolean
|
||||||
});
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
PluginSchema.index({ type: 1, init: 1 });
|
||||||
PluginSchema.index({ teamId: 1, parentId: 1 });
|
PluginSchema.index({ teamId: 1, parentId: 1 });
|
||||||
PluginSchema.index({ teamId: 1, name: 1, intro: 1 });
|
PluginSchema.index({ teamId: 1, name: 1, intro: 1 });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
@@ -1,31 +0,0 @@
|
|||||||
import { connectionMongo, type Model } from '../../../common/mongo';
|
|
||||||
const { Schema, model, models } = connectionMongo;
|
|
||||||
import type { PluginItemSchema } from '@fastgpt/global/core/plugin/type.d';
|
|
||||||
|
|
||||||
import { PluginCollectionName } from '../schema';
|
|
||||||
|
|
||||||
export const ModuleCollectionName = 'plugins';
|
|
||||||
|
|
||||||
const PluginStoreSchema = new Schema({
|
|
||||||
pluginId: {
|
|
||||||
type: Schema.Types.ObjectId,
|
|
||||||
ref: PluginCollectionName,
|
|
||||||
required: true
|
|
||||||
},
|
|
||||||
price: {
|
|
||||||
type: Number,
|
|
||||||
default: 0
|
|
||||||
},
|
|
||||||
updateTime: {
|
|
||||||
type: Date,
|
|
||||||
default: () => new Date()
|
|
||||||
},
|
|
||||||
modules: {
|
|
||||||
type: Array,
|
|
||||||
default: []
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
export const MongoPluginStore: Model<PluginItemSchema> =
|
|
||||||
models[ModuleCollectionName] || model(ModuleCollectionName, PluginStoreSchema);
|
|
||||||
MongoPluginStore.syncIndexes();
|
|
||||||
@@ -42,6 +42,7 @@ export const dispatchRunCode = async (props: RunCodeType): Promise<RunCodeRespon
|
|||||||
customOutputs: runResult.data.codeReturn,
|
customOutputs: runResult.data.codeReturn,
|
||||||
codeLog: runResult.data.log
|
codeLog: runResult.data.log
|
||||||
},
|
},
|
||||||
|
[DispatchNodeResponseKeyEnum.toolResponses]: runResult.data.codeReturn,
|
||||||
...runResult.data.codeReturn
|
...runResult.data.codeReturn
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -217,16 +217,16 @@ export async function dispatchWorkFlow(data: Props): Promise<DispatchFlowRespons
|
|||||||
|
|
||||||
if (res?.closed || props.maxRunTimes <= 0) return;
|
if (res?.closed || props.maxRunTimes <= 0) return;
|
||||||
props.maxRunTimes--;
|
props.maxRunTimes--;
|
||||||
console.log(props.maxRunTimes, user._id);
|
addLog.debug(`Run node`, { maxRunTimes: props.maxRunTimes, uid: user._id });
|
||||||
|
|
||||||
await surrenderProcess();
|
await surrenderProcess();
|
||||||
|
|
||||||
if (status === 'run') {
|
if (status === 'run') {
|
||||||
addLog.info(`[dispatchWorkFlow] nodeRunWithActive: ${node.name}`);
|
addLog.debug(`[dispatchWorkFlow] nodeRunWithActive: ${node.name}`);
|
||||||
return nodeRunWithActive(node);
|
return nodeRunWithActive(node);
|
||||||
}
|
}
|
||||||
if (status === 'skip') {
|
if (status === 'skip') {
|
||||||
addLog.info(`[dispatchWorkFlow] nodeRunWithSkip: ${node.name}`);
|
addLog.debug(`[dispatchWorkFlow] nodeRunWithSkip: ${node.name}`);
|
||||||
return nodeRunWithSkip(node);
|
return nodeRunWithSkip(node);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -270,8 +270,6 @@ export async function dispatchWorkFlow(data: Props): Promise<DispatchFlowRespons
|
|||||||
nodes: runtimeNodes,
|
nodes: runtimeNodes,
|
||||||
variables
|
variables
|
||||||
});
|
});
|
||||||
// console.log(JSON.stringify(input, null, 2), '=====================');
|
|
||||||
|
|
||||||
// format valueType
|
// format valueType
|
||||||
params[input.key] = valueTypeFormat(value, input.valueType);
|
params[input.key] = valueTypeFormat(value, input.valueType);
|
||||||
});
|
});
|
||||||
@@ -407,13 +405,13 @@ export function responseStatus({
|
|||||||
/* get system variable */
|
/* get system variable */
|
||||||
export function getSystemVariable({
|
export function getSystemVariable({
|
||||||
user,
|
user,
|
||||||
appId,
|
app,
|
||||||
chatId,
|
chatId,
|
||||||
responseChatItemId,
|
responseChatItemId,
|
||||||
histories = []
|
histories = []
|
||||||
}: Props) {
|
}: Props) {
|
||||||
return {
|
return {
|
||||||
appId,
|
appId: String(app._id),
|
||||||
chatId,
|
chatId,
|
||||||
responseChatItemId,
|
responseChatItemId,
|
||||||
histories,
|
histories,
|
||||||
|
|||||||
@@ -3,8 +3,7 @@ import { dispatchWorkFlow } from '../index';
|
|||||||
import { FlowNodeTypeEnum } from '@fastgpt/global/core/workflow/node/constant';
|
import { FlowNodeTypeEnum } from '@fastgpt/global/core/workflow/node/constant';
|
||||||
import { NodeInputKeyEnum } from '@fastgpt/global/core/workflow/constants';
|
import { NodeInputKeyEnum } from '@fastgpt/global/core/workflow/constants';
|
||||||
import { DispatchNodeResponseKeyEnum } from '@fastgpt/global/core/workflow/runtime/constants';
|
import { DispatchNodeResponseKeyEnum } from '@fastgpt/global/core/workflow/runtime/constants';
|
||||||
import { getPluginRuntimeById } from '../../../plugin/controller';
|
import { getPluginRuntimeById, splitCombinePluginId } from '../../../app/plugin/controller';
|
||||||
import { authPluginCanUse } from '../../../../support/permission/auth/plugin';
|
|
||||||
import {
|
import {
|
||||||
getDefaultEntryNodeIds,
|
getDefaultEntryNodeIds,
|
||||||
initWorkflowEdgeStatus,
|
initWorkflowEdgeStatus,
|
||||||
@@ -13,6 +12,9 @@ import {
|
|||||||
import { DispatchNodeResultType } from '@fastgpt/global/core/workflow/runtime/type';
|
import { DispatchNodeResultType } from '@fastgpt/global/core/workflow/runtime/type';
|
||||||
import { updateToolInputValue } from '../agent/runTool/utils';
|
import { updateToolInputValue } from '../agent/runTool/utils';
|
||||||
import { replaceVariable } from '@fastgpt/global/common/string/tools';
|
import { replaceVariable } from '@fastgpt/global/common/string/tools';
|
||||||
|
import { authAppByTmbId } from '../../../../support/permission/app/auth';
|
||||||
|
import { ReadPermissionVal } from '@fastgpt/global/support/permission/constant';
|
||||||
|
import { PluginSourceEnum } from '@fastgpt/global/core/plugin/constants';
|
||||||
|
|
||||||
type RunPluginProps = ModuleDispatchProps<{
|
type RunPluginProps = ModuleDispatchProps<{
|
||||||
[key: string]: any;
|
[key: string]: any;
|
||||||
@@ -22,9 +24,9 @@ type RunPluginResponse = DispatchNodeResultType<{}>;
|
|||||||
export const dispatchRunPlugin = async (props: RunPluginProps): Promise<RunPluginResponse> => {
|
export const dispatchRunPlugin = async (props: RunPluginProps): Promise<RunPluginResponse> => {
|
||||||
const {
|
const {
|
||||||
node: { pluginId },
|
node: { pluginId },
|
||||||
|
app: workflowApp,
|
||||||
mode,
|
mode,
|
||||||
teamId,
|
teamId,
|
||||||
tmbId,
|
|
||||||
params: data
|
params: data
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
@@ -32,7 +34,15 @@ export const dispatchRunPlugin = async (props: RunPluginProps): Promise<RunPlugi
|
|||||||
return Promise.reject('pluginId can not find');
|
return Promise.reject('pluginId can not find');
|
||||||
}
|
}
|
||||||
|
|
||||||
await authPluginCanUse({ id: pluginId, teamId, tmbId });
|
// auth plugin
|
||||||
|
const { source } = await splitCombinePluginId(pluginId);
|
||||||
|
if (source === PluginSourceEnum.personal) {
|
||||||
|
await authAppByTmbId({
|
||||||
|
appId: pluginId,
|
||||||
|
tmbId: workflowApp.tmbId,
|
||||||
|
per: ReadPermissionVal
|
||||||
|
});
|
||||||
|
}
|
||||||
const plugin = await getPluginRuntimeById(pluginId);
|
const plugin = await getPluginRuntimeById(pluginId);
|
||||||
|
|
||||||
// concat dynamic inputs
|
// concat dynamic inputs
|
||||||
|
|||||||
@@ -43,14 +43,13 @@ export const dispatchHttp468Request = async (props: HttpRequestProps): Promise<H
|
|||||||
let {
|
let {
|
||||||
res,
|
res,
|
||||||
detail,
|
detail,
|
||||||
appId,
|
app: { _id: appId },
|
||||||
chatId,
|
chatId,
|
||||||
stream,
|
stream,
|
||||||
responseChatItemId,
|
responseChatItemId,
|
||||||
variables,
|
variables,
|
||||||
node: { outputs },
|
node: { outputs },
|
||||||
histories,
|
histories,
|
||||||
isToolCall,
|
|
||||||
params: {
|
params: {
|
||||||
system_httpMethod: httpMethod = 'POST',
|
system_httpMethod: httpMethod = 'POST',
|
||||||
system_httpReqUrl: httpReqUrl,
|
system_httpReqUrl: httpReqUrl,
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ import type { ChatItemType } from '@fastgpt/global/core/chat/type.d';
|
|||||||
import type { ModuleDispatchProps } from '@fastgpt/global/core/workflow/type/index.d';
|
import type { ModuleDispatchProps } from '@fastgpt/global/core/workflow/type/index.d';
|
||||||
import { SelectAppItemType } from '@fastgpt/global/core/workflow/type/index.d';
|
import { SelectAppItemType } from '@fastgpt/global/core/workflow/type/index.d';
|
||||||
import { dispatchWorkFlow } from '../index';
|
import { dispatchWorkFlow } from '../index';
|
||||||
import { MongoApp } from '../../../../core/app/schema';
|
|
||||||
import { responseWrite } from '../../../../common/response';
|
import { responseWrite } from '../../../../common/response';
|
||||||
import { ChatRoleEnum } from '@fastgpt/global/core/chat/constants';
|
import { ChatRoleEnum } from '@fastgpt/global/core/chat/constants';
|
||||||
import { SseResponseEventEnum } from '@fastgpt/global/core/workflow/runtime/constants';
|
import { SseResponseEventEnum } from '@fastgpt/global/core/workflow/runtime/constants';
|
||||||
@@ -17,6 +16,8 @@ import { DispatchNodeResponseKeyEnum } from '@fastgpt/global/core/workflow/runti
|
|||||||
import { getHistories } from '../utils';
|
import { getHistories } from '../utils';
|
||||||
import { chatValue2RuntimePrompt, runtimePrompt2ChatsValue } from '@fastgpt/global/core/chat/adapt';
|
import { chatValue2RuntimePrompt, runtimePrompt2ChatsValue } from '@fastgpt/global/core/chat/adapt';
|
||||||
import { DispatchNodeResultType } from '@fastgpt/global/core/workflow/runtime/type';
|
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<{
|
type Props = ModuleDispatchProps<{
|
||||||
[NodeInputKeyEnum.userChatInput]: string;
|
[NodeInputKeyEnum.userChatInput]: string;
|
||||||
@@ -31,28 +32,25 @@ type Response = DispatchNodeResultType<{
|
|||||||
export const dispatchAppRequest = async (props: Props): Promise<Response> => {
|
export const dispatchAppRequest = async (props: Props): Promise<Response> => {
|
||||||
const {
|
const {
|
||||||
res,
|
res,
|
||||||
teamId,
|
app: workflowApp,
|
||||||
stream,
|
stream,
|
||||||
detail,
|
detail,
|
||||||
histories,
|
histories,
|
||||||
query,
|
query,
|
||||||
params: { userChatInput, history, app }
|
params: { userChatInput, history, app }
|
||||||
} = props;
|
} = props;
|
||||||
let start = Date.now();
|
|
||||||
|
|
||||||
if (!userChatInput) {
|
if (!userChatInput) {
|
||||||
return Promise.reject('Input is empty');
|
return Promise.reject('Input is empty');
|
||||||
}
|
}
|
||||||
|
|
||||||
const appData = await MongoApp.findOne({
|
// 检查该工作流的tmb是否有调用该app的权限(不是校验对话的人,是否有权限)
|
||||||
_id: app.id,
|
const { app: appData } = await authAppByTmbId({
|
||||||
teamId
|
appId: app.id,
|
||||||
|
tmbId: workflowApp.tmbId,
|
||||||
|
per: ReadPermissionVal
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!appData) {
|
|
||||||
return Promise.reject('App not found');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (res && stream) {
|
if (res && stream) {
|
||||||
responseWrite({
|
responseWrite({
|
||||||
res,
|
res,
|
||||||
@@ -64,18 +62,19 @@ export const dispatchAppRequest = async (props: Props): Promise<Response> => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const chatHistories = getHistories(history, histories);
|
const chatHistories = getHistories(history, histories);
|
||||||
|
const { files } = chatValue2RuntimePrompt(query);
|
||||||
|
|
||||||
const { flowResponses, flowUsages, assistantResponses } = await dispatchWorkFlow({
|
const { flowResponses, flowUsages, assistantResponses } = await dispatchWorkFlow({
|
||||||
...props,
|
...props,
|
||||||
appId: app.id,
|
app: appData,
|
||||||
runtimeNodes: storeNodes2RuntimeNodes(appData.modules, getDefaultEntryNodeIds(appData.modules)),
|
runtimeNodes: storeNodes2RuntimeNodes(appData.modules, getDefaultEntryNodeIds(appData.modules)),
|
||||||
runtimeEdges: initWorkflowEdgeStatus(appData.edges),
|
runtimeEdges: initWorkflowEdgeStatus(appData.edges),
|
||||||
histories: chatHistories,
|
histories: chatHistories,
|
||||||
query,
|
query: runtimePrompt2ChatsValue({
|
||||||
variables: {
|
files,
|
||||||
...props.variables,
|
text: userChatInput
|
||||||
userChatInput
|
}),
|
||||||
}
|
variables: props.variables
|
||||||
});
|
});
|
||||||
|
|
||||||
const completeMessages = chatHistories.concat([
|
const completeMessages = chatHistories.concat([
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ const UNDEFINED_SIGN = 'UNDEFINED_SIGN';
|
|||||||
|
|
||||||
export const dispatchLafRequest = async (props: LafRequestProps): Promise<LafResponse> => {
|
export const dispatchLafRequest = async (props: LafRequestProps): Promise<LafResponse> => {
|
||||||
let {
|
let {
|
||||||
appId,
|
app: { _id: appId },
|
||||||
chatId,
|
chatId,
|
||||||
responseChatItemId,
|
responseChatItemId,
|
||||||
variables,
|
variables,
|
||||||
|
|||||||
@@ -8,8 +8,7 @@ import {
|
|||||||
NodeInputKeyEnum
|
NodeInputKeyEnum
|
||||||
} from '@fastgpt/global/core/workflow/constants';
|
} from '@fastgpt/global/core/workflow/constants';
|
||||||
import { DispatchNodeResponseKeyEnum } from '@fastgpt/global/core/workflow/runtime/constants';
|
import { DispatchNodeResponseKeyEnum } from '@fastgpt/global/core/workflow/runtime/constants';
|
||||||
import { splitCombinePluginId } from '../../../plugin/controller';
|
import { splitCombinePluginId } from '../../../app/plugin/controller';
|
||||||
import { authPluginCanUse } from '../../../../support/permission/auth/plugin';
|
|
||||||
import { setEntryEntries, DYNAMIC_INPUT_KEY } from '../utils';
|
import { setEntryEntries, DYNAMIC_INPUT_KEY } from '../utils';
|
||||||
import { DispatchNodeResultType } from '@fastgpt/global/core/workflow/runtime/type';
|
import { DispatchNodeResultType } from '@fastgpt/global/core/workflow/runtime/type';
|
||||||
import { PluginRuntimeType, PluginTemplateType } from '@fastgpt/global/core/plugin/type';
|
import { PluginRuntimeType, PluginTemplateType } from '@fastgpt/global/core/plugin/type';
|
||||||
@@ -73,8 +72,10 @@ export const dispatchRunPlugin = async (props: RunPluginProps): Promise<RunPlugi
|
|||||||
return Promise.reject('pluginId can not find');
|
return Promise.reject('pluginId can not find');
|
||||||
}
|
}
|
||||||
|
|
||||||
await authPluginCanUse({ id: pluginId, teamId, tmbId });
|
|
||||||
const plugin = await getPluginRuntimeById(pluginId);
|
const plugin = await getPluginRuntimeById(pluginId);
|
||||||
|
if (plugin.teamId && plugin.teamId !== teamId) {
|
||||||
|
return Promise.reject('plugin not found');
|
||||||
|
}
|
||||||
|
|
||||||
// concat dynamic inputs
|
// concat dynamic inputs
|
||||||
const inputModule = plugin.modules.find((item) => item.flowType === FlowNodeTypeEnum.pluginInput);
|
const inputModule = plugin.modules.find((item) => item.flowType === FlowNodeTypeEnum.pluginInput);
|
||||||
|
|||||||
@@ -20,6 +20,7 @@
|
|||||||
"joplin-turndown-plugin-gfm": "^1.0.12",
|
"joplin-turndown-plugin-gfm": "^1.0.12",
|
||||||
"json5": "^2.2.3",
|
"json5": "^2.2.3",
|
||||||
"jsonwebtoken": "^9.0.2",
|
"jsonwebtoken": "^9.0.2",
|
||||||
|
"lodash": "^4.17.21",
|
||||||
"mammoth": "^1.6.0",
|
"mammoth": "^1.6.0",
|
||||||
"mongoose": "^7.0.2",
|
"mongoose": "^7.0.2",
|
||||||
"multer": "1.4.5-lts.1",
|
"multer": "1.4.5-lts.1",
|
||||||
@@ -38,6 +39,7 @@
|
|||||||
"@types/cookie": "^0.5.2",
|
"@types/cookie": "^0.5.2",
|
||||||
"@types/decompress": "^4.2.7",
|
"@types/decompress": "^4.2.7",
|
||||||
"@types/jsonwebtoken": "^9.0.3",
|
"@types/jsonwebtoken": "^9.0.3",
|
||||||
|
"@types/lodash": "^4.14.191",
|
||||||
"@types/multer": "^1.4.10",
|
"@types/multer": "^1.4.10",
|
||||||
"@types/node-cron": "^3.0.11",
|
"@types/node-cron": "^3.0.11",
|
||||||
"@types/papaparse": "5.3.7",
|
"@types/papaparse": "5.3.7",
|
||||||
|
|||||||
82
packages/service/support/permission/app/auth.ts
Normal file
82
packages/service/support/permission/app/auth.ts
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
/* 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 ({
|
||||||
|
tmbId,
|
||||||
|
appId,
|
||||||
|
per
|
||||||
|
}: {
|
||||||
|
tmbId: string;
|
||||||
|
appId: string;
|
||||||
|
per: PermissionValueType;
|
||||||
|
}) => {
|
||||||
|
const { teamId, 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) === String(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 { tmbId } = result;
|
||||||
|
|
||||||
|
const { app } = await authAppByTmbId({
|
||||||
|
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
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -1,201 +0,0 @@
|
|||||||
import { AuthModeType } from '../type';
|
|
||||||
import { parseHeaderCert } from '../controller';
|
|
||||||
import { DatasetErrEnum } from '@fastgpt/global/common/error/code/dataset';
|
|
||||||
import { MongoDataset } from '../../../core/dataset/schema';
|
|
||||||
import { getCollectionWithDataset } from '../../../core/dataset/controller';
|
|
||||||
import { PermissionTypeEnum } from '@fastgpt/global/support/permission/constant';
|
|
||||||
import { TeamMemberRoleEnum } from '@fastgpt/global/support/user/team/constant';
|
|
||||||
import { AuthResponseType } from '@fastgpt/global/support/permission/type';
|
|
||||||
import {
|
|
||||||
CollectionWithDatasetType,
|
|
||||||
DatasetFileSchema,
|
|
||||||
DatasetSchemaType
|
|
||||||
} from '@fastgpt/global/core/dataset/type';
|
|
||||||
import { getFileById } from '../../../common/file/gridfs/controller';
|
|
||||||
import { BucketNameEnum } from '@fastgpt/global/common/file/constants';
|
|
||||||
import { getTmbInfoByTmbId } from '../../user/team/controller';
|
|
||||||
import { CommonErrEnum } from '@fastgpt/global/common/error/code/common';
|
|
||||||
import { MongoDatasetCollection } from '../../../core/dataset/collection/schema';
|
|
||||||
|
|
||||||
export async function authDatasetByTmbId({
|
|
||||||
teamId,
|
|
||||||
tmbId,
|
|
||||||
datasetId,
|
|
||||||
per
|
|
||||||
}: {
|
|
||||||
teamId: string;
|
|
||||||
tmbId: string;
|
|
||||||
datasetId: string;
|
|
||||||
per: AuthModeType['per'];
|
|
||||||
}) {
|
|
||||||
const { role } = await getTmbInfoByTmbId({ tmbId });
|
|
||||||
|
|
||||||
const { dataset, isOwner, canWrite } = await (async () => {
|
|
||||||
const dataset = await MongoDataset.findOne({ _id: datasetId, teamId }).lean();
|
|
||||||
|
|
||||||
if (!dataset) {
|
|
||||||
return Promise.reject(DatasetErrEnum.unAuthDataset);
|
|
||||||
}
|
|
||||||
|
|
||||||
const isOwner =
|
|
||||||
role !== TeamMemberRoleEnum.visitor &&
|
|
||||||
(String(dataset.tmbId) === tmbId || role === TeamMemberRoleEnum.owner);
|
|
||||||
const canWrite =
|
|
||||||
isOwner ||
|
|
||||||
(role !== TeamMemberRoleEnum.visitor && dataset.permission === PermissionTypeEnum.public);
|
|
||||||
if (per === 'r') {
|
|
||||||
if (!isOwner && dataset.permission !== PermissionTypeEnum.public) {
|
|
||||||
return Promise.reject(DatasetErrEnum.unAuthDataset);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (per === 'w' && !canWrite) {
|
|
||||||
return Promise.reject(DatasetErrEnum.unAuthDataset);
|
|
||||||
}
|
|
||||||
if (per === 'owner' && !isOwner) {
|
|
||||||
return Promise.reject(DatasetErrEnum.unAuthDataset);
|
|
||||||
}
|
|
||||||
|
|
||||||
return { dataset, isOwner, canWrite };
|
|
||||||
})();
|
|
||||||
|
|
||||||
return {
|
|
||||||
dataset,
|
|
||||||
isOwner,
|
|
||||||
canWrite
|
|
||||||
};
|
|
||||||
}
|
|
||||||
export async function authDataset({
|
|
||||||
datasetId,
|
|
||||||
per = 'owner',
|
|
||||||
...props
|
|
||||||
}: AuthModeType & {
|
|
||||||
datasetId: string;
|
|
||||||
}): Promise<
|
|
||||||
AuthResponseType & {
|
|
||||||
dataset: DatasetSchemaType;
|
|
||||||
}
|
|
||||||
> {
|
|
||||||
const result = await parseHeaderCert(props);
|
|
||||||
const { teamId, tmbId } = result;
|
|
||||||
const { dataset, isOwner, canWrite } = await authDatasetByTmbId({
|
|
||||||
teamId,
|
|
||||||
tmbId,
|
|
||||||
datasetId,
|
|
||||||
per
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
|
||||||
...result,
|
|
||||||
dataset,
|
|
||||||
isOwner,
|
|
||||||
canWrite
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
Read: in team and dataset permission is public
|
|
||||||
Write: in team, not visitor and dataset permission is public
|
|
||||||
*/
|
|
||||||
export async function authDatasetCollection({
|
|
||||||
collectionId,
|
|
||||||
per = 'owner',
|
|
||||||
...props
|
|
||||||
}: AuthModeType & {
|
|
||||||
collectionId: string;
|
|
||||||
}): Promise<
|
|
||||||
AuthResponseType & {
|
|
||||||
collection: CollectionWithDatasetType;
|
|
||||||
}
|
|
||||||
> {
|
|
||||||
const { teamId, tmbId } = await parseHeaderCert(props);
|
|
||||||
const { role } = await getTmbInfoByTmbId({ tmbId });
|
|
||||||
|
|
||||||
const { collection, isOwner, canWrite } = await (async () => {
|
|
||||||
const collection = await getCollectionWithDataset(collectionId);
|
|
||||||
|
|
||||||
if (!collection || String(collection.teamId) !== teamId) {
|
|
||||||
return Promise.reject(DatasetErrEnum.unAuthDatasetCollection);
|
|
||||||
}
|
|
||||||
|
|
||||||
const isOwner = String(collection.tmbId) === tmbId || role === TeamMemberRoleEnum.owner;
|
|
||||||
const canWrite =
|
|
||||||
isOwner ||
|
|
||||||
(role !== TeamMemberRoleEnum.visitor &&
|
|
||||||
collection.datasetId.permission === PermissionTypeEnum.public);
|
|
||||||
|
|
||||||
if (per === 'r') {
|
|
||||||
if (!isOwner && collection.datasetId.permission !== PermissionTypeEnum.public) {
|
|
||||||
return Promise.reject(DatasetErrEnum.unAuthDatasetCollection);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (per === 'w' && !canWrite) {
|
|
||||||
return Promise.reject(DatasetErrEnum.unAuthDatasetCollection);
|
|
||||||
}
|
|
||||||
if (per === 'owner' && !isOwner) {
|
|
||||||
return Promise.reject(DatasetErrEnum.unAuthDatasetCollection);
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
collection,
|
|
||||||
isOwner,
|
|
||||||
canWrite
|
|
||||||
};
|
|
||||||
})();
|
|
||||||
|
|
||||||
return {
|
|
||||||
teamId,
|
|
||||||
tmbId,
|
|
||||||
collection,
|
|
||||||
isOwner,
|
|
||||||
canWrite
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function authDatasetFile({
|
|
||||||
fileId,
|
|
||||||
per = 'owner',
|
|
||||||
...props
|
|
||||||
}: AuthModeType & {
|
|
||||||
fileId: string;
|
|
||||||
}): Promise<
|
|
||||||
AuthResponseType & {
|
|
||||||
file: DatasetFileSchema;
|
|
||||||
}
|
|
||||||
> {
|
|
||||||
const { teamId, tmbId } = await parseHeaderCert(props);
|
|
||||||
|
|
||||||
const [file, collection] = await Promise.all([
|
|
||||||
getFileById({ bucketName: BucketNameEnum.dataset, fileId }),
|
|
||||||
MongoDatasetCollection.findOne({
|
|
||||||
teamId,
|
|
||||||
fileId
|
|
||||||
})
|
|
||||||
]);
|
|
||||||
|
|
||||||
if (!file) {
|
|
||||||
return Promise.reject(CommonErrEnum.fileNotFound);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!collection) {
|
|
||||||
return Promise.reject(DatasetErrEnum.unAuthDatasetFile);
|
|
||||||
}
|
|
||||||
|
|
||||||
// file role = collection role
|
|
||||||
try {
|
|
||||||
const { isOwner, canWrite } = await authDatasetCollection({
|
|
||||||
...props,
|
|
||||||
collectionId: collection._id,
|
|
||||||
per
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
|
||||||
teamId,
|
|
||||||
tmbId,
|
|
||||||
file,
|
|
||||||
isOwner,
|
|
||||||
canWrite
|
|
||||||
};
|
|
||||||
} catch (error) {
|
|
||||||
return Promise.reject(DatasetErrEnum.unAuthDatasetFile);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,16 +1,18 @@
|
|||||||
import { AuthResponseType } from '@fastgpt/global/support/permission/type';
|
|
||||||
import { AuthModeType } from '../type';
|
import { AuthModeType } from '../type';
|
||||||
import { DatasetFileSchema } from '@fastgpt/global/core/dataset/type';
|
import { DatasetFileSchema } from '@fastgpt/global/core/dataset/type';
|
||||||
import { parseHeaderCert } from '../controller';
|
import { parseHeaderCert } from '../controller';
|
||||||
import { getFileById } from '../../../common/file/gridfs/controller';
|
import { getFileById } from '../../../common/file/gridfs/controller';
|
||||||
import { BucketNameEnum } from '@fastgpt/global/common/file/constants';
|
import { BucketNameEnum } from '@fastgpt/global/common/file/constants';
|
||||||
import { CommonErrEnum } from '@fastgpt/global/common/error/code/common';
|
import { CommonErrEnum } from '@fastgpt/global/common/error/code/common';
|
||||||
|
import { OwnerPermissionVal, ReadPermissionVal } from '@fastgpt/global/support/permission/constant';
|
||||||
|
import { AuthPropsType, AuthResponseType } from '../type/auth';
|
||||||
|
import { Permission } from '@fastgpt/global/support/permission/controller';
|
||||||
|
|
||||||
export async function authFile({
|
export async function authFile({
|
||||||
fileId,
|
fileId,
|
||||||
per = 'owner',
|
per = OwnerPermissionVal,
|
||||||
...props
|
...props
|
||||||
}: AuthModeType & {
|
}: AuthPropsType & {
|
||||||
fileId: string;
|
fileId: string;
|
||||||
}): Promise<
|
}): Promise<
|
||||||
AuthResponseType & {
|
AuthResponseType & {
|
||||||
@@ -29,14 +31,19 @@ export async function authFile({
|
|||||||
if (file.metadata?.teamId !== teamId) {
|
if (file.metadata?.teamId !== teamId) {
|
||||||
return Promise.reject(CommonErrEnum.unAuthFile);
|
return Promise.reject(CommonErrEnum.unAuthFile);
|
||||||
}
|
}
|
||||||
if (per === 'owner' && file.metadata?.tmbId !== tmbId) {
|
|
||||||
|
const permission = new Permission({
|
||||||
|
per: ReadPermissionVal,
|
||||||
|
isOwner: file.metadata?.tmbId === tmbId
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!permission.checkPer(per)) {
|
||||||
return Promise.reject(CommonErrEnum.unAuthFile);
|
return Promise.reject(CommonErrEnum.unAuthFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...authRes,
|
...authRes,
|
||||||
isOwner: per === 'owner',
|
permission,
|
||||||
canWrite: per === 'owner',
|
|
||||||
file
|
file
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,84 +0,0 @@
|
|||||||
import { AuthResponseType } from '@fastgpt/global/support/permission/type';
|
|
||||||
import { AuthModeType } from '../type';
|
|
||||||
import { parseHeaderCert } from '../controller';
|
|
||||||
import { getTmbInfoByTmbId } from '../../user/team/controller';
|
|
||||||
import { TeamMemberRoleEnum } from '@fastgpt/global/support/user/team/constant';
|
|
||||||
import { MongoPlugin } from '../../../core/plugin/schema';
|
|
||||||
import { PluginErrEnum } from '@fastgpt/global/common/error/code/plugin';
|
|
||||||
import { PluginItemSchema } from '@fastgpt/global/core/plugin/type';
|
|
||||||
import { splitCombinePluginId } from '../../../core/plugin/controller';
|
|
||||||
import { PluginSourceEnum } from '@fastgpt/global/core/plugin/constants';
|
|
||||||
|
|
||||||
export async function authPluginCrud({
|
|
||||||
pluginId,
|
|
||||||
per = 'owner',
|
|
||||||
...props
|
|
||||||
}: AuthModeType & {
|
|
||||||
pluginId: string;
|
|
||||||
}): Promise<
|
|
||||||
AuthResponseType & {
|
|
||||||
plugin: PluginItemSchema;
|
|
||||||
}
|
|
||||||
> {
|
|
||||||
const result = await parseHeaderCert(props);
|
|
||||||
const { tmbId, teamId } = result;
|
|
||||||
|
|
||||||
const { role } = await getTmbInfoByTmbId({ tmbId });
|
|
||||||
|
|
||||||
const { plugin, isOwner, canWrite } = await (async () => {
|
|
||||||
const plugin = await MongoPlugin.findOne({ _id: pluginId, teamId });
|
|
||||||
|
|
||||||
if (!plugin) {
|
|
||||||
throw new Error(PluginErrEnum.unExist);
|
|
||||||
}
|
|
||||||
|
|
||||||
const isOwner = String(plugin.tmbId) === tmbId || role === TeamMemberRoleEnum.owner;
|
|
||||||
const canWrite = isOwner;
|
|
||||||
|
|
||||||
if (per === 'w' && !canWrite) {
|
|
||||||
return Promise.reject(PluginErrEnum.unAuth);
|
|
||||||
}
|
|
||||||
if (per === 'owner' && !isOwner) {
|
|
||||||
return Promise.reject(PluginErrEnum.unAuth);
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
plugin,
|
|
||||||
isOwner,
|
|
||||||
canWrite
|
|
||||||
};
|
|
||||||
})();
|
|
||||||
|
|
||||||
return {
|
|
||||||
...result,
|
|
||||||
plugin,
|
|
||||||
isOwner,
|
|
||||||
canWrite
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function authPluginCanUse({
|
|
||||||
id,
|
|
||||||
teamId,
|
|
||||||
tmbId
|
|
||||||
}: {
|
|
||||||
id: string;
|
|
||||||
teamId: string;
|
|
||||||
tmbId: string;
|
|
||||||
}) {
|
|
||||||
const { source, pluginId } = await splitCombinePluginId(id);
|
|
||||||
|
|
||||||
if (source === PluginSourceEnum.community) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (source === PluginSourceEnum.personal) {
|
|
||||||
const { role } = await getTmbInfoByTmbId({ tmbId });
|
|
||||||
const plugin = await MongoPlugin.findOne({ _id: pluginId, teamId });
|
|
||||||
if (!plugin) {
|
|
||||||
return Promise.reject(PluginErrEnum.unExist);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
@@ -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 jwt from 'jsonwebtoken';
|
||||||
import { NextApiResponse } from 'next';
|
import { NextApiResponse } from 'next';
|
||||||
import type { AuthModeType, ReqHeaderAuthType } from './type.d';
|
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 { authOpenApiKey } from '../openapi/auth';
|
||||||
import { FileTokenQuery } from '@fastgpt/global/common/file/type';
|
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 */
|
/* create token */
|
||||||
export function createJWT(user: { _id?: string; team?: { teamId?: string; tmbId: string } }) {
|
export function createJWT(user: { _id?: string; team?: { teamId?: string; tmbId: string } }) {
|
||||||
const key = process.env.TOKEN_KEY as string;
|
const key = process.env.TOKEN_KEY as string;
|
||||||
|
|||||||
215
packages/service/support/permission/dataset/auth.ts
Normal file
215
packages/service/support/permission/dataset/auth.ts
Normal file
@@ -0,0 +1,215 @@
|
|||||||
|
import { PermissionValueType } from '@fastgpt/global/support/permission/type';
|
||||||
|
import { getResourcePermission, parseHeaderCert } from '../controller';
|
||||||
|
import { AuthPropsType, AuthResponseType } from '../type/auth';
|
||||||
|
import {
|
||||||
|
CollectionWithDatasetType,
|
||||||
|
DatasetDataItemType,
|
||||||
|
DatasetFileSchema,
|
||||||
|
DatasetSchemaType
|
||||||
|
} from '@fastgpt/global/core/dataset/type';
|
||||||
|
import { getTmbInfoByTmbId } from '../../user/team/controller';
|
||||||
|
import { MongoDataset } from '../../../core/dataset/schema';
|
||||||
|
import { PerResourceTypeEnum } from '@fastgpt/global/support/permission/constant';
|
||||||
|
import { DatasetErrEnum } from '@fastgpt/global/common/error/code/dataset';
|
||||||
|
import { DatasetPermission } from '@fastgpt/global/support/permission/dataset/controller';
|
||||||
|
import { getCollectionWithDataset } from '../../../core/dataset/controller';
|
||||||
|
import { MongoDatasetCollection } from '../../../core/dataset/collection/schema';
|
||||||
|
import { getFileById } from '../../../common/file/gridfs/controller';
|
||||||
|
import { BucketNameEnum } from '@fastgpt/global/common/file/constants';
|
||||||
|
import { CommonErrEnum } from '@fastgpt/global/common/error/code/common';
|
||||||
|
import { MongoDatasetData } from '../../../core/dataset/data/schema';
|
||||||
|
import { DatasetDefaultPermissionVal } from '@fastgpt/global/support/permission/dataset/constant';
|
||||||
|
|
||||||
|
export async function authDatasetByTmbId({
|
||||||
|
tmbId,
|
||||||
|
datasetId,
|
||||||
|
per
|
||||||
|
}: {
|
||||||
|
tmbId: string;
|
||||||
|
datasetId: string;
|
||||||
|
per: PermissionValueType;
|
||||||
|
}) {
|
||||||
|
const { teamId, permission: tmbPer } = await getTmbInfoByTmbId({ tmbId });
|
||||||
|
|
||||||
|
const dataset = await (async () => {
|
||||||
|
// get app and per
|
||||||
|
const [dataset, rp] = await Promise.all([
|
||||||
|
MongoDataset.findOne({ _id: datasetId, teamId }).lean(),
|
||||||
|
getResourcePermission({
|
||||||
|
teamId,
|
||||||
|
tmbId,
|
||||||
|
resourceId: datasetId,
|
||||||
|
resourceType: PerResourceTypeEnum.dataset
|
||||||
|
}) // this could be null
|
||||||
|
]);
|
||||||
|
|
||||||
|
if (!dataset) {
|
||||||
|
return Promise.reject(DatasetErrEnum.unExist);
|
||||||
|
}
|
||||||
|
|
||||||
|
const isOwner = tmbPer.isOwner || String(dataset.tmbId) === String(tmbId);
|
||||||
|
const Per = new DatasetPermission({
|
||||||
|
per: rp?.permission ?? dataset.defaultPermission,
|
||||||
|
isOwner
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!Per.checkPer(per)) {
|
||||||
|
return Promise.reject(DatasetErrEnum.unAuthDataset);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
...dataset,
|
||||||
|
defaultPermission: dataset.defaultPermission ?? DatasetDefaultPermissionVal,
|
||||||
|
permission: Per
|
||||||
|
};
|
||||||
|
})();
|
||||||
|
|
||||||
|
return { dataset };
|
||||||
|
}
|
||||||
|
|
||||||
|
// Auth Dataset
|
||||||
|
export async function authDataset({
|
||||||
|
datasetId,
|
||||||
|
per,
|
||||||
|
...props
|
||||||
|
}: AuthPropsType & {
|
||||||
|
datasetId: string;
|
||||||
|
}): Promise<
|
||||||
|
AuthResponseType<DatasetPermission> & {
|
||||||
|
dataset: DatasetSchemaType;
|
||||||
|
}
|
||||||
|
> {
|
||||||
|
const { teamId, tmbId } = await parseHeaderCert(props);
|
||||||
|
|
||||||
|
const { dataset } = await authDatasetByTmbId({
|
||||||
|
tmbId,
|
||||||
|
datasetId,
|
||||||
|
per
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
teamId,
|
||||||
|
tmbId,
|
||||||
|
dataset,
|
||||||
|
permission: dataset.permission
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// the temporary solution for authDatasetCollection is getting the
|
||||||
|
export async function authDatasetCollection({
|
||||||
|
collectionId,
|
||||||
|
per,
|
||||||
|
...props
|
||||||
|
}: AuthPropsType & {
|
||||||
|
collectionId: string;
|
||||||
|
}): Promise<
|
||||||
|
AuthResponseType<DatasetPermission> & {
|
||||||
|
collection: CollectionWithDatasetType;
|
||||||
|
}
|
||||||
|
> {
|
||||||
|
const { teamId, tmbId } = await parseHeaderCert(props);
|
||||||
|
const collection = await getCollectionWithDataset(collectionId);
|
||||||
|
|
||||||
|
if (!collection) {
|
||||||
|
return Promise.reject(DatasetErrEnum.unExist);
|
||||||
|
}
|
||||||
|
|
||||||
|
const { dataset } = await authDatasetByTmbId({
|
||||||
|
tmbId,
|
||||||
|
datasetId: collection.datasetId._id,
|
||||||
|
per
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
teamId,
|
||||||
|
tmbId,
|
||||||
|
collection,
|
||||||
|
permission: dataset.permission
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function authDatasetFile({
|
||||||
|
fileId,
|
||||||
|
per,
|
||||||
|
...props
|
||||||
|
}: AuthPropsType & {
|
||||||
|
fileId: string;
|
||||||
|
}): Promise<
|
||||||
|
AuthResponseType<DatasetPermission> & {
|
||||||
|
file: DatasetFileSchema;
|
||||||
|
}
|
||||||
|
> {
|
||||||
|
const { teamId, tmbId } = await parseHeaderCert(props);
|
||||||
|
|
||||||
|
const [file, collection] = await Promise.all([
|
||||||
|
getFileById({ bucketName: BucketNameEnum.dataset, fileId }),
|
||||||
|
MongoDatasetCollection.findOne({
|
||||||
|
teamId,
|
||||||
|
fileId
|
||||||
|
})
|
||||||
|
]);
|
||||||
|
|
||||||
|
if (!file) {
|
||||||
|
return Promise.reject(CommonErrEnum.fileNotFound);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!collection) {
|
||||||
|
return Promise.reject(DatasetErrEnum.unAuthDatasetFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const { permission } = await authDatasetCollection({
|
||||||
|
...props,
|
||||||
|
collectionId: collection._id,
|
||||||
|
per
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
teamId,
|
||||||
|
tmbId,
|
||||||
|
file,
|
||||||
|
permission
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
return Promise.reject(DatasetErrEnum.unAuthDatasetFile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function authDatasetData({
|
||||||
|
dataId,
|
||||||
|
...props
|
||||||
|
}: AuthPropsType & {
|
||||||
|
dataId: string;
|
||||||
|
}) {
|
||||||
|
// get mongo dataset.data
|
||||||
|
const datasetData = await MongoDatasetData.findById(dataId);
|
||||||
|
|
||||||
|
if (!datasetData) {
|
||||||
|
return Promise.reject('core.dataset.error.Data not found');
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = await authDatasetCollection({
|
||||||
|
...props,
|
||||||
|
collectionId: datasetData.collectionId
|
||||||
|
});
|
||||||
|
|
||||||
|
const data: DatasetDataItemType = {
|
||||||
|
id: String(datasetData._id),
|
||||||
|
teamId: datasetData.teamId,
|
||||||
|
q: datasetData.q,
|
||||||
|
a: datasetData.a,
|
||||||
|
chunkIndex: datasetData.chunkIndex,
|
||||||
|
indexes: datasetData.indexes,
|
||||||
|
datasetId: String(datasetData.datasetId),
|
||||||
|
collectionId: String(datasetData.collectionId),
|
||||||
|
sourceName: result.collection.name || '',
|
||||||
|
sourceId: result.collection?.fileId || result.collection?.rawLink,
|
||||||
|
isOwner: String(datasetData.tmbId) === String(result.tmbId),
|
||||||
|
canWrite: result.permission.hasWritePer
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
...result,
|
||||||
|
datasetData: data
|
||||||
|
};
|
||||||
|
}
|
||||||
68
packages/service/support/permission/publish/authLink.ts
Normal file
68
packages/service/support/permission/publish/authLink.ts
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
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({
|
||||||
|
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;
|
|
||||||
}
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user